mirror of
https://github.com/jonasbark/swiftcontrol.git
synced 2026-02-18 00:17:40 +01:00
294 lines
9.6 KiB
Dart
294 lines
9.6 KiB
Dart
import 'package:dartx/dartx.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
|
import 'package:swift_control/gen/l10n.dart';
|
|
import 'package:swift_control/pages/button_edit.dart';
|
|
import 'package:swift_control/pages/markdown.dart';
|
|
import 'package:swift_control/utils/i18n_extension.dart';
|
|
import 'package:swift_control/utils/requirements/platform.dart';
|
|
import 'package:swift_control/widgets/ui/beta_pill.dart';
|
|
import 'package:swift_control/widgets/ui/small_progress_indicator.dart';
|
|
import 'package:swift_control/widgets/ui/toast.dart';
|
|
|
|
enum ConnectionMethodType {
|
|
bluetooth,
|
|
network,
|
|
openBikeControl,
|
|
local,
|
|
}
|
|
|
|
class ConnectionMethod extends StatefulWidget {
|
|
final String title;
|
|
final String description;
|
|
final String? instructionLink;
|
|
final ConnectionMethodType type;
|
|
final Widget? additionalChild;
|
|
final bool? isConnected;
|
|
final bool? isStarted;
|
|
final bool isEnabled;
|
|
final bool showTroubleshooting;
|
|
final List<PlatformRequirement> requirements;
|
|
final Function(bool) onChange;
|
|
|
|
const ConnectionMethod({
|
|
super.key,
|
|
required this.title,
|
|
required this.type,
|
|
required this.isEnabled,
|
|
this.additionalChild,
|
|
required this.description,
|
|
this.instructionLink,
|
|
this.showTroubleshooting = false,
|
|
required this.onChange,
|
|
required this.requirements,
|
|
this.isConnected,
|
|
this.isStarted,
|
|
});
|
|
|
|
@override
|
|
State<ConnectionMethod> createState() => _ConnectionMethodState();
|
|
}
|
|
|
|
class _ConnectionMethodState extends State<ConnectionMethod> with WidgetsBindingObserver {
|
|
@override
|
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
if (widget.requirements.isNotEmpty && widget.isEnabled) {
|
|
if (state == AppLifecycleState.resumed) {
|
|
_recheckRequirements();
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
super.dispose();
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addObserver(this);
|
|
if (widget.requirements.isNotEmpty && widget.isEnabled && widget.isStarted == false) {
|
|
Future.wait(widget.requirements.map((e) => e.getStatus())).then((states) {
|
|
final allDone = states.all((e) => e);
|
|
if (allDone && widget.isEnabled) {
|
|
widget.onChange(true);
|
|
} else if (!allDone && widget.isEnabled) {
|
|
widget.onChange(false);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SelectableCard(
|
|
onPressed: () {
|
|
if (kIsWeb) {
|
|
buildToast(context, title: 'Not Supported on Web :)');
|
|
} else if (widget.requirements.isEmpty) {
|
|
widget.onChange(!widget.isEnabled);
|
|
} else {
|
|
Future.wait(widget.requirements.map((e) => e.getStatus())).then((_) async {
|
|
final notDone = widget.requirements.filter((e) => !e.status).toList();
|
|
if (notDone.isEmpty) {
|
|
widget.onChange(!widget.isEnabled);
|
|
} else {
|
|
await openPermissionSheet(context, notDone);
|
|
_recheckRequirements();
|
|
setState(() {});
|
|
}
|
|
});
|
|
}
|
|
},
|
|
isActive: widget.isEnabled,
|
|
icon: widget.isEnabled ? Icons.check_box : Icons.check_box_outline_blank,
|
|
title: Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
spacing: 8,
|
|
children: [
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
spacing: 8,
|
|
children: [
|
|
PrimaryBadge(
|
|
trailing: widget.isStarted == true && (widget.isConnected == false)
|
|
? SizedBox(
|
|
width: 19,
|
|
height: 19,
|
|
child: SmallProgressIndicator(
|
|
color: Theme.of(context).colorScheme.primaryForeground,
|
|
),
|
|
)
|
|
: switch (widget.type) {
|
|
ConnectionMethodType.bluetooth => Icon(Icons.bluetooth),
|
|
ConnectionMethodType.network => Icon(Icons.wifi),
|
|
ConnectionMethodType.openBikeControl => Icon(Icons.directions_bike),
|
|
ConnectionMethodType.local => Icon(Icons.keyboard),
|
|
},
|
|
child: Text(widget.type.name.capitalize()),
|
|
),
|
|
if (widget.title == context.i18n.enablePairingProcess ||
|
|
widget.title == context.i18n.enableZwiftControllerBluetooth)
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 1.0),
|
|
child: BetaPill(),
|
|
),
|
|
],
|
|
),
|
|
Text(widget.title),
|
|
Text(
|
|
widget.description,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.normal,
|
|
),
|
|
),
|
|
if (widget.isEnabled) ?widget.additionalChild,
|
|
if (widget.instructionLink != null || widget.showTroubleshooting) SizedBox(height: 8),
|
|
if (widget.instructionLink != null)
|
|
Button(
|
|
style: widget.isEnabled && Theme.of(context).brightness == Brightness.light
|
|
? ButtonStyle.outline().withBorder(border: Border.all(color: Colors.gray.shade500))
|
|
: ButtonStyle.outline(),
|
|
leading: Icon(Icons.help_outline),
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (c) => MarkdownPage(assetPath: widget.instructionLink!)),
|
|
);
|
|
},
|
|
child: Text(AppLocalizations.of(context).instructions),
|
|
),
|
|
if (widget.showTroubleshooting && widget.instructionLink == null)
|
|
Button(
|
|
style: widget.isEnabled && Theme.of(context).brightness == Brightness.light
|
|
? ButtonStyle.outline().withBorder(border: Border.all(color: Colors.gray.shade500))
|
|
: ButtonStyle.outline(),
|
|
leading: Icon(Icons.help_outline),
|
|
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (_) => MarkdownPage(assetPath: 'TROUBLESHOOTING.md'),
|
|
),
|
|
);
|
|
},
|
|
child: Text(context.i18n.troubleshootingGuide),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
void _recheckRequirements() {
|
|
Future.wait(widget.requirements.map((e) => e.getStatus())).then((result) {
|
|
final allDone = result.every((e) => e);
|
|
|
|
if (context.mounted) {
|
|
widget.onChange(allDone);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
Future openPermissionSheet(BuildContext context, List<PlatformRequirement> notDone) {
|
|
return openSheet(
|
|
context: context,
|
|
draggable: true,
|
|
builder: (context) => _PermissionList(requirements: notDone),
|
|
position: OverlayPosition.bottom,
|
|
);
|
|
}
|
|
|
|
class _PermissionList extends StatefulWidget {
|
|
final List<PlatformRequirement> requirements;
|
|
const _PermissionList({super.key, required this.requirements});
|
|
|
|
@override
|
|
State<_PermissionList> createState() => _PermissionListState();
|
|
}
|
|
|
|
class _PermissionListState extends State<_PermissionList> with WidgetsBindingObserver {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
WidgetsBinding.instance.addObserver(this);
|
|
}
|
|
|
|
@override
|
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
if (widget.requirements.isNotEmpty) {
|
|
if (state == AppLifecycleState.resumed) {
|
|
Future.wait(widget.requirements.map((e) => e.getStatus())).then((_) {
|
|
final allDone = widget.requirements.every((e) => e.status);
|
|
if (allDone && context.mounted) {
|
|
closeSheet(context);
|
|
} else if (context.mounted) {
|
|
setState(() {});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
super.dispose();
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
padding: EdgeInsets.all(16),
|
|
height: 120 + widget.requirements.length * 70,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
spacing: 18,
|
|
children: [
|
|
Text(
|
|
context.i18n.theFollowingPermissionsRequired,
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
...widget.requirements.map(
|
|
(e) => Row(
|
|
children: [
|
|
Expanded(
|
|
child: Basic(
|
|
title: Text(e.name),
|
|
subtitle: e.description != null ? Text(e.description!) : null,
|
|
trailing: Button(
|
|
style: e.status ? ButtonStyle.secondary() : ButtonStyle.primary(),
|
|
onPressed: e.status
|
|
? null
|
|
: () {
|
|
e
|
|
.call(context, () {
|
|
setState(() {});
|
|
})
|
|
.then((_) {
|
|
setState(() {});
|
|
if (widget.requirements.all((e) => e.status)) {
|
|
closeSheet(context);
|
|
}
|
|
});
|
|
},
|
|
child: e.status ? Text(context.i18n.granted) : Text(context.i18n.grant),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|