automatic find in page for search results

This commit is contained in:
Fabian Freund
2025-07-02 01:41:41 +02:00
parent 25e2ffb1cd
commit 46149d1965
18 changed files with 292 additions and 70 deletions
@@ -9,6 +9,7 @@ import 'package:weblibre/core/logger.dart';
import 'package:weblibre/core/providers/router.dart'; import 'package:weblibre/core/providers/router.dart';
import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/bangs/domain/providers/bangs.dart'; import 'package:weblibre/features/bangs/domain/providers/bangs.dart';
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart'; import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart';
@@ -50,9 +51,12 @@ GeckoSelectionActionService selectionActionService(Ref ref) {
} }
}), }),
FindInPageAction((text) async { FindInPageAction((text) async {
await ref final tabId = ref.read(selectedTabProvider);
.read(findInPageControllerProvider.notifier) if (tabId != null) {
.findAll(text: text); await ref
.read(findInPageControllerProvider(tabId).notifier)
.findAll(text: text);
}
}), }),
ShareAction((text) async { ShareAction((text) async {
await SharePlus.instance.share(ShareParams(text: text)); await SharePlus.instance.share(ShareParams(text: text));
@@ -7,7 +7,7 @@ part of 'providers.dart';
// ************************************************************************** // **************************************************************************
String _$selectionActionServiceHash() => String _$selectionActionServiceHash() =>
r'a3682b3f97d9798aadbca3be882d5be74085b762'; r'ab6aeb105020a16ed24911acd73ba8553e5b3905';
/// See also [selectionActionService]. /// See also [selectionActionService].
@ProviderFor(selectionActionService) @ProviderFor(selectionActionService)
@@ -1,4 +1,5 @@
import 'package:fast_equatable/fast_equatable.dart'; import 'package:fast_equatable/fast_equatable.dart';
import 'package:nullability/nullability.dart';
import 'package:riverpod/riverpod.dart'; import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/features/bangs/data/models/bang_data.dart'; import 'package:weblibre/features/bangs/data/models/bang_data.dart';
@@ -98,9 +99,16 @@ EquatableValue<List<TabEntity>> seamlessFilteredTabEntities(
.watch( .watch(
tabSearchRepositoryProvider(searchPartition).select( tabSearchRepositoryProvider(searchPartition).select(
(value) => EquatableValue( (value) => EquatableValue(
value.valueOrNull value.valueOrNull.mapNotNull(
?.map((tab) => SingleTabEntity(tabId: tab.id)) (result) => result.results
.toList(), .map(
(tab) => SearchResultTabEntity(
tabId: tab.id,
searchQuery: result.query,
),
)
.toList(),
),
), ),
), ),
) )
@@ -109,7 +117,7 @@ EquatableValue<List<TabEntity>> seamlessFilteredTabEntities(
final availableTabs = ref.watch( final availableTabs = ref.watch(
availableTabIdsProvider(containerFilter).select( availableTabIdsProvider(containerFilter).select(
(value) => EquatableValue( (value) => EquatableValue(
value.value.map((tab) => SingleTabEntity(tabId: tab)).toList(), value.value.map((tab) => DefaultTabEntity(tabId: tab)).toList(),
), ),
), ),
); );
@@ -148,7 +156,13 @@ EquatableValue<List<TabEntity>> seamlessFilteredTabEntities(
} }
return EquatableValue( return EquatableValue(
tabSearchResults.where((tab) => availableTabs.value.contains(tab)).toList(), tabSearchResults
.where(
(tab) => availableTabs.value.any(
(available) => available.tabId == tab.tabId,
),
)
.toList(),
); );
} }
@@ -181,6 +195,7 @@ EquatableValue<List<TabPreview>> seamlessFilteredTabPreviews(
url: state.url, url: state.url,
highlightedUrl: null, highlightedUrl: null,
content: null, content: null,
sourceSearchQuery: null,
), ),
) )
.toList(), .toList(),
@@ -188,7 +203,7 @@ EquatableValue<List<TabPreview>> seamlessFilteredTabPreviews(
} }
return EquatableValue( return EquatableValue(
tabSearchResults tabSearchResults.results
.where((tab) => availableTabStates.value.containsKey(tab.id)) .where((tab) => availableTabStates.value.containsKey(tab.id))
.map((tab) { .map((tab) {
return TabPreview( return TabPreview(
@@ -198,6 +213,7 @@ EquatableValue<List<TabPreview>> seamlessFilteredTabPreviews(
url: tab.cleanUrl ?? availableTabStates.value[tab.id]!.url, url: tab.cleanUrl ?? availableTabStates.value[tab.id]!.url,
highlightedUrl: tab.url, highlightedUrl: tab.url,
content: tab.extractedContent ?? tab.fullContent, content: tab.extractedContent ?? tab.fullContent,
sourceSearchQuery: tabSearchResults.query,
); );
}) })
.whereType<TabPreview>() .whereType<TabPreview>()
@@ -283,7 +283,7 @@ class _AvailableTabStatesProviderElement
} }
String _$seamlessFilteredTabEntitiesHash() => String _$seamlessFilteredTabEntitiesHash() =>
r'0ef5c14f3fa018e3417e097759817bc871be3e0d'; r'2cd698d4e7c9d9cda9e17abbf9a99e512425e4cd';
/// See also [seamlessFilteredTabEntities]. /// See also [seamlessFilteredTabEntities].
@ProviderFor(seamlessFilteredTabEntities) @ProviderFor(seamlessFilteredTabEntities)
@@ -456,7 +456,7 @@ class _SeamlessFilteredTabEntitiesProviderElement
} }
String _$seamlessFilteredTabPreviewsHash() => String _$seamlessFilteredTabPreviewsHash() =>
r'c59c2819eda4f132e1a0f5f7784ddb3b5ceec817'; r'c50a2d0f9f3ba2bc3db7c96e74d9acdcac2e84e4';
/// See also [seamlessFilteredTabPreviews]. /// See also [seamlessFilteredTabPreviews].
@ProviderFor(seamlessFilteredTabPreviews) @ProviderFor(seamlessFilteredTabPreviews)
@@ -7,7 +7,7 @@ part of 'tree_view.dart';
// ************************************************************************** // **************************************************************************
String _$treeViewControllerHash() => String _$treeViewControllerHash() =>
r'53da9cc3cc89eab2fee4a3aaf4feccc5ecbfeb20'; r'1fac2a133d369e3bd3f8f75cdd2d0c862a6ab942';
/// See also [TreeViewController]. /// See also [TreeViewController].
@ProviderFor(TreeViewController) @ProviderFor(TreeViewController)
@@ -172,6 +172,7 @@ class TabTreeDialog extends HookConsumerWidget {
.read(bottomSheetControllerProvider.notifier) .read(bottomSheetControllerProvider.notifier)
.dismiss(); .dismiss();
}, },
sourceSearchQuery: null,
), ),
); );
}, },
@@ -12,6 +12,7 @@ import 'package:weblibre/extensions/media_query.dart';
import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart'; import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart';
import 'package:weblibre/features/geckoview/domain/controllers/overlay.dart'; import 'package:weblibre/features/geckoview/domain/controllers/overlay.dart';
import 'package:weblibre/features/geckoview/domain/providers.dart'; import 'package:weblibre/features/geckoview/domain/providers.dart';
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_list.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_list.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
@@ -311,7 +312,12 @@ class _BrowserView extends StatelessWidget {
), ),
); );
return FindInPageWidget(padding: value); final tabId = ref.watch(selectedTabProvider);
if (tabId == null) {
return const SizedBox.shrink();
}
return FindInPageWidget(tabId: tabId, padding: value);
}, },
), ),
), ),
@@ -286,7 +286,12 @@ class BrowserBottomAppBar extends HookConsumerWidget {
if (selectedTabId != null) if (selectedTabId != null)
MenuItemButton( MenuItemButton(
onPressed: () { onPressed: () {
ref.read(findInPageControllerProvider.notifier).show(); final tabId = ref.read(selectedTabProvider);
if (tabId != null) {
ref
.read(findInPageControllerProvider(tabId).notifier)
.show();
}
}, },
leadingIcon: const Icon(Icons.search), leadingIcon: const Icon(Icons.search),
child: const Text('Find in page'), child: const Text('Find in page'),
@@ -314,13 +314,18 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
final tabs = useMemoized(() { final tabs = useMemoized(() {
return filteredTabIds.value return filteredTabIds.value
.whereType<SingleTabEntity>() .where((entity) => entity is! TabTreeEntity)
.mapIndexed((index, entity) { .mapIndexed((index, entity) {
final child = Consumer( final child = Consumer(
child: SingleTabPreview( child: SingleTabPreview(
tabId: entity.tabId, tabId: entity.tabId,
activeTabId: activeTab, activeTabId: activeTab,
onClose: onClose, onClose: onClose,
sourceSearchQuery: switch (entity) {
DefaultTabEntity() => null,
SearchResultTabEntity() => entity.searchQuery,
TabTreeEntity() => throw UnimplementedError(),
},
), ),
builder: (context, ref, child) { builder: (context, ref, child) {
final dragData = ref.watch( final dragData = ref.watch(
@@ -10,6 +10,8 @@ import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_icon.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_icon.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/domain/entities/find_in_page_state.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart';
import 'package:weblibre/features/geckoview/features/tabs/data/entities/tab_entity.dart'; import 'package:weblibre/features/geckoview/features/tabs/data/entities/tab_entity.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart';
@@ -194,6 +196,7 @@ class TabPreview extends HookWidget {
class SingleTabPreview extends HookConsumerWidget { class SingleTabPreview extends HookConsumerWidget {
final String tabId; final String tabId;
final String? activeTabId; final String? activeTabId;
final String? sourceSearchQuery;
final void Function() onClose; final void Function() onClose;
@@ -201,6 +204,7 @@ class SingleTabPreview extends HookConsumerWidget {
required this.tabId, required this.tabId,
required this.activeTabId, required this.activeTabId,
required this.onClose, required this.onClose,
required this.sourceSearchQuery,
}) : super(key: ValueKey(tabId)); }) : super(key: ValueKey(tabId));
@override @override
@@ -219,6 +223,13 @@ class SingleTabPreview extends HookConsumerWidget {
//Close first to avoid rebuilds //Close first to avoid rebuilds
onClose(); onClose();
await ref.read(tabRepositoryProvider.notifier).selectTab(tab.id); await ref.read(tabRepositoryProvider.notifier).selectTab(tab.id);
if (sourceSearchQuery.isNotEmpty &&
ref.read(findInPageControllerProvider(tabId)) ==
FindInPageState.hidden()) {
await ref
.read(findInPageControllerProvider(tabId).notifier)
.findAll(text: sourceSearchQuery!);
}
} else { } else {
onClose(); onClose();
} }
@@ -1,14 +1,13 @@
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/core/logger.dart'; import 'package:weblibre/core/logger.dart';
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/domain/entities/find_in_page_state.dart'; import 'package:weblibre/features/geckoview/features/find_in_page/domain/entities/find_in_page_state.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/domain/repositories/find_in_page.dart'; import 'package:weblibre/features/geckoview/features/find_in_page/domain/repositories/find_in_page.dart';
part 'find_in_page.g.dart'; part 'find_in_page.g.dart';
@Riverpod() @Riverpod(keepAlive: true)
class FindInPageController extends _$FindInPageController { class FindInPageController extends _$FindInPageController {
void show() { void show() {
state = state.copyWith.visible(true); state = state.copyWith.visible(true);
@@ -20,7 +19,6 @@ class FindInPageController extends _$FindInPageController {
} }
Future<void> findAll({required String text}) { Future<void> findAll({required String text}) {
final tabId = ref.read(selectedTabProvider);
final service = ref.read(findInPageRepositoryProvider(tabId).notifier); final service = ref.read(findInPageRepositoryProvider(tabId).notifier);
state = FindInPageState(visible: true, lastSearchText: text); state = FindInPageState(visible: true, lastSearchText: text);
@@ -29,7 +27,6 @@ class FindInPageController extends _$FindInPageController {
} }
Future<void> findNext({required String fallbackText, bool forward = true}) { Future<void> findNext({required String fallbackText, bool forward = true}) {
final tabId = ref.read(selectedTabProvider);
final service = ref.read(findInPageRepositoryProvider(tabId).notifier); final service = ref.read(findInPageRepositoryProvider(tabId).notifier);
final hasMatches = final hasMatches =
@@ -45,17 +42,16 @@ class FindInPageController extends _$FindInPageController {
} }
Future<void> clearMatches() { Future<void> clearMatches() {
final tabId = ref.read(selectedTabProvider);
final service = ref.read(findInPageRepositoryProvider(tabId).notifier); final service = ref.read(findInPageRepositoryProvider(tabId).notifier);
return service.clearMatches(); return service.clearMatches();
} }
@override @override
FindInPageState build() { FindInPageState build(String tabId) {
ref.listen( ref.listen(
fireImmediately: true, fireImmediately: true,
selectedTabStateProvider, tabStateProvider(tabId),
(previous, next) async { (previous, next) async {
//Ensure state is already initialized //Ensure state is already initialized
if (stateOrNull != null) { if (stateOrNull != null) {
@@ -63,10 +59,8 @@ class FindInPageController extends _$FindInPageController {
if (previous != null && next != null) { if (previous != null && next != null) {
final loadingOrReloading = final loadingOrReloading =
previous.isLoading == true && next.isLoading == false; previous.isLoading == true && next.isLoading == false;
final tabSwitchWithoutResults =
previous.id != next.id && !next.findResultState.hasMatches;
if (loadingOrReloading || tabSwitchWithoutResults) { if (loadingOrReloading) {
await findAll(text: state.lastSearchText!); await findAll(text: state.lastSearchText!);
} }
} }
@@ -7,21 +7,158 @@ part of 'find_in_page.dart';
// ************************************************************************** // **************************************************************************
String _$findInPageControllerHash() => String _$findInPageControllerHash() =>
r'fd335be9345b405856cf2aa68312d4d1535d8c5e'; r'da72e38225809af5c78a372e949a80207d7ce7c9';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
abstract class _$FindInPageController
extends BuildlessNotifier<FindInPageState> {
late final String tabId;
FindInPageState build(String tabId);
}
/// See also [FindInPageController]. /// See also [FindInPageController].
@ProviderFor(FindInPageController) @ProviderFor(FindInPageController)
final findInPageControllerProvider = const findInPageControllerProvider = FindInPageControllerFamily();
AutoDisposeNotifierProvider<FindInPageController, FindInPageState>.internal(
FindInPageController.new, /// See also [FindInPageController].
name: r'findInPageControllerProvider', class FindInPageControllerFamily extends Family<FindInPageState> {
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') /// See also [FindInPageController].
? null const FindInPageControllerFamily();
: _$findInPageControllerHash,
dependencies: null, /// See also [FindInPageController].
allTransitiveDependencies: null, FindInPageControllerProvider call(String tabId) {
); return FindInPageControllerProvider(tabId);
}
@override
FindInPageControllerProvider getProviderOverride(
covariant FindInPageControllerProvider provider,
) {
return call(provider.tabId);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'findInPageControllerProvider';
}
/// See also [FindInPageController].
class FindInPageControllerProvider
extends NotifierProviderImpl<FindInPageController, FindInPageState> {
/// See also [FindInPageController].
FindInPageControllerProvider(String tabId)
: this._internal(
() => FindInPageController()..tabId = tabId,
from: findInPageControllerProvider,
name: r'findInPageControllerProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$findInPageControllerHash,
dependencies: FindInPageControllerFamily._dependencies,
allTransitiveDependencies:
FindInPageControllerFamily._allTransitiveDependencies,
tabId: tabId,
);
FindInPageControllerProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.tabId,
}) : super.internal();
final String tabId;
@override
FindInPageState runNotifierBuild(covariant FindInPageController notifier) {
return notifier.build(tabId);
}
@override
Override overrideWith(FindInPageController Function() create) {
return ProviderOverride(
origin: this,
override: FindInPageControllerProvider._internal(
() => create()..tabId = tabId,
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
tabId: tabId,
),
);
}
@override
NotifierProviderElement<FindInPageController, FindInPageState>
createElement() {
return _FindInPageControllerProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is FindInPageControllerProvider && other.tabId == tabId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, tabId.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin FindInPageControllerRef on NotifierProviderRef<FindInPageState> {
/// The parameter `tabId` of this provider.
String get tabId;
}
class _FindInPageControllerProviderElement
extends NotifierProviderElement<FindInPageController, FindInPageState>
with FindInPageControllerRef {
_FindInPageControllerProviderElement(super.provider);
@override
String get tabId => (origin as FindInPageControllerProvider).tabId;
}
typedef _$FindInPageController = AutoDisposeNotifier<FindInPageState>;
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
@@ -5,13 +5,14 @@ import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart'; import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart';
class FindInPageWidget extends HookConsumerWidget { class FindInPageWidget extends HookConsumerWidget {
final String tabId;
final EdgeInsetsGeometry padding; final EdgeInsetsGeometry padding;
const FindInPageWidget({this.padding = EdgeInsets.zero}); const FindInPageWidget({required this.tabId, this.padding = EdgeInsets.zero});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final findInPageState = ref.watch(findInPageControllerProvider); final findInPageState = ref.watch(findInPageControllerProvider(tabId));
final searchResult = ref.watch( final searchResult = ref.watch(
selectedTabStateProvider.select((state) => state?.findResultState), selectedTabStateProvider.select((state) => state?.findResultState),
); );
@@ -43,11 +44,11 @@ class FindInPageWidget extends HookConsumerWidget {
onSubmitted: (value) async { onSubmitted: (value) async {
if (value == '') { if (value == '') {
await ref await ref
.read(findInPageControllerProvider.notifier) .read(findInPageControllerProvider(tabId).notifier)
.clearMatches(); .clearMatches();
} else { } else {
await ref await ref
.read(findInPageControllerProvider.notifier) .read(findInPageControllerProvider(tabId).notifier)
.findAll(text: value); .findAll(text: value);
} }
}, },
@@ -62,7 +63,7 @@ class FindInPageWidget extends HookConsumerWidget {
icon: const Icon(Icons.arrow_upward), icon: const Icon(Icons.arrow_upward),
onPressed: () async { onPressed: () async {
await ref await ref
.read(findInPageControllerProvider.notifier) .read(findInPageControllerProvider(tabId).notifier)
.findNext( .findNext(
forward: false, forward: false,
fallbackText: textController.text, fallbackText: textController.text,
@@ -73,14 +74,17 @@ class FindInPageWidget extends HookConsumerWidget {
icon: const Icon(Icons.arrow_downward), icon: const Icon(Icons.arrow_downward),
onPressed: () async { onPressed: () async {
await ref await ref
.read(findInPageControllerProvider.notifier) .read(findInPageControllerProvider(tabId).notifier)
.findNext(fallbackText: textController.text); .findNext(fallbackText: textController.text);
}, },
), ),
IconButton( IconButton(
icon: const Icon(Icons.clear), icon: const Icon(Icons.clear),
onPressed: () async { onPressed: () async {
await ref.read(findInPageControllerProvider.notifier).hide(); await ref
.read(findInPageControllerProvider(tabId).notifier)
.hide();
textController.clear(); textController.clear();
focusNode.requestFocus(); focusNode.requestFocus();
}, },
@@ -12,6 +12,8 @@ class TabPreview with FastEquatable {
final String? content; final String? content;
final String? sourceSearchQuery;
TabPreview({ TabPreview({
required this.id, required this.id,
required this.title, required this.title,
@@ -19,6 +21,7 @@ class TabPreview with FastEquatable {
required this.url, required this.url,
required this.highlightedUrl, required this.highlightedUrl,
required this.content, required this.content,
required this.sourceSearchQuery,
}); });
@override @override
@@ -29,5 +32,6 @@ class TabPreview with FastEquatable {
url, url,
highlightedUrl, highlightedUrl,
content, content,
sourceSearchQuery,
]; ];
} }
@@ -9,6 +9,8 @@ import 'package:sliver_tools/sliver_tools.dart';
import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart'; import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart';
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart'; import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/domain/entities/find_in_page_state.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart';
import 'package:weblibre/features/geckoview/features/tabs/data/entities/container_filter.dart'; import 'package:weblibre/features/geckoview/features/tabs/data/entities/container_filter.dart';
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart'; import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selected_container.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selected_container.dart';
@@ -139,6 +141,13 @@ class TabSearch extends HookConsumerWidget {
await ref await ref
.read(tabRepositoryProvider.notifier) .read(tabRepositoryProvider.notifier)
.selectTab(result.id); .selectTab(result.id);
if (result.sourceSearchQuery.isNotEmpty &&
ref.read(findInPageControllerProvider(result.id)) ==
FindInPageState.hidden()) {
await ref
.read(findInPageControllerProvider(result.id).notifier)
.findAll(text: result.sourceSearchQuery!);
}
if (context.mounted) { if (context.mounted) {
ref.read(bottomSheetControllerProvider.notifier).dismiss(); ref.read(bottomSheetControllerProvider.notifier).dismiss();
@@ -4,16 +4,28 @@ sealed class TabEntity with FastEquatable {
String get tabId; String get tabId;
} }
class SingleTabEntity extends TabEntity { class DefaultTabEntity extends TabEntity {
@override @override
final String tabId; final String tabId;
SingleTabEntity({required this.tabId}); DefaultTabEntity({required this.tabId});
@override @override
List<Object?> get hashParameters => [tabId]; List<Object?> get hashParameters => [tabId];
} }
class SearchResultTabEntity extends TabEntity {
@override
final String tabId;
final String searchQuery;
SearchResultTabEntity({required this.tabId, required this.searchQuery});
@override
List<Object?> get hashParameters => [tabId, searchQuery];
}
class TabTreeEntity extends TabEntity { class TabTreeEntity extends TabEntity {
@override @override
final String tabId; final String tabId;
@@ -18,26 +18,31 @@ class TabSearchRepository extends _$TabSearchRepository {
String ellipsis = '', String ellipsis = '',
}) async { }) async {
if (input.isNotEmpty) { if (input.isNotEmpty) {
state = await AsyncValue.guard( state = await AsyncValue.guard(() async {
() => ref return (
.read(tabDatabaseProvider) query: input,
.tabDao results: await ref
.queryTabs( .read(tabDatabaseProvider)
matchPrefix: matchPrefix, .tabDao
matchSuffix: matchSuffix, .queryTabs(
ellipsis: ellipsis, matchPrefix: matchPrefix,
snippetLength: snippetLength, matchSuffix: matchSuffix,
searchString: input, ellipsis: ellipsis,
) snippetLength: snippetLength,
.get(), searchString: input,
); )
.get(),
);
});
} else { } else {
state = const AsyncValue.data(null); state = const AsyncValue.data(null);
} }
} }
@override @override
Future<List<TabQueryResult>?> build(TabSearchPartition partition) { Future<({String query, List<TabQueryResult> results})?> build(
TabSearchPartition partition,
) {
return Future.value(); return Future.value();
} }
} }
@@ -7,7 +7,7 @@ part of 'tab_search.dart';
// ************************************************************************** // **************************************************************************
String _$tabSearchRepositoryHash() => String _$tabSearchRepositoryHash() =>
r'd442c384a5a867f4ff28465f524a6ef67521d5ae'; r'ac2381c692b9caf93f26f302d15e0160c098917a';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@@ -31,10 +31,15 @@ class _SystemHash {
} }
abstract class _$TabSearchRepository abstract class _$TabSearchRepository
extends BuildlessAutoDisposeAsyncNotifier<List<TabQueryResult>?> { extends
BuildlessAutoDisposeAsyncNotifier<
({String query, List<TabQueryResult> results})?
> {
late final TabSearchPartition partition; late final TabSearchPartition partition;
FutureOr<List<TabQueryResult>?> build(TabSearchPartition partition); FutureOr<({String query, List<TabQueryResult> results})?> build(
TabSearchPartition partition,
);
} }
/// See also [TabSearchRepository]. /// See also [TabSearchRepository].
@@ -43,7 +48,8 @@ const tabSearchRepositoryProvider = TabSearchRepositoryFamily();
/// See also [TabSearchRepository]. /// See also [TabSearchRepository].
class TabSearchRepositoryFamily class TabSearchRepositoryFamily
extends Family<AsyncValue<List<TabQueryResult>?>> { extends
Family<AsyncValue<({String query, List<TabQueryResult> results})?>> {
/// See also [TabSearchRepository]. /// See also [TabSearchRepository].
const TabSearchRepositoryFamily(); const TabSearchRepositoryFamily();
@@ -79,7 +85,7 @@ class TabSearchRepositoryProvider
extends extends
AutoDisposeAsyncNotifierProviderImpl< AutoDisposeAsyncNotifierProviderImpl<
TabSearchRepository, TabSearchRepository,
List<TabQueryResult>? ({String query, List<TabQueryResult> results})?
> { > {
/// See also [TabSearchRepository]. /// See also [TabSearchRepository].
TabSearchRepositoryProvider(TabSearchPartition partition) TabSearchRepositoryProvider(TabSearchPartition partition)
@@ -109,7 +115,7 @@ class TabSearchRepositoryProvider
final TabSearchPartition partition; final TabSearchPartition partition;
@override @override
FutureOr<List<TabQueryResult>?> runNotifierBuild( FutureOr<({String query, List<TabQueryResult> results})?> runNotifierBuild(
covariant TabSearchRepository notifier, covariant TabSearchRepository notifier,
) { ) {
return notifier.build(partition); return notifier.build(partition);
@@ -134,7 +140,7 @@ class TabSearchRepositoryProvider
@override @override
AutoDisposeAsyncNotifierProviderElement< AutoDisposeAsyncNotifierProviderElement<
TabSearchRepository, TabSearchRepository,
List<TabQueryResult>? ({String query, List<TabQueryResult> results})?
> >
createElement() { createElement() {
return _TabSearchRepositoryProviderElement(this); return _TabSearchRepositoryProviderElement(this);
@@ -157,7 +163,10 @@ class TabSearchRepositoryProvider
@Deprecated('Will be removed in 3.0. Use Ref instead') @Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element // ignore: unused_element
mixin TabSearchRepositoryRef mixin TabSearchRepositoryRef
on AutoDisposeAsyncNotifierProviderRef<List<TabQueryResult>?> { on
AutoDisposeAsyncNotifierProviderRef<
({String query, List<TabQueryResult> results})?
> {
/// The parameter `partition` of this provider. /// The parameter `partition` of this provider.
TabSearchPartition get partition; TabSearchPartition get partition;
} }
@@ -166,7 +175,7 @@ class _TabSearchRepositoryProviderElement
extends extends
AutoDisposeAsyncNotifierProviderElement< AutoDisposeAsyncNotifierProviderElement<
TabSearchRepository, TabSearchRepository,
List<TabQueryResult>? ({String query, List<TabQueryResult> results})?
> >
with TabSearchRepositoryRef { with TabSearchRepositoryRef {
_TabSearchRepositoryProviderElement(super.provider); _TabSearchRepositoryProviderElement(super.provider);