Fixes#543. The statistics screen was crashing on Android with a
populated library (~75+ favourites). Repro: open the More -> Statistics
screen on an account with a real-sized library; app hangs and is
killed.
Cause is in `getStatistics()` in `lib/modules/more/statistics/
statistics_provider.dart`. The provider materialised every favourite
manga *and* every chapter of every favourite into a Dart list, then
walked the lists in memory:
final items = await isar.mangas.filter()...findAll();
final chapters = await isar.chapters.filter()
.manga((q) => q.favoriteEqualTo(true)...).findAll();
For a library with 75 favourites at ~100 chapters each that is 7,500+
heavy Isar objects materialised at once just to compute six counts.
The "completed items" loop then re-fetched chapters per completed
manga via the link relation (`item.chapters.toList()`), and the
reading-time aggregation loaded every History row to sum a single
int field.
This commit rewrites the function to do all aggregation inside Isar:
* total / read / completed-items / downloads => `count()` queries on
indexed filter chains. No object hydration.
* completed favourites => projects only the IDs (`idProperty()`),
then runs two cheap `count()` queries per item to check
"has-chapters && unread == 0". Logic is unchanged from the original
`every(isRead) && isNotEmpty` check.
* reading time => projects the `readingTimeSeconds` field
(`readingTimeSecondsProperty()`) and folds the resulting List<int?>.
Avoids hydrating full History rows.
Memory: peak in-memory list shrinks from ~7,500 fully-hydrated chapters
plus all favourite mangas plus all history rows, to (at most) the IDs
of completed favourites (typically <50 ints) and the projected reading-
time list (one int per history row, decoded as a flat list).
DB round-trips: the completed-items branch now does 2 round-trips per
completed favourite instead of 1 link-traversal per completed favourite.
On indexed `mangaId` + `isRead` filters this is sub-millisecond per
query and well below the cost of materialising thousands of rows. The
overall function is faster end-to-end on any non-trivial library.
Behaviour preserved: same six fields, same definitions, same edge
cases (manga with no chapters is not counted as completed).
Verified
- `flutter analyze` clean on the touched file
- `flutter build macos --release` succeeds
- Manual smoke test on macOS with the local-all-fixes build (the bug
reproduces only with a large mobile library; macOS desktop with a
small test library returns the same numbers as before)
- Added new localization strings for total, mean per title, completion rate, watching time, reading time, average chapters per title, read percentage, and entries in multiple languages.
- Enhanced the History model to include readingTimeSeconds.
- Updated AnimeStreamController and ReaderController to track reading time and save it to history.
- Implemented reading time tracking in Anime and Novel reader views.
- Introduced statistics calculations for total reading time across histories.
- Updated statistics screen to display total reading time and average reading time per title.
- new file item_type_filters.dart for the hiddenItemTypes function.
- reduces code duplication in statistics_screen, categories_screen and base_library_tab_screen
- Extract localizedItemType() from `base_library_tab_screen.dart`
- Add localizedSources() and localizedExtensions() for browse_screen.dart
- Reduce if-statements in statistics_screen.dart
- Reduce if-statements in categories_screen.dart
- Reduce if-statements in browse_screen.dart