improve performance of search modules

This commit is contained in:
Fabian Freund
2026-03-09 16:30:44 +01:00
parent 7939b73146
commit 6cb7ec8302
12 changed files with 117 additions and 74 deletions
@@ -41,7 +41,7 @@ final class HistoryRepositoryProvider
}
}
String _$historyRepositoryHash() => r'19f883fdb644e34bc89483b4a8ddb60f54c52114';
String _$historyRepositoryHash() => r'414b68ec6be3cc2eca3681cbc00990e53e6127ce';
abstract class _$HistoryRepository extends $Notifier<void> {
void build();
@@ -58,7 +58,7 @@ final class SearchModuleOrderProvider
}
}
String _$searchModuleOrderHash() => r'7e7822de86688cf87dff1c9991bc2551dc44e002';
String _$searchModuleOrderHash() => r'eeea86534497671a12c1383cbf251a8df797c1fc';
final class SearchModuleOrderFamily extends $Family
with
@@ -378,7 +378,7 @@ class _TopSiteGridTile extends StatelessWidget {
const textLines = 2;
const gap = 6.0;
const minIconSize = 18.0;
const textHeightPadding = 2.0;
const textHeightPadding = 16.0;
final minTextHeight = lineHeight + textHeightPadding;
final maxTextHeight =
lineHeight * textLines + textHeightPadding;
@@ -26,6 +26,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/features/bangs/data/models/bang_data.dart';
import 'package:weblibre/features/geckoview/features/search/domain/providers/engine_suggestions.dart';
import 'package:weblibre/features/user/domain/providers.dart';
import 'package:weblibre/presentation/hooks/on_listenable_change_selector.dart';
import 'package:weblibre/presentation/widgets/auto_suggest_text_field.dart';
import 'package:weblibre/presentation/widgets/qr_scanner_button.dart';
import 'package:weblibre/presentation/widgets/speech_to_text_button.dart';
@@ -79,26 +80,31 @@ class SearchField extends HookConsumerWidget {
final lastText = useRef<String>(textEditingController.text);
if (showSuggestions) {
useOnListenableChange(textEditingController, () async {
if (textEditingController.text.isEmpty) {
suggestion.value = null;
} else if (textEditingController.text != lastText.value) {
final isDeleting =
textEditingController.text.length < lastText.value.length;
if (isDeleting) {
useOnListenableChangeSelector(
textEditingController,
() => textEditingController.text,
() async {
final text = textEditingController.text;
if (text.isEmpty) {
suggestion.value = null;
} else {
final result = await ref
.read(engineSuggestionsProvider.notifier)
.getAutocompleteSuggestion(textEditingController.text);
final isDeleting = text.length < lastText.value.length;
suggestion.value = result;
if (isDeleting) {
suggestion.value = null;
} else {
await ref
.read(engineSuggestionsProvider.notifier)
.getAutocompleteSuggestion(text)
.then((result) {
suggestion.value = result;
});
}
}
}
lastText.value = textEditingController.text;
});
lastText.value = text;
},
);
}
return AutoSuggestTextField(
@@ -19,11 +19,11 @@
*/
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/providers/bookmarks.dart';
import 'package:weblibre/features/geckoview/features/search/domain/providers/search_modules_view.dart';
import 'package:weblibre/features/geckoview/features/search/presentation/widgets/search_modules/search_module_section.dart';
import 'package:weblibre/presentation/hooks/on_listenable_change_selector.dart';
import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart';
import 'package:weblibre/presentation/widgets/url_icon.dart';
@@ -42,11 +42,15 @@ class BookmarkSearch extends HookConsumerWidget {
final bookmarkResults = ref.watch(bookmarkSearchResultsProvider);
final totalResults = bookmarkResults.length;
useOnListenableChange(searchTextListenable, () async {
await ref
.read(bookmarkSearchResultsProvider.notifier)
.search(searchTextListenable.value.text);
});
useOnListenableChangeSelector(
searchTextListenable,
() => searchTextListenable.value.text,
() async {
await ref
.read(bookmarkSearchResultsProvider.notifier)
.search(searchTextListenable.value.text);
},
);
if (bookmarkResults.isEmpty) {
return const SliverToBoxAdapter(child: SizedBox.shrink());
@@ -19,7 +19,6 @@
*/
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:nullability/nullability.dart';
import 'package:skeletonizer/skeletonizer.dart';
@@ -33,6 +32,7 @@ import 'package:weblibre/features/web_feed/data/models/feed_link.dart';
import 'package:weblibre/features/web_feed/domain/providers.dart';
import 'package:weblibre/features/web_feed/extensions/atom.dart';
import 'package:weblibre/features/web_feed/extensions/feed_article.dart';
import 'package:weblibre/presentation/hooks/on_listenable_change_selector.dart';
import 'package:weblibre/presentation/widgets/failure_widget.dart';
import 'package:weblibre/presentation/widgets/url_icon.dart';
import 'package:weblibre/utils/text_highlight.dart';
@@ -52,17 +52,21 @@ class FeedSearch extends HookConsumerWidget {
final articlesAsync = ref.watch(articleSearchProvider(null));
final totalResults = articlesAsync.value?.length ?? 0;
useOnListenableChange(searchTextNotifier, () async {
await ref
.read(articleSearchProvider(null).notifier)
.search(
searchTextNotifier.value.text,
// ignore: avoid_redundant_argument_values dont break things
matchPrefix: _matchPrefix,
// ignore: avoid_redundant_argument_values dont break things
matchSuffix: _matchSuffix,
);
});
useOnListenableChangeSelector(
searchTextNotifier,
() => searchTextNotifier.value.text,
() async {
await ref
.read(articleSearchProvider(null).notifier)
.search(
searchTextNotifier.value.text,
// ignore: avoid_redundant_argument_values dont break things
matchPrefix: _matchPrefix,
// ignore: avoid_redundant_argument_values dont break things
matchSuffix: _matchSuffix,
);
},
);
if (articlesAsync.hasValue && (articlesAsync.value.isEmpty)) {
return const SliverToBoxAdapter(child: SizedBox.shrink());
@@ -25,6 +25,7 @@ import 'package:nullability/nullability.dart';
import 'package:weblibre/extensions/uri.dart';
import 'package:weblibre/features/bangs/data/models/bang_data.dart';
import 'package:weblibre/features/geckoview/features/search/domain/providers/search_suggestions.dart';
import 'package:weblibre/presentation/hooks/on_listenable_change_selector.dart';
import 'package:weblibre/utils/uri_parser.dart' as uri_parser;
class FixedSearchTermSuggestions extends HookConsumerWidget {
@@ -59,13 +60,17 @@ class FixedSearchTermSuggestions extends HookConsumerWidget {
final searchSuggestions = ref.watch(searchSuggestionsProvider());
useOnListenableChange(searchTextController, () {
if (ref.exists(searchSuggestionsProvider())) {
ref
.read(searchSuggestionsProvider().notifier)
.addQuery(searchTextController.text);
}
});
useOnListenableChangeSelector(
searchTextController,
() => searchTextController.text,
() {
if (ref.exists(searchSuggestionsProvider())) {
ref
.read(searchSuggestionsProvider().notifier)
.addQuery(searchTextController.text);
}
},
);
final prioritizedSuggestions = isSuggestableText
? [
@@ -30,6 +30,7 @@ import 'package:weblibre/features/bangs/domain/providers/bangs.dart';
import 'package:weblibre/features/bangs/domain/repositories/data.dart';
import 'package:weblibre/features/geckoview/features/search/domain/providers/search_suggestions.dart';
import 'package:weblibre/features/geckoview/features/search/presentation/widgets/smart_bang_selector.dart';
import 'package:weblibre/presentation/hooks/on_listenable_change_selector.dart';
class FullSearchTermSuggestions extends HookConsumerWidget {
final TextEditingController searchTextController;
@@ -62,11 +63,15 @@ class FullSearchTermSuggestions extends HookConsumerWidget {
persistedBoolProvider(PersistedBoolKey.searchSuggestionsExpanded),
);
useOnListenableChange(searchTextController, () {
ref
.read(searchSuggestionsProvider().notifier)
.addQuery(searchTextController.text);
});
useOnListenableChangeSelector(
searchTextController,
() => searchTextController.text,
() {
ref
.read(searchSuggestionsProvider().notifier)
.addQuery(searchTextController.text);
},
);
final showHistory =
!searchTextIsNotEmpty && (searchHistory.value.isNotEmpty);
@@ -29,6 +29,7 @@ import 'package:weblibre/features/geckoview/features/search/domain/providers/sea
import 'package:weblibre/features/geckoview/features/search/presentation/widgets/search_modules/search_module_section.dart';
import 'package:weblibre/features/geckoview/utils/image_helper.dart';
import 'package:weblibre/presentation/hooks/cached_future.dart';
import 'package:weblibre/presentation/hooks/on_listenable_change_selector.dart';
import 'package:weblibre/presentation/widgets/failure_widget.dart';
import 'package:weblibre/presentation/widgets/safe_raw_image.dart';
import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart';
@@ -48,13 +49,17 @@ class HistorySuggestions extends HookConsumerWidget {
final historySuggestionsAsync = ref.watch(engineHistorySuggestionsProvider);
final totalResults = historySuggestionsAsync.value?.length ?? 0;
useOnListenableChange(searchTextListenable, () async {
if (ref.exists(engineSuggestionsProvider)) {
await ref
.watch(engineSuggestionsProvider.notifier)
.addQuery(searchTextListenable.value.text);
}
});
useOnListenableChangeSelector(
searchTextListenable,
() => searchTextListenable.value.text,
() async {
if (ref.exists(engineSuggestionsProvider)) {
await ref
.read(engineSuggestionsProvider.notifier)
.addQuery(searchTextListenable.value.text);
}
},
);
if (historySuggestionsAsync.hasValue &&
(historySuggestionsAsync.value.isEmpty)) {
@@ -40,6 +40,7 @@ import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selec
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab_search.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/container_chips.dart';
import 'package:weblibre/presentation/hooks/on_listenable_change_selector.dart';
import 'package:weblibre/presentation/widgets/safe_raw_image.dart';
import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart';
import 'package:weblibre/presentation/widgets/url_icon.dart';
@@ -109,21 +110,27 @@ class TabSearch extends HookConsumerWidget {
}),
);
useOnListenableChange(searchTextListenable, () async {
if (ref.exists(tabSearchRepositoryProvider(TabSearchPartition.search))) {
await ref
.read(
tabSearchRepositoryProvider(TabSearchPartition.search).notifier,
)
.addQuery(
searchTextListenable.value.text,
// ignore: avoid_redundant_argument_values dont break things
matchPrefix: _matchPrefix,
// ignore: avoid_redundant_argument_values dont break things
matchSuffix: _matchSuffix,
);
}
});
useOnListenableChangeSelector(
searchTextListenable,
() => searchTextListenable.value.text,
() async {
if (ref.exists(
tabSearchRepositoryProvider(TabSearchPartition.search),
)) {
await ref
.read(
tabSearchRepositoryProvider(TabSearchPartition.search).notifier,
)
.addQuery(
searchTextListenable.value.text,
// ignore: avoid_redundant_argument_values dont break things
matchPrefix: _matchPrefix,
// ignore: avoid_redundant_argument_values dont break things
matchSuffix: _matchSuffix,
);
}
},
);
if (tabSearchResults.value.isEmpty) {
return const SliverToBoxAdapter(child: SizedBox.shrink());
@@ -30,6 +30,7 @@ import 'package:weblibre/features/bangs/domain/providers/search.dart';
import 'package:weblibre/features/bangs/domain/repositories/data.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart';
import 'package:weblibre/features/geckoview/features/search/presentation/dialogs/reset_bang_dialog.dart';
import 'package:weblibre/presentation/hooks/on_listenable_change_selector.dart';
import 'package:weblibre/presentation/widgets/selectable_chips.dart';
import 'package:weblibre/presentation/widgets/url_icon.dart';
import 'package:weblibre/utils/uri_parser.dart' as uri_parser;
@@ -94,9 +95,15 @@ class SmartBangSelector extends HookConsumerWidget {
final globalBangs = searchBangs.isNotEmpty ? searchBangs : frequentBangs;
// Trigger search when text changes
useOnListenableChange(searchTextController, () {
ref.read(seamlessBangProvider.notifier).search(searchTextController.text);
});
useOnListenableChangeSelector(
searchTextController,
() => searchTextController.text,
() {
ref
.read(seamlessBangProvider.notifier)
.search(searchTextController.text);
},
);
// Determine if we should show tabs
final showTabs = isEditMode && siteBangs.isNotEmpty;
@@ -41,7 +41,7 @@ final class TopSiteRepositoryProvider
}
}
String _$topSiteRepositoryHash() => r'e32ddb20e61fcd3830c4eb88704824a57e7a536a';
String _$topSiteRepositoryHash() => r'8aa231fadabe1e115d92cd37e032f27ba8da55e6';
abstract class _$TopSiteRepository extends $Notifier<void> {
void build();