split settings into tiles

This commit is contained in:
Fabian Freund
2026-01-10 18:28:27 +01:00
parent f84b8838e1
commit e6e3201059
3 changed files with 1663 additions and 1248 deletions
@@ -38,18 +38,11 @@ import 'package:weblibre/features/user/domain/repositories/engine_settings.dart'
import 'package:weblibre/utils/exit_app.dart'; import 'package:weblibre/utils/exit_app.dart';
import 'package:weblibre/utils/ui_helper.dart'; import 'package:weblibre/utils/ui_helper.dart';
class DeveloperSettingsScreen extends HookConsumerWidget { class DeveloperSettingsScreen extends StatelessWidget {
const DeveloperSettingsScreen(); const DeveloperSettingsScreen();
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context) {
final engineSettings = ref.watch(engineSettingsWithDefaultsProvider);
final userAgentTextController = useTextEditingController(
text: engineSettings.userAgent,
keys: [engineSettings.userAgent],
);
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Developer Settings')), appBar: AppBar(title: const Text('Developer Settings')),
body: FadingScroll( body: FadingScroll(
@@ -57,15 +50,43 @@ class DeveloperSettingsScreen extends HookConsumerWidget {
builder: (context, controller) { builder: (context, controller) {
return ListView( return ListView(
controller: controller, controller: controller,
children: [ padding: const EdgeInsets.symmetric(horizontal: 12.0),
SwitchListTile.adaptive( children: const [
_JavaScriptTile(),
_UserAgentTile(),
_EnterpriseRootsTile(),
_FingerprintProtectionTile(),
_ErrorLogsTile(),
_DartVmTile(),
_AddonCollectionTile(),
_ResetUITile(),
],
);
},
),
);
}
}
class _JavaScriptTile extends HookConsumerWidget {
const _JavaScriptTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final javascriptEnabled = ref.watch(
engineSettingsWithDefaultsProvider.select(
(s) => s.javascriptEnabled,
),
);
return SwitchListTile.adaptive(
title: const Text('Enable JavaScript'), title: const Text('Enable JavaScript'),
subtitle: const Text( subtitle: const Text(
'While turning off JavaScript can boost security, privacy, and speed, it may cause some sites to not work as intended.', 'While turning off JavaScript can boost security, privacy, and speed, it may cause some sites to not work as intended.',
), ),
// ignore: deprecated_member_use use this icon for now // ignore: deprecated_member_use use this icon for now
secondary: const Icon(MdiIcons.languageJavascript), secondary: const Icon(MdiIcons.languageJavascript),
value: engineSettings.javascriptEnabled, value: javascriptEnabled,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveEngineSettingsControllerProvider.notifier) .read(saveEngineSettingsControllerProvider.notifier)
@@ -74,8 +95,25 @@ class DeveloperSettingsScreen extends HookConsumerWidget {
currentSettings.copyWith.javascriptEnabled(value), currentSettings.copyWith.javascriptEnabled(value),
); );
}, },
), );
ListTile( }
}
class _UserAgentTile extends HookConsumerWidget {
const _UserAgentTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final userAgent = ref.watch(
engineSettingsWithDefaultsProvider.select((s) => s.userAgent),
);
final userAgentTextController = useTextEditingController(
text: userAgent,
keys: [userAgent],
);
return ListTile(
leading: const Icon(MdiIcons.cardAccountDetails), leading: const Icon(MdiIcons.cardAccountDetails),
title: TextField( title: TextField(
controller: userAgentTextController, controller: userAgentTextController,
@@ -88,8 +126,7 @@ class DeveloperSettingsScreen extends HookConsumerWidget {
await ref await ref
.read(saveEngineSettingsControllerProvider.notifier) .read(saveEngineSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => (currentSettings) => currentSettings.copyWith.userAgent(value),
currentSettings.copyWith.userAgent(value),
); );
if (context.mounted) { if (context.mounted) {
@@ -101,24 +138,46 @@ class DeveloperSettingsScreen extends HookConsumerWidget {
} }
}, },
), ),
);
}
}
class _EnterpriseRootsTile extends HookConsumerWidget {
const _EnterpriseRootsTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final enterpriseRootsEnabled = ref.watch(
engineSettingsWithDefaultsProvider.select(
(s) => s.enterpriseRootsEnabled,
), ),
SwitchListTile.adaptive( );
return SwitchListTile.adaptive(
title: const Text('Use third party CA certificates'), title: const Text('Use third party CA certificates'),
subtitle: const Text( subtitle: const Text(
'Allows the use of third party certificates from the Android CA store', 'Allows the use of third party certificates from the Android CA store',
), ),
secondary: const Icon(MdiIcons.certificate), secondary: const Icon(MdiIcons.certificate),
value: engineSettings.enterpriseRootsEnabled, value: enterpriseRootsEnabled,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveEngineSettingsControllerProvider.notifier) .read(saveEngineSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.enterpriseRootsEnabled(value), currentSettings.copyWith.enterpriseRootsEnabled(value),
); );
}, },
), );
ListTile( }
}
class _FingerprintProtectionTile extends StatelessWidget {
const _FingerprintProtectionTile();
@override
Widget build(BuildContext context) {
return ListTile(
title: const Text('Fingerprint Protection'), title: const Text('Fingerprint Protection'),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
vertical: 8.0, vertical: 8.0,
@@ -129,8 +188,16 @@ class DeveloperSettingsScreen extends HookConsumerWidget {
onTap: () async { onTap: () async {
await FingerprintSettingsRoute().push(context); await FingerprintSettingsRoute().push(context);
}, },
), );
CustomListTile( }
}
class _ErrorLogsTile extends StatelessWidget {
const _ErrorLogsTile();
@override
Widget build(BuildContext context) {
return CustomListTile(
title: 'Error Logs', title: 'Error Logs',
subtitle: 'Copy logs for issue reporting', subtitle: 'Copy logs for issue reporting',
prefix: Padding( prefix: Padding(
@@ -158,9 +225,18 @@ class DeveloperSettingsScreen extends HookConsumerWidget {
icon: const Icon(Icons.copy), icon: const Icon(Icons.copy),
label: const Text('Copy'), label: const Text('Copy'),
), ),
), );
if (kDebugMode) }
CustomListTile( }
class _DartVmTile extends StatelessWidget {
const _DartVmTile();
@override
Widget build(BuildContext context) {
if (!kDebugMode) return const SizedBox.shrink();
return CustomListTile(
title: 'Dart VM', title: 'Dart VM',
subtitle: 'Copy Dart VM service URL', subtitle: 'Copy Dart VM service URL',
prefix: Padding( prefix: Padding(
@@ -177,9 +253,7 @@ class DeveloperSettingsScreen extends HookConsumerWidget {
await Clipboard.setData( await Clipboard.setData(
ClipboardData( ClipboardData(
text: text: serviceProtocolInfo.serverUri?.toString() ?? 'Error',
serviceProtocolInfo.serverUri?.toString() ??
'Error',
), ),
); );
@@ -190,8 +264,16 @@ class DeveloperSettingsScreen extends HookConsumerWidget {
icon: const Icon(Icons.copy), icon: const Icon(Icons.copy),
label: const Text('Copy'), label: const Text('Copy'),
), ),
), );
ListTile( }
}
class _AddonCollectionTile extends StatelessWidget {
const _AddonCollectionTile();
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(MdiIcons.puzzle), leading: const Icon(MdiIcons.puzzle),
title: const Text('Custom Extension Collection'), title: const Text('Custom Extension Collection'),
subtitle: const Text( subtitle: const Text(
@@ -201,8 +283,16 @@ class DeveloperSettingsScreen extends HookConsumerWidget {
onTap: () async { onTap: () async {
await AddonCollectionRoute().push(context); await AddonCollectionRoute().push(context);
}, },
), );
CustomListTile( }
}
class _ResetUITile extends ConsumerWidget {
const _ResetUITile();
@override
Widget build(BuildContext context, WidgetRef ref) {
return CustomListTile(
title: 'Reset UI', title: 'Reset UI',
subtitle: 'Rebuild the entire browser UI', subtitle: 'Rebuild the entire browser UI',
prefix: Padding( prefix: Padding(
@@ -220,11 +310,6 @@ class DeveloperSettingsScreen extends HookConsumerWidget {
icon: const Icon(Icons.restore), icon: const Icon(Icons.restore),
label: const Text('Reset'), label: const Text('Reset'),
), ),
),
],
);
},
),
); );
} }
} }
@@ -35,13 +35,11 @@ import 'package:weblibre/features/user/domain/providers.dart';
import 'package:weblibre/features/user/domain/repositories/cache.dart'; import 'package:weblibre/features/user/domain/repositories/cache.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
class GeneralSettingsScreen extends HookConsumerWidget { class GeneralSettingsScreen extends StatelessWidget {
const GeneralSettingsScreen({super.key}); const GeneralSettingsScreen({super.key});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context) {
final generalSettings = ref.watch(generalSettingsWithDefaultsProvider);
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('General Settings')), appBar: AppBar(title: const Text('General Settings')),
body: FadingScroll( body: FadingScroll(
@@ -49,8 +47,44 @@ class GeneralSettingsScreen extends HookConsumerWidget {
builder: (context, controller) { builder: (context, controller) {
return ListView( return ListView(
controller: controller, controller: controller,
children: [ children: const [
Padding( _ThemeSection(),
_NewTabDefaultSection(),
_ExternalLinkHandlingSection(),
_DefaultSearchProviderSection(),
_AutocompleteProviderSection(),
_OnDeviceAiTile(),
_EnableReaderModeTile(),
_EnforceReaderModeTile(),
_ReaderModeInTabBarTile(),
_TabBarPositionSection(),
_ShowContextualTabBarTile(),
_ShowQuickTabSwitcherBarTile(),
_QuickTabSwitcherModeSection(),
_CreateChildTabsTile(),
_ShowExtensionShortcutTile(),
_AutoHideTabBarTile(),
_BottomSheetTabViewTile(),
_TabBarSwipeBehaviorSection(),
_IconCacheTile(),
],
);
},
),
);
}
}
class _ThemeSection extends HookConsumerWidget {
const _ThemeSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final themeMode = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.themeMode),
);
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -83,23 +117,35 @@ class GeneralSettingsScreen extends HookConsumerWidget {
label: Text('Dark'), label: Text('Dark'),
), ),
], ],
selected: {generalSettings.themeMode}, selected: {themeMode},
onSelectionChanged: (value) async { onSelectionChanged: (value) async {
await ref await ref
.read( .read(
saveGeneralSettingsControllerProvider.notifier, saveGeneralSettingsControllerProvider.notifier,
) )
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.themeMode(value.first), currentSettings.copyWith.themeMode(value.first),
); );
}, },
), ),
), ),
], ],
), ),
), );
Padding( }
}
class _NewTabDefaultSection extends HookConsumerWidget {
const _NewTabDefaultSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final defaultCreateTabType = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.defaultCreateTabType),
);
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -131,7 +177,7 @@ class GeneralSettingsScreen extends HookConsumerWidget {
icon: Icon(MdiIcons.tabUnselected), icon: Icon(MdiIcons.tabUnselected),
), ),
], ],
selected: {generalSettings.defaultCreateTabType}, selected: {defaultCreateTabType},
onSelectionChanged: (value) async { onSelectionChanged: (value) async {
await ref await ref
.read( .read(
@@ -142,11 +188,10 @@ class GeneralSettingsScreen extends HookConsumerWidget {
.defaultCreateTabType(value.first), .defaultCreateTabType(value.first),
); );
}, },
style: switch (generalSettings.defaultCreateTabType) { style: switch (defaultCreateTabType) {
TabType.regular => null, TabType.regular => null,
TabType.private => SegmentedButton.styleFrom( TabType.private => SegmentedButton.styleFrom(
selectedBackgroundColor: selectedBackgroundColor: AppColors.privateSelectionOverlay,
AppColors.privateSelectionOverlay,
), ),
TabType.child => null, TabType.child => null,
}, },
@@ -154,8 +199,20 @@ class GeneralSettingsScreen extends HookConsumerWidget {
), ),
], ],
), ),
), );
Padding( }
}
class _ExternalLinkHandlingSection extends HookConsumerWidget {
const _ExternalLinkHandlingSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabIntentOpenSetting = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.tabIntentOpenSetting),
);
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -192,7 +249,7 @@ class GeneralSettingsScreen extends HookConsumerWidget {
icon: Icon(MdiIcons.tabUnselected), icon: Icon(MdiIcons.tabUnselected),
), ),
], ],
selected: {generalSettings.tabIntentOpenSetting}, selected: {tabIntentOpenSetting},
onSelectionChanged: (value) async { onSelectionChanged: (value) async {
await ref await ref
.read( .read(
@@ -203,12 +260,10 @@ class GeneralSettingsScreen extends HookConsumerWidget {
.tabIntentOpenSetting(value.first), .tabIntentOpenSetting(value.first),
); );
}, },
style: switch (generalSettings.tabIntentOpenSetting) { style: switch (tabIntentOpenSetting) {
TabIntentOpenSetting.regular => null, TabIntentOpenSetting.regular => null,
TabIntentOpenSetting.private => TabIntentOpenSetting.private => SegmentedButton.styleFrom(
SegmentedButton.styleFrom( selectedBackgroundColor: AppColors.privateSelectionOverlay,
selectedBackgroundColor:
AppColors.privateSelectionOverlay,
), ),
TabIntentOpenSetting.ask => null, TabIntentOpenSetting.ask => null,
}, },
@@ -216,8 +271,16 @@ class GeneralSettingsScreen extends HookConsumerWidget {
), ),
], ],
), ),
), );
const Padding( }
}
class _DefaultSearchProviderSection extends StatelessWidget {
const _DefaultSearchProviderSection();
@override
Widget build(BuildContext context) {
return const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@@ -234,8 +297,22 @@ class GeneralSettingsScreen extends HookConsumerWidget {
), ),
], ],
), ),
), );
Padding( }
}
class _AutocompleteProviderSection extends HookConsumerWidget {
const _AutocompleteProviderSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final defaultSearchSuggestionsProvider = ref.watch(
generalSettingsWithDefaultsProvider
.select((s) => s.defaultSearchSuggestionsProvider),
);
final relatedBang = defaultSearchSuggestionsProvider.relatedBang;
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -252,20 +329,15 @@ class GeneralSettingsScreen extends HookConsumerWidget {
Padding( Padding(
padding: const EdgeInsets.only(left: 40), padding: const EdgeInsets.only(left: 40),
child: DropdownMenu<SearchSuggestionProviders>( child: DropdownMenu<SearchSuggestionProviders>(
initialSelection: initialSelection: defaultSearchSuggestionsProvider,
generalSettings.defaultSearchSuggestionsProvider,
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
prefixIconConstraints: BoxConstraints.tight( prefixIconConstraints: BoxConstraints.tight(
const Size.square(24), const Size.square(24),
), ),
), ),
width: double.infinity, width: double.infinity,
leadingIcon: generalSettings leadingIcon:
.defaultSearchSuggestionsProvider relatedBang.mapNotNull((trigger) => BangIcon(trigger: trigger)),
.relatedBang
.mapNotNull(
(trigger) => BangIcon(trigger: trigger),
),
dropdownMenuEntries: SearchSuggestionProviders.values dropdownMenuEntries: SearchSuggestionProviders.values
.map((provider) { .map((provider) {
return DropdownMenuEntry( return DropdownMenuEntry(
@@ -281,8 +353,7 @@ class GeneralSettingsScreen extends HookConsumerWidget {
if (value != null) { if (value != null) {
await ref await ref
.read( .read(
saveGeneralSettingsControllerProvider saveGeneralSettingsControllerProvider.notifier,
.notifier,
) )
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) => currentSettings.copyWith
@@ -294,30 +365,54 @@ class GeneralSettingsScreen extends HookConsumerWidget {
), ),
], ],
), ),
), );
SwitchListTile.adaptive( }
}
class _OnDeviceAiTile extends HookConsumerWidget {
const _OnDeviceAiTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final enableLocalAiFeatures = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.enableLocalAiFeatures),
);
return SwitchListTile.adaptive(
title: const Text('On Device AI'), title: const Text('On Device AI'),
subtitle: const Text( subtitle: const Text(
'Local on-device features including container topic and tab suggestions', 'Local on-device features including container topic and tab suggestions',
), ),
secondary: const Icon(MdiIcons.creation), secondary: const Icon(MdiIcons.creation),
value: generalSettings.enableLocalAiFeatures, value: enableLocalAiFeatures,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.enableLocalAiFeatures(value), currentSettings.copyWith.enableLocalAiFeatures(value),
); );
}, },
), );
SwitchListTile.adaptive( }
}
class _EnableReaderModeTile extends HookConsumerWidget {
const _EnableReaderModeTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final enableReadability = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.enableReadability),
);
return SwitchListTile.adaptive(
title: const Text('Enable Reader Mode'), title: const Text('Enable Reader Mode'),
subtitle: const Text( subtitle: const Text(
'Optional browser app bar tool that extracts and simplifies web pages for improved readability by removing ads, sidebars, and other non-essential elements.', 'Optional browser app bar tool that extracts and simplifies web pages for improved readability by removing ads, sidebars, and other non-essential elements.',
), ),
secondary: const Icon(MdiIcons.bookOpen), secondary: const Icon(MdiIcons.bookOpen),
value: generalSettings.enableReadability, value: enableReadability,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
@@ -326,36 +421,61 @@ class GeneralSettingsScreen extends HookConsumerWidget {
currentSettings.copyWith.enableReadability(value), currentSettings.copyWith.enableReadability(value),
); );
}, },
), );
SwitchListTile.adaptive( }
}
class _EnforceReaderModeTile extends HookConsumerWidget {
const _EnforceReaderModeTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final enableReadability = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.enableReadability),
);
final enforceReadability = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.enforceReadability),
);
return SwitchListTile.adaptive(
title: const Text('Enforce Reader Mode'), title: const Text('Enforce Reader Mode'),
subtitle: const Text( subtitle: const Text(
'Override readability probability of websites and always show Reader Mode capabilities even the site might not be compatible.', 'Override readability probability of websites and always show Reader Mode capabilities even the site might not be compatible.',
), ),
secondary: const Icon(MdiIcons.bookCheck), secondary: const Icon(MdiIcons.bookCheck),
value: value: enableReadability && enforceReadability,
generalSettings.enableReadability && onChanged: enableReadability
generalSettings.enforceReadability,
onChanged: generalSettings.enableReadability
? (value) async { ? (value) async {
await ref await ref
.read( .read(
saveGeneralSettingsControllerProvider.notifier, saveGeneralSettingsControllerProvider.notifier,
) )
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.enforceReadability(value), currentSettings.copyWith.enforceReadability(value),
); );
} }
: null, : null,
), );
SwitchListTile.adaptive( }
}
class _ReaderModeInTabBarTile extends HookConsumerWidget {
const _ReaderModeInTabBarTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabBarReaderView = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.tabBarReaderView),
);
return SwitchListTile.adaptive(
title: const Text('Reader Mode in Tab Bar'), title: const Text('Reader Mode in Tab Bar'),
subtitle: const Text( subtitle: const Text(
'Show reader mode button in the tab bar instead of only in the tab menu', 'Show reader mode button in the tab bar instead of only in the tab menu',
), ),
secondary: const Icon(MdiIcons.bookHeart), secondary: const Icon(MdiIcons.bookHeart),
value: generalSettings.tabBarReaderView, value: tabBarReaderView,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
@@ -364,8 +484,20 @@ class GeneralSettingsScreen extends HookConsumerWidget {
currentSettings.copyWith.tabBarReaderView(value), currentSettings.copyWith.tabBarReaderView(value),
); );
}, },
), );
Padding( }
}
class _TabBarPositionSection extends HookConsumerWidget {
const _TabBarPositionSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabBarPosition = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.tabBarPosition),
);
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -380,7 +512,7 @@ class GeneralSettingsScreen extends HookConsumerWidget {
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
), ),
RadioGroup( RadioGroup(
groupValue: generalSettings.tabBarPosition, groupValue: tabBarPosition,
onChanged: (value) async { onChanged: (value) async {
if (value != null) { if (value != null) {
await ref await ref
@@ -388,8 +520,8 @@ class GeneralSettingsScreen extends HookConsumerWidget {
saveGeneralSettingsControllerProvider.notifier, saveGeneralSettingsControllerProvider.notifier,
) )
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.tabBarPosition(value), currentSettings.copyWith.tabBarPosition(value),
); );
} }
}, },
@@ -412,31 +544,56 @@ class GeneralSettingsScreen extends HookConsumerWidget {
), ),
], ],
), ),
), );
}
}
SwitchListTile.adaptive( class _ShowContextualTabBarTile extends HookConsumerWidget {
const _ShowContextualTabBarTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabBarShowContextualBar = ref.watch(
generalSettingsWithDefaultsProvider
.select((s) => s.tabBarShowContextualBar),
);
return SwitchListTile.adaptive(
title: const Text('Show Contextual Tab Bar'), title: const Text('Show Contextual Tab Bar'),
subtitle: const Text( subtitle: const Text(
'Show additional bottom toolbar for navigation and actions', 'Show additional bottom toolbar for navigation and actions',
), ),
secondary: const Icon(MdiIcons.dockBottom), secondary: const Icon(MdiIcons.dockBottom),
value: generalSettings.tabBarShowContextualBar, value: tabBarShowContextualBar,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.tabBarShowContextualBar(value), currentSettings.copyWith.tabBarShowContextualBar(value),
); );
}, },
), );
SwitchListTile.adaptive( }
}
class _ShowQuickTabSwitcherBarTile extends HookConsumerWidget {
const _ShowQuickTabSwitcherBarTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabBarShowQuickTabSwitcherBar = ref.watch(
generalSettingsWithDefaultsProvider
.select((s) => s.tabBarShowQuickTabSwitcherBar),
);
return SwitchListTile.adaptive(
title: const Text('Show Quick Tab Switcher Bar'), title: const Text('Show Quick Tab Switcher Bar'),
subtitle: const Text( subtitle: const Text(
'Show additional toolbar to quickly switch to recently used tabs', 'Show additional toolbar to quickly switch to recently used tabs',
), ),
secondary: const Icon(MdiIcons.dockBottom), secondary: const Icon(MdiIcons.dockBottom),
value: generalSettings.tabBarShowQuickTabSwitcherBar, value: tabBarShowQuickTabSwitcherBar,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
@@ -445,8 +602,20 @@ class GeneralSettingsScreen extends HookConsumerWidget {
.tabBarShowQuickTabSwitcherBar(value), .tabBarShowQuickTabSwitcherBar(value),
); );
}, },
), );
Padding( }
}
class _QuickTabSwitcherModeSection extends HookConsumerWidget {
const _QuickTabSwitcherModeSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final quickTabSwitcherMode = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.quickTabSwitcherMode),
);
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -461,7 +630,7 @@ class GeneralSettingsScreen extends HookConsumerWidget {
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
), ),
RadioGroup( RadioGroup(
groupValue: generalSettings.quickTabSwitcherMode, groupValue: quickTabSwitcherMode,
onChanged: (value) async { onChanged: (value) async {
if (value != null) { if (value != null) {
await ref await ref
@@ -495,44 +664,80 @@ class GeneralSettingsScreen extends HookConsumerWidget {
), ),
], ],
), ),
), );
SwitchListTile.adaptive( }
}
class _CreateChildTabsTile extends HookConsumerWidget {
const _CreateChildTabsTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final createChildTabsOption = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.createChildTabsOption),
);
return SwitchListTile.adaptive(
title: const Text('Create Child Tabs'), title: const Text('Create Child Tabs'),
subtitle: const Text( subtitle: const Text(
'Display a button to create a child tab under the current tab (tree view only)', 'Display a button to create a child tab under the current tab (tree view only)',
), ),
secondary: const Icon(MdiIcons.fileTree), secondary: const Icon(MdiIcons.fileTree),
value: generalSettings.createChildTabsOption, value: createChildTabsOption,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.createChildTabsOption(value), currentSettings.copyWith.createChildTabsOption(value),
); );
}, },
), );
SwitchListTile.adaptive( }
}
class _ShowExtensionShortcutTile extends HookConsumerWidget {
const _ShowExtensionShortcutTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final showExtensionShortcut = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.showExtensionShortcut),
);
return SwitchListTile.adaptive(
title: const Text('Show Extension Shortcut'), title: const Text('Show Extension Shortcut'),
subtitle: const Text( subtitle: const Text(
'Display an extension menu directly on the tab bar', 'Display an extension menu directly on the tab bar',
), ),
secondary: const Icon(MdiIcons.puzzleHeart), secondary: const Icon(MdiIcons.puzzleHeart),
value: generalSettings.showExtensionShortcut, value: showExtensionShortcut,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.showExtensionShortcut(value), currentSettings.copyWith.showExtensionShortcut(value),
); );
}, },
), );
SwitchListTile.adaptive( }
}
class _AutoHideTabBarTile extends HookConsumerWidget {
const _AutoHideTabBarTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final autoHideTabBar = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.autoHideTabBar),
);
return SwitchListTile.adaptive(
title: const Text('Auto Hide Tab Bar'), title: const Text('Auto Hide Tab Bar'),
subtitle: const Text('Hide tab bar when scrolling'), subtitle: const Text('Hide tab bar when scrolling'),
secondary: const Icon(MdiIcons.folderHidden), secondary: const Icon(MdiIcons.folderHidden),
value: generalSettings.autoHideTabBar, value: autoHideTabBar,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
@@ -541,14 +746,26 @@ class GeneralSettingsScreen extends HookConsumerWidget {
currentSettings.copyWith.autoHideTabBar(value), currentSettings.copyWith.autoHideTabBar(value),
); );
}, },
), );
SwitchListTile.adaptive( }
}
class _BottomSheetTabViewTile extends HookConsumerWidget {
const _BottomSheetTabViewTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabViewBottomSheet = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.tabViewBottomSheet),
);
return SwitchListTile.adaptive(
title: const Text('Bottom Sheet Tab View'), title: const Text('Bottom Sheet Tab View'),
subtitle: const Text( subtitle: const Text(
'Display tabs in a bottom sheet instead of fullscreen', 'Display tabs in a bottom sheet instead of fullscreen',
), ),
secondary: const Icon(MdiIcons.dockBottom), secondary: const Icon(MdiIcons.dockBottom),
value: generalSettings.tabViewBottomSheet, value: tabViewBottomSheet,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
@@ -557,8 +774,20 @@ class GeneralSettingsScreen extends HookConsumerWidget {
currentSettings.copyWith.tabViewBottomSheet(value), currentSettings.copyWith.tabViewBottomSheet(value),
); );
}, },
), );
Padding( }
}
class _TabBarSwipeBehaviorSection extends HookConsumerWidget {
const _TabBarSwipeBehaviorSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabBarSwipeAction = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.tabBarSwipeAction),
);
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -573,7 +802,7 @@ class GeneralSettingsScreen extends HookConsumerWidget {
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
), ),
RadioGroup( RadioGroup(
groupValue: generalSettings.tabBarSwipeAction, groupValue: tabBarSwipeAction,
onChanged: (value) async { onChanged: (value) async {
if (value != null) { if (value != null) {
await ref await ref
@@ -581,8 +810,8 @@ class GeneralSettingsScreen extends HookConsumerWidget {
saveGeneralSettingsControllerProvider.notifier, saveGeneralSettingsControllerProvider.notifier,
) )
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.tabBarSwipeAction(value), currentSettings.copyWith.tabBarSwipeAction(value),
); );
} }
}, },
@@ -607,13 +836,17 @@ class GeneralSettingsScreen extends HookConsumerWidget {
), ),
], ],
), ),
), );
Consumer( }
builder: (context, ref, child) { }
class _IconCacheTile extends HookConsumerWidget {
const _IconCacheTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final size = ref.watch( final size = ref.watch(
iconCacheSizeMegabytesProvider.select( iconCacheSizeMegabytesProvider.select((value) => value.value),
(value) => value.value,
),
); );
return CustomListTile( return CustomListTile(
@@ -648,20 +881,11 @@ class GeneralSettingsScreen extends HookConsumerWidget {
), ),
suffix: FilledButton.icon( suffix: FilledButton.icon(
onPressed: () async { onPressed: () async {
await ref await ref.read(cacheRepositoryProvider.notifier).clearCache();
.read(cacheRepositoryProvider.notifier)
.clearCache();
}, },
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
label: const Text('Clear'), label: const Text('Clear'),
), ),
); );
},
),
],
);
},
),
);
} }
} }
@@ -31,14 +31,11 @@ import 'package:weblibre/features/user/data/models/general_settings.dart';
import 'package:weblibre/features/user/domain/repositories/engine_settings.dart'; import 'package:weblibre/features/user/domain/repositories/engine_settings.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
class WebEngineSettingsScreen extends HookConsumerWidget { class WebEngineSettingsScreen extends StatelessWidget {
const WebEngineSettingsScreen(); const WebEngineSettingsScreen();
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context) {
final generalSettings = ref.watch(generalSettingsWithDefaultsProvider);
final engineSettings = ref.watch(engineSettingsWithDefaultsProvider);
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Web Engine Settings')), appBar: AppBar(title: const Text('Web Engine Settings')),
body: FadingScroll( body: FadingScroll(
@@ -46,6 +43,40 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
builder: (context, controller) { builder: (context, controller) {
return ListView( return ListView(
controller: controller, controller: controller,
padding: const EdgeInsets.symmetric(horizontal: 12.0),
children: const [
_IncognitoModeSection(),
_DeleteBrowsingDataTile(),
_AutoClearHistorySection(),
_BrowserLanguagesTile(),
_GlobalPrivacyControlTile(),
_HttpsOnlyModeSection(),
_DnsTile(),
_TrackingProtectionSection(),
_BounceTrackingProtectionTile(),
_QueryParameterStrippingSection(),
_PdfViewerTile(),
_WebEngineHardeningTile(),
],
);
},
),
);
}
}
class _IncognitoModeSection extends HookConsumerWidget {
const _IncognitoModeSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final deleteBrowsingDataOnQuit = ref.watch(
generalSettingsWithDefaultsProvider.select(
(s) => s.deleteBrowsingDataOnQuit,
),
);
return Column(
children: [ children: [
SwitchListTile.adaptive( SwitchListTile.adaptive(
title: const Text('Incognito Mode'), title: const Text('Incognito Mode'),
@@ -53,7 +84,7 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
'Deletes selected browsing data upon app restart for enhanced privacy.', 'Deletes selected browsing data upon app restart for enhanced privacy.',
), ),
secondary: const Icon(MdiIcons.incognito), secondary: const Icon(MdiIcons.incognito),
value: generalSettings.deleteBrowsingDataOnQuit != null, value: deleteBrowsingDataOnQuit != null,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
@@ -68,8 +99,23 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
); );
}, },
), ),
if (generalSettings.deleteBrowsingDataOnQuit != null) if (deleteBrowsingDataOnQuit != null)
Padding( _DeleteBrowsingDataTypes(
selectedTypes: deleteBrowsingDataOnQuit,
),
],
);
}
}
class _DeleteBrowsingDataTypes extends HookConsumerWidget {
final Set<DeleteBrowsingDataType> selectedTypes;
const _DeleteBrowsingDataTypes({required this.selectedTypes});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -78,8 +124,7 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
children: [ children: [
for (final type in DeleteBrowsingDataType.values) for (final type in DeleteBrowsingDataType.values)
CheckboxListTile.adaptive( CheckboxListTile.adaptive(
value: generalSettings.deleteBrowsingDataOnQuit! value: selectedTypes.contains(type),
.contains(type),
controlAffinity: ListTileControlAffinity.leading, controlAffinity: ListTileControlAffinity.leading,
title: Text(type.title), title: Text(type.title),
subtitle: type.description.mapNotNull( subtitle: type.description.mapNotNull(
@@ -94,8 +139,7 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
await notifier.save( await notifier.save(
(currentSettings) => currentSettings.copyWith (currentSettings) => currentSettings.copyWith
.deleteBrowsingDataOnQuit({ .deleteBrowsingDataOnQuit({
...currentSettings ...currentSettings.deleteBrowsingDataOnQuit!,
.deleteBrowsingDataOnQuit!,
type, type,
}), }),
); );
@@ -104,8 +148,7 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
(currentSettings) => currentSettings.copyWith (currentSettings) => currentSettings.copyWith
.deleteBrowsingDataOnQuit( .deleteBrowsingDataOnQuit(
{ {
...currentSettings ...currentSettings.deleteBrowsingDataOnQuit!,
.deleteBrowsingDataOnQuit!,
}..remove(type), }..remove(type),
), ),
); );
@@ -114,8 +157,16 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
), ),
], ],
), ),
), );
ListTile( }
}
class _DeleteBrowsingDataTile extends StatelessWidget {
const _DeleteBrowsingDataTile();
@override
Widget build(BuildContext context) {
return ListTile(
title: const Text('Delete Browsing Data'), title: const Text('Delete Browsing Data'),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
vertical: 8.0, vertical: 8.0,
@@ -131,8 +182,22 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
}, },
); );
}, },
);
}
}
class _AutoClearHistorySection extends HookConsumerWidget {
const _AutoClearHistorySection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final historyAutoCleanInterval = ref.watch(
generalSettingsWithDefaultsProvider.select(
(s) => s.historyAutoCleanInterval,
), ),
Padding( );
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -151,9 +216,8 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
), ),
Padding( Padding(
padding: const EdgeInsets.only(left: 40.0), padding: const EdgeInsets.only(left: 40.0),
child: DropdownMenu( child: DropdownMenu<Duration>(
initialSelection: initialSelection: historyAutoCleanInterval,
generalSettings.historyAutoCleanInterval,
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
prefixIconConstraints: BoxConstraints.tight( prefixIconConstraints: BoxConstraints.tight(
const Size.square(24), const Size.square(24),
@@ -202,8 +266,16 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
), ),
], ],
), ),
), );
ListTile( }
}
class _BrowserLanguagesTile extends StatelessWidget {
const _BrowserLanguagesTile();
@override
Widget build(BuildContext context) {
return ListTile(
title: const Text('Browser Languages'), title: const Text('Browser Languages'),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
vertical: 8.0, vertical: 8.0,
@@ -214,21 +286,47 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
onTap: () async { onTap: () async {
await LocaleSettingsRoute().push(context); await LocaleSettingsRoute().push(context);
}, },
);
}
}
class _GlobalPrivacyControlTile extends HookConsumerWidget {
const _GlobalPrivacyControlTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final globalPrivacyControlEnabled = ref.watch(
engineSettingsWithDefaultsProvider.select(
(s) => s.globalPrivacyControlEnabled,
), ),
SwitchListTile.adaptive( );
return SwitchListTile.adaptive(
title: const Text('Global Privacy Control (GPC)'), title: const Text('Global Privacy Control (GPC)'),
secondary: const Icon(MdiIcons.incognitoCircleOff), secondary: const Icon(MdiIcons.incognitoCircleOff),
value: engineSettings.globalPrivacyControlEnabled, value: globalPrivacyControlEnabled,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveEngineSettingsControllerProvider.notifier) .read(saveEngineSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.globalPrivacyControlEnabled(value), currentSettings.copyWith.globalPrivacyControlEnabled(value),
); );
}, },
), );
Padding( }
}
class _HttpsOnlyModeSection extends HookConsumerWidget {
const _HttpsOnlyModeSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final httpsOnlyMode = ref.watch(
engineSettingsWithDefaultsProvider.select((s) => s.httpsOnlyMode),
);
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -258,23 +356,31 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
label: Text('Private mode only'), label: Text('Private mode only'),
), ),
], ],
selected: {engineSettings.httpsOnlyMode}, selected: {httpsOnlyMode},
onSelectionChanged: (value) async { onSelectionChanged: (value) async {
await ref await ref
.read( .read(
saveEngineSettingsControllerProvider.notifier, saveEngineSettingsControllerProvider.notifier,
) )
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.httpsOnlyMode(value.first), currentSettings.copyWith.httpsOnlyMode(value.first),
); );
}, },
), ),
), ),
], ],
), ),
), );
ListTile( }
}
class _DnsTile extends StatelessWidget {
const _DnsTile();
@override
Widget build(BuildContext context) {
return ListTile(
title: const Text('DNS over HTTPS'), title: const Text('DNS over HTTPS'),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
vertical: 8.0, vertical: 8.0,
@@ -285,8 +391,22 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
onTap: () async { onTap: () async {
await DohSettingsRoute().push(context); await DohSettingsRoute().push(context);
}, },
);
}
}
class _TrackingProtectionSection extends HookConsumerWidget {
const _TrackingProtectionSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final trackingProtectionPolicy = ref.watch(
engineSettingsWithDefaultsProvider.select(
(s) => s.trackingProtectionPolicy,
), ),
Padding( );
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -301,13 +421,13 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
), ),
RadioGroup( RadioGroup(
groupValue: engineSettings.trackingProtectionPolicy, groupValue: trackingProtectionPolicy,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveEngineSettingsControllerProvider.notifier) .read(saveEngineSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.trackingProtectionPolicy(value), currentSettings.copyWith.trackingProtectionPolicy(value),
); );
}, },
child: const Column( child: const Column(
@@ -342,77 +462,35 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
), ),
], ],
), ),
);
}
}
class _BounceTrackingProtectionTile extends HookConsumerWidget {
const _BounceTrackingProtectionTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final bounceTrackingProtectionMode = ref.watch(
engineSettingsWithDefaultsProvider.select(
(s) => s.contentBlocking.bounceTrackingProtectionMode,
), ),
// Padding( );
// padding: const EdgeInsets.symmetric(
// horizontal: 16.0, final isEnabled = switch (bounceTrackingProtectionMode) {
// vertical: 8, BounceTrackingProtectionMode.disabled => false,
// ), BounceTrackingProtectionMode.enabled => true,
// child: Column( BounceTrackingProtectionMode.enabledStandby => false,
// mainAxisSize: MainAxisSize.min, BounceTrackingProtectionMode.enabledDryRun => false,
// crossAxisAlignment: CrossAxisAlignment.start, };
// children: [
// const ListTile( return SwitchListTile.adaptive(
// title: Text('Cookie Banner Blocker'),
// leading: Icon(MdiIcons.cookieSettings),
// contentPadding: EdgeInsets.zero,
// ),
// CheckboxListTile.adaptive(
// value:
// engineSettings.cookieBannerHandlingMode ==
// CookieBannerHandlingMode.rejectAll,
// title: const Text('In normal browsing'),
// controlAffinity: ListTileControlAffinity.leading,
// onChanged: (value) async {
// await ref
// .read(saveEngineSettingsControllerProvider.notifier)
// .save(
// (currentSettings) => currentSettings.copyWith
// .cookieBannerHandlingMode(
// value!
// ? CookieBannerHandlingMode.rejectAll
// : CookieBannerHandlingMode.disabled,
// ),
// );
// },
// ),
// CheckboxListTile.adaptive(
// value:
// engineSettings
// .cookieBannerHandlingModePrivateBrowsing ==
// CookieBannerHandlingMode.rejectAll,
// title: const Text('In private browsing'),
// controlAffinity: ListTileControlAffinity.leading,
// onChanged: (value) async {
// await ref
// .read(saveEngineSettingsControllerProvider.notifier)
// .save(
// (currentSettings) => currentSettings.copyWith
// .cookieBannerHandlingModePrivateBrowsing(
// value!
// ? CookieBannerHandlingMode.rejectAll
// : CookieBannerHandlingMode.disabled,
// ),
// );
// },
// ),
// ],
// ),
// ),
SwitchListTile.adaptive(
title: const Text('Bounce Tracking Protection'), title: const Text('Bounce Tracking Protection'),
subtitle: const Text( subtitle: const Text(
'Blocks redirect trackers that collect data through intermediate URL redirects between websites', 'Blocks redirect trackers that collect data through intermediate URL redirects between websites',
), ),
secondary: const Icon(MdiIcons.securityNetwork), secondary: const Icon(MdiIcons.securityNetwork),
value: switch (engineSettings value: isEnabled,
.contentBlocking
.bounceTrackingProtectionMode) {
BounceTrackingProtectionMode.disabled => false,
BounceTrackingProtectionMode.enabled => true,
BounceTrackingProtectionMode.enabledStandby => false,
BounceTrackingProtectionMode.enabledDryRun => false,
},
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveEngineSettingsControllerProvider.notifier) .read(saveEngineSettingsControllerProvider.notifier)
@@ -425,8 +503,22 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
), ),
); );
}, },
);
}
}
class _QueryParameterStrippingSection extends HookConsumerWidget {
const _QueryParameterStrippingSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final queryParameterStripping = ref.watch(
engineSettingsWithDefaultsProvider.select(
(s) => s.queryParameterStripping,
), ),
Padding( );
return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
vertical: 8, vertical: 8,
@@ -459,7 +551,7 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
label: Text('Private mode only'), label: Text('Private mode only'),
), ),
], ],
selected: {engineSettings.queryParameterStripping}, selected: {queryParameterStripping},
onSelectionChanged: (value) async { onSelectionChanged: (value) async {
await ref await ref
.read( .read(
@@ -474,24 +566,43 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
), ),
], ],
), ),
), );
SwitchListTile.adaptive( }
}
class _PdfViewerTile extends HookConsumerWidget {
const _PdfViewerTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final enablePdfJs = ref.watch(
engineSettingsWithDefaultsProvider.select((s) => s.enablePdfJs),
);
return SwitchListTile.adaptive(
title: const Text('Built-in PDF Viewer'), title: const Text('Built-in PDF Viewer'),
subtitle: const Text( subtitle: const Text(
'Open PDF files directly in the browser without downloading', 'Open PDF files directly in the browser without downloading',
), ),
secondary: const Icon(MdiIcons.filePdfBox), secondary: const Icon(MdiIcons.filePdfBox),
value: engineSettings.enablePdfJs, value: enablePdfJs,
onChanged: (value) async { onChanged: (value) async {
await ref await ref
.read(saveEngineSettingsControllerProvider.notifier) .read(saveEngineSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => (currentSettings) => currentSettings.copyWith.enablePdfJs(value),
currentSettings.copyWith.enablePdfJs(value),
); );
}, },
), );
ListTile( }
}
class _WebEngineHardeningTile extends StatelessWidget {
const _WebEngineHardeningTile();
@override
Widget build(BuildContext context) {
return ListTile(
title: const Text('Web Engine Hardening'), title: const Text('Web Engine Hardening'),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
vertical: 8.0, vertical: 8.0,
@@ -502,11 +613,6 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
onTap: () async { onTap: () async {
await WebEngineHardeningRoute().push(context); await WebEngineHardeningRoute().push(context);
}, },
),
],
);
},
),
); );
} }
} }