word_search: add history count bubble in appbar
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
|
||||
- Added infinite scroll for word search results.
|
||||
- Added infinite scroll for history view.
|
||||
- Added a bubble in the top bar of word search results to indicate how many times it has been searched before.
|
||||
|
||||
## Changes 🔧
|
||||
|
||||
|
||||
@@ -8,17 +8,20 @@ class HistoryEntry {
|
||||
final String? kanji;
|
||||
final String? word;
|
||||
final DateTime lastTimestamp;
|
||||
final int? timestampCount;
|
||||
int? _timestampCount;
|
||||
|
||||
/// Whether this item is a kanji search or a word search
|
||||
bool get isKanji => word == null;
|
||||
|
||||
int? get timestampCount => _timestampCount;
|
||||
|
||||
HistoryEntry.withKanji({
|
||||
required this.id,
|
||||
required this.kanji,
|
||||
required this.lastTimestamp,
|
||||
this.timestampCount,
|
||||
timestampCount,
|
||||
}) : word = null,
|
||||
_timestampCount = timestampCount,
|
||||
assert(
|
||||
kanji!.runes.length == 1,
|
||||
'Kanji must be a single character',
|
||||
@@ -28,8 +31,9 @@ class HistoryEntry {
|
||||
required this.id,
|
||||
required this.word,
|
||||
required this.lastTimestamp,
|
||||
this.timestampCount,
|
||||
timestampCount,
|
||||
}) : kanji = null,
|
||||
_timestampCount = timestampCount,
|
||||
assert(
|
||||
word == word!.trim(),
|
||||
'Word must not contain leading or trailing whitespace',
|
||||
@@ -67,6 +71,40 @@ class HistoryEntry {
|
||||
: null,
|
||||
);
|
||||
|
||||
static Future<HistoryEntry?> getWord(
|
||||
DatabaseExecutor db,
|
||||
String word,
|
||||
) async {
|
||||
final result = await db.query(
|
||||
HistoryTableNames.historyEntryOrderedByTimestamp,
|
||||
where: 'word = ?',
|
||||
whereArgs: [word],
|
||||
);
|
||||
|
||||
if (result.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return HistoryEntry.fromDBMap(result.first);
|
||||
}
|
||||
|
||||
static Future<HistoryEntry?> getKanji(
|
||||
DatabaseExecutor db,
|
||||
String kanji,
|
||||
) async {
|
||||
final result = await db.query(
|
||||
HistoryTableNames.historyEntryOrderedByTimestamp,
|
||||
where: 'kanji = ?',
|
||||
whereArgs: [kanji],
|
||||
);
|
||||
|
||||
if (result.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return HistoryEntry.fromDBMap(result.first);
|
||||
}
|
||||
|
||||
// TODO: There is a lot in common with
|
||||
// insertKanji,
|
||||
// insertWord,
|
||||
@@ -395,6 +433,19 @@ class HistoryEntry {
|
||||
return result.map((e) => HistoryEntry.fromDBMap(e)).toList();
|
||||
}
|
||||
|
||||
Future<void> populateTimestampCount(DatabaseExecutor db) async {
|
||||
final result = await db.query(
|
||||
columns: ['COUNT(*) AS count'],
|
||||
HistoryTableNames.historyEntryTimestamp,
|
||||
where: 'entryId = ?',
|
||||
whereArgs: [id],
|
||||
);
|
||||
|
||||
final count = result.firstOrNull?['count'] as int? ?? 0;
|
||||
|
||||
_timestampCount = count;
|
||||
}
|
||||
|
||||
Future<void> delete(DatabaseExecutor db) => db.delete(
|
||||
HistoryTableNames.historyEntry,
|
||||
where: 'id = ?',
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:jadb/models/word_search/word_search_result.dart';
|
||||
import 'package:jadb/search.dart' show JaDBConnection;
|
||||
import 'package:mdi/mdi.dart';
|
||||
import 'package:mugiten/bloc/theme/theme_bloc.dart';
|
||||
import 'package:mugiten/components/search/search_results_body/parts/circle_badge.dart';
|
||||
import 'package:mugiten/models/history/history_entry.dart';
|
||||
import 'package:mugiten/services/snackbar.dart';
|
||||
import 'package:mugiten/settings.dart';
|
||||
@@ -28,6 +31,7 @@ class WordSearchResultPage extends StatefulWidget {
|
||||
|
||||
class _WordSearchResultPageState extends State<WordSearchResultPage> {
|
||||
bool addedToDatabase = false;
|
||||
HistoryEntry? historyEntry;
|
||||
|
||||
late final _pagingController = PagingController<int, WordSearchResult>(
|
||||
getNextPageKey: (state) =>
|
||||
@@ -50,8 +54,25 @@ class _WordSearchResultPageState extends State<WordSearchResultPage> {
|
||||
HistoryEntry.insertWord(
|
||||
db: GetIt.instance.get<Database>(),
|
||||
word: widget.searchTerm,
|
||||
).then((historyEntry) async {
|
||||
await historyEntry
|
||||
.populateTimestampCount(GetIt.instance.get<Database>());
|
||||
return historyEntry;
|
||||
}).then(
|
||||
(entry) => setState(() {
|
||||
addedToDatabase = true;
|
||||
historyEntry = entry;
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
HistoryEntry.getWord(
|
||||
GetIt.instance.get<Database>(),
|
||||
widget.searchTerm,
|
||||
).then(
|
||||
(entry) => setState(() {
|
||||
historyEntry = entry;
|
||||
}),
|
||||
);
|
||||
addedToDatabase = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +94,16 @@ class _WordSearchResultPageState extends State<WordSearchResultPage> {
|
||||
onPressed: () =>
|
||||
showSnackbar(context, 'History tracking is disabled'),
|
||||
),
|
||||
if (historyEntry != null && (historyEntry!.timestampCount ?? 0) > 1)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: BlocBuilder<ThemeBloc, ThemeState>(
|
||||
builder: (context, themeState) => CircleBadge(
|
||||
color: themeState.theme.menuGreyNormal.background,
|
||||
child: Text('${historyEntry!.timestampCount}'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: FutureBuilder(
|
||||
|
||||
Reference in New Issue
Block a user