Skip to content

Commit b1508f6

Browse files
committed
feat: full and prefix partial text search and ranking on history items
1 parent 5a7da54 commit b1508f6

File tree

4 files changed

+317
-46
lines changed

4 files changed

+317
-46
lines changed

lib/presentation/history/bloc/history_bloc.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ class HistoryBloc extends Bloc<HistoryEvent, HistoryState> {
2929
}
3030
}
3131

32-
void _onHistoryFiltered(
32+
Future<void> _onHistoryFiltered(
3333
HistoryFiltered event,
3434
Emitter<HistoryState> emit,
35-
) {
36-
final filteredHistory = historyRepository.filterHistory(
35+
) async {
36+
final filteredHistory = await historyRepository.filterHistory(
3737
state.history,
3838
event.query,
3939
);

lib/presentation/history/repositories/history_repository.dart

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,31 +27,31 @@ final class HistoryRepository {
2727
);
2828
}
2929

30-
List<TranslationHistory> filterHistory(
30+
Future<List<TranslationHistory>> filterHistory(
3131
List<TranslationHistory> history,
3232
String query,
33-
) {
33+
) async {
3434
if (query.isEmpty) return history;
3535

36-
final queryLower = query.toLowerCase().trim();
37-
38-
final matchingInput = history.where((entry) {
39-
final inputLower = entry.input.toLowerCase();
40-
return inputLower.contains(queryLower);
41-
}).toList();
42-
43-
// Include entries where any translation output matches the query
44-
// TODO: Rank the outputs based on how well the output matches the query
45-
final matchingOutput = history.where((entry) {
46-
// ignore: omit_local_variable_types
47-
for (final TranslationHistoryItem item in entry.items ?? []) {
48-
if (item.output.toLowerCase().contains(queryLower)) {
49-
return true;
50-
}
51-
}
52-
return false;
36+
final ftsQuery = '${query.trim()}*';
37+
final likeQuery =
38+
// ignore: lines_longer_than_80_chars
39+
'${query.trim().replaceAllMapped(RegExp(r'[\\%_]'), (m) => '\\${m[0]}')}%';
40+
41+
final result =
42+
await appDatabase.queryTranslationHistory(ftsQuery, likeQuery).get();
43+
44+
if (result.isEmpty) return [];
45+
// Convert the result to TranslationHistory objects
46+
return result.map((entry) {
47+
return TranslationHistory(
48+
id: entry.id,
49+
timestamp: DateTime.parse(entry.createdAt),
50+
input: entry.input,
51+
items: TranslationHistoryItem.stringToList(
52+
entry.historyItems,
53+
),
54+
);
5355
}).toList();
54-
55-
return <TranslationHistory>{...matchingInput, ...matchingOutput}.toList();
5656
}
5757
}

lib/presentation/history/view/history_page.dart

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,24 @@ class _HistoryViewState extends State<HistoryView> {
6666
current.status != HistoryStatus.loading,
6767
builder: (context, state) {
6868
if (state.status == HistoryStatus.error) {
69-
return Center(
70-
child: Text(
71-
'Failed to load history',
72-
style: AppTextStyle.displayXS.medium.copyWith(
73-
color: theme.errorColor,
69+
return SliverToBoxAdapter(
70+
child: Center(
71+
child: Text(
72+
'Failed to load history',
73+
style: AppTextStyle.displayXS.medium.copyWith(
74+
color: theme.errorColor,
75+
),
7476
),
7577
),
7678
);
7779
} else if (state.history.isEmpty) {
78-
return Center(
79-
child: Text(
80-
'No history available',
81-
style: AppTextStyle.displayXS.medium.copyWith(
82-
color: theme.textColor1,
80+
return SliverToBoxAdapter(
81+
child: Center(
82+
child: Text(
83+
'No history available',
84+
style: AppTextStyle.displayXS.medium.copyWith(
85+
color: theme.textColor1,
86+
),
8387
),
8488
),
8589
);

0 commit comments

Comments
 (0)