From fc66e8ae2deefeae44c32b40dca9692d5b84d838 Mon Sep 17 00:00:00 2001 From: nyne Date: Tue, 11 Feb 2025 13:16:16 +0800 Subject: [PATCH 01/16] Fix getLocale --- lib/foundation/js_engine.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/foundation/js_engine.dart b/lib/foundation/js_engine.dart index 9a5e723..574c69c 100644 --- a/lib/foundation/js_engine.dart +++ b/lib/foundation/js_engine.dart @@ -156,7 +156,7 @@ class JsEngine with _JSEngineApi, JsUiApi { case "UI": return handleUIMessage(Map.from(message)); case "getLocale": - return "${App.locale.languageCode}-${App.locale.countryCode}"; + return "${App.locale.languageCode}_${App.locale.countryCode}"; case "getPlatform": return Platform.operatingSystem; } From 9fb63e47ea82e0fc3254c1be306bad67161d196b Mon Sep 17 00:00:00 2001 From: nyne Date: Tue, 11 Feb 2025 13:23:51 +0800 Subject: [PATCH 02/16] Fix deleting comic in favorites page. --- lib/components/comic.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/comic.dart b/lib/components/comic.dart index 97d5ecd..cf3f4a2 100644 --- a/lib/components/comic.dart +++ b/lib/components/comic.dart @@ -742,7 +742,7 @@ class _SliverGridComicsState extends State { @override void didUpdateWidget(covariant SliverGridComics oldWidget) { - if (oldWidget.comics != widget.comics) { + if (oldWidget.comics.isEqualsTo(widget.comics)) { comics.clear(); for (var comic in widget.comics) { if (isBlocked(comic) == null) { From 5c7cd7a30494e2a8710ec982ed81140d0400d7ff Mon Sep 17 00:00:00 2001 From: nyne Date: Tue, 11 Feb 2025 13:51:19 +0800 Subject: [PATCH 03/16] Improve multi-folder favorites management. --- assets/translation.json | 20 ++++- lib/foundation/comic_source/favorites.dart | 3 + lib/foundation/comic_source/parser.dart | 2 + lib/pages/comic_page.dart | 38 ++++++++ .../favorites/network_favorites_page.dart | 86 +++++++++---------- 5 files changed, 98 insertions(+), 51 deletions(-) diff --git a/assets/translation.json b/assets/translation.json index 73d62cc..7be6c04 100644 --- a/assets/translation.json +++ b/assets/translation.json @@ -139,8 +139,8 @@ "Block": "屏蔽", "Add new favorite to": "添加新收藏到", "Move favorite after reading": "阅读后移动收藏", - "Delete folder?" : "刪除文件夾?", - "Delete folder '@f' ?" : "删除文件夹 '@f' ?", + "Delete folder?" : "删除文件夹?", + "Delete folder '@f' ?" : "删除文件夹 '@f' ?", "Import from file": "从文件导入", "Failed to import": "导入失败", "Cache Limit": "缓存限制", @@ -324,7 +324,13 @@ "Success": "成功", "Compressing": "压缩中", "Exporting": "导出中", - "Search Sources": "搜索源" + "Search Sources": "搜索源", + "Removed": "已移除", + "Added to favorites": "已添加到收藏", + "Not added": "未添加", + "Create a folder": "新建收藏夹", + "Created successfully": "创建成功", + "name": "名称" }, "zh_TW": { "Home": "首頁", @@ -651,6 +657,12 @@ "Success": "成功", "Compressing": "壓縮中", "Exporting": "匯出中", - "Search Sources": "搜索源" + "Search Sources": "搜索源", + "Removed": "已移除", + "Added to favorites": "已添加到收藏", + "Not added": "未添加", + "Create a folder": "新建收藏夾", + "Created successfully": "創建成功", + "name": "名稱" } } \ No newline at end of file diff --git a/lib/foundation/comic_source/favorites.dart b/lib/foundation/comic_source/favorites.dart index a6423f5..fb845b6 100644 --- a/lib/foundation/comic_source/favorites.dart +++ b/lib/foundation/comic_source/favorites.dart @@ -37,6 +37,8 @@ class FavoriteData { final AddOrDelFavFunc? addOrDelFavorite; + final bool singleFolderForSingleComic; + const FavoriteData({ required this.key, required this.title, @@ -49,6 +51,7 @@ class FavoriteData { this.allFavoritesId, this.addOrDelFavorite, this.isOldToNewSort, + this.singleFolderForSingleComic = false, }); } diff --git a/lib/foundation/comic_source/parser.dart b/lib/foundation/comic_source/parser.dart index 4266193..08d399a 100644 --- a/lib/foundation/comic_source/parser.dart +++ b/lib/foundation/comic_source/parser.dart @@ -620,6 +620,7 @@ class ComicSourceParser { final bool multiFolder = _getValue("favorites.multiFolder"); final bool? isOldToNewSort = _getValue("favorites.isOldToNewSort"); + final bool? singleFolderForSingleComic = _getValue("favorites.singleFolderForSingleComic"); Future> retryZone(Future> Function() func) async { if (!ComicSource.find(_key!)!.isLogged) { @@ -773,6 +774,7 @@ class ComicSourceParser { deleteFolder: deleteFolder, addOrDelFavorite: addOrDelFavFunc, isOldToNewSort: isOldToNewSort, + singleFolderForSingleComic: singleFolderForSingleComic ?? false, ); } diff --git a/lib/pages/comic_page.dart b/lib/pages/comic_page.dart index 6bf09da..d1f2db9 100644 --- a/lib/pages/comic_page.dart +++ b/lib/pages/comic_page.dart @@ -614,8 +614,10 @@ abstract mixin class _ComicPageActions { update(); } + /// whether the comic is added to local favorite bool isAddToLocalFav = false; + /// whether the comic is favorite on the server bool isFavorite = false; FavoriteItem _toFavoriteItem() { @@ -1672,6 +1674,42 @@ class _NetworkFavoritesState extends State<_NetworkFavorites> { } Widget buildMultiFolder() { + if (widget.isFavorite == true && + widget.comicSource.favoriteData!.singleFolderForSingleComic) { + return Column( + children: [ + Expanded( + child: Center( + child: Text("Added to favorites".tl), + ), + ), + Center( + child: Button.filled( + isLoading: isLoading, + onPressed: () async { + setState(() { + isLoading = true; + }); + + var res = await widget.comicSource.favoriteData! + .addOrDelFavorite!(widget.cid, '', false, null); + if (res.success) { + widget.onFavorite(false); + context.pop(); + App.rootContext.showMessage(message: "Removed".tl); + } else { + setState(() { + isLoading = false; + }); + context.showMessage(message: res.errorMessage!); + } + }, + child: Text("Remove".tl), + ).paddingVertical(8), + ), + ], + ); + } if (isLoadingFolders) { loadFolders(); return const Center(child: CircularProgressIndicator()); diff --git a/lib/pages/favorites/network_favorites_page.dart b/lib/pages/favorites/network_favorites_page.dart index 8acc3b1..5baef91 100644 --- a/lib/pages/favorites/network_favorites_page.dart +++ b/lib/pages/favorites/network_favorites_page.dart @@ -476,55 +476,47 @@ class _CreateFolderDialogState extends State<_CreateFolderDialog> { @override Widget build(BuildContext context) { - return SimpleDialog( - title: Text("Create a folder".tl), - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), - child: TextField( - controller: controller, - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: "name".tl, - ), - ), - ), - const SizedBox( - width: 200, - height: 10, - ), - if (loading) - Center( - child: const CircularProgressIndicator( - strokeWidth: 2, - ).fixWidth(24).fixHeight(24), - ) - else - SizedBox( - height: 35, - child: Center( - child: TextButton( - onPressed: () { - setState(() { - loading = true; - }); - widget.data.addFolder!(controller.text).then((b) { - if (b.error) { - context.showMessage(message: b.errorMessage!); - setState(() { - loading = false; - }); - } else { - context.pop(); - context.showMessage(message: "Created successfully".tl); - widget.updateState(); - } - }); - }, - child: Text("Submit".tl), + return ContentDialog( + title: "Create a folder".tl, + content: Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 0), + child: TextField( + controller: controller, + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: "name".tl, ), ), - ) + ), + const SizedBox( + height: 16 + ), + ], + ), + actions: [ + Button.filled( + isLoading: loading, + onPressed: () { + setState(() { + loading = true; + }); + widget.data.addFolder!(controller.text).then((b) { + if (b.error) { + context.showMessage(message: b.errorMessage!); + setState(() { + loading = false; + }); + } else { + context.pop(); + context.showMessage(message: "Created successfully".tl); + widget.updateState(); + } + }); + }, + child: Text("Submit".tl), + ) ], ); } From a4e2d4f6e42c2a1b1c0a79c7de84544c082c0d0e Mon Sep 17 00:00:00 2001 From: nyne Date: Tue, 11 Feb 2025 13:58:17 +0800 Subject: [PATCH 04/16] Update js api --- assets/init.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/assets/init.js b/assets/init.js index 37ad17e..553f404 100644 --- a/assets/init.js +++ b/assets/init.js @@ -496,7 +496,7 @@ let Network = { /** * [fetch] function for sending HTTP requests. Same api as the browser fetch. * @param url {string} - * @param options {{method: string, headers: Object, body: any}} + * @param [options] {{method?: string, headers?: Object, body?: any}} * @returns {Promise<{ok: boolean, status: number, statusText: string, headers: {}, arrayBuffer: (function(): Promise), text: (function(): Promise), json: (function(): Promise)}>} * @since 1.2.0 */ @@ -921,7 +921,7 @@ function Comic({id, title, subtitle, subTitle, cover, tags, description, maxPage * @param description {string?} * @param tags {Map | {} | null | undefined} * @param chapters {Map | {} | null | undefined} - key: chapter id, value: chapter title - * @param isFavorite {boolean | null | undefined} - favorite status. If the comic source supports multiple folders, this field should be null + * @param isFavorite {boolean | null | undefined} - favorite status. * @param subId {string?} - a param which is passed to comments api * @param thumbnails {string[]?} - for multiple page thumbnails, set this to null, and use `loadThumbnails` api to load thumbnails * @param recommend {Comic[]?} - related comics @@ -1086,6 +1086,19 @@ class ComicSource { }); } + translation = {} + + /** + * Translate given string with the current locale using the translation object. + * @param key {string} + * @returns {string} + * @since 1.2.5 + */ + translate(key) { + let locale = APP.locale; + return this.translation[locale]?.[key] ?? key; + } + init() { } static sources = {} From c7d72347a94b0e366ff77c37019cda00d1566854 Mon Sep 17 00:00:00 2001 From: nyne Date: Tue, 11 Feb 2025 17:55:17 +0800 Subject: [PATCH 05/16] typo --- lib/components/comic.dart | 2 +- lib/pages/categories_page.dart | 2 +- lib/pages/explore_page.dart | 2 +- lib/pages/search_page.dart | 2 +- lib/pages/search_result_page.dart | 2 +- lib/utils/ext.dart | 8 +++----- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/components/comic.dart b/lib/components/comic.dart index cf3f4a2..28c4a23 100644 --- a/lib/components/comic.dart +++ b/lib/components/comic.dart @@ -742,7 +742,7 @@ class _SliverGridComicsState extends State { @override void didUpdateWidget(covariant SliverGridComics oldWidget) { - if (oldWidget.comics.isEqualsTo(widget.comics)) { + if (oldWidget.comics.isEqualTo(widget.comics)) { comics.clear(); for (var comic in widget.comics) { if (isBlocked(comic) == null) { diff --git a/lib/pages/categories_page.dart b/lib/pages/categories_page.dart index 78f46c3..f46d975 100644 --- a/lib/pages/categories_page.dart +++ b/lib/pages/categories_page.dart @@ -32,7 +32,7 @@ class _CategoriesPageState extends State { .toList(); categories = categories.where((element) => allCategories.contains(element)).toList(); - if (!categories.isEqualsTo(this.categories)) { + if (!categories.isEqualTo(this.categories)) { setState(() { this.categories = categories; }); diff --git a/lib/pages/explore_page.dart b/lib/pages/explore_page.dart index 421be80..21a32e9 100644 --- a/lib/pages/explore_page.dart +++ b/lib/pages/explore_page.dart @@ -37,7 +37,7 @@ class _ExplorePageState extends State .expand((e) => e.map((e) => e.title)) .toList(); explorePages = explorePages.where((e) => all.contains(e)).toList(); - if (!pages.isEqualsTo(explorePages)) { + if (!pages.isEqualTo(explorePages)) { setState(() { pages = explorePages; controller = TabController( diff --git a/lib/pages/search_page.dart b/lib/pages/search_page.dart index 6258e0d..81d7466 100644 --- a/lib/pages/search_page.dart +++ b/lib/pages/search_page.dart @@ -189,7 +189,7 @@ class _SearchPageState extends State { void updateSearchSourcesIfNeeded() { var old = searchSources; findSearchSources(); - if (old.isEqualsTo(searchSources)) { + if (old.isEqualTo(searchSources)) { return; } setState(() {}); diff --git a/lib/pages/search_result_page.dart b/lib/pages/search_result_page.dart index a0251ee..e7cff3d 100644 --- a/lib/pages/search_result_page.dart +++ b/lib/pages/search_result_page.dart @@ -196,7 +196,7 @@ class _SearchResultPageState extends State { return _SearchSettingsDialog(state: this); }, ); - if (!previousOptions.isEqualsTo(options) || + if (!previousOptions.isEqualTo(options) || previousSourceKey != sourceKey) { text = checkAutoLanguage(controller.text); controller.currentText = text; diff --git a/lib/utils/ext.dart b/lib/utils/ext.dart index 5a9e534..c4fa3ed 100644 --- a/lib/utils/ext.dart +++ b/lib/utils/ext.dart @@ -25,7 +25,9 @@ extension ListExt on List{ } } - bool isEqualsTo(List list){ + /// Compare every element of this list with another list. + /// Return true if all elements are equal. + bool isEqualTo(List list){ if(length != list.length){ return false; } @@ -81,10 +83,6 @@ extension StringExt on String{ return '$before$to$after'; } - static bool hasMatch(String? value, String pattern) { - return (value == null) ? false : RegExp(pattern).hasMatch(value); - } - bool _isURL(){ final regex = RegExp( r'^((http|https|ftp)://)[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-|]*[\w@?^=%&/~+#-])?$', From f4804faf5293d86045d0ab4a25c27587806b7973 Mon Sep 17 00:00:00 2001 From: nyne Date: Tue, 11 Feb 2025 18:51:27 +0800 Subject: [PATCH 06/16] Improve reader gesture. Close #185 --- lib/pages/reader/gesture.dart | 7 +++++- lib/pages/reader/images.dart | 43 +++++++++++++++++++++++++++++++++-- lib/pages/reader/reader.dart | 3 +++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/pages/reader/gesture.dart b/lib/pages/reader/gesture.dart index 7c513fb..72f7dac 100644 --- a/lib/pages/reader/gesture.dart +++ b/lib/pages/reader/gesture.dart @@ -24,6 +24,8 @@ class _ReaderGestureDetectorState extends State<_ReaderGestureDetector> { int fingers = 0; + late _ReaderState reader; + @override void initState() { _tapGestureRecognizer = TapGestureRecognizer() @@ -33,6 +35,7 @@ class _ReaderGestureDetectorState extends State<_ReaderGestureDetector> { }; super.initState(); context.readerScaffold._gestureDetectorState = this; + reader = context.reader; } @override @@ -166,7 +169,9 @@ class _ReaderGestureDetectorState extends State<_ReaderGestureDetector> { } void onTap(Offset location) { - if (context.readerScaffold.isOpen) { + if (reader._imageViewController!.handleOnTap(location)) { + return; + } else if (context.readerScaffold.isOpen) { context.readerScaffold.openOrClose(); } else { if (appdata.settings['enableTapToTurnPages']) { diff --git a/lib/pages/reader/images.dart b/lib/pages/reader/images.dart index 5bb1212..63493c8 100644 --- a/lib/pages/reader/images.dart +++ b/lib/pages/reader/images.dart @@ -335,6 +335,11 @@ class _GalleryModeState extends State<_GalleryMode> } } } + + @override + bool handleOnTap(Offset location) { + return false; + } } const Set _kTouchLikeDeviceTypes = { @@ -366,6 +371,18 @@ class _ContinuousModeState extends State<_ContinuousMode> var fingers = 0; bool disableScroll = false; + /// Whether the user was scrolling the page. + /// The gesture detector has a delay to detect tap event. + /// To handle the tap event, we need to know if the user was scrolling before the delay. + bool delayedIsScrolling = false; + + void delayedSetIsScrolling(bool value) { + Future.delayed( + const Duration(milliseconds: 300), + () => delayedIsScrolling = value, + ); + } + @override void initState() { reader = context.reader; @@ -374,6 +391,12 @@ class _ContinuousModeState extends State<_ContinuousMode> super.initState(); } + @override + void dispose() { + itemPositionsListener.itemPositions.removeListener(onPositionChanged); + super.dispose(); + } + void onPositionChanged() { var page = itemPositionsListener.itemPositions.value.first.index; page = page.clamp(1, reader.maxPage); @@ -516,8 +539,14 @@ class _ContinuousModeState extends State<_ContinuousMode> child: widget, ); - widget = NotificationListener( + widget = NotificationListener( onNotification: (notification) { + if (notification is ScrollStartNotification) { + delayedSetIsScrolling(true); + } else if (notification is ScrollEndNotification) { + delayedSetIsScrolling(false); + } + var length = reader.maxChapter; if (!scrollController.hasClients) return false; if (scrollController.position.pixels <= @@ -592,7 +621,7 @@ class _ContinuousModeState extends State<_ContinuousMode> @override void handleLongPressDown(Offset location) { - if (!appdata.settings['enableLongPressToZoom']) { + if (!appdata.settings['enableLongPressToZoom'] || delayedIsScrolling) { return; } double target = photoViewController.getInitialScale!.call()! * 1.75; @@ -667,6 +696,16 @@ class _ContinuousModeState extends State<_ContinuousMode> ); } } + + @override + bool handleOnTap(Offset location) { + if (delayedIsScrolling) { + print('isScrolling'); + return true; + } + print('isNotScrolling'); + return false; + } } ImageProvider _createImageProviderFromKey( diff --git a/lib/pages/reader/reader.dart b/lib/pages/reader/reader.dart index e38407b..34db62a 100644 --- a/lib/pages/reader/reader.dart +++ b/lib/pages/reader/reader.dart @@ -430,4 +430,7 @@ abstract interface class _ImageViewController { void handleLongPressUp(Offset location); void handleKeyEvent(KeyEvent event); + + /// Returns true if the event is handled. + bool handleOnTap(Offset location); } From c600d99c58a91150e8dd2d11f60945e74f9024c6 Mon Sep 17 00:00:00 2001 From: nyne Date: Tue, 11 Feb 2025 19:02:16 +0800 Subject: [PATCH 07/16] Add Reverse Tap to Turn Page. Close #186 --- assets/translation.json | 6 ++++-- lib/foundation/appdata.dart | 1 + lib/pages/reader/gesture.dart | 18 ++++++++++++------ lib/pages/reader/images.dart | 2 -- lib/pages/settings/reader.dart | 7 +++++++ 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/assets/translation.json b/assets/translation.json index 7be6c04..6a9019d 100644 --- a/assets/translation.json +++ b/assets/translation.json @@ -330,7 +330,8 @@ "Not added": "未添加", "Create a folder": "新建收藏夹", "Created successfully": "创建成功", - "name": "名称" + "name": "名称", + "Reverse tap to turn Pages": "反转点击翻页" }, "zh_TW": { "Home": "首頁", @@ -663,6 +664,7 @@ "Not added": "未添加", "Create a folder": "新建收藏夾", "Created successfully": "創建成功", - "name": "名稱" + "name": "名稱", + "Reverse tap to turn Pages": "反轉點擊翻頁" } } \ No newline at end of file diff --git a/lib/foundation/appdata.dart b/lib/foundation/appdata.dart index b68c39a..485e8cb 100644 --- a/lib/foundation/appdata.dart +++ b/lib/foundation/appdata.dart @@ -135,6 +135,7 @@ class _Settings with ChangeNotifier { 'readerMode': 'galleryLeftToRight', // values of [ReaderMode] 'readerScreenPicNumber': 1, // 1 - 5 'enableTapToTurnPages': true, + 'reverseTapToTurnPages': false, 'enablePageAnimation': true, 'language': 'system', // system, zh-CN, zh-TW, en-US 'cacheSize': 2048, // in MB diff --git a/lib/pages/reader/gesture.dart b/lib/pages/reader/gesture.dart index 72f7dac..1da7f3e 100644 --- a/lib/pages/reader/gesture.dart +++ b/lib/pages/reader/gesture.dart @@ -191,31 +191,37 @@ class _ReaderGestureDetectorState extends State<_ReaderGestureDetector> { isBottom = true; } bool isCenter = false; + var prev = context.reader.toPrevPage; + var next = context.reader.toNextPage; + if (appdata.settings['reverseTapToTurnPages']) { + prev = context.reader.toNextPage; + next = context.reader.toPrevPage; + } switch (context.reader.mode) { case ReaderMode.galleryLeftToRight: case ReaderMode.continuousLeftToRight: if (isLeft) { - context.reader.toPrevPage(); + prev(); } else if (isRight) { - context.reader.toNextPage(); + next(); } else { isCenter = true; } case ReaderMode.galleryRightToLeft: case ReaderMode.continuousRightToLeft: if (isLeft) { - context.reader.toNextPage(); + next(); } else if (isRight) { - context.reader.toPrevPage(); + prev(); } else { isCenter = true; } case ReaderMode.galleryTopToBottom: case ReaderMode.continuousTopToBottom: if (isTop) { - context.reader.toPrevPage(); + prev(); } else if (isBottom) { - context.reader.toNextPage(); + next(); } else { isCenter = true; } diff --git a/lib/pages/reader/images.dart b/lib/pages/reader/images.dart index 63493c8..5c65483 100644 --- a/lib/pages/reader/images.dart +++ b/lib/pages/reader/images.dart @@ -700,10 +700,8 @@ class _ContinuousModeState extends State<_ContinuousMode> @override bool handleOnTap(Offset location) { if (delayedIsScrolling) { - print('isScrolling'); return true; } - print('isNotScrolling'); return false; } } diff --git a/lib/pages/settings/reader.dart b/lib/pages/settings/reader.dart index 79f3fa8..82b088c 100644 --- a/lib/pages/settings/reader.dart +++ b/lib/pages/settings/reader.dart @@ -22,6 +22,13 @@ class _ReaderSettingsState extends State { widget.onChanged?.call("enableTapToTurnPages"); }, ).toSliver(), + _SwitchSetting( + title: "Reverse tap to turn Pages".tl, + settingKey: "reverseTapToTurnPages", + onChanged: () { + widget.onChanged?.call("reverseTapToTurnPages"); + }, + ).toSliver(), _SwitchSetting( title: "Page animation".tl, settingKey: "enablePageAnimation", From 53e5ebbbf6ef62a5bb2a1d8e68557c73e9d741c4 Mon Sep 17 00:00:00 2001 From: nyne Date: Tue, 11 Feb 2025 19:21:44 +0800 Subject: [PATCH 08/16] Update version code --- lib/foundation/app.dart | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/foundation/app.dart b/lib/foundation/app.dart index 56a99be..1af13eb 100644 --- a/lib/foundation/app.dart +++ b/lib/foundation/app.dart @@ -10,7 +10,7 @@ export "widget_utils.dart"; export "context.dart"; class _App { - final version = "1.2.4"; + final version = "1.2.5"; bool get isAndroid => Platform.isAndroid; diff --git a/pubspec.yaml b/pubspec.yaml index 5f4e03e..267c905 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: venera description: "A comic app." publish_to: 'none' -version: 1.2.4+124 +version: 1.2.5+125 environment: sdk: '>=3.6.0 <4.0.0' From 51c2bf0d6f29aad10bbe954da850360a2b2e4e0f Mon Sep 17 00:00:00 2001 From: nyne Date: Tue, 11 Feb 2025 20:08:02 +0800 Subject: [PATCH 09/16] [windows] Replace desktop_webview_window with flutter_inappwebview --- lib/network/cloudflare.dart | 2 +- lib/pages/comic_source_page.dart | 4 +- lib/pages/webview.dart | 43 ++++++++++-------- pubspec.lock | 77 +++++++++++++++++--------------- pubspec.yaml | 6 ++- 5 files changed, 74 insertions(+), 58 deletions(-) diff --git a/lib/network/cloudflare.dart b/lib/network/cloudflare.dart index 810bafa..4c078e2 100644 --- a/lib/network/cloudflare.dart +++ b/lib/network/cloudflare.dart @@ -118,7 +118,7 @@ void passCloudflare(CloudflareException e, void Function() onFinished) async { // windows version of package `flutter_inappwebview` cannot get some cookies // Using DesktopWebview instead - if (App.isLinux || App.isWindows) { + if (App.isLinux) { var webview = DesktopWebview( initialUrl: url, onTitleChange: (title, controller) async { diff --git a/lib/pages/comic_source_page.dart b/lib/pages/comic_source_page.dart index 55298d0..fed0d8a 100644 --- a/lib/pages/comic_source_page.dart +++ b/lib/pages/comic_source_page.dart @@ -1011,7 +1011,7 @@ class _LoginPageState extends State<_LoginPage> { if (widget.config.loginWebsite != null) TextButton( onPressed: () { - if (App.isWindows || App.isLinux) { + if (App.isLinux) { loginWithWebview2(); } else { loginWithWebview(); @@ -1127,7 +1127,7 @@ class _LoginPageState extends State<_LoginPage> { } } - // for windows and linux + // for linux void loginWithWebview2() async { if (!await DesktopWebview.isAvailable()) { context.showMessage(message: "Webview is not available".tl); diff --git a/lib/pages/webview.dart b/lib/pages/webview.dart index 70655f6..88590e2 100644 --- a/lib/pages/webview.dart +++ b/lib/pages/webview.dart @@ -25,8 +25,13 @@ extension WebviewExtension on InAppWebViewController { if (url[url.length - 1] == '/') { url = url.substring(0, url.length - 1); } - CookieManager cookieManager = CookieManager.instance(); - final cookies = await cookieManager.getCookies(url: WebUri(url)); + CookieManager cookieManager = CookieManager.instance( + webViewEnvironment: AppWebview.webViewEnvironment, + ); + final cookies = await cookieManager.getCookies( + url: WebUri(url), + webViewController: this, + ); var res = []; for (var cookie in cookies) { var c = io.Cookie(cookie.name, cookie.value); @@ -90,7 +95,8 @@ class _AppWebviewState extends State { var proxy = appdata.settings['proxy'].toString(); if (proxy != "system" && proxy != "direct") { var proxyAvailable = await WebViewFeature.isFeatureSupported( - WebViewFeature.PROXY_OVERRIDE); + WebViewFeature.PROXY_OVERRIDE, + ); if (proxyAvailable) { ProxyController proxyController = ProxyController.instance(); await proxyController.clearProxyOverride(); @@ -147,22 +153,21 @@ class _AppWebviewState extends State { ) ]; - Widget body = (App.isWindows && AppWebview.webViewEnvironment == null) - ? FutureBuilder( - future: future, - builder: (context, e) { - if (e.error != null) { - return Center(child: Text("Error: ${e.error}")); - } - if (e.data == null) { - return const Center(child: CircularProgressIndicator()); - } - AppWebview.webViewEnvironment = e.data; - return createWebviewWithEnvironment( - AppWebview.webViewEnvironment); - }, - ) - : createWebviewWithEnvironment(AppWebview.webViewEnvironment); + Widget body = FutureBuilder( + future: future, + builder: (context, e) { + if (e.error != null) { + return Center(child: Text("Error: ${e.error}")); + } + if (e.data == null) { + return const SizedBox(); + } + AppWebview.webViewEnvironment = e.data; + return createWebviewWithEnvironment( + AppWebview.webViewEnvironment, + ); + }, + ); body = Stack( children: [ diff --git a/pubspec.lock b/pubspec.lock index 6f5ba9f..dde4d00 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -307,19 +307,21 @@ packages: flutter_inappwebview: dependency: "direct main" description: - name: flutter_inappwebview - sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" - url: "https://pub.dev" - source: hosted - version: "6.1.5" + path: flutter_inappwebview + ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + resolved-ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + url: "https://github.com/pichillilorenzo/flutter_inappwebview" + source: git + version: "6.2.0-beta.3" flutter_inappwebview_android: dependency: transitive description: - name: flutter_inappwebview_android - sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" - url: "https://pub.dev" - source: hosted - version: "1.1.3" + path: flutter_inappwebview_android + ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + resolved-ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + url: "https://github.com/pichillilorenzo/flutter_inappwebview" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -331,43 +333,48 @@ packages: flutter_inappwebview_ios: dependency: transitive description: - name: flutter_inappwebview_ios - sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_ios + ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + resolved-ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + url: "https://github.com/pichillilorenzo/flutter_inappwebview" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_macos: dependency: transitive description: - name: flutter_inappwebview_macos - sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_macos + ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + resolved-ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + url: "https://github.com/pichillilorenzo/flutter_inappwebview" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_platform_interface: dependency: transitive description: - name: flutter_inappwebview_platform_interface - sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 - url: "https://pub.dev" - source: hosted - version: "1.3.0+1" + path: flutter_inappwebview_platform_interface + ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + resolved-ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + url: "https://github.com/pichillilorenzo/flutter_inappwebview" + source: git + version: "1.4.0-beta.3" flutter_inappwebview_web: dependency: transitive description: - name: flutter_inappwebview_web - sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_web + ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + resolved-ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + url: "https://github.com/pichillilorenzo/flutter_inappwebview" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_windows: dependency: transitive description: - name: flutter_inappwebview_windows - sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" - url: "https://pub.dev" - source: hosted - version: "0.6.0" + path: flutter_inappwebview_windows + ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + resolved-ref: "0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676" + url: "https://github.com/pichillilorenzo/flutter_inappwebview" + source: git + version: "0.7.0-beta.3" flutter_lints: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index 267c905..d1cbe25 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,7 +43,11 @@ dependencies: git: url: https://github.com/wgh136/flutter_desktop_webview path: packages/desktop_webview_window - flutter_inappwebview: ^6.1.5 + flutter_inappwebview: + git: + url: https://github.com/pichillilorenzo/flutter_inappwebview + path: flutter_inappwebview + ref: 0aaf7a0bfc01d61a4d1453cefb57fb6783b6e676 app_links: ^6.3.3 sliver_tools: ^0.2.12 flutter_file_dialog: ^3.0.2 From dd7e2d6744f8d97640c4bbe71c4a171e627dff2c Mon Sep 17 00:00:00 2001 From: nyne Date: Tue, 11 Feb 2025 21:13:57 +0800 Subject: [PATCH 10/16] Improve aggregated_search_page --- lib/foundation/comic_source/comic_source.dart | 2 +- lib/pages/aggregated_search_page.dart | 37 +++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/lib/foundation/comic_source/comic_source.dart b/lib/foundation/comic_source/comic_source.dart index 7eed7e4..a0d3cc7 100644 --- a/lib/foundation/comic_source/comic_source.dart +++ b/lib/foundation/comic_source/comic_source.dart @@ -417,7 +417,7 @@ class SearchOptions { const SearchOptions(this.options, this.label, this.type, this.defaultVal); - String get defaultValue => defaultVal ?? options.keys.first; + String get defaultValue => defaultVal ?? options.keys.firstOrNull ?? ""; } typedef CategoryComicsLoader = Future>> Function( diff --git a/lib/pages/aggregated_search_page.dart b/lib/pages/aggregated_search_page.dart index 48fc849..a84c471 100644 --- a/lib/pages/aggregated_search_page.dart +++ b/lib/pages/aggregated_search_page.dart @@ -58,7 +58,11 @@ class _AggregatedSearchPageState extends State { delegate: SliverChildBuilderDelegate( (context, index) { final source = sources[index]; - return _SliverSearchResult(source: source, keyword: _keyword); + return _SliverSearchResult( + key: ValueKey(source.key), + source: source, + keyword: _keyword, + ); }, childCount: sources.length, ), @@ -68,7 +72,11 @@ class _AggregatedSearchPageState extends State { } class _SliverSearchResult extends StatefulWidget { - const _SliverSearchResult({required this.source, required this.keyword}); + const _SliverSearchResult({ + required this.source, + required this.keyword, + super.key, + }); final ComicSource source; @@ -90,6 +98,8 @@ class _SliverSearchResultState extends State<_SliverSearchResult> List? comics; + String? error; + void load() async { final data = widget.source.searchPageData!; var options = @@ -101,6 +111,11 @@ class _SliverSearchResultState extends State<_SliverSearchResult> comics = res.data; isLoading = false; }); + } else { + setState(() { + error = res.errorMessage ?? "Unknown error".tl; + isLoading = false; + }); } } else if (data.loadNext != null) { var res = await data.loadNext!(widget.keyword, null, options); @@ -109,6 +124,11 @@ class _SliverSearchResultState extends State<_SliverSearchResult> comics = res.data; isLoading = false; }); + } else { + setState(() { + error = res.errorMessage ?? "Unknown error".tl; + isLoading = false; + }); } } } @@ -139,6 +159,9 @@ class _SliverSearchResultState extends State<_SliverSearchResult> @override Widget build(BuildContext context) { + if (error != null && error!.startsWith("CloudflareException")) { + error = "Cloudflare verification required".tl; + } super.build(context); return InkWell( onTap: () { @@ -181,7 +204,7 @@ class _SliverSearchResultState extends State<_SliverSearchResult> }), ), ) - else if (comics == null || comics!.isEmpty) + else if (error != null || comics == null || comics!.isEmpty) SizedBox( height: _kComicHeight, child: Column( @@ -190,7 +213,13 @@ class _SliverSearchResultState extends State<_SliverSearchResult> children: [ const Icon(Icons.error_outline), const SizedBox(width: 8), - Text("No search results found".tl), + Expanded( + child: Text( + error ?? "No search results found".tl, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ) ], ), const Spacer(), From 9b1bafcbe139575566414e750b6d49c98d62cf96 Mon Sep 17 00:00:00 2001 From: nyne Date: Thu, 13 Feb 2025 09:43:36 +0800 Subject: [PATCH 11/16] Improve gesture --- lib/pages/reader/images.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/pages/reader/images.dart b/lib/pages/reader/images.dart index 5c65483..0801938 100644 --- a/lib/pages/reader/images.dart +++ b/lib/pages/reader/images.dart @@ -512,6 +512,14 @@ class _ContinuousModeState extends State<_ContinuousMode> }); } }, + onPointerCancel: (event) { + fingers--; + if (fingers <= 1 && disableScroll) { + setState(() { + disableScroll = false; + }); + } + }, onPointerPanZoomUpdate: (event) { if (event.scale == 1.0) { smoothTo(0 - event.panDelta.dy); From 18c5d5d85a398d916ac590b3d043ce652ac7014f Mon Sep 17 00:00:00 2001 From: nyne Date: Thu, 13 Feb 2025 09:49:05 +0800 Subject: [PATCH 12/16] Fix image overflow --- lib/pages/reader/scaffold.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pages/reader/scaffold.dart b/lib/pages/reader/scaffold.dart index 7c3927c..453081b 100644 --- a/lib/pages/reader/scaffold.dart +++ b/lib/pages/reader/scaffold.dart @@ -660,12 +660,16 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> { App.rootContext.pop(); }, child: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(16)), + foregroundDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), border: Border.all( color: Theme.of(context).colorScheme.outline, ), ), + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + ), width: double.infinity, height: double.infinity, child: Image( From 34194559f50311f301fd62e55090fbb46e045c2b Mon Sep 17 00:00:00 2001 From: nyne Date: Thu, 13 Feb 2025 10:05:38 +0800 Subject: [PATCH 13/16] Improve chapters display --- assets/translation.json | 6 +- lib/pages/comic_page.dart | 180 ++++++++++++++++++++------------------ 2 files changed, 101 insertions(+), 85 deletions(-) diff --git a/assets/translation.json b/assets/translation.json index 6a9019d..1e6361f 100644 --- a/assets/translation.json +++ b/assets/translation.json @@ -331,7 +331,8 @@ "Create a folder": "新建收藏夹", "Created successfully": "创建成功", "name": "名称", - "Reverse tap to turn Pages": "反转点击翻页" + "Reverse tap to turn Pages": "反转点击翻页", + "Show all": "显示全部" }, "zh_TW": { "Home": "首頁", @@ -665,6 +666,7 @@ "Create a folder": "新建收藏夾", "Created successfully": "創建成功", "name": "名稱", - "Reverse tap to turn Pages": "反轉點擊翻頁" + "Reverse tap to turn Pages": "反轉點擊翻頁", + "Show all": "顯示全部" } } \ No newline at end of file diff --git a/lib/pages/comic_page.dart b/lib/pages/comic_page.dart index d1f2db9..62f5bc0 100644 --- a/lib/pages/comic_page.dart +++ b/lib/pages/comic_page.dart @@ -1106,94 +1106,108 @@ class _ComicChaptersState extends State<_ComicChapters> { Widget build(BuildContext context) { final eps = state.comic.chapters!; - int length = eps.length; + return SliverLayoutBuilder( + builder: (context, constrains) { + int length = eps.length; + bool canShowAll = showAll; + if (!showAll) { + var width = constrains.crossAxisExtent; + var crossItems = width ~/ 200; + if (width % 200 != 0) { + crossItems += 1; + } + length = math.min(length, crossItems * 8); + if (length == eps.length) { + canShowAll = true; + } + } - if (!showAll) { - length = math.min(length, 20); - } - - return SliverMainAxisGroup( - slivers: [ - SliverToBoxAdapter( - child: ListTile( - title: Text("Chapters".tl), - trailing: Tooltip( - message: "Order".tl, - child: IconButton( - icon: Icon(reverse - ? Icons.vertical_align_top - : Icons.vertical_align_bottom_outlined), - onPressed: () { - setState(() { - reverse = !reverse; - }); - }, - ), - ), - ), - ), - SliverGrid( - delegate: - SliverChildBuilderDelegate(childCount: length, (context, i) { - if (reverse) { - i = eps.length - i - 1; - } - var key = eps.keys.elementAt(i); - var value = eps[key]!; - bool visited = - (state.history?.readEpisode ?? const {}).contains(i + 1); - return Padding( - padding: const EdgeInsets.fromLTRB(8, 4, 8, 4), - child: Material( - color: context.colorScheme.surfaceContainer, - borderRadius: const BorderRadius.all(Radius.circular(12)), - child: InkWell( - onTap: () => state.read(i + 1), - borderRadius: const BorderRadius.all(Radius.circular(12)), - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - child: Center( - child: Text( - value, - maxLines: 1, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: visited ? context.colorScheme.outline : null, - ), - ), - ), + return SliverMainAxisGroup( + slivers: [ + SliverToBoxAdapter( + child: ListTile( + title: Text("Chapters".tl), + trailing: Tooltip( + message: "Order".tl, + child: IconButton( + icon: Icon(reverse + ? Icons.vertical_align_top + : Icons.vertical_align_bottom_outlined), + onPressed: () { + setState(() { + reverse = !reverse; + }); + }, ), ), ), - ); - }), - gridDelegate: const SliverGridDelegateWithFixedHeight( - maxCrossAxisExtent: 200, itemHeight: 48), - ).sliverPadding(const EdgeInsets.symmetric(horizontal: 8)), - if (eps.length > 20 && !showAll) - SliverToBoxAdapter( - child: Align( - alignment: Alignment.center, - child: FilledButton.tonal( - style: ButtonStyle( - shape: WidgetStateProperty.all(const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(8)))), - ), - onPressed: () { - setState(() { - showAll = true; - }); - }, - child: Text("${"Show all".tl} (${eps.length})"), - ).paddingTop(12), ), - ), - const SliverToBoxAdapter( - child: Divider(), - ), - ], + SliverGrid( + delegate: SliverChildBuilderDelegate( + childCount: length, + (context, i) { + if (reverse) { + i = eps.length - i - 1; + } + var key = eps.keys.elementAt(i); + var value = eps[key]!; + bool visited = + (state.history?.readEpisode ?? const {}).contains(i + 1); + return Padding( + padding: const EdgeInsets.fromLTRB(6, 4, 6, 4), + child: Material( + color: context.colorScheme.surfaceContainer, + borderRadius: BorderRadius.circular(16), + child: InkWell( + onTap: () => state.read(i + 1), + borderRadius: BorderRadius.circular(16), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Center( + child: Text( + value, + maxLines: 1, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: visited + ? context.colorScheme.outline + : null, + ), + ), + ), + ), + ), + ), + ); + }, + ), + gridDelegate: const SliverGridDelegateWithFixedHeight( + maxCrossAxisExtent: 200, + itemHeight: 48, + ), + ).sliverPadding(const EdgeInsets.symmetric(horizontal: 8)), + if (eps.length > 20 && !canShowAll) + SliverToBoxAdapter( + child: Align( + alignment: Alignment.center, + child: TextButton.icon( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + setState(() { + showAll = true; + }); + }, + label: Text("${"Show all".tl} (${eps.length})"), + ).paddingTop(12), + ), + ), + const SliverToBoxAdapter( + child: Divider(), + ), + ], + ); + }, ); } } From d2aca7ce444c25aa3d94d15d8b92fe48b1debcdd Mon Sep 17 00:00:00 2001 From: nyne Date: Thu, 13 Feb 2025 10:09:08 +0800 Subject: [PATCH 14/16] Improve sorting images when importing comic. --- lib/utils/cbz.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/utils/cbz.dart b/lib/utils/cbz.dart index d36d54a..0bd00d2 100644 --- a/lib/utils/cbz.dart +++ b/lib/utils/cbz.dart @@ -115,7 +115,17 @@ abstract class CBZ { cache.deleteSync(recursive: true); throw Exception('No images found in the archive'); } - files.sort((a, b) => a.path.compareTo(b.path)); + files.sort((a, b) { + var aName = a.basenameWithoutExt; + var bName = b.basenameWithoutExt; + var aIndex = int.tryParse(aName); + var bIndex = int.tryParse(bName); + if (aIndex != null && bIndex != null) { + return aIndex.compareTo(bIndex); + } else { + return a.path.compareTo(b.path); + } + }); var coverFile = files.firstWhereOrNull( (element) => element.path.endsWith('cover.${element.path.split('.').last}'), From 14c3e9ea4369d48bf6f6a43e78a56b09dc844d34 Mon Sep 17 00:00:00 2001 From: nyne Date: Thu, 13 Feb 2025 10:47:54 +0800 Subject: [PATCH 15/16] Fixed the storage of chapter read information. --- lib/pages/comic_page.dart | 51 +++++++++++++++++++++++++----------- lib/pages/reader/reader.dart | 1 + 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/lib/pages/comic_page.dart b/lib/pages/comic_page.dart index 62f5bc0..f39abcd 100644 --- a/lib/pages/comic_page.dart +++ b/lib/pages/comic_page.dart @@ -49,19 +49,19 @@ class ComicPage extends StatefulWidget { class _ComicPageState extends LoadingState with _ComicPageActions { + @override + History? history; + bool showAppbarTitle = false; var scrollController = ScrollController(); bool isDownloaded = false; - void updateHistory() async { - var newHistory = await HistoryManager() - .find(widget.id, ComicType(widget.sourceKey.hashCode)); - if (newHistory?.ep != history?.ep || newHistory?.page != history?.page) { - history = newHistory; - update(); - } + @override + void onReadEnd() { + // The history is passed by reference, so it will be updated automatically. + update(); } @override @@ -77,14 +77,12 @@ class _ComicPageState extends LoadingState @override void initState() { scrollController.addListener(onScroll); - HistoryManager().addListener(updateHistory); super.initState(); } @override void dispose() { scrollController.removeListener(onScroll); - HistoryManager().removeListener(updateHistory); super.dispose(); } @@ -552,7 +550,7 @@ class _ComicPageState extends LoadingState if (comic.chapters == null) { return const SliverPadding(padding: EdgeInsets.zero); } - return const _ComicChapters(); + return _ComicChapters(history); } Widget buildThumbnails() { @@ -594,7 +592,7 @@ abstract mixin class _ComicPageActions { ComicSource get comicSource => ComicSource.find(comic.sourceKey)!; - History? history; + History? get history; bool isLiking = false; @@ -688,11 +686,13 @@ abstract mixin class _ComicPageActions { chapters: comic.chapters, initialChapter: ep, initialPage: page, - history: History.fromModel(model: comic, ep: 0, page: 0), + history: history ?? History.fromModel(model: comic, ep: 0, page: 0), author: comic.findAuthor() ?? '', tags: comic.plainTags, ), - ); + ).then((_) { + onReadEnd(); + }); } void continueRead() { @@ -701,6 +701,8 @@ abstract mixin class _ComicPageActions { read(ep, page); } + void onReadEnd(); + void download() async { if (LocalManager().isDownloading(comic.id, comic.comicType)) { App.rootContext.showMessage(message: "The comic is downloading".tl); @@ -1083,7 +1085,9 @@ class _ActionButton extends StatelessWidget { } class _ComicChapters extends StatefulWidget { - const _ComicChapters(); + const _ComicChapters(this.history); + + final History? history; @override State<_ComicChapters> createState() => _ComicChaptersState(); @@ -1096,12 +1100,28 @@ class _ComicChaptersState extends State<_ComicChapters> { bool showAll = false; + late History? history; + + @override + void initState() { + super.initState(); + history = widget.history; + } + @override void didChangeDependencies() { state = context.findAncestorStateOfType<_ComicPageState>()!; super.didChangeDependencies(); } + @override + void didUpdateWidget(covariant _ComicChapters oldWidget) { + super.didUpdateWidget(oldWidget); + setState(() { + history = widget.history; + }); + } + @override Widget build(BuildContext context) { final eps = state.comic.chapters!; @@ -1151,8 +1171,7 @@ class _ComicChaptersState extends State<_ComicChapters> { } var key = eps.keys.elementAt(i); var value = eps[key]!; - bool visited = - (state.history?.readEpisode ?? const {}).contains(i + 1); + bool visited = (history?.readEpisode ?? {}).contains(i + 1); return Padding( padding: const EdgeInsets.fromLTRB(6, 4, 6, 4), child: Material( diff --git a/lib/pages/reader/reader.dart b/lib/pages/reader/reader.dart index 34db62a..7d4a686 100644 --- a/lib/pages/reader/reader.dart +++ b/lib/pages/reader/reader.dart @@ -237,6 +237,7 @@ class _ReaderState extends State with _ReaderLocation, _ReaderWindow { history!.maxPage = maxPage; } history!.readEpisode.add(chapter); + print(history!.readEpisode); history!.time = DateTime.now(); HistoryManager().addHistory(history!); } From ed124d04192d79170a983b47b7b2eedbc8076b0e Mon Sep 17 00:00:00 2001 From: nyne Date: Thu, 13 Feb 2025 11:01:42 +0800 Subject: [PATCH 16/16] Fix calculation --- lib/pages/comic_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/comic_page.dart b/lib/pages/comic_page.dart index f39abcd..caeb801 100644 --- a/lib/pages/comic_page.dart +++ b/lib/pages/comic_page.dart @@ -1131,7 +1131,7 @@ class _ComicChaptersState extends State<_ComicChapters> { int length = eps.length; bool canShowAll = showAll; if (!showAll) { - var width = constrains.crossAxisExtent; + var width = constrains.crossAxisExtent - 16; var crossItems = width ~/ 200; if (width % 200 != 0) { crossItems += 1;