diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2cea0cf7..42d6301e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -32,7 +32,7 @@ jobs: - os: ubuntu-20.04 target: linux build_path: build/linux/x64/release/bundle - - os: macos-10.15 # Catalina + - os: macos-latest # Catalina target: macos build_path: build/macos/Build/Products/Release - os: windows-2019 diff --git a/lib/src/features/browse_center/presentation/source_manga_list/controller/source_manga_controller.dart b/lib/src/features/browse_center/presentation/source_manga_list/controller/source_manga_controller.dart index b9e6aef7..f7260e5c 100644 --- a/lib/src/features/browse_center/presentation/source_manga_list/controller/source_manga_controller.dart +++ b/lib/src/features/browse_center/presentation/source_manga_list/controller/source_manga_controller.dart @@ -19,10 +19,10 @@ import '../../../domain/source/source_model.dart'; part 'source_manga_controller.g.dart'; @riverpod -Future source(SourceRef ref, String sourceId) { +FutureOr source(SourceRef ref, String sourceId) async { final token = CancelToken(); ref.onDispose(token.cancel); - final result = ref + final result = await ref .watch(sourceRepositoryProvider) .getSource(sourceId: sourceId, cancelToken: token); ref.keepAlive(); @@ -33,11 +33,12 @@ Future source(SourceRef ref, String sourceId) { Future?> baseSourceMangaFilterList( BaseSourceMangaFilterListRef ref, String sourceId, -) { +) async { final token = CancelToken(); ref.onDispose(token.cancel); - final result = - ref.watch(sourceRepositoryProvider).getFilterList(sourceId: sourceId); + final result = await ref + .watch(sourceRepositoryProvider) + .getFilterList(sourceId: sourceId); ref.keepAlive(); return result; } diff --git a/lib/src/features/browse_center/presentation/source_manga_list/controller/source_manga_controller.g.dart b/lib/src/features/browse_center/presentation/source_manga_list/controller/source_manga_controller.g.dart index c239afc7..8d5cd604 100644 --- a/lib/src/features/browse_center/presentation/source_manga_list/controller/source_manga_controller.g.dart +++ b/lib/src/features/browse_center/presentation/source_manga_list/controller/source_manga_controller.g.dart @@ -149,7 +149,7 @@ abstract class _$SourceDisplayMode extends AutoDisposeNotifier { DisplayMode? build(); } -String $sourceHash() => r'4b9780685f4d7657fd9d31eb5b535275b9896577'; +String $sourceHash() => r'02ebbe306c0a96174aa221c8e90e9f2c96f398ee'; /// See also [source]. class SourceProvider extends AutoDisposeFutureProvider { @@ -220,7 +220,7 @@ class SourceFamily extends Family> { } String $baseSourceMangaFilterListHash() => - r'a576d9d103923d24ee4fd66836dfe07e0b896a5a'; + r'21b6028eb4e3ea54f9708d2faf1c73f21129721e'; /// See also [baseSourceMangaFilterList]. class BaseSourceMangaFilterListProvider diff --git a/lib/src/features/manga_book/presentation/manga_details/controller/manga_details_controller.dart b/lib/src/features/manga_book/presentation/manga_details/controller/manga_details_controller.dart index 43d7ccae..8aaa35ab 100644 --- a/lib/src/features/manga_book/presentation/manga_details/controller/manga_details_controller.dart +++ b/lib/src/features/manga_book/presentation/manga_details/controller/manga_details_controller.dart @@ -88,7 +88,7 @@ class MangaChapterList extends _$MangaChapterList { } @riverpod -List? mangaChapterListWithFilter( +AsyncValue?> mangaChapterListWithFilter( MangaChapterListWithFilterRef ref, { required String mangaId, }) { @@ -130,8 +130,32 @@ List? mangaChapterListWithFilter( } } - return chapterList.valueOrNull?.where(applyChapterFilter).toList() - ?..sort(applyChapterSort); + return chapterList.copyWithData( + (data) => [...?data?.where(applyChapterFilter)]..sort(applyChapterSort), + ); +} + +@riverpod +Chapter? firstUnreadInFilteredChapterList( + FirstUnreadInFilteredChapterListRef ref, { + required String mangaId, +}) { + final isAscSorted = ref.watch(mangaChapterSortDirectionProvider) ?? + DBKeys.chapterSortDirection.initial; + final filteredList = ref + .watch(mangaChapterListWithFilterProvider(mangaId: mangaId)) + .valueOrNull; + if (filteredList == null) { + return null; + } else { + if (isAscSorted) { + return filteredList + .firstWhereOrNull((element) => !element.read.ifNull(true)); + } else { + return filteredList + .lastWhereOrNull((element) => !element.read.ifNull(true)); + } + } } @riverpod @@ -140,18 +164,26 @@ Pair? getPreviousAndNextChapters( required String mangaId, required String chapterIndex, }) { - final filteredList = - ref.watch(mangaChapterListWithFilterProvider(mangaId: mangaId)); - if (filteredList == null) return null; - final currentChapterIndex = - filteredList.indexWhere((element) => "${element.index}" == chapterIndex); - return Pair( - first: - currentChapterIndex > 0 ? filteredList[currentChapterIndex - 1] : null, - second: currentChapterIndex < (filteredList.length - 1) + final isAscSorted = ref.watch(mangaChapterSortDirectionProvider) ?? + DBKeys.chapterSortDirection.initial; + final filteredList = ref + .watch(mangaChapterListWithFilterProvider(mangaId: mangaId)) + .valueOrNull; + if (filteredList == null) { + return null; + } else { + final currentChapterIndex = filteredList + .indexWhere((element) => "${element.index}" == chapterIndex); + final prevChapter = + currentChapterIndex > 0 ? filteredList[currentChapterIndex - 1] : null; + final nextChapter = currentChapterIndex < (filteredList.length - 1) ? filteredList[currentChapterIndex + 1] - : null, - ); + : null; + return Pair( + first: isAscSorted ? nextChapter : prevChapter, + second: isAscSorted ? prevChapter : nextChapter, + ); + } } @riverpod diff --git a/lib/src/features/manga_book/presentation/manga_details/controller/manga_details_controller.g.dart b/lib/src/features/manga_book/presentation/manga_details/controller/manga_details_controller.g.dart index 142edf36..2eedaba1 100644 --- a/lib/src/features/manga_book/presentation/manga_details/controller/manga_details_controller.g.dart +++ b/lib/src/features/manga_book/presentation/manga_details/controller/manga_details_controller.g.dart @@ -388,11 +388,11 @@ abstract class _$MangaCategoryList } String $mangaChapterListWithFilterHash() => - r'60995f367091cb899dfedab8812f58878418d2fc'; + r'a5ca407ed7a6d0989e90ce3d56614bb387597ac2'; /// See also [mangaChapterListWithFilter]. class MangaChapterListWithFilterProvider - extends AutoDisposeProvider?> { + extends AutoDisposeProvider?>> { MangaChapterListWithFilterProvider({ required this.mangaId, }) : super( @@ -425,12 +425,14 @@ class MangaChapterListWithFilterProvider } } -typedef MangaChapterListWithFilterRef = AutoDisposeProviderRef?>; +typedef MangaChapterListWithFilterRef + = AutoDisposeProviderRef?>>; /// See also [mangaChapterListWithFilter]. final mangaChapterListWithFilterProvider = MangaChapterListWithFilterFamily(); -class MangaChapterListWithFilterFamily extends Family?> { +class MangaChapterListWithFilterFamily + extends Family?>> { MangaChapterListWithFilterFamily(); MangaChapterListWithFilterProvider call({ @@ -442,7 +444,7 @@ class MangaChapterListWithFilterFamily extends Family?> { } @override - AutoDisposeProvider?> getProviderOverride( + AutoDisposeProvider?>> getProviderOverride( covariant MangaChapterListWithFilterProvider provider, ) { return call( @@ -460,8 +462,82 @@ class MangaChapterListWithFilterFamily extends Family?> { String? get name => r'mangaChapterListWithFilterProvider'; } +String $firstUnreadInFilteredChapterListHash() => + r'60d7452267e74bafdc290890223cc3a641e06305'; + +/// See also [firstUnreadInFilteredChapterList]. +class FirstUnreadInFilteredChapterListProvider + extends AutoDisposeProvider { + FirstUnreadInFilteredChapterListProvider({ + required this.mangaId, + }) : super( + (ref) => firstUnreadInFilteredChapterList( + ref, + mangaId: mangaId, + ), + from: firstUnreadInFilteredChapterListProvider, + name: r'firstUnreadInFilteredChapterListProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : $firstUnreadInFilteredChapterListHash, + ); + + final String mangaId; + + @override + bool operator ==(Object other) { + return other is FirstUnreadInFilteredChapterListProvider && + other.mangaId == mangaId; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, mangaId.hashCode); + + return _SystemHash.finish(hash); + } +} + +typedef FirstUnreadInFilteredChapterListRef = AutoDisposeProviderRef; + +/// See also [firstUnreadInFilteredChapterList]. +final firstUnreadInFilteredChapterListProvider = + FirstUnreadInFilteredChapterListFamily(); + +class FirstUnreadInFilteredChapterListFamily extends Family { + FirstUnreadInFilteredChapterListFamily(); + + FirstUnreadInFilteredChapterListProvider call({ + required String mangaId, + }) { + return FirstUnreadInFilteredChapterListProvider( + mangaId: mangaId, + ); + } + + @override + AutoDisposeProvider getProviderOverride( + covariant FirstUnreadInFilteredChapterListProvider provider, + ) { + return call( + mangaId: provider.mangaId, + ); + } + + @override + List? get allTransitiveDependencies => null; + + @override + List? get dependencies => null; + + @override + String? get name => r'firstUnreadInFilteredChapterListProvider'; +} + String $getPreviousAndNextChaptersHash() => - r'8db14cff31abbea310152b38f25bcfe581efd7ab'; + r'c96d69daa0b66402a78052eb11af57f223c3e465'; /// See also [getPreviousAndNextChapters]. class GetPreviousAndNextChaptersProvider diff --git a/lib/src/features/manga_book/presentation/manga_details/manga_details_screen.dart b/lib/src/features/manga_book/presentation/manga_details/manga_details_screen.dart index 06978589..bc7c056e 100644 --- a/lib/src/features/manga_book/presentation/manga_details/manga_details_screen.dart +++ b/lib/src/features/manga_book/presentation/manga_details/manga_details_screen.dart @@ -30,12 +30,20 @@ class MangaDetailsScreen extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final provider = mangaWithIdProvider(mangaId: mangaId); - final chapterListProvider = mangaChapterListProvider(mangaId: mangaId); + final provider = useMemoized(() => mangaWithIdProvider(mangaId: mangaId)); + final chapterListProvider = useMemoized( + () => mangaChapterListProvider(mangaId: mangaId), + ); final manga = ref.watch(provider); - final chapterList = ref.watch(chapterListProvider); + final filteredChapterList = + ref.watch(mangaChapterListWithFilterProvider(mangaId: mangaId)); + + final firstUnreadChapter = ref.watch( + firstUnreadInFilteredChapterListProvider(mangaId: mangaId), + ); + final selectedChapters = useState>({}); - refresh([onlineFetch = false]) async { + final refresh = useCallback(([onlineFetch = false]) async { if (context.mounted && onlineFetch) { ref.read(toastProvider(context)).show( LocaleKeys.updating.tr(), @@ -50,10 +58,10 @@ class MangaDetailsScreen extends HookConsumerWidget { withMicrotask: true, ); } - } + }, []); useEffect(() { - if (!chapterList.isLoading && !manga.isLoading) refresh(); + if (!filteredChapterList.isLoading && !manga.isLoading) refresh(); return; }, []); @@ -75,10 +83,14 @@ class MangaDetailsScreen extends HookConsumerWidget { onPressed: () { selectedChapters.value = { for (Chapter i in [ - ...?ref.read(mangaChapterListWithFilterProvider( - mangaId: mangaId)) + ...?ref + .read( + mangaChapterListWithFilterProvider( + mangaId: mangaId), + ) + .valueOrNull ]) - i.id ?? -1: i + if (i.id != null) i.id!: i }; }, icon: const Icon(Icons.select_all_rounded), @@ -87,12 +99,15 @@ class MangaDetailsScreen extends HookConsumerWidget { onPressed: () { final newMap = { for (Chapter i in [ - ...?ref.read(mangaChapterListWithFilterProvider( - mangaId: mangaId)) + ...?ref + .read(mangaChapterListWithFilterProvider( + mangaId: mangaId)) + .valueOrNull ]) - i.id ?? -1: i - }..removeWhere((key, value) => - selectedChapters.value.containsKey(key)); + if (i.id != null && + !selectedChapters.value.containsKey(key)) + i.id!: i + }; selectedChapters.value = newMap; }, icon: const Icon(Icons.flip_to_back_rounded), @@ -162,32 +177,36 @@ class MangaDetailsScreen extends HookConsumerWidget { selectedChapters: selectedChapters, ) : null, - floatingActionButton: FloatingActionButton.extended( - isExtended: context.isTablet, - label: Text( - data?.lastChapterRead?.index != null - ? LocaleKeys.resume.tr() - : LocaleKeys.start.tr(), - ), - icon: const Icon(Icons.play_arrow_rounded), - onPressed: () { - context.push( - Routes.getReader( - "${data?.id}", "${data?.lastChapterRead?.index ?? 1}"), - ); - }, - ), + floatingActionButton: firstUnreadChapter != null + ? FloatingActionButton.extended( + isExtended: context.isTablet, + label: Text( + data?.lastChapterRead?.index != null + ? LocaleKeys.resume.tr() + : LocaleKeys.start.tr(), + ), + icon: const Icon(Icons.play_arrow_rounded), + onPressed: () { + context.push( + Routes.getReader( + "${firstUnreadChapter.mangaId ?? mangaId}", + "${firstUnreadChapter.index ?? 0}", + ), + ); + }, + ) + : null, body: data != null ? context.isTablet ? BigScreenMangaDetails( - chapterList: chapterList, + chapterList: filteredChapterList, manga: data, mangaId: mangaId, onRefresh: refresh, selectedChapters: selectedChapters, ) : SmallScreenMangaDetails( - chapterList: chapterList, + chapterList: filteredChapterList, manga: data, mangaId: mangaId, onRefresh: refresh, diff --git a/lib/src/features/manga_book/presentation/manga_details/widgets/big_screen_manga_details.dart b/lib/src/features/manga_book/presentation/manga_details/widgets/big_screen_manga_details.dart index 18bc4806..09ffd7ca 100644 --- a/lib/src/features/manga_book/presentation/manga_details/widgets/big_screen_manga_details.dart +++ b/lib/src/features/manga_book/presentation/manga_details/widgets/big_screen_manga_details.dart @@ -15,7 +15,6 @@ import '../../../../../widgets/emoticons.dart'; import '../../../data/manga_book_repository.dart'; import '../../../domain/chapter/chapter_model.dart'; import '../../../domain/manga/manga_model.dart'; -import '../controller/manga_details_controller.dart'; import 'chapter_list_tile.dart'; import 'manga_description.dart'; @@ -35,9 +34,7 @@ class BigScreenMangaDetails extends ConsumerWidget { final AsyncValue?> chapterList; @override Widget build(BuildContext context, WidgetRef ref) { - final filteredChapterList = - ref.watch(mangaChapterListWithFilterProvider(mangaId: mangaId)); - + final filteredChapterList = chapterList.valueOrNull; return RefreshIndicator( onRefresh: () => onRefresh(true), child: Row( diff --git a/lib/src/features/manga_book/presentation/manga_details/widgets/chapter_list_tile.dart b/lib/src/features/manga_book/presentation/manga_details/widgets/chapter_list_tile.dart index 0028d144..469fee4f 100644 --- a/lib/src/features/manga_book/presentation/manga_details/widgets/chapter_list_tile.dart +++ b/lib/src/features/manga_book/presentation/manga_details/widgets/chapter_list_tile.dart @@ -72,14 +72,14 @@ class ChapterListTile extends StatelessWidget { color: chapter.read ?? false ? Colors.grey : null, ), ), - if ((chapter.lastPageRead).ifNullOrNegative().isNotEquals([ - 0, - chapter.pageCount ?? 0, - ((chapter.pageCount ?? 1) - 1) - ])) + if (!chapter.read.ifNull() && + (chapter.lastPageRead).ifNullOrNegative() != 0) Text( " • ${LocaleKeys.page.tr( - namedArgs: {"number": "${chapter.lastPageRead}"}, + namedArgs: { + "number": + "${chapter.lastPageRead.ifNullOrNegative() + 1}" + }, )}", style: const TextStyle(color: Colors.grey), ), diff --git a/lib/src/features/manga_book/presentation/manga_details/widgets/small_screen_manga_details.dart b/lib/src/features/manga_book/presentation/manga_details/widgets/small_screen_manga_details.dart index 70e40ffc..96f3ca34 100644 --- a/lib/src/features/manga_book/presentation/manga_details/widgets/small_screen_manga_details.dart +++ b/lib/src/features/manga_book/presentation/manga_details/widgets/small_screen_manga_details.dart @@ -15,7 +15,6 @@ import '../../../../../widgets/emoticons.dart'; import '../../../data/manga_book_repository.dart'; import '../../../domain/chapter/chapter_model.dart'; import '../../../domain/manga/manga_model.dart'; -import '../controller/manga_details_controller.dart'; import 'chapter_list_tile.dart'; import 'manga_description.dart'; @@ -35,9 +34,7 @@ class SmallScreenMangaDetails extends ConsumerWidget { final AsyncValue?> chapterList; @override Widget build(BuildContext context, WidgetRef ref) { - final filteredChapterList = - ref.watch(mangaChapterListWithFilterProvider(mangaId: mangaId)); - + final filteredChapterList = chapterList.valueOrNull; return RefreshIndicator( onRefresh: () => onRefresh(true), child: CustomScrollView( diff --git a/lib/src/features/manga_book/presentation/reader/controller/reader_controller.dart b/lib/src/features/manga_book/presentation/reader/controller/reader_controller.dart index 1c237c58..f639e312 100644 --- a/lib/src/features/manga_book/presentation/reader/controller/reader_controller.dart +++ b/lib/src/features/manga_book/presentation/reader/controller/reader_controller.dart @@ -17,10 +17,10 @@ FutureOr chapter( ChapterRef ref, { required String mangaId, required String chapterIndex, -}) { +}) async { final token = CancelToken(); ref.onDispose(token.cancel); - final result = ref.watch(mangaBookRepositoryProvider).getChapter( + final result = await ref.watch(mangaBookRepositoryProvider).getChapter( mangaId: mangaId, chapterIndex: chapterIndex, ); diff --git a/lib/src/features/manga_book/presentation/reader/controller/reader_controller.g.dart b/lib/src/features/manga_book/presentation/reader/controller/reader_controller.g.dart index ac95cb40..68b9b160 100644 --- a/lib/src/features/manga_book/presentation/reader/controller/reader_controller.g.dart +++ b/lib/src/features/manga_book/presentation/reader/controller/reader_controller.g.dart @@ -29,7 +29,7 @@ class _SystemHash { } } -String $chapterHash() => r'0b6a4b8db86a7e4732a22b54bec76e9f6ecbd26e'; +String $chapterHash() => r'80779674a17d3466d24f1b08fc563a41c58f8070'; /// See also [chapter]. class ChapterProvider extends AutoDisposeFutureProvider { diff --git a/lib/src/features/manga_book/presentation/reader/reader_screen.dart b/lib/src/features/manga_book/presentation/reader/reader_screen.dart index c62d7693..c7e26b8f 100644 --- a/lib/src/features/manga_book/presentation/reader/reader_screen.dart +++ b/lib/src/features/manga_book/presentation/reader/reader_screen.dart @@ -38,80 +38,89 @@ class ReaderScreen extends HookConsumerWidget { final defaultReaderMode = ref.watch(readerModeKeyProvider); final onPageChanged = useCallback>((int currentPage) async { + final chapterValue = chapter.valueOrNull; + final isReadingCompeted = chapterValue != null && + ((chapterValue.read).ifNull() || + (currentPage > + ((chapterValue.pageCount).ifNullOrNegative() - 1))); await AsyncValue.guard( () => ref.read(mangaBookRepositoryProvider).putChapter( mangaId: mangaId, chapterIndex: chapterIndex, patch: ChapterPut( lastPageRead: currentPage, - read: (currentPage == -1), + read: isReadingCompeted, ), ), ); - if (currentPage == -1) { - ref.invalidate(mangaChapterListProvider(mangaId: mangaId)); - } return; - }, []); + }, [chapter]); - return SafeArea( - child: manga.showUiWhenData( - addScaffoldWrapper: true, - refresh: () => ref.refresh(mangaProvider), - (data) { - if (data == null) return const SizedBox.shrink(); - final readerMode = data.meta.isNotBlank && - data.meta!.containsKey(ChapterMeta.readerMode.key) - ? ReaderMode.values.firstWhere( - (element) => - element.name == data.meta![ChapterMeta.readerMode.key], - orElse: () => defaultReaderMode ?? ReaderMode.webtoon, - ) - : defaultReaderMode; - return chapter.showUiWhenData( - refresh: () => ref.refresh(provider), - addScaffoldWrapper: true, - (chapterData) { - if (chapterData == null) return const SizedBox.shrink(); - switch (readerMode) { - case ReaderMode.singleVertical: - return SinglePageReaderMode( - chapter: chapterData, - manga: data, - onPageChanged: onPageChanged, - scrollDirection: Axis.vertical, - ); - case ReaderMode.singleHorizontalRTL: - return SinglePageReaderMode( - chapter: chapterData, - manga: data, - onPageChanged: onPageChanged, - reverse: true, - ); - case ReaderMode.singleHorizontalLTR: - return SinglePageReaderMode( - chapter: chapterData, - manga: data, - onPageChanged: onPageChanged, - ); - case ReaderMode.continuousVertical: - return WebtoonReaderMode( - chapter: chapterData, - manga: data, - onPageChanged: onPageChanged, - showSeparator: true, - ); - case ReaderMode.webtoon: - default: - return WebtoonReaderMode( - chapter: chapterData, - manga: data, - onPageChanged: onPageChanged, - ); - } - }, - ); - }, + return WillPopScope( + onWillPop: () async { + ref.invalidate(provider); + ref.invalidate(mangaChapterListProvider(mangaId: mangaId)); + return true; + }, + child: SafeArea( + child: manga.showUiWhenData( + addScaffoldWrapper: true, + refresh: () => ref.refresh(mangaProvider), + (data) { + if (data == null) return const SizedBox.shrink(); + final readerMode = data.meta.isNotBlank && + data.meta!.containsKey(ChapterMeta.readerMode.key) + ? ReaderMode.values.firstWhere( + (element) => + element.name == data.meta![ChapterMeta.readerMode.key], + orElse: () => defaultReaderMode ?? ReaderMode.webtoon, + ) + : defaultReaderMode; + return chapter.showUiWhenData( + refresh: () => ref.refresh(provider), + addScaffoldWrapper: true, + (chapterData) { + if (chapterData == null) return const SizedBox.shrink(); + switch (readerMode) { + case ReaderMode.singleVertical: + return SinglePageReaderMode( + chapter: chapterData, + manga: data, + onPageChanged: onPageChanged, + scrollDirection: Axis.vertical, + ); + case ReaderMode.singleHorizontalRTL: + return SinglePageReaderMode( + chapter: chapterData, + manga: data, + onPageChanged: onPageChanged, + reverse: true, + ); + case ReaderMode.singleHorizontalLTR: + return SinglePageReaderMode( + chapter: chapterData, + manga: data, + onPageChanged: onPageChanged, + ); + case ReaderMode.continuousVertical: + return WebtoonReaderMode( + chapter: chapterData, + manga: data, + onPageChanged: onPageChanged, + showSeparator: true, + ); + case ReaderMode.webtoon: + default: + return WebtoonReaderMode( + chapter: chapterData, + manga: data, + onPageChanged: onPageChanged, + ); + } + }, + ); + }, + ), ), ); } diff --git a/lib/src/features/manga_book/presentation/reader/widgets/reader_mode/single_page_reader_mode.dart b/lib/src/features/manga_book/presentation/reader/widgets/reader_mode/single_page_reader_mode.dart index 0b03eacb..c38d97d8 100644 --- a/lib/src/features/manga_book/presentation/reader/widgets/reader_mode/single_page_reader_mode.dart +++ b/lib/src/features/manga_book/presentation/reader/widgets/reader_mode/single_page_reader_mode.dart @@ -4,20 +4,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import '../../../../../../constants/app_constants.dart'; import '../../../../../../constants/endpoints.dart'; -import '../../../../../../i18n/locale_keys.g.dart'; import '../../../../../../utils/extensions/custom_extensions.dart'; import '../../../../../../widgets/custom_circular_progress_indicator.dart'; import '../../../../../../widgets/server_image.dart'; import '../../../../domain/chapter/chapter_model.dart'; import '../../../../domain/manga/manga_model.dart'; -import '../chapter_separator.dart'; import '../reader_wrapper.dart'; class SinglePageReaderMode extends HookWidget { @@ -37,26 +34,19 @@ class SinglePageReaderMode extends HookWidget { final Axis scrollDirection; @override Widget build(BuildContext context) { - final scrollController = - usePageController(initialPage: chapter.lastPageRead.ifNullOrNegative()); + final scrollController = usePageController( + initialPage: + chapter.read.ifNull() ? 0 : chapter.lastPageRead.ifNullOrNegative(), + ); final currentIndex = useState(scrollController.initialPage); useEffect(() { - final index = currentIndex.value; - if (onPageChanged != null) { - if (index == ((chapter.pageCount ?? 0) - 1)) { - onPageChanged!(-1); - } else { - onPageChanged!(index); - } - } + if (onPageChanged != null) onPageChanged!(currentIndex.value); return; }, [currentIndex.value]); useEffect(() { listener() { final currentPage = scrollController.page; - if (currentPage != null) { - currentIndex.value = currentPage.toInt(); - } + if (currentPage != null) currentIndex.value = currentPage.toInt(); } scrollController.addListener(listener); @@ -96,31 +86,7 @@ class SinglePageReaderMode extends HookWidget { ); return InteractiveViewer( maxScale: 8, - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (index == 0) - ChapterSeparator( - title: LocaleKeys.current.tr(), - name: chapter.name ?? - LocaleKeys.chapterNumber.tr(namedArgs: { - 'chapterNumber': "${chapter.chapterNumber ?? 0}" - }), - ), - image, - if (index == (chapter.pageCount ?? 0) - 1) - ChapterSeparator( - title: LocaleKeys.finished.tr(), - name: chapter.name ?? - LocaleKeys.chapterNumber.tr(namedArgs: { - 'chapterNumber': "${chapter.chapterNumber ?? 0}" - }), - ), - ], - ), - ), + child: image, ); }, itemCount: chapter.pageCount ?? 0, diff --git a/lib/src/features/manga_book/presentation/reader/widgets/reader_mode/webtoon_reader_mode.dart b/lib/src/features/manga_book/presentation/reader/widgets/reader_mode/webtoon_reader_mode.dart index 189f1ead..51df6565 100644 --- a/lib/src/features/manga_book/presentation/reader/widgets/reader_mode/webtoon_reader_mode.dart +++ b/lib/src/features/manga_book/presentation/reader/widgets/reader_mode/webtoon_reader_mode.dart @@ -37,16 +37,11 @@ class WebtoonReaderMode extends HookWidget { Widget build(BuildContext context) { final scrollController = useMemoized(() => ItemScrollController()); final positionsListener = useMemoized(() => ItemPositionsListener.create()); - final currentIndex = useState((chapter.lastPageRead).ifNullOrNegative()); + final currentIndex = useState( + chapter.read.ifNull() ? 0 : (chapter.lastPageRead).ifNullOrNegative(), + ); useEffect(() { - final index = currentIndex.value; - if (onPageChanged != null) { - if (index == ((chapter.pageCount ?? 0) - 1)) { - onPageChanged!(-1); - } else { - onPageChanged!(index); - } - } + if (onPageChanged != null) onPageChanged!(currentIndex.value); return; }, [currentIndex.value]); useEffect(() { @@ -108,7 +103,9 @@ class WebtoonReaderMode extends HookWidget { child: ScrollablePositionedList.separated( itemScrollController: scrollController, itemPositionsListener: positionsListener, - initialScrollIndex: chapter.lastPageRead.ifNullOrNegative(), + initialScrollIndex: chapter.read.ifNull() + ? 0 + : chapter.lastPageRead.ifNullOrNegative(), itemBuilder: (BuildContext context, int index) { final image = ServerImage( fit: BoxFit.fitWidth, diff --git a/lib/src/features/manga_book/presentation/reader/widgets/reader_quick_settings.dart b/lib/src/features/manga_book/presentation/reader/widgets/reader_quick_settings.dart index 536404c2..1de67638 100644 --- a/lib/src/features/manga_book/presentation/reader/widgets/reader_quick_settings.dart +++ b/lib/src/features/manga_book/presentation/reader/widgets/reader_quick_settings.dart @@ -45,16 +45,14 @@ class ReaderQuickSettings extends HookConsumerWidget { value: enumValue.name, ), ); - ref.invalidate( - mangaWithIdProvider(mangaId: "${manga.id}"), - ); + ref.invalidate(mangaWithIdProvider(mangaId: "${manga.id}")); }, ), ), []); - final VoidCallback showReaderNavigationLayoutPopup = useCallback( - () => showDialog( + final showReaderNavigationLayoutPopup = useCallback( + (BuildContext context) => showDialog( context: context, builder: (context) => EnumPopup( enumList: ReaderNavigationLayout.values, @@ -106,7 +104,7 @@ class ReaderQuickSettings extends HookConsumerWidget { ), onTap: () { context.pop(); - showReaderNavigationLayoutPopup(); + showReaderNavigationLayoutPopup(context); }, ), ], diff --git a/lib/src/utils/extensions/custom_extensions/iterable_extensions.dart b/lib/src/utils/extensions/custom_extensions/iterable_extensions.dart index 59c75200..df98bc99 100644 --- a/lib/src/utils/extensions/custom_extensions/iterable_extensions.dart +++ b/lib/src/utils/extensions/custom_extensions/iterable_extensions.dart @@ -23,4 +23,22 @@ extension IterableExtensions on Iterable? { } String get toPath => isNotBlank ? this!.join("/") : "/"; + + T? lastWhereOrNull(bool Function(T element) test, {T Function()? orElse}) { + if (isNull) return null; + try { + return this!.lastWhere(test, orElse: orElse); + } catch (e) { + return null; + } + } + + T? firstWhereOrNull(bool Function(T element) test, {T Function()? orElse}) { + if (isNull) return null; + try { + return this!.firstWhere(test, orElse: orElse); + } catch (e) { + return null; + } + } } diff --git a/pubspec.yaml b/pubspec.yaml index a378a221..c83976ff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: tachidesk_sorayomi description: A new Flutter frontend for Tachidesk. publish_to: "none" -version: 0.3.2+1 +version: 0.3.3+1 environment: sdk: ">=2.18.1 <3.0.0"