fix formatting

This commit is contained in:
Fabian Freund
2026-01-13 18:08:23 +01:00
parent 33e3fdd111
commit fa6878d2b2
22 changed files with 243 additions and 321 deletions
+2 -2
View File
@@ -25,8 +25,8 @@ class EquatableImage {
bool _isDisposed = false; bool _isDisposed = false;
EquatableImage(Image value, {required int hash}) EquatableImage(Image value, {required int hash})
: _value = value, : _value = value,
_imageHash = hash; _imageHash = hash;
/// The underlying ui.Image. Returns null if disposed. /// The underlying ui.Image. Returns null if disposed.
Image? get value => _isDisposed ? null : _value; Image? get value => _isDisposed ? null : _value;
+1 -4
View File
@@ -90,10 +90,7 @@ class GenericWebsiteService extends _$GenericWebsiteService {
GenericWebsiteService() GenericWebsiteService()
: _iconsService = GeckoIconService(), : _iconsService = GeckoIconService(),
_browserIconCache = LRUCache( _browserIconCache = LRUCache(50, onEvict: (icon) => icon.image.dispose());
50,
onEvict: (icon) => icon.image.dispose(),
);
@override @override
void build() { void build() {
@@ -460,14 +460,17 @@ class TabRepository extends _$TabRepository {
.read(tabDataRepositoryProvider.notifier) .read(tabDataRepositoryProvider.notifier)
.getTabContainerData(tabState.id); .getTabContainerData(tabState.id);
final contextChanging = containerData.metadata.contextualIdentity != final contextChanging =
containerData.metadata.contextualIdentity !=
currentContainerData?.metadata.contextualIdentity; currentContainerData?.metadata.contextualIdentity;
await addTab( await addTab(
url: uri, url: uri,
private: tabState.isPrivate, private: tabState.isPrivate,
container: Value(containerData), container: Value(containerData),
parentId: contextChanging ? null : tabState.id, // Break parent chain if context changes parentId: contextChanging
? null
: tabState.id, // Break parent chain if context changes
selectTab: true, selectTab: true,
); );
@@ -31,7 +31,9 @@ part 'bookmarks.g.dart';
bool _isEmptyRootFolder(BookmarkFolder folder) { bool _isEmptyRootFolder(BookmarkFolder folder) {
if (folder.children == null) return true; if (folder.children == null) return true;
// A root folder is empty if it has no children, or only contains other root folders // A root folder is empty if it has no children, or only contains other root folders
return folder.children!.every((child) => bookmarkRootIds.contains(child.guid)); return folder.children!.every(
(child) => bookmarkRootIds.contains(child.guid),
);
} }
T? _selectChildRecursive<T extends BookmarkItem>( T? _selectChildRecursive<T extends BookmarkItem>(
@@ -152,10 +154,12 @@ AsyncValue<T?> bookmarks<T extends BookmarkItem>(
entryGuid == BookmarkRoot.root.id && entryGuid == BookmarkRoot.root.id &&
result is BookmarkFolder) { result is BookmarkFolder) {
final filteredChildren = result.children final filteredChildren = result.children
?.where((child) => ?.where(
child is! BookmarkFolder || (child) =>
child.guid == BookmarkRoot.mobile.id || child is! BookmarkFolder ||
!_isEmptyRootFolder(child)) child.guid == BookmarkRoot.mobile.id ||
!_isEmptyRootFolder(child),
)
.toList(); .toList();
result = result.copyWith.children(filteredChildren) as T; result = result.copyWith.children(filteredChildren) as T;
} }
@@ -187,8 +191,16 @@ class SeamlessBookmarks extends _$SeamlessBookmarks {
} }
@override @override
AsyncValue<BookmarkItem?> build(String entryGuid, {bool hideEmptyRoots = false}) { AsyncValue<BookmarkItem?> build(
final bookmarks = ref.watch(bookmarksProvider<BookmarkItem>(entryGuid, hideEmptyRoots: hideEmptyRoots)); String entryGuid, {
bool hideEmptyRoots = false,
}) {
final bookmarks = ref.watch(
bookmarksProvider<BookmarkItem>(
entryGuid,
hideEmptyRoots: hideEmptyRoots,
),
);
if (_hasSearch) { if (_hasSearch) {
final filterGuids = ref.watch(bookmarksSearchProvider); final filterGuids = ref.watch(bookmarksSearchProvider);
@@ -49,15 +49,30 @@ class BookmarkListScreen extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final treeKey = useMemoized(() => GlobalKey<TreeViewState>()); final treeKey = useMemoized(() => GlobalKey<TreeViewState>());
final hideEmptyRoots = useState(true); final hideEmptyRoots = useState(true);
final bookmarkList = ref.watch(seamlessBookmarksProvider(entryGuid, hideEmptyRoots: hideEmptyRoots.value)); final bookmarkList = ref.watch(
seamlessBookmarksProvider(
entryGuid,
hideEmptyRoots: hideEmptyRoots.value,
),
);
final textFilterEnabled = useState(false); final textFilterEnabled = useState(false);
final textFilterController = useTextEditingController(); final textFilterController = useTextEditingController();
useOnListenableChange(textFilterController, () { useOnListenableChange(textFilterController, () {
if (ref.exists(seamlessBookmarksProvider(entryGuid, hideEmptyRoots: hideEmptyRoots.value))) { if (ref.exists(
seamlessBookmarksProvider(
entryGuid,
hideEmptyRoots: hideEmptyRoots.value,
),
)) {
ref ref
.read(seamlessBookmarksProvider(entryGuid, hideEmptyRoots: hideEmptyRoots.value).notifier) .read(
seamlessBookmarksProvider(
entryGuid,
hideEmptyRoots: hideEmptyRoots.value,
).notifier,
)
.search(textFilterController.text); .search(textFilterController.text);
} }
}); });
@@ -109,12 +124,14 @@ class BookmarkListScreen extends HookConsumerWidget {
), ),
if (entryGuid == BookmarkRoot.root.id) if (entryGuid == BookmarkRoot.root.id)
MenuItemButton( MenuItemButton(
leadingIcon: Icon(hideEmptyRoots.value leadingIcon: Icon(
? MdiIcons.eyeOff hideEmptyRoots.value ? MdiIcons.eyeOff : MdiIcons.eye,
: MdiIcons.eye), ),
child: Text(hideEmptyRoots.value child: Text(
? 'Show Empty Root Folders' hideEmptyRoots.value
: 'Hide Empty Root Folders'), ? 'Show Empty Root Folders'
: 'Hide Empty Root Folders',
),
onPressed: () { onPressed: () {
hideEmptyRoots.value = !hideEmptyRoots.value; hideEmptyRoots.value = !hideEmptyRoots.value;
}, },
@@ -128,10 +128,7 @@ class BrowserBottomAppBar extends HookConsumerWidget {
color: Theme.of(context).colorScheme.surfaceContainer, color: Theme.of(context).colorScheme.surfaceContainer,
child: Padding( child: Padding(
padding: EdgeInsets.only(bottom: bottomPadding), padding: EdgeInsets.only(bottom: bottomPadding),
child: SizedBox( child: SizedBox(height: _size.height, child: _tabBar),
height: _size.height,
child: _tabBar,
),
), ),
); );
} }
@@ -319,7 +319,9 @@ class ListTabPreview extends HookConsumerWidget {
child: UriBreadcrumb( child: UriBreadcrumb(
uri: tabState.url, uri: tabState.url,
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: tabState.isPrivate ? colorScheme.onSurface : null, color: tabState.isPrivate
? colorScheme.onSurface
: null,
), ),
), ),
), ),
@@ -184,7 +184,9 @@ class ViewTabTreesWidget extends HookConsumerWidget {
searchPartition: TabSearchPartition.preview, searchPartition: TabSearchPartition.preview,
// ignore: document_ignores using fast equatable // ignore: document_ignores using fast equatable
// ignore: provider_parameters // ignore: provider_parameters
containerFilter: ContainerFilterById(containerId: containerId), containerFilter: ContainerFilterById(
containerId: containerId,
),
groupTrees: true, groupTrees: true,
), ),
); );
@@ -260,7 +260,8 @@ class TabDao extends DatabaseAccessor<TabDatabase> with $TabDaoMixin {
for (final state in next.values) { for (final state in next.values) {
final previousState = previous?[state.id]; final previousState = previous?[state.id];
if (previousState?.parentId != state.parentId && state.parentId != null) { if (previousState?.parentId != state.parentId &&
state.parentId != null) {
if (next.containsKey(state.parentId)) { if (next.containsKey(state.parentId)) {
// Parent exists in current state // Parent exists in current state
validatedParentIds[state.id] = state.parentId; validatedParentIds[state.id] = state.parentId;
@@ -272,8 +273,9 @@ class TabDao extends DatabaseAccessor<TabDatabase> with $TabDaoMixin {
} }
// Batch validate parent IDs that aren't in the current state // Batch validate parent IDs that aren't in the current state
final existingParentIds = final existingParentIds = await getExistingTabIds(
await getExistingTabIds(parentIdsToValidate).get().then((ids) => ids.toSet()); parentIdsToValidate,
).get().then((ids) => ids.toSet());
// Complete validation map // Complete validation map
for (final state in next.values) { for (final state in next.values) {
@@ -282,7 +284,8 @@ class TabDao extends DatabaseAccessor<TabDatabase> with $TabDaoMixin {
if (previousState?.parentId != state.parentId) { if (previousState?.parentId != state.parentId) {
if (!validatedParentIds.containsKey(state.id)) { if (!validatedParentIds.containsKey(state.id)) {
// This parent ID needed database validation // This parent ID needed database validation
if (state.parentId != null && existingParentIds.contains(state.parentId)) { if (state.parentId != null &&
existingParentIds.contains(state.parentId)) {
validatedParentIds[state.id] = state.parentId; validatedParentIds[state.id] = state.parentId;
} else { } else {
validatedParentIds[state.id] = null; validatedParentIds[state.id] = null;
@@ -51,17 +51,17 @@ class AdvancedSettingsScreen extends StatelessWidget {
child: FadingScroll( child: FadingScroll(
fadingSize: 25, fadingSize: 25,
builder: (context, controller) { builder: (context, controller) {
return ListView( return ListView(
controller: controller, controller: controller,
padding: const EdgeInsets.symmetric(horizontal: 12.0), padding: const EdgeInsets.symmetric(horizontal: 12.0),
children: const [ children: const [
_ContentBehaviorSection(), _ContentBehaviorSection(),
_StorageDebuggingSection(), _StorageDebuggingSection(),
_ResetSection(), _ResetSection(),
], ],
); );
}, },
), ),
), ),
); );
} }
@@ -119,9 +119,7 @@ class _JavaScriptTile extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final javascriptEnabled = ref.watch( final javascriptEnabled = ref.watch(
engineSettingsWithDefaultsProvider.select( engineSettingsWithDefaultsProvider.select((s) => s.javascriptEnabled),
(s) => s.javascriptEnabled,
),
); );
return SwitchListTile.adaptive( return SwitchListTile.adaptive(
@@ -111,10 +111,7 @@ class _ThemeSection extends HookConsumerWidget {
); );
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -146,9 +143,7 @@ class _ThemeSection extends HookConsumerWidget {
selected: {themeMode}, selected: {themeMode},
onSelectionChanged: (value) async { onSelectionChanged: (value) async {
await ref await ref
.read( .read(saveGeneralSettingsControllerProvider.notifier)
saveGeneralSettingsControllerProvider.notifier,
)
.save( .save(
(currentSettings) => (currentSettings) =>
currentSettings.copyWith.themeMode(value.first), currentSettings.copyWith.themeMode(value.first),
@@ -172,10 +167,7 @@ class _TabBarPositionSection extends HookConsumerWidget {
); );
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -190,9 +182,7 @@ class _TabBarPositionSection extends HookConsumerWidget {
onChanged: (value) async { onChanged: (value) async {
if (value != null) { if (value != null) {
await ref await ref
.read( .read(saveGeneralSettingsControllerProvider.notifier)
saveGeneralSettingsControllerProvider.notifier,
)
.save( .save(
(currentSettings) => (currentSettings) =>
currentSettings.copyWith.tabBarPosition(value), currentSettings.copyWith.tabBarPosition(value),
@@ -204,9 +194,7 @@ class _TabBarPositionSection extends HookConsumerWidget {
RadioListTile.adaptive( RadioListTile.adaptive(
value: TabBarPosition.top, value: TabBarPosition.top,
title: Text('Top'), title: Text('Top'),
subtitle: Text( subtitle: Text('Persistent tab bar without auto-hide'),
'Persistent tab bar without auto-hide',
),
), ),
RadioListTile.adaptive( RadioListTile.adaptive(
value: TabBarPosition.bottom, value: TabBarPosition.bottom,
@@ -228,8 +216,9 @@ class _ShowContextualTabBarTile extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final tabBarShowContextualBar = ref.watch( final tabBarShowContextualBar = ref.watch(
generalSettingsWithDefaultsProvider generalSettingsWithDefaultsProvider.select(
.select((s) => s.tabBarShowContextualBar), (s) => s.tabBarShowContextualBar,
),
); );
return SwitchListTile.adaptive( return SwitchListTile.adaptive(
@@ -257,8 +246,9 @@ class _ShowQuickTabSwitcherBarTile extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final tabBarShowQuickTabSwitcherBar = ref.watch( final tabBarShowQuickTabSwitcherBar = ref.watch(
generalSettingsWithDefaultsProvider generalSettingsWithDefaultsProvider.select(
.select((s) => s.tabBarShowQuickTabSwitcherBar), (s) => s.tabBarShowQuickTabSwitcherBar,
),
); );
return SwitchListTile.adaptive( return SwitchListTile.adaptive(
@@ -272,8 +262,8 @@ class _ShowQuickTabSwitcherBarTile extends HookConsumerWidget {
await ref await ref
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.tabBarShowQuickTabSwitcherBar(value), currentSettings.copyWith.tabBarShowQuickTabSwitcherBar(value),
); );
}, },
); );
@@ -290,10 +280,7 @@ class _QuickTabSwitcherModeSection extends HookConsumerWidget {
); );
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -308,12 +295,10 @@ class _QuickTabSwitcherModeSection extends HookConsumerWidget {
onChanged: (value) async { onChanged: (value) async {
if (value != null) { if (value != null) {
await ref await ref
.read( .read(saveGeneralSettingsControllerProvider.notifier)
saveGeneralSettingsControllerProvider.notifier,
)
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.quickTabSwitcherMode(value), currentSettings.copyWith.quickTabSwitcherMode(value),
); );
} }
}, },
@@ -322,16 +307,12 @@ class _QuickTabSwitcherModeSection extends HookConsumerWidget {
RadioListTile.adaptive( RadioListTile.adaptive(
value: QuickTabSwitcherMode.lastUsedTabs, value: QuickTabSwitcherMode.lastUsedTabs,
title: Text('Recently Used Tabs'), title: Text('Recently Used Tabs'),
subtitle: Text( subtitle: Text('Recently used tabs across all containers'),
'Recently used tabs across all containers',
),
), ),
RadioListTile.adaptive( RadioListTile.adaptive(
value: QuickTabSwitcherMode.containerTabs, value: QuickTabSwitcherMode.containerTabs,
title: Text('Container Tabs'), title: Text('Container Tabs'),
subtitle: Text( subtitle: Text('Ordered tabs of the selected container'),
'Ordered tabs of the selected container',
),
), ),
], ],
), ),
@@ -34,17 +34,17 @@ class FingerprintingSettingsScreen extends StatelessWidget {
child: FadingScroll( child: FadingScroll(
fadingSize: 25, fadingSize: 25,
builder: (context, controller) { builder: (context, controller) {
return ListView( return ListView(
controller: controller, controller: controller,
padding: const EdgeInsets.symmetric(horizontal: 12.0), padding: const EdgeInsets.symmetric(horizontal: 12.0),
children: const [ children: const [
_MainProtectionSection(), _MainProtectionSection(),
_IdentitySection(), _IdentitySection(),
_AdvancedSection(), _AdvancedSection(),
], ],
); );
}, },
), ),
), ),
); );
} }
@@ -120,9 +120,7 @@ class _FingerprintProtectionTile extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListTile( return ListTile(
title: const Text('Fingerprint Protection'), title: const Text('Fingerprint Protection'),
subtitle: const Text( subtitle: const Text('Granular control over browser fingerprinting'),
'Granular control over browser fingerprinting',
),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
vertical: 8.0, vertical: 8.0,
horizontal: 16.0, horizontal: 16.0,
@@ -143,9 +141,7 @@ class _ResistFingerprintingTile extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListTile( return ListTile(
title: const Text('Resist Fingerprinting'), title: const Text('Resist Fingerprinting'),
subtitle: const Text( subtitle: const Text('Advanced fingerprinting protection hardening'),
'Advanced fingerprinting protection hardening',
),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
vertical: 8.0, vertical: 8.0,
horizontal: 16.0, horizontal: 16.0,
@@ -43,19 +43,19 @@ class PrivacySecuritySettingsScreen extends StatelessWidget {
child: FadingScroll( child: FadingScroll(
fadingSize: 25, fadingSize: 25,
builder: (context, controller) { builder: (context, controller) {
return ListView( return ListView(
controller: controller, controller: controller,
padding: const EdgeInsets.symmetric(horizontal: 12.0), padding: const EdgeInsets.symmetric(horizontal: 12.0),
children: const [ children: const [
_PrivacyModesSection(), _PrivacyModesSection(),
_TrackingProtectionSection(), _TrackingProtectionSection(),
_ConnectionSecuritySection(), _ConnectionSecuritySection(),
_DataManagementSection(), _DataManagementSection(),
_AdvancedSection(), _AdvancedSection(),
], ],
); );
}, },
), ),
), ),
); );
} }
@@ -161,19 +161,13 @@ class _IncognitoModeSection extends HookConsumerWidget {
.read(saveGeneralSettingsControllerProvider.notifier) .read(saveGeneralSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => value (currentSettings) => value
? currentSettings.copyWith.deleteBrowsingDataOnQuit( ? currentSettings.copyWith.deleteBrowsingDataOnQuit({})
{}, : currentSettings.copyWith.deleteBrowsingDataOnQuit(null),
)
: currentSettings.copyWith.deleteBrowsingDataOnQuit(
null,
),
); );
}, },
), ),
if (deleteBrowsingDataOnQuit != null) if (deleteBrowsingDataOnQuit != null)
_DeleteBrowsingDataTypes( _DeleteBrowsingDataTypes(selectedTypes: deleteBrowsingDataOnQuit),
selectedTypes: deleteBrowsingDataOnQuit,
),
], ],
); );
} }
@@ -187,10 +181,7 @@ class _DeleteBrowsingDataTypes extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
children: [ children: [
for (final type in DeleteBrowsingDataType.values) for (final type in DeleteBrowsingDataType.values)
@@ -208,20 +199,19 @@ class _DeleteBrowsingDataTypes extends HookConsumerWidget {
if (value == true) { if (value == true) {
await notifier.save( await notifier.save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.deleteBrowsingDataOnQuit({ currentSettings.copyWith.deleteBrowsingDataOnQuit({
...currentSettings.deleteBrowsingDataOnQuit!, ...currentSettings.deleteBrowsingDataOnQuit!,
type, type,
}), }),
); );
} else { } else {
await notifier.save( await notifier.save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.deleteBrowsingDataOnQuit( currentSettings.copyWith.deleteBrowsingDataOnQuit(
{ {...currentSettings.deleteBrowsingDataOnQuit!}
...currentSettings.deleteBrowsingDataOnQuit!, ..remove(type),
}..remove(type), ),
),
); );
} }
}, },
@@ -269,10 +259,7 @@ class _AutoClearHistorySection extends HookConsumerWidget {
); );
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -296,41 +283,19 @@ class _AutoClearHistorySection extends HookConsumerWidget {
), ),
width: double.infinity, width: double.infinity,
dropdownMenuEntries: const [ dropdownMenuEntries: const [
DropdownMenuEntry( DropdownMenuEntry(value: Duration.zero, label: 'Never'),
value: Duration.zero, DropdownMenuEntry(value: Duration(days: 1), label: '1 Day'),
label: 'Never', DropdownMenuEntry(value: Duration(days: 7), label: '1 Week'),
), DropdownMenuEntry(value: Duration(days: 14), label: '2 Weeks'),
DropdownMenuEntry( DropdownMenuEntry(value: Duration(days: 30), label: '1 Month'),
value: Duration(days: 1), DropdownMenuEntry(value: Duration(days: 90), label: '3 Months'),
label: '1 Day',
),
DropdownMenuEntry(
value: Duration(days: 7),
label: '1 Week',
),
DropdownMenuEntry(
value: Duration(days: 14),
label: '2 Weeks',
),
DropdownMenuEntry(
value: Duration(days: 30),
label: '1 Month',
),
DropdownMenuEntry(
value: Duration(days: 90),
label: '3 Months',
),
], ],
onSelected: (value) async { onSelected: (value) async {
await ref await ref
.read( .read(saveGeneralSettingsControllerProvider.notifier)
saveGeneralSettingsControllerProvider.notifier,
)
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) => currentSettings.copyWith
.historyAutoCleanInterval( .historyAutoCleanInterval(value ?? Duration.zero),
value ?? Duration.zero,
),
); );
}, },
), ),
@@ -378,10 +343,7 @@ class _HttpsOnlyModeSection extends HookConsumerWidget {
); );
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -410,9 +372,7 @@ class _HttpsOnlyModeSection extends HookConsumerWidget {
selected: {httpsOnlyMode}, selected: {httpsOnlyMode},
onSelectionChanged: (value) async { onSelectionChanged: (value) async {
await ref await ref
.read( .read(saveEngineSettingsControllerProvider.notifier)
saveEngineSettingsControllerProvider.notifier,
)
.save( .save(
(currentSettings) => (currentSettings) =>
currentSettings.copyWith.httpsOnlyMode(value.first), currentSettings.copyWith.httpsOnlyMode(value.first),
@@ -458,10 +418,7 @@ class _EnhancedTrackingProtectionSection extends HookConsumerWidget {
); );
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -477,8 +434,8 @@ class _EnhancedTrackingProtectionSection extends HookConsumerWidget {
await ref await ref
.read(saveEngineSettingsControllerProvider.notifier) .read(saveEngineSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => (currentSettings) => currentSettings.copyWith
currentSettings.copyWith.trackingProtectionPolicy(value), .trackingProtectionPolicy(value),
); );
}, },
child: const Column( child: const Column(
@@ -504,9 +461,7 @@ class _EnhancedTrackingProtectionSection extends HookConsumerWidget {
RadioListTile<TrackingProtectionPolicy>.adaptive( RadioListTile<TrackingProtectionPolicy>.adaptive(
value: TrackingProtectionPolicy.custom, value: TrackingProtectionPolicy.custom,
title: Text('Custom'), title: Text('Custom'),
subtitle: Text( subtitle: Text('Choose which trackers and scripts to block.'),
'Choose which trackers and scripts to block.',
),
), ),
], ],
), ),
@@ -546,8 +501,8 @@ class _BounceTrackingProtectionTile extends HookConsumerWidget {
await ref await ref
.read(saveEngineSettingsControllerProvider.notifier) .read(saveEngineSettingsControllerProvider.notifier)
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) =>
.bounceTrackingProtectionMode( currentSettings.copyWith.bounceTrackingProtectionMode(
value value
? BounceTrackingProtectionMode.enabled ? BounceTrackingProtectionMode.enabled
: BounceTrackingProtectionMode.disabled, : BounceTrackingProtectionMode.disabled,
@@ -570,10 +525,7 @@ class _QueryParameterStrippingSection extends HookConsumerWidget {
); );
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -605,12 +557,10 @@ class _QueryParameterStrippingSection extends HookConsumerWidget {
selected: {queryParameterStripping}, selected: {queryParameterStripping},
onSelectionChanged: (value) async { onSelectionChanged: (value) async {
await ref await ref
.read( .read(saveEngineSettingsControllerProvider.notifier)
saveEngineSettingsControllerProvider.notifier,
)
.save( .save(
(currentSettings) => (currentSettings) => currentSettings.copyWith
currentSettings.copyWith.queryParameterStripping(value.first), .queryParameterStripping(value.first),
); );
}, },
), ),
@@ -44,18 +44,18 @@ class SearchContentSettingsScreen extends StatelessWidget {
child: FadingScroll( child: FadingScroll(
fadingSize: 25, fadingSize: 25,
builder: (context, controller) { builder: (context, controller) {
return ListView( return ListView(
controller: controller, controller: controller,
padding: const EdgeInsets.symmetric(horizontal: 12.0), padding: const EdgeInsets.symmetric(horizontal: 12.0),
children: const [ children: const [
_SearchSection(), _SearchSection(),
_ContentViewingSection(), _ContentViewingSection(),
_ContentEnhancementSection(), _ContentEnhancementSection(),
_ExtensionsSection(), _ExtensionsSection(),
], ],
); );
}, },
), ),
), ),
); );
} }
@@ -154,16 +154,14 @@ class _AutocompleteProviderSection extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final defaultSearchSuggestionsProvider = ref.watch( final defaultSearchSuggestionsProvider = ref.watch(
generalSettingsWithDefaultsProvider generalSettingsWithDefaultsProvider.select(
.select((s) => s.defaultSearchSuggestionsProvider), (s) => s.defaultSearchSuggestionsProvider,
),
); );
final relatedBang = defaultSearchSuggestionsProvider.relatedBang; final relatedBang = defaultSearchSuggestionsProvider.relatedBang;
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -183,28 +181,27 @@ class _AutocompleteProviderSection extends HookConsumerWidget {
), ),
), ),
width: double.infinity, width: double.infinity,
leadingIcon: leadingIcon: relatedBang.mapNotNull(
relatedBang.mapNotNull((trigger) => BangIcon(trigger: trigger)), (trigger) => BangIcon(trigger: trigger),
dropdownMenuEntries: SearchSuggestionProviders.values ),
.map((provider) { dropdownMenuEntries: SearchSuggestionProviders.values.map((
return DropdownMenuEntry( provider,
value: provider, ) {
label: provider.label, return DropdownMenuEntry(
leadingIcon: provider.relatedBang.mapNotNull( value: provider,
(trigger) => BangIcon(trigger: trigger), label: provider.label,
), leadingIcon: provider.relatedBang.mapNotNull(
); (trigger) => BangIcon(trigger: trigger),
}) ),
.toList(), );
}).toList(),
onSelected: (value) async { onSelected: (value) async {
if (value != null) { if (value != null) {
await ref await ref
.read( .read(saveGeneralSettingsControllerProvider.notifier)
saveGeneralSettingsControllerProvider.notifier,
)
.save( .save(
(currentSettings) => (currentSettings) => currentSettings.copyWith
currentSettings.copyWith.defaultSearchSuggestionsProvider(value), .defaultSearchSuggestionsProvider(value),
); );
} }
}, },
@@ -243,7 +240,9 @@ class _OnDeviceAiTile extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final enableLocalAiFeatures = ref.watch( final enableLocalAiFeatures = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.enableLocalAiFeatures), generalSettingsWithDefaultsProvider.select(
(s) => s.enableLocalAiFeatures,
),
); );
return SwitchListTile.adaptive( return SwitchListTile.adaptive(
@@ -315,9 +314,7 @@ class _EnforceReaderModeTile extends HookConsumerWidget {
onChanged: enableReadability onChanged: enableReadability
? (value) async { ? (value) async {
await ref await ref
.read( .read(saveGeneralSettingsControllerProvider.notifier)
saveGeneralSettingsControllerProvider.notifier,
)
.save( .save(
(currentSettings) => (currentSettings) =>
currentSettings.copyWith.enforceReadability(value), currentSettings.copyWith.enforceReadability(value),
@@ -39,17 +39,17 @@ class TabsBehaviorSettingsScreen extends StatelessWidget {
child: FadingScroll( child: FadingScroll(
fadingSize: 25, fadingSize: 25,
builder: (context, controller) { builder: (context, controller) {
return ListView( return ListView(
controller: controller, controller: controller,
padding: const EdgeInsets.symmetric(horizontal: 12.0), padding: const EdgeInsets.symmetric(horizontal: 12.0),
children: const [ children: const [
_TabCreationSection(), _TabCreationSection(),
_TabOrganizationSection(), _TabOrganizationSection(),
_TabInteractionSection(), _TabInteractionSection(),
], ],
); );
}, },
), ),
), ),
); );
} }
@@ -110,19 +110,14 @@ class _NewTabDefaultSection extends HookConsumerWidget {
); );
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const ListTile( const ListTile(
title: Text('New Tab Default'), title: Text('New Tab Default'),
subtitle: Text( subtitle: Text('Choose the default type for manually created tabs'),
'Choose the default type for manually created tabs',
),
leading: Icon(MdiIcons.tab), leading: Icon(MdiIcons.tab),
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
), ),
@@ -144,12 +139,10 @@ class _NewTabDefaultSection extends HookConsumerWidget {
selected: {defaultCreateTabType}, selected: {defaultCreateTabType},
onSelectionChanged: (value) async { onSelectionChanged: (value) async {
await ref await ref
.read( .read(saveGeneralSettingsControllerProvider.notifier)
saveGeneralSettingsControllerProvider.notifier,
)
.save( .save(
(currentSettings) => (currentSettings) => currentSettings.copyWith
currentSettings.copyWith.defaultCreateTabType(value.first), .defaultCreateTabType(value.first),
); );
}, },
style: switch (defaultCreateTabType) { style: switch (defaultCreateTabType) {
@@ -178,19 +171,14 @@ class _ExternalLinkHandlingSection extends HookConsumerWidget {
); );
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const ListTile( const ListTile(
title: Text('External Link Handling'), title: Text('External Link Handling'),
subtitle: Text( subtitle: Text('Choose how external links open in WebLibre'),
'Choose how external links open in WebLibre',
),
leading: Icon(MdiIcons.tabPlus), leading: Icon(MdiIcons.tabPlus),
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
), ),
@@ -217,12 +205,10 @@ class _ExternalLinkHandlingSection extends HookConsumerWidget {
selected: {tabIntentOpenSetting}, selected: {tabIntentOpenSetting},
onSelectionChanged: (value) async { onSelectionChanged: (value) async {
await ref await ref
.read( .read(saveGeneralSettingsControllerProvider.notifier)
saveGeneralSettingsControllerProvider.notifier,
)
.save( .save(
(currentSettings) => (currentSettings) => currentSettings.copyWith
currentSettings.copyWith.tabIntentOpenSetting(value.first), .tabIntentOpenSetting(value.first),
); );
}, },
style: switch (tabIntentOpenSetting) { style: switch (tabIntentOpenSetting) {
@@ -246,7 +232,9 @@ class _CreateChildTabsTile extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final createChildTabsOption = ref.watch( final createChildTabsOption = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.createChildTabsOption), generalSettingsWithDefaultsProvider.select(
(s) => s.createChildTabsOption,
),
); );
return SwitchListTile.adaptive( return SwitchListTile.adaptive(
@@ -274,14 +262,14 @@ class _ShowExtensionShortcutTile extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final showExtensionShortcut = ref.watch( final showExtensionShortcut = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.showExtensionShortcut), generalSettingsWithDefaultsProvider.select(
(s) => s.showExtensionShortcut,
),
); );
return SwitchListTile.adaptive( 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: showExtensionShortcut, value: showExtensionShortcut,
onChanged: (value) async { onChanged: (value) async {
@@ -306,10 +294,7 @@ class _TabBarSwipeBehaviorSection extends HookConsumerWidget {
); );
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
horizontal: 16.0,
vertical: 8,
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -324,9 +309,7 @@ class _TabBarSwipeBehaviorSection extends HookConsumerWidget {
onChanged: (value) async { onChanged: (value) async {
if (value != null) { if (value != null) {
await ref await ref
.read( .read(saveGeneralSettingsControllerProvider.notifier)
saveGeneralSettingsControllerProvider.notifier,
)
.save( .save(
(currentSettings) => (currentSettings) =>
currentSettings.copyWith.tabBarSwipeAction(value), currentSettings.copyWith.tabBarSwipeAction(value),
@@ -10,11 +10,7 @@ import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
/// Keyboard visibility event data /// Keyboard visibility event data
typedef KeyboardEvent = ({ typedef KeyboardEvent = ({int heightPx, bool isVisible, bool isAnimating});
int heightPx,
bool isVisible,
bool isAnimating,
});
/// Service for controlling GeckoView's viewport behavior. /// Service for controlling GeckoView's viewport behavior.
/// ///
@@ -57,9 +53,9 @@ class GeckoViewportService extends GeckoViewportEvents {
BinaryMessenger? binaryMessenger, BinaryMessenger? binaryMessenger,
String messageChannelSuffix = '', String messageChannelSuffix = '',
}) : _api = GeckoViewportApi( }) : _api = GeckoViewportApi(
binaryMessenger: binaryMessenger, binaryMessenger: binaryMessenger,
messageChannelSuffix: messageChannelSuffix, messageChannelSuffix: messageChannelSuffix,
); );
/// Sets up the service to receive events from native. /// Sets up the service to receive events from native.
/// ///
@@ -110,15 +106,11 @@ class GeckoViewportService extends GeckoViewportEvents {
bool isVisible, bool isVisible,
bool isAnimating, bool isAnimating,
) { ) {
_keyboardSubject.addWhenMoreRecent( _keyboardSubject.addWhenMoreRecent(timestamp, null, (
timestamp, heightPx: heightPx,
null, isVisible: isVisible,
( isAnimating: isAnimating,
heightPx: heightPx, ));
isVisible: isVisible,
isAnimating: isAnimating,
),
);
} }
/// Disposes the service and closes all streams. /// Disposes the service and closes all streams.
@@ -18,8 +18,8 @@ void main() {
// Verify that platform version is retrieved. // Verify that platform version is retrieved.
expect( expect(
find.byWidgetPredicate( find.byWidgetPredicate(
(Widget widget) => widget is Text && (Widget widget) =>
widget.data!.startsWith('Running on:'), widget is Text && widget.data!.startsWith('Running on:'),
), ),
findsOneWidget, findsOneWidget,
); );
@@ -6,7 +6,6 @@
// For more information about Flutter integration tests, please see // For more information about Flutter integration tests, please see
// https://flutter.dev/to/integration-testing // https://flutter.dev/to/integration-testing
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
@@ -84,11 +84,7 @@ class _MyAppState extends State<MyApp> {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon( const Icon(Icons.mic, size: 80, color: Colors.blue),
Icons.mic,
size: 80,
color: Colors.blue,
),
const SizedBox(height: 24), const SizedBox(height: 24),
Text( Text(
_recognizedText, _recognizedText,
@@ -18,8 +18,8 @@ void main() {
// Verify that platform version is retrieved. // Verify that platform version is retrieved.
expect( expect(
find.byWidgetPredicate( find.byWidgetPredicate(
(Widget widget) => widget is Text && (Widget widget) =>
widget.data!.startsWith('Running on:'), widget is Text && widget.data!.startsWith('Running on:'),
), ),
findsOneWidget, findsOneWidget,
); );
@@ -48,11 +48,9 @@ class SpeechToTextDialog implements SpeechToTextEvents {
/// ///
/// Optionally provide a custom [api] for testing or dependency injection. /// Optionally provide a custom [api] for testing or dependency injection.
/// If [binaryMessenger] is provided, it will be used for Pigeon communication. /// If [binaryMessenger] is provided, it will be used for Pigeon communication.
SpeechToTextDialog({ SpeechToTextDialog({SpeechToTextApi? api, BinaryMessenger? binaryMessenger})
SpeechToTextApi? api, : _api = api ?? SpeechToTextApi(binaryMessenger: binaryMessenger),
BinaryMessenger? binaryMessenger, _binaryMessenger = binaryMessenger {
}) : _api = api ?? SpeechToTextApi(binaryMessenger: binaryMessenger),
_binaryMessenger = binaryMessenger {
// Set up the event handler // Set up the event handler
SpeechToTextEvents.setUp(this, binaryMessenger: binaryMessenger); SpeechToTextEvents.setUp(this, binaryMessenger: binaryMessenger);
} }
@@ -35,7 +35,6 @@ import 'package:pigeon/pigeon.dart';
dartPackageName: 'speech_to_text_dialog', dartPackageName: 'speech_to_text_dialog',
), ),
) )
/// Host API - methods called from Flutter to native Android. /// Host API - methods called from Flutter to native Android.
@HostApi() @HostApi()
abstract class SpeechToTextApi { abstract class SpeechToTextApi {