import 'package:color_blindness/color_blindness.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:logging/logging.dart'; import '../controls/padding.dart'; import '../controls/responsive_max_width.dart'; import '../controls/standard_appbar.dart'; import '../globals.dart'; import '../riverpod_controllers/account_services.dart'; import '../riverpod_controllers/cache_clear_service.dart'; import '../riverpod_controllers/settings_services.dart'; import '../routes.dart'; import '../utils/known_network_extensions.dart'; import '../utils/theme_mode_extensions.dart'; class SettingsScreen extends ConsumerWidget { const SettingsScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: StandardAppBar.build(context, 'Settings'), body: Center( child: Padding( padding: const EdgeInsets.all(8.0), child: ResponsiveMaxWidth( child: ListView( children: const [ _BuildVersionWidget(), _LowBandwidthWidget(), _NetworkTimeoutWidget(), _NotificationGroupingWidget(), _SpoilerHidingWidget(), _ThemeWidget(), if (!kReleaseMode) _ColorBlindnessWidget(), _ClearCachesWidget(), _LogPanelWidget(), _NetworkCapabilitiesWidget(), ], ), ), ), ), ); } } class _NetworkTimeoutWidget extends ConsumerWidget { const _NetworkTimeoutWidget(); @override Widget build(BuildContext context, WidgetRef ref) { final timeout = ref.watch(friendicaApiTimeoutSettingProvider); return ListTile( title: const Text('Network Request Timeout (seconds)'), trailing: DropdownButton( value: timeout.inSeconds, items: List.generate(100, (i) { final timeout = (i + 1) * 10; return DropdownMenuItem(value: timeout, child: Text('$timeout')); }), onChanged: (value) { ref.read(friendicaApiTimeoutSettingProvider.notifier).value = Duration(seconds: value!); }), ); } } class _NetworkCapabilitiesWidget extends ConsumerWidget { const _NetworkCapabilitiesWidget(); @override Widget build(BuildContext context, WidgetRef ref) { final nc = ref.watch(networkCapabilitiesSettingProvider); final rows = []; for (int i = 0; i < nc.length; i++) { final e = nc[i]; final r = DataRow( cells: [ DataCell( Text( '${e.network.forkAwesomeUnicode} ${e.network.labelName}', style: const TextStyle(fontFamily: 'ForkAwesome'), ), ), DataCell( Checkbox( value: e.react, onChanged: (bool? value) { nc[i] = e.copyWith(react: value ?? false); ref.read(networkCapabilitiesSettingProvider.notifier).value = nc; }, ), ), DataCell( Checkbox( value: e.reshare, onChanged: (bool? value) { nc[i] = e.copyWith(reshare: value ?? false); ref.read(networkCapabilitiesSettingProvider.notifier).value = nc; }, ), ), DataCell( Checkbox( value: e.comment, onChanged: (bool? value) { nc[i] = e.copyWith(comment: value ?? false); ref.read(networkCapabilitiesSettingProvider.notifier).value = nc; }, ), ), ], ); rows.add(r); } return ListTile( title: const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Network Capabilities'), ElevatedButton( onPressed: null, child: Text('Reset to Defaults'), ), ], ), subtitle: SingleChildScrollView( scrollDirection: Axis.horizontal, child: DataTable( columnSpacing: 10.0, columns: const [ DataColumn( label: Text('Network'), ), DataColumn(label: Text('React')), DataColumn(label: Text('Reshare')), DataColumn(label: Text('Comment')), ], rows: rows, ), )); } } class _BuildVersionWidget extends StatelessWidget { const _BuildVersionWidget(); @override Widget build(BuildContext context) { return Center( child: Text( 'Relatica $appVersion', style: const TextStyle( decoration: TextDecoration.underline, fontWeight: FontWeight.bold, ), ), ); } } class _LowBandwidthWidget extends ConsumerWidget { const _LowBandwidthWidget(); @override Widget build(BuildContext context, WidgetRef ref) { final value = ref.watch(lowBandwidthModeSettingProvider); return ListTile( title: const Text('Low bandwidth mode'), trailing: Switch( onChanged: (value) { ref.read(lowBandwidthModeSettingProvider.notifier).value = value; }, value: value, ), ); } } class _NotificationGroupingWidget extends ConsumerWidget { const _NotificationGroupingWidget(); @override Widget build(BuildContext context, WidgetRef ref) { final notificationGrouping = ref.watch(notificationGroupingSettingProvider); return ListTile( title: const Text('Group notifications by type'), trailing: Switch( onChanged: (value) { ref.read(notificationGroupingSettingProvider.notifier).value = value; }, value: notificationGrouping, ), ); } } class _SpoilerHidingWidget extends ConsumerWidget { const _SpoilerHidingWidget(); @override Widget build(BuildContext context, WidgetRef ref) { final spoilingHidingEnabled = ref.watch(spoilerHidingSettingProvider); return ListTile( title: const Text('Spoiler/Content Warning Hiding'), trailing: Switch( onChanged: (value) { ref.read(spoilerHidingSettingProvider.notifier).value = value; }, value: spoilingHidingEnabled, ), ); } } class _ThemeWidget extends ConsumerWidget { const _ThemeWidget(); @override Widget build(BuildContext context, WidgetRef ref) { final themeMode = ref.watch(themeModeSettingProvider); return ListTile( title: const Text('Dark Mode Theme:'), trailing: DropdownButton( value: themeMode, items: ThemeMode.values .map((m) => DropdownMenuItem(value: m, child: Text(m.toLabel()))) .toList(), onChanged: (value) { if (value != null) { ref.read(themeModeSettingProvider.notifier).value = value; } }, ), ); } } class _ColorBlindnessWidget extends ConsumerWidget { const _ColorBlindnessWidget(); @override Widget build(BuildContext context, WidgetRef ref) { final colorBlindnessType = ref.watch(colorBlindnessTestingModeSettingProvider); return ListTile( title: const Text('Color Blindness Testing'), trailing: DropdownButton( value: colorBlindnessType, items: ColorBlindnessType.values .map((c) => DropdownMenuItem(value: c, child: Text(c.name))) .toList(), onChanged: (value) { ref.read(colorBlindnessTestingModeSettingProvider.notifier).value = value ?? ColorBlindnessType.none; }), ); } } class _ClearCachesWidget extends ConsumerWidget { const _ClearCachesWidget(); @override Widget build(BuildContext context, WidgetRef ref) { final profile = ref.watch(activeProfileProvider); return ListTile( title: const Text('Clear local caches'), subtitle: const Text('This does not affect server side data at all.'), trailing: ElevatedButton( onPressed: () async { final confirm = await showYesNoDialog(context, 'Are you sure you want to clear all local data for this account?'); if (confirm != true) { return; } ref .read(clearCachesProvider.notifier) .profileCacheClearService(profile); }, child: const Text('Clear'), ), ); } } class _LogPanelWidget extends ConsumerWidget { const _LogPanelWidget(); @override Widget build(BuildContext context, WidgetRef ref) { final logLevel = ref.watch(logLevelSettingProvider); return ListTile( title: Wrap( children: [ const Text('Log Level'), const HorizontalPadding(), ElevatedButton( onPressed: () => context.pushNamed(ScreenPaths.logViewer), child: const Text('Open Log Viewer'), ) ], ), trailing: DropdownButton( value: logLevel, items: Level.LEVELS .map((c) => DropdownMenuItem(value: c, child: Text(c.name))) .toList(), onChanged: (value) { ref.read(logLevelSettingProvider.notifier).value = value ?? Level.OFF; }), ); } }