From f17af25e2c0da61c811778ddb8f868063379032a Mon Sep 17 00:00:00 2001 From: nyne Date: Sun, 27 Oct 2024 18:39:33 +0800 Subject: [PATCH] fix local comic display --- lib/components/comic.dart | 5 +++ lib/components/components.dart | 1 + lib/components/loading.dart | 2 ++ lib/foundation/comic_type.dart | 8 +++++ lib/foundation/context.dart | 5 +++ lib/foundation/favorites.dart | 17 ++++++--- lib/foundation/local.dart | 6 ++-- lib/network/cache.dart | 2 +- lib/network/download.dart | 2 ++ lib/pages/comic_page.dart | 44 ++++++++++++++++++++--- lib/pages/explore_page.dart | 11 +++++- lib/pages/favorites/favorite_actions.dart | 2 +- lib/pages/history_page.dart | 16 +++++++-- lib/pages/home_page.dart | 22 +++++++++--- lib/pages/search_page.dart | 2 +- lib/utils/ext.dart | 2 +- 16 files changed, 123 insertions(+), 24 deletions(-) diff --git a/lib/components/comic.dart b/lib/components/comic.dart index da0b708..7b096d9 100644 --- a/lib/components/comic.dart +++ b/lib/components/comic.dart @@ -149,6 +149,11 @@ class ComicTile extends StatelessWidget { ImageProvider image; if (comic is LocalComic) { image = FileImage((comic as LocalComic).coverFile); + } else if (comic.cover.startsWith('file://')) { + image = FileImage(File(comic.cover.substring(7))); + } else if (comic.sourceKey == 'local') { + var localComic = LocalManager().find(comic.id, ComicType.local); + image = FileImage(localComic!.coverFile); } else { image = CachedImageProvider(comic.cover, sourceKey: comic.sourceKey); } diff --git a/lib/components/components.dart b/lib/components/components.dart index bfc9d3d..6d04529 100644 --- a/lib/components/components.dart +++ b/lib/components/components.dart @@ -26,6 +26,7 @@ import 'package:venera/network/cloudflare.dart'; import 'package:venera/pages/comic_page.dart'; import 'package:venera/pages/favorites/favorites_page.dart'; import 'package:venera/utils/ext.dart'; +import 'package:venera/utils/io.dart'; import 'package:venera/utils/tags_translation.dart'; import 'package:venera/utils/translations.dart'; diff --git a/lib/components/loading.dart b/lib/components/loading.dart index 103a18d..e03ba40 100644 --- a/lib/components/loading.dart +++ b/lib/components/loading.dart @@ -113,6 +113,7 @@ abstract class LoadingState if (res.success) { return res; } else { + if(!mounted) return res; if (retry >= 3) { return res; } @@ -170,6 +171,7 @@ abstract class LoadingState isLoading = true; Future.microtask(() { loadDataWithRetry().then((value) async { + if(!mounted) return; if (value.success) { data = value.data; await onDataLoaded(); diff --git a/lib/foundation/comic_type.dart b/lib/foundation/comic_type.dart index fedd6d3..36c8ad6 100644 --- a/lib/foundation/comic_type.dart +++ b/lib/foundation/comic_type.dart @@ -11,6 +11,14 @@ class ComicType { @override int get hashCode => value.hashCode; + String get sourceKey { + if(this == local) { + return "local"; + } else { + return comicSource!.key; + } + } + ComicSource? get comicSource { if(this == local) { return null; diff --git a/lib/foundation/context.dart b/lib/foundation/context.dart index ec83cc6..00ae620 100644 --- a/lib/foundation/context.dart +++ b/lib/foundation/context.dart @@ -19,6 +19,11 @@ extension Navigation on BuildContext { .push(AppPageRoute(builder: (context) => builder())); } + Future toReplacement(Widget Function() builder) { + return Navigator.of(this) + .pushReplacement(AppPageRoute(builder: (context) => builder())); + } + double get width => MediaQuery.of(this).size.width; double get height => MediaQuery.of(this).size.height; diff --git a/lib/foundation/favorites.dart b/lib/foundation/favorites.dart index bd81d46..8a3e537 100644 --- a/lib/foundation/favorites.dart +++ b/lib/foundation/favorites.dart @@ -55,7 +55,7 @@ class FavoriteItem implements Comic { @override String toString() { var s = "FavoriteItem: $name $author $coverPath $hashCode $tags"; - if(s.length > 100) { + if (s.length > 100) { return s.substring(0, 100); } return s; @@ -65,7 +65,9 @@ class FavoriteItem implements Comic { String get cover => coverPath; @override - String get description => "$time | ${type.comicSource?.name ?? "Unknown"}"; + String get description { + return "$time | ${type == ComicType.local ? 'local' : type.comicSource?.name ?? "Unknown"}"; + } @override String? get favoriteId => null; @@ -77,7 +79,7 @@ class FavoriteItem implements Comic { int? get maxPage => null; @override - String get sourceKey => type.comicSource?.key ?? "Unknown:${type.value}"; + String get sourceKey => type == ComicType.local ? 'local' : type.comicSource?.key ?? "Unknown:${type.value}"; @override double? get stars => null; @@ -514,6 +516,13 @@ class LocalFavoritesManager { update "$folder" set name = ?, author = ?, cover_path = ?, tags = ? where id == ? and type == ?; - """, [comic.name, comic.author, comic.coverPath, comic.tags.join(","), comic.id, comic.type.value]); + """, [ + comic.name, + comic.author, + comic.coverPath, + comic.tags.join(","), + comic.id, + comic.type.value + ]); } } diff --git a/lib/foundation/local.dart b/lib/foundation/local.dart index 5255eff..0cbccb8 100644 --- a/lib/foundation/local.dart +++ b/lib/foundation/local.dart @@ -79,7 +79,7 @@ class LocalComic with HistoryMixin implements Comic { String get description => ""; @override - String get sourceKey => comicType.comicSource?.key ?? '_local_'; + String get sourceKey => comicType == ComicType.local ? "local" : ""; @override Map toJson() { @@ -214,7 +214,7 @@ class LocalManager with ChangeNotifier { SELECT id FROM comics WHERE comic_type = ? ORDER BY CAST(id AS INTEGER) DESC LIMIT 1; - '''[type.value], + ''', [type.value], ); if (res.isEmpty) { return '1'; @@ -341,7 +341,7 @@ class LocalManager with ChangeNotifier { if (comic == null) return false; if (comic.chapters == null) return true; return comic.downloadedChapters - .contains(comic.chapters!.keys.elementAt(ep)); + .contains(comic.chapters!.keys.elementAt(ep-1)); } List downloadingTasks = []; diff --git a/lib/network/cache.dart b/lib/network/cache.dart index 9732e21..b7c0bd0 100644 --- a/lib/network/cache.dart +++ b/lib/network/cache.dart @@ -161,7 +161,7 @@ class NetworkCacheManager implements Interceptor { return handler.next(response); } var size = _calculateSize(response.data); - if(size != null && size < 1024 * 1024) { + if(size != null && size < 1024 * 1024 && size > 1024) { var cache = NetworkCache( uri: response.requestOptions.uri, requestHeaders: response.requestOptions.headers, diff --git a/lib/network/download.dart b/lib/network/download.dart index 3e336a2..052526d 100644 --- a/lib/network/download.dart +++ b/lib/network/download.dart @@ -203,6 +203,8 @@ class ImagesDownloadTask extends DownloadTask with _TransferSpeedMixin { @override void resume() async { if (_isRunning) return; + _isError = false; + _message = "Resuming..."; _isRunning = true; notifyListeners(); runRecorder(); diff --git a/lib/pages/comic_page.dart b/lib/pages/comic_page.dart index 7176809..85aea33 100644 --- a/lib/pages/comic_page.dart +++ b/lib/pages/comic_page.dart @@ -72,6 +72,8 @@ class _ComicPageState extends LoadingState } } + var isFirst = true; + @override Widget buildContent(BuildContext context, ComicDetails data) { return SmoothCustomScrollView( @@ -91,8 +93,37 @@ class _ComicPageState extends LoadingState @override Future> loadData() async { + if (widget.sourceKey == 'local') { + var localComic = LocalManager().find(widget.id, ComicType.local); + if (localComic == null) { + return const Res.error('Local comic not found'); + } + var history = await HistoryManager().find(widget.id, ComicType.local); + if(isFirst) { + Future.microtask(() { + App.rootContext.to(() { + return Reader( + type: ComicType.local, + cid: widget.id, + name: localComic.title, + chapters: localComic.chapters, + history: history ?? + History.fromModel( + model: localComic, + ep: 0, + page: 0, + ), + ); + }); + App.mainNavigatorKey!.currentContext!.pop(); + }); + isFirst = false; + } + await Future.delayed(const Duration(milliseconds: 200)); + return const Res.error('Local comic'); + } var comicSource = ComicSource.find(widget.sourceKey); - if(comicSource == null) { + if (comicSource == null) { return const Res.error('Comic source not found'); } isAddToLocalFav = LocalFavoritesManager().isExist( @@ -101,7 +132,7 @@ class _ComicPageState extends LoadingState ); history = await HistoryManager() .find(widget.id, ComicType(widget.sourceKey.hashCode)); - return comicSource!.loadComicInfo!(widget.id); + return comicSource.loadComicInfo!(widget.id); } @override @@ -500,7 +531,11 @@ abstract mixin class _ComicPageActions { } void share() { - Share.shareText(comic.title); + var text = comic.title; + if (comic.url != null) { + text += '\n${comic.url}'; + } + Share.shareText(text); } /// read the comic @@ -694,8 +729,7 @@ abstract mixin class _ComicPageActions { setState(() { isLoading = true; }); - comicSource.starRatingFunc! - (comic.id, rating.round()) + comicSource.starRatingFunc!(comic.id, rating.round()) .then((value) { if (value.success) { App.rootContext diff --git a/lib/pages/explore_page.dart b/lib/pages/explore_page.dart index e341a31..f17db5a 100644 --- a/lib/pages/explore_page.dart +++ b/lib/pages/explore_page.dart @@ -66,7 +66,16 @@ class _ExplorePageState extends State return NetworkError( message: "No Explore Pages".tl, retry: () { - setState(() {}); + setState(() { + pages = ComicSource.all() + .map((e) => e.explorePages) + .expand((e) => e.map((e) => e.title)) + .toList(); + controller = TabController( + length: pages.length, + vsync: this, + ); + }); }, withAppbar: false, ); diff --git a/lib/pages/favorites/favorite_actions.dart b/lib/pages/favorites/favorite_actions.dart index 0182543..844bb31 100644 --- a/lib/pages/favorites/favorite_actions.dart +++ b/lib/pages/favorites/favorite_actions.dart @@ -98,7 +98,7 @@ void addFavorite(Comic comic) { name: comic.title, coverPath: comic.cover, author: comic.subtitle ?? '', - type: ComicType(comic.sourceKey.hashCode), + type: ComicType((comic.sourceKey == 'local' ? 0 : comic.sourceKey.hashCode)), tags: comic.tags ?? [], ), ); diff --git a/lib/pages/history_page.dart b/lib/pages/history_page.dart index 4107d12..f7e07c0 100644 --- a/lib/pages/history_page.dart +++ b/lib/pages/history_page.dart @@ -4,6 +4,8 @@ import 'package:venera/foundation/app.dart'; import 'package:venera/foundation/comic_source/comic_source.dart'; import 'package:venera/foundation/comic_type.dart'; import 'package:venera/foundation/history.dart'; +import 'package:venera/foundation/local.dart'; +import 'package:venera/utils/ext.dart'; import 'package:venera/utils/translations.dart'; class HistoryPage extends StatefulWidget { @@ -78,9 +80,19 @@ class _HistoryPageState extends State { SliverGridComics( comics: comics.map( (e) { + var cover = e.cover; + if (!cover.isURL) { + var localComic = LocalManager().find( + e.id, + e.type, + ); + if(localComic != null) { + cover = "file://${localComic.coverFile.path}"; + } + } return Comic( e.title, - e.cover, + cover, e.id, e.subtitle, null, @@ -100,7 +112,7 @@ class _HistoryPageState extends State { icon: Icons.remove, text: 'Remove'.tl, onClick: () { - if(c.sourceKey.startsWith("Invalid")) { + if (c.sourceKey.startsWith("Invalid")) { HistoryManager().remove( c.id, ComicType(int.parse(c.sourceKey.split(':')[1])), diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index db7416b..821b7af 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -15,6 +15,7 @@ import 'package:venera/pages/comic_source_page.dart'; import 'package:venera/pages/downloading_page.dart'; import 'package:venera/pages/history_page.dart'; import 'package:venera/pages/search_page.dart'; +import 'package:venera/utils/ext.dart'; import 'package:venera/utils/io.dart'; import 'package:venera/utils/translations.dart'; @@ -155,12 +156,26 @@ class _HistoryState extends State<_History> { scrollDirection: Axis.horizontal, itemCount: history.length, itemBuilder: (context, index) { + var cover = history[index].cover; + ImageProvider imageProvider = CachedImageProvider( + cover, + sourceKey: history[index].type.comicSource?.key, + ); + if (!cover.isURL) { + var localComic = LocalManager().find( + history[index].id, + history[index].type, + ); + if (localComic != null) { + imageProvider = FileImage(localComic.coverFile); + } + } return InkWell( onTap: () { context.to( () => ComicPage( id: history[index].id, - sourceKey: history[index].type.comicSource!.key, + sourceKey: history[index].type.sourceKey, ), ); }, @@ -177,10 +192,7 @@ class _HistoryState extends State<_History> { ), clipBehavior: Clip.antiAlias, child: AnimatedImage( - image: CachedImageProvider( - history[index].cover, - sourceKey: history[index].type.comicSource?.key, - ), + image: imageProvider, width: 96, height: 128, fit: BoxFit.cover, diff --git a/lib/pages/search_page.dart b/lib/pages/search_page.dart index 684c0be..c16a61e 100644 --- a/lib/pages/search_page.dart +++ b/lib/pages/search_page.dart @@ -196,7 +196,7 @@ class _SearchPageState extends State { runSpacing: 8, children: sources.map((e) { return OptionChip( - text: e.name.tl, + text: e.name, isSelected: searchTarget == e.key, onTap: () { setState(() { diff --git a/lib/utils/ext.dart b/lib/utils/ext.dart index eff2b8d..ba414a2 100644 --- a/lib/utils/ext.dart +++ b/lib/utils/ext.dart @@ -75,7 +75,7 @@ extension StringExt on String{ bool _isURL(){ final regex = RegExp( - r'^((http|https|ftp)://)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-|]*[\w@?^=%&/~+#-])?$', + r'^((http|https|ftp)://)[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-|]*[\w@?^=%&/~+#-])?$', caseSensitive: false); return regex.hasMatch(this); }