Commit graph

1084 commits

Author SHA1 Message Date
Moustapha Kodjo Amadou
f93d9277b9 refactor: simplify cover image resolution in LibraryGridViewWidget and LibraryListViewWidget 2026-05-11 09:04:37 +01:00
Moustapha Kodjo Amadou
2d9eebe94e
Merge pull request #728 from mrandhawa14/fix/cover-decode-resize
perf(library): decode covers at thumbnail resolution to cut image-cache RAM (refs #609)
2026-05-11 09:00:42 +01:00
Moustapha Kodjo Amadou
6449cfdd5a + 2026-05-11 08:59:18 +01:00
Mehakdeep Singh
e16f0ba9be Merge remote-tracking branch 'upstream/main' into fix/cover-decode-resize
# Conflicts:
#	lib/modules/library/widgets/library_gridview_widget.dart
#	lib/modules/library/widgets/library_listview_widget.dart
2026-05-11 00:56:00 -07:00
Moustapha Kodjo Amadou
a961834a88
Merge pull request #723 from NBA2K1/reduce-code-duplication
New file to reduce code duplication
2026-05-11 08:42:22 +01:00
Moustapha Kodjo Amadou
b507f17846
Merge pull request #719 from NBA2K1/main
Make Code More Readable
2026-05-11 08:38:24 +01:00
Mehakdeep Singh
861ee65113 perf(library): decode covers at thumbnail resolution to cut image-cache RAM
Refs #609 (high RAM with stutters).

Manga / anime covers from sources are typically 720x1080 or larger
(~3 MB decoded RGBA per cover). The library grid, library list and
generic browse / search card widgets render those covers at roughly
150x220 logical pixels, but every cover decoded to its full source
resolution and that decoded bitmap landed in Flutter's `imageCache`.
With 30-50 covers in flight during a normal scroll, the default
100 MB cache filled and the engine started evicting + re-decoding
aggressively — exactly the symptom in #609 (stutters + high RAM).

Mangayomi already had `ExtendedResizeImage` available via the
`extended_image_library` package and used it in one place
(`cachedCompressedNetworkImage`, called only from the History
screen). This commit generalises that pattern.

Add a `coverProvider()` helper in `lib/utils/cached_network.dart`
that wraps `CustomExtendedNetworkImageProvider` in
`ExtendedResizeImage` with a 200 KB encoded budget — sharp at
typical thumbnail size on high-DPR screens, ~3.6x smaller decoded
than a full-resolution cover. Pass through the same `cache` /
`cacheMaxAge` knobs the underlying provider exposes so existing
disk-cache behaviour is preserved.

Swap the three high-traffic thumbnail call sites to use it:

* `lib/modules/library/widgets/library_gridview_widget.dart`
* `lib/modules/library/widgets/library_listview_widget.dart`
* `lib/modules/widgets/manga_image_card_widget.dart`
  (both `MangaImageCardWidget` and `MangaImageCardListTileWidget`,
  used by browse and search results)

Deliberately not changed:

* The manga / anime detail page hero cover — large display, full
  resolution is appropriate.
* Reader pages — already memory-managed by `ChapterPreloadManager`
  and need full resolution for actual reading.
* `cachedNetworkImage()` and other lower-traffic thumbnail surfaces
  (tracker results, calendar, recommendation grid). Easy to extend
  in a follow-up if anyone asks; kept narrow here so review is
  manageable.

Verified

* `flutter analyze` clean on every touched file
* `flutter build macos --release` succeeds
* Smoke-tested on macOS with the local-all-fixes build: library
  grid, library list and browse card all render identical-looking
  covers at typical thumbnail sizes; no visible quality regression
  at the displayed scale
2026-05-09 23:37:44 -07:00
Mehakdeep Singh
8babb975ab perf(statistics): aggregate inside Isar instead of loading all favourite chapters into memory
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)
2026-05-09 23:12:49 -07:00
NBA2K1
116ade1bb8 New file to reduce code duplication
+ listview now also shows unread chapter count like gridview did, instead of all chapter count.
2026-05-09 18:11:20 +02:00
Moustapha Kodjo Amadou
25c1d72c8b
Merge pull request #721 from NBA2K1/improve-updates
Improve updates
2026-05-09 16:07:59 +01:00
Moustapha Kodjo Amadou
e2d67e7157
Merge pull request #722 from NBA2K1/fix-exception
Fix null pointer exception
2026-05-09 16:06:47 +01:00
NBA2K1
caab37f662 Fix null pointer exception 2026-05-09 15:03:26 +02:00
NBA2K1
1b630954b1 Remove BuildContext from checkForUpdate Provider
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.
2026-05-09 12:59:32 +02:00
NBA2K1
27690975f4 Make _updateLibrary() fully async
also make _updateNumbers() readable.
2026-05-09 12:59:12 +02:00
NBA2K1
e0a8301595 Use downloadedChapterIdsProvider instead
Use the already-existing downloadedChapterIdsProvider and do a simple Set.contains() lookup instead of the synchronous Isar query.

Performance improvement
2026-05-09 12:52:26 +02:00
NBA2K1
134a7406d9 remove redundant helper 2026-05-09 00:36:16 +02:00
NBA2K1
0316afc2c6 Make downloadedChapterIdsProvider async 2026-05-09 00:33:36 +02:00
NBA2K1
8914357d89 Fix downloadedChapterIdsProvider
Before the "Downloaded" filter in the library AND the "Downloaded chapters" badge would not update, unless you restart the app.

Now the "Downloaded chapters" badge is increasing and the "Downloaded" filter is showing new downloaded manga chapters and hiding deleted manga chapters.
2026-05-08 23:52:52 +02:00
NBA2K1
b799a6d377 No need to reverse the list
before:
list, reverse, first
list, reverse, last

after:
list, last
list, first

Small performance improvement
2026-05-07 21:29:59 +02:00
NBA2K1
40fce92a87 Extract helper to keep build() readable
No functional change. Just a simple method extraction from inside `itemBuilder` to `_buildPagedItem()`.
2026-05-07 21:28:41 +02:00
NBA2K1
0672d1de67 Reduce Code Duplication 2026-05-07 19:14:50 +02:00
NBA2K1
3fe74f8883 Clean up _setReaderMode()
No logic difference. This refactor just makes the helper more readable.
2026-04-30 19:47:46 +02:00
NBA2K1
ffe8dee065 Improve _handlePageNavigation()
The helper now handles every continuous reading mode, not just webtoon.
2026-04-30 17:32:15 +02:00
NBA2K1
456589ccfb Use ReaderModeExtension 2026-04-30 17:30:43 +02:00
abdelmonm alsnajleh
bc19e2d7cb
Merge branch 'main' into QoF-webtoon 2026-04-30 14:25:01 +03:00
Moustapha Kodjo Amadou
8c416563d4
Merge pull request #714 from NBA2K1/main
Rewrite chapter update & sorting; Preserve read state & improve ChapterRecognition & adapt EndOfMangaCard to reading mode & Clean Code
2026-04-30 11:19:11 +01:00
Abdelmonm Alsnajleh
0894b94767 Refactor page navigation handling in reader view
* Extracted page navigation to its own function
* Allow for better reading in webtoon mode
2026-04-27 20:14:24 +03:00
NBA2K1
8d5aa05952 Fix potential focus flicker
`wrapWithKeyboardListener` creates a `FocusNode()` internally on every call.
If no `focusNode` is passed, a new one is allocated every rebuild, which can cause focus flicker.
Without this fix, keyboard focus can be intermittently lost after widget rebuilds,
which would silently swallow keyboard shortcuts.
2026-04-27 00:08:15 +02:00
NBA2K1
ad4207da82 Add Copilot suggested change
https://github.com/kodjodevf/mangayomi/pull/714#discussion_r3144115142
2026-04-26 23:57:23 +02:00
NBA2K1
49eed6405e Add Copilot suggested change
https://github.com/kodjodevf/mangayomi/pull/714#discussion_r3144115192
2026-04-26 23:41:35 +02:00
NBA2K1
64d22741a5 Add Copilot suggested change
https://github.com/kodjodevf/mangayomi/pull/714#discussion_r3144115131
2026-04-26 23:33:14 +02:00
NBA2K1
262fb44792 Add Copilot suggested change
https://github.com/kodjodevf/mangayomi/pull/714#discussion_r3144115200
2026-04-26 23:26:01 +02:00
NBA2K1
bf25129b56 Reduce Code Duplication in novel_reader_view.dart
`ReaderKeyboardHandler` already does everything here.
2026-04-26 23:12:54 +02:00
NBA2K1
da2682aa48 Reduce Code Duplication
by extracting `hasPreviousChapter` and `hasNextChapter` logic to the `ChapterControllerMixin`.
2026-04-26 22:49:34 +02:00
NBA2K1
8a2a57fbe5 Reduce Code Duplication in novel_reader_view.dart
Why duplicate the entire `ReaderAutoScrollButton` inside novel_reader_view.dart??
2026-04-26 21:55:00 +02:00
NBA2K1
b1459cffc1 Reduce Code Duplication in novel_reader_view.dart
Why duplicate the entire `ReaderAppBar` inside novel_reader_view.dart??
2026-04-26 21:50:22 +02:00
NBA2K1
dd6ff2580a Use Platform Helper Everywhere Possible 2026-04-26 19:42:13 +02:00
NBA2K1
5bab1492a4 Reduce Code Duplication by Adding Platform Helper
New file `lib/utils/platform_utils.dart`, to stop defining isDesktop and isMobile everywhere.
2026-04-26 19:39:14 +02:00
NBA2K1
ffa8f15c88 ReaderModeExtension to Reduce Code Duplication 2026-04-26 19:12:56 +02:00
NBA2K1
c09eb5351d Reduce Code Duplication in reader_view.dart
extract helper method `_goToChapter()`, to reduce the code duplication in the `ReaderKeyboardHandler` and `ReaderBottomBar` call.
2026-04-26 17:45:02 +02:00
NBA2K1
3aa5c73dba Improve Performance
`_resize(fit)` was called on every Build.
`_resize` posts a WidgetsBinding frame callback unconditionally. If fit hasn't changed, this is wasted work.
2026-04-26 17:38:26 +02:00
NBA2K1
47f3296e9e Reduce Code Duplication in anime_player_view.dart
by extracting the helper methods `_seekTo()` and `_seekBy()`.
This also fixes a potential bug, where in line 1634 it was calculated
`skipDuration - _currentPosition.value.inSeconds` instead of the other way around.
That doesn't make sense. If currentPosition = 120 and skipDuration = 10, this becomes:
`_tempPosition = Duration(seconds: 10 - 120)`; so `= Duration(seconds: -110)`
A negative duration makes no sense as a UI indicator of a seek target.
2026-04-26 15:28:52 +02:00
NBA2K1
cc189fd4e3 Reduce Code Duplication in anime_player_view.dart
by extracting the same MPV Event Handler Boilerplate into a helper method.
2026-04-26 15:05:23 +02:00
NBA2K1
b05c17518f Reduce Code Duplication Across 3 Files
By extracting:

```
      SystemChrome.setEnabledSystemUIMode(
        SystemUiMode.manual,
        overlays: SystemUiOverlay.values,
      );
```

to a file `system_ui.dart` and calling the method `restoreSystemUI()`
2026-04-26 15:03:56 +02:00
NBA2K1
d625b9c77d Improve EndOfMangaCard
When in vertical mode, it now shows the last_page icon pointing down,
2026-04-26 03:32:28 +02:00
NBA2K1
a9e307b2a4 Add multi-season support and split parse methods
Changes:
- Add season-keyword regex (staffel, season, saison, temporada) and
  episode-keyword regex (folge, episode, ep.) to reliably extract
  the correct number regardless of name format
- parseChapterNumber() now encodes season context into the sort key
  (season * 100000 + episode) so multi-season anime sort correctly
  across seasons without mixing episode numbers
- Add parseEpisodeNumber() which strips season context and returns
  only the episode number within a season; use this for tracker
  updates (MAL/AniList/Kitsu) and AniSkip lookups, where the tracker
  entry is already season-specific
- Switch updateTrackChapterRead and getAniSkipResults to
  parseEpisodeNumber to fix incorrect episode reporting for
  multi-season anime
- Compile all RegExp objects as static finals instead of per-call
  instantiation
- Refactor duplicated parse logic into a single private _parse()
  method with an applySeason flag
2026-04-26 03:32:28 +02:00
NBA2K1
29f202d31d remove redundant methods
`_getFilteredAndSortedChapters()` and `_filterAndSortChapter()` are duplicates of `getFilteredChapterList()` from MangaExtensions.
2026-04-26 03:25:59 +02:00
NBA2K1
4a44dd6658 New Setting: Mark duplicate chapter as read
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.
2026-04-26 01:09:28 +02:00
NBA2K1
8ac25750a2 Why reverse the reading list and then calculate -1 2026-04-25 00:15:13 +02:00
NBA2K1
3434a2b16e Add Copilot suggested change (3/3)
https://github.com/kodjodevf/mangayomi/pull/714#discussion_r3139743045
2026-04-24 22:23:57 +02:00