Add weighted search

Fixes #74
This commit is contained in:
Henry Hiles 2026-11-03 15:50:46 -05:00
commit 20eed13884
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
2 changed files with 142 additions and 116 deletions

View file

@ -1,6 +1,7 @@
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:material_ui/material_ui.dart";
import "package:nexus/helpers/extensions/weighted_search.dart";
/// The [chosen] items, above a field searching [options] to choose more.
class const SearchChooser({
@ -30,15 +31,10 @@ class const SearchChooser({
}) extends HookWidget {
@override
Widget build(BuildContext context) {
final query = useValueListenable(controller).text.trim().toLowerCase();
final query = useValueListenable(controller).text;
final shown = options
.where(
(id) =>
!chosen.contains(id) &&
(nameOf(id).toLowerCase().contains(query) ||
id.toLowerCase().contains(query)),
)
.toIList();
.where((id) => !chosen.contains(id))
.search(query, (id) => [(nameOf(id), 2), (id, 1)]);
ListTile tileOf(String id, {Widget? trailing, VoidCallback? onTap}) =>
ListTile(

View file

@ -7,6 +7,7 @@ import "package:hooks_riverpod/misc.dart";
import "package:m3e_card_list/m3e_card_list.dart";
import "package:navigation_rail_m3e/navigation_rail_m3e.dart";
import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/weighted_search.dart";
import "package:nexus/models/settings_category.dart";
import "package:nexus/widgets/pages/settings_category.dart";
import "package:nexus/widgets/divider_text.dart";
@ -43,32 +44,22 @@ class const SettingsLayout({
// Only searched once shown, by which point these have loaded
final categories =
ref.read(sectionsProvider).value ?? const IMap.empty();
final query = controller.text.toLowerCase();
final matches = categories.values.flattenedToList
.asMap()
.entries
.expand(
(categoryEntry) => categoryEntry.value.settings
.asMap()
.entries
.where(
(settingEntry) =>
settingEntry.value.title.toLowerCase().contains(
query,
) ||
settingEntry.value.description
.toLowerCase()
.contains(query),
)
.map(
(categoryEntry) =>
categoryEntry.value.settings.asMap().entries.map(
(settingEntry) => (
(categoryEntry.key, settingEntry.key),
settingEntry.value,
),
),
)
.toIList();
.search(
controller.text,
(match) => [(match.$2.title, 2), (match.$2.description, 1)],
);
return matches.map(
(match) => ListTile(