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)
Do not pass Context into any provider.
Handle logic and BotToasts in the onTap of the AboutScreen, not inside the provider.
Also make _checkUpdate() more efficient, by fetching the `/latest` API.
Before, it was fetching 10 releases and immediately discarding 9, only leaving the latest.
When this setting is turned on, the app will automatically mark chapters as read if they have the same chapter number as a chapter you've already read.
This is useful for sources where:
- multiple scanlators upload the same chapter number
- duplicate entries appear due to different naming formats
- a series has repeated or alternative releases of the same chapter
With the setting enabled, you won’t have to manually mark each duplicate as read.
- Replace synchronous Isar writes with async transactions in `AboutScreen`
- Make `AppLogger.init()` and `dispose()` fully async and guarded with `_busy`
- Remove `StreamController` queue and write directly to the IOSink
- Prevent double‑initialization and race conditions using `_initialized` + `_busy`
- Update log toggle handler to await logger init/dispose for consistency
Fix following Exception when disabling and re-enabling logger:
```
Exception has occurred.
StateError (Bad state: Stream has already been listened to.)
```
- 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.
- Implemented multiple navigation layouts for the reader, including L-shaped, Kindle, Edge, Right & Left, and Disabled modes.
- Added settings for keeping the screen on, showing page gaps, and adjusting webtoon side padding.
- Enhanced the reader settings modal to include new options and improved UI for navigation layout selection.
- Color filters: invert/gray/BCS
- Added AppLockScreen for biometric authentication to unlock the app.
- Introduced security settings screen to enable/disable app lock.
- Integrated local_auth package for biometric authentication support.
- Created security state providers to manage app lock state.
- Updated chapter list tile widget to support dismiss actions for bookmarking and marking chapters as read.
- Enhanced CBZ conversion process to include ComicInfo.xml metadata.
- Added conditional UI elements based on platform capabilities.
- Added Completed & Tracked filter in library
- Remove unnecessary email and password state variables
- Rely on TextEditingController values instead of onChanged
- Reset error state on submit
- Dispose controllers after dialog closes
- Wrap login fields in AutofillGroup and add proper autofillHints for email, username, and password
- Add keyboard types, textInputAction, and onFieldSubmitted to improve form navigation
- Disable suggestions/autocorrect for password field
- Add TextInput.finishAutofillContext() on successful login
- Improve focus handling and overall form behavior
- Minor UI/structure adjustments for clarity and consistency