MangasListState previously stored selected manga IDs as List<int>.
Every visible library card called .contains() on that list once per
rebuild to determine its highlight state, making each check O(n) in
the number of selected items. The provider's own update/selectAll/
selectSome methods also used .contains() and .remove() on a List.
Change the state type to Set<int> throughout, making all membership
checks O(1). Updated all consumers: library_gridview_widget,
library_listview_widget, library_app_bar, library_dialogs, and
MangasSetIsReadState.
Problem
- Genz Updates could still return 403/Cloudflare in extension requests even when the same site worked in WebView.
- The extension HTTP path primarily used cached cookies from settings instead of reliably using the current WebView cookie jar.
- Cloudflare challenge detection was too narrow (mostly English markers) and could misclassify localized challenge pages as resolved.
Solution
- Prefer live WebView cookies for outgoing extension HTTP requests, with settings cookies as fallback.
- Sync cookie + user-agent from embedded WebView on onLoadStop in addition to history updates.
- Expand challenge detection with additional multilingual and platform markers.
Implementation Details
- Updated `MCookieManager.interceptRequest` to read cookies from `CookieManager.getCookies()` and use them as the request `Cookie` header when available.
- Added `_syncCookieAndUaFromWebView()` in `MangaWebView` and invoked it from both `onLoadStop` and `onUpdateVisitedHistory` to persist fresh clearance cookies immediately.
- Introduced a centralized `_cloudflareChallengePattern` in `m_client.dart` and reused it in `_containsCloudflareChallengeHtml` and `_isCloudflareChallengePage` for consistent challenge detection.
- prevent stale settings overwrites by reloading settings inside Isar write txn before updating chapterPageUrlsList
- guard getPageLength() against missing chapter entries and empty urls to avoid No element crashes
- simplify read-threshold calculation in setPageIndex() via totalPages/pagesRemaining for continuous and paged modes
- map visible continuous double-page indices to actual page indices in _readProgressListener before persisting progress
- snapshot item positions and clamp indices during fast scrolling to avoid volatile first/last access races
This commit improves memory management, reduces redundant interpreter
instantiation, and standardizes service usage patterns.
- Add `dispose()` to `ExtensionService` interface and implement it across
Dart, JS, LNReader, and Mihon services.
- Replace repeated interpreter creation in `DartExtensionService` with a
persistent `_interpreter` instance initialized once in the constructor.
- Add disposal logic for JS DOM selector and Cheerio instances to prevent
memory leaks.
- Introduce `withExtensionService()` helper to ensure services are always
disposed after use.
- Update call sites across the codebase to use `withExtensionService()`
or manual try/finally disposal.
- Improve isolate service message handling by extracting `responsePort`
earlier.
- Ensure safer defaults (e.g., returning empty lists, const lists) when
service calls fail.
- Add StreamSubscription to manage ReceivePort listener lifecycle
- Introduce handshake timeout when waiting for isolate SendPort to prevent hangs
- Add response timeout in get<T>() to avoid indefinitely waiting for isolate replies
- Replace direct ReceivePort.listen with tracked subscription for safer cleanup
- Improve error handling for invalid or missing isolate responses
- Strengthen isolate startup reliability and shutdown consistency
- Use the logger to log failed updates
- After the update-botToast another botToast is being spawned to show exactly which manga(s) couldn't be updated.
- Remove the doWhile loop because it is unnecessary. The condition of mangaList.length == numbers is always true, meaning it only runs once.