diff --git a/lib/modules/library/library_screen.dart b/lib/modules/library/library_screen.dart index 9fa6a8e2..5c115b6f 100644 --- a/lib/modules/library/library_screen.dart +++ b/lib/modules/library/library_screen.dart @@ -18,11 +18,13 @@ import 'package:mangayomi/models/settings.dart'; import 'package:mangayomi/models/update.dart'; import 'package:mangayomi/modules/library/providers/add_torrent.dart'; import 'package:mangayomi/modules/library/providers/local_archive.dart'; +import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart'; import 'package:mangayomi/modules/manga/detail/providers/update_manga_detail_providers.dart'; import 'package:mangayomi/modules/more/categories/providers/isar_providers.dart'; import 'package:mangayomi/modules/more/providers/downloaded_only_state_provider.dart'; import 'package:mangayomi/modules/more/settings/appearance/providers/theme_mode_state_provider.dart'; import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart'; +import 'package:mangayomi/modules/widgets/bottom_select_bar.dart'; import 'package:mangayomi/modules/widgets/category_selection_dialog.dart'; import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart'; import 'package:mangayomi/modules/widgets/manga_image_card_widget.dart'; @@ -570,159 +572,85 @@ class _LibraryScreenState extends ConsumerState return const ProgressCenter(); }, ), - bottomNavigationBar: Consumer( - builder: (context, ref, child) { - final isLongPressed = ref.watch(isLongPressedMangaStateProvider); - final color = Theme.of(context).textTheme.bodyLarge!.color!; + bottomNavigationBar: Builder( + builder: (context) { final mangaIds = ref.watch(mangasListStateProvider); - return AnimatedContainer( - curve: Curves.easeIn, - decoration: BoxDecoration( - color: context.primaryColor.withValues(alpha: 0.2), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(20), - topRight: Radius.circular(20), + final color = Theme.of(context).textTheme.bodyLarge!.color!; + return BottomSelectBar( + isVisible: ref.watch(isLongPressedStateProvider), + actions: [ + BottomSelectButton( + icon: Icon(Icons.label_outline_rounded, color: color), + onPressed: () { + final mangaIdsList = ref.watch(mangasListStateProvider); + final List bulkMangas = mangaIdsList + .map((id) => isar.mangas.getSync(id)!) + .toList(); + showCategorySelectionDialog( + context: context, + ref: ref, + itemType: widget.itemType, + bulkMangas: bulkMangas, + ); + }, ), - ), - duration: const Duration(milliseconds: 100), - height: isLongPressed ? 70 : 0, - width: context.width(1), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - shadowColor: Colors.transparent, - elevation: 0, - backgroundColor: Colors.transparent, - ), - onPressed: () { - final mangaIdsList = ref.watch( - mangasListStateProvider, - ); - final List bulkMangas = mangaIdsList - .map((id) => isar.mangas.getSync(id)!) - .toList(); - showCategorySelectionDialog( - context: context, - ref: ref, - itemType: widget.itemType, - bulkMangas: bulkMangas, - ); - }, - child: Icon( - Icons.label_outline_rounded, - color: color, - ), + BottomSelectButton( + icon: Icon(Icons.done_all_sharp, color: color), + onPressed: () { + ref + .read( + mangasSetIsReadStateProvider( + mangaIds: mangaIds, + markAsRead: true, + ).notifier, + ) + .set(); + ref.invalidate( + getAllMangaWithoutCategoriesStreamProvider( + itemType: widget.itemType, ), - ), - ), - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 0, - backgroundColor: Colors.transparent, - shadowColor: Colors.transparent, - ), - onPressed: () { - ref - .read( - mangasSetIsReadStateProvider( - mangaIds: mangaIds, - ).notifier, - ) - .set(); - ref.invalidate( - getAllMangaWithoutCategoriesStreamProvider( - itemType: widget.itemType, - ), - ); - ref.invalidate( - getAllMangaStreamProvider( - categoryId: null, - itemType: widget.itemType, - ), - ); - }, - child: Icon(Icons.done_all_sharp, color: color), + ); + ref.invalidate( + getAllMangaStreamProvider( + categoryId: null, + itemType: widget.itemType, ), - ), - ), - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 0, - backgroundColor: Colors.transparent, - shadowColor: Colors.transparent, - ), - onPressed: () { - ref - .read( - mangasSetUnReadStateProvider( - mangaIds: mangaIds, - ).notifier, - ) - .set(); - ref.invalidate( - getAllMangaWithoutCategoriesStreamProvider( - itemType: widget.itemType, - ), - ); - ref.invalidate( - getAllMangaStreamProvider( - categoryId: null, - itemType: widget.itemType, - ), - ); - }, - child: Icon(Icons.remove_done_sharp, color: color), + ); + }, + ), + BottomSelectButton( + icon: Icon(Icons.remove_done_sharp, color: color), + onPressed: () { + ref + .read( + mangasSetIsReadStateProvider( + mangaIds: mangaIds, + markAsRead: false, + ).notifier, + ) + .set(); + ref.invalidate( + getAllMangaWithoutCategoriesStreamProvider( + itemType: widget.itemType, ), - ), - ), - // Expanded( - // child: SizedBox( - // height: 70, - // child: ElevatedButton( - // style: ElevatedButton.styleFrom( - // elevation: 0, - // backgroundColor: Colors.transparent, - // shadowColor: Colors.transparent, - // ), - // onPressed: () {}, - // child: Icon( - // Icons.download_outlined, - // color: color, - // )), - // ), - // ), - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 0, - backgroundColor: Colors.transparent, - shadowColor: Colors.transparent, - ), - onPressed: () { - _deleteManga(); - }, - child: Icon( - Icons.delete_outline_outlined, - color: color, - ), + ); + ref.invalidate( + getAllMangaStreamProvider( + categoryId: null, + itemType: widget.itemType, ), - ), - ), - ], - ), + ); + }, + ), + // BottomBarAction( + // icon: Icon(Icons.download_outlined, color: color), + // onPressed: () {} + // ), + BottomSelectButton( + icon: Icon(Icons.delete_outline_outlined, color: color), + onPressed: () => _deleteManga(), + ), + ], ); }, ), @@ -1229,7 +1157,7 @@ class _LibraryScreenState extends ConsumerState ref.read(mangasListStateProvider.notifier).clear(); ref - .read(isLongPressedMangaStateProvider.notifier) + .read(isLongPressedStateProvider.notifier) .update(false); if (mounted) { Navigator.pop(context); @@ -1882,7 +1810,7 @@ class _LibraryScreenState extends ConsumerState int? categoryId, Settings settings, ) { - final isLongPressed = ref.watch(isLongPressedMangaStateProvider); + final isLongPressed = ref.watch(isLongPressedStateProvider); final mangaIdsList = ref.watch(mangasListStateProvider); final manga = categoryId == null ? ref.watch( @@ -1911,7 +1839,7 @@ class _LibraryScreenState extends ConsumerState ref.read(mangasListStateProvider.notifier).clear(); ref - .read(isLongPressedMangaStateProvider.notifier) + .read(isLongPressedStateProvider.notifier) .update(!isLongPressed); }, icon: const Icon(Icons.clear), @@ -1936,7 +1864,7 @@ class _LibraryScreenState extends ConsumerState .selectSome(manga); } ref - .read(isLongPressedMangaStateProvider.notifier) + .read(isLongPressedStateProvider.notifier) .update(false); } else { for (var manga in data) { diff --git a/lib/modules/library/providers/library_state_provider.dart b/lib/modules/library/providers/library_state_provider.dart index 175c737c..a6c91ae7 100644 --- a/lib/modules/library/providers/library_state_provider.dart +++ b/lib/modules/library/providers/library_state_provider.dart @@ -3,6 +3,7 @@ import 'package:mangayomi/main.dart'; import 'package:mangayomi/models/chapter.dart'; import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/models/settings.dart'; +import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart'; import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart'; import 'package:mangayomi/providers/l10n_providers.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -785,7 +786,7 @@ class MangasListState extends _$MangasListState { newList.add(value.id!); } if (newList.isEmpty) { - ref.read(isLongPressedMangaStateProvider.notifier).update(false); + ref.read(isLongPressedStateProvider.notifier).update(false); } state = newList; } @@ -814,65 +815,24 @@ class MangasListState extends _$MangasListState { } } -@riverpod -class IsLongPressedMangaState extends _$IsLongPressedMangaState { - @override - bool build() { - return false; - } - - void update(bool value) { - state = value; - } -} - @riverpod class MangasSetIsReadState extends _$MangasSetIsReadState { @override - void build({required List mangaIds}) {} + void build({required List mangaIds, required bool markAsRead}) {} void set() { final allChapters = []; final allMangas = []; + final now = DateTime.now().millisecondsSinceEpoch; for (var mangaid in mangaIds) { final manga = isar.mangas.getSync(mangaid)!; final chapters = manga.chapters; - if (chapters.isNotEmpty) { - chapters.last.updateTrackChapterRead(ref); - for (var chapter in chapters) { - chapter.isRead = true; - chapter.lastPageRead = "1"; - chapter.updatedAt = DateTime.now().millisecondsSinceEpoch; - chapter.manga.value = manga; - allChapters.add(chapter); - } - allMangas.add(manga); - } - } - - isar.writeTxnSync(() { - isar.chapters.putAllSync(allChapters); - isar.mangas.putAllSync(allMangas); - }); - - ref.read(isLongPressedMangaStateProvider.notifier).update(false); - ref.read(mangasListStateProvider.notifier).clear(); - } -} - -@riverpod -class MangasSetUnReadState extends _$MangasSetUnReadState { - @override - void build({required List mangaIds}) {} - - void set() { - final allChapters = []; - final allMangas = []; - for (var mangaid in mangaIds) { - final manga = isar.mangas.getSync(mangaid)!; - for (var chapter in manga.chapters) { - chapter.isRead = false; - chapter.updatedAt = DateTime.now().millisecondsSinceEpoch; + if (chapters.isEmpty) continue; + if (markAsRead) chapters.last.updateTrackChapterRead(ref); + for (var chapter in chapters) { + chapter.isRead = markAsRead; + if (markAsRead) chapter.lastPageRead = "1"; + chapter.updatedAt = now; chapter.manga.value = manga; allChapters.add(chapter); } @@ -884,7 +844,7 @@ class MangasSetUnReadState extends _$MangasSetUnReadState { isar.mangas.putAllSync(allMangas); }); - ref.read(isLongPressedMangaStateProvider.notifier).update(false); + ref.read(isLongPressedStateProvider.notifier).update(false); ref.read(mangasListStateProvider.notifier).clear(); } } diff --git a/lib/modules/library/providers/library_state_provider.g.dart b/lib/modules/library/providers/library_state_provider.g.dart index 6f33272d..e4cc60af 100644 --- a/lib/modules/library/providers/library_state_provider.g.dart +++ b/lib/modules/library/providers/library_state_provider.g.dart @@ -2517,7 +2517,7 @@ class _SortLibraryMangaStateProviderElement Settings get settings => (origin as SortLibraryMangaStateProvider).settings; } -String _$mangasListStateHash() => r'ad1cc419dfd3793bfc8c90f3ce8b7726561dd9ad'; +String _$mangasListStateHash() => r'bbd2e3600ec22a774b1774ae3c221815e52bfef6'; /// See also [MangasListState]. @ProviderFor(MangasListState) @@ -2533,32 +2533,17 @@ final mangasListStateProvider = ); typedef _$MangasListState = AutoDisposeNotifier>; -String _$isLongPressedMangaStateHash() => - r'f77076b0335e92df26a75ea0c338d4214a330184'; - -/// See also [IsLongPressedMangaState]. -@ProviderFor(IsLongPressedMangaState) -final isLongPressedMangaStateProvider = - AutoDisposeNotifierProvider.internal( - IsLongPressedMangaState.new, - name: r'isLongPressedMangaStateProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$isLongPressedMangaStateHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$IsLongPressedMangaState = AutoDisposeNotifier; String _$mangasSetIsReadStateHash() => - r'8f4f7f22ea8e82cf2370fb997033e1a4ec03168f'; + r'2a1b1005e2ed5068d36188a3fb969d21b64bfef6'; abstract class _$MangasSetIsReadState extends BuildlessAutoDisposeNotifier { late final List mangaIds; + late final bool markAsRead; void build({ required List mangaIds, + required bool markAsRead, }); } @@ -2574,9 +2559,11 @@ class MangasSetIsReadStateFamily extends Family { /// See also [MangasSetIsReadState]. MangasSetIsReadStateProvider call({ required List mangaIds, + required bool markAsRead, }) { return MangasSetIsReadStateProvider( mangaIds: mangaIds, + markAsRead: markAsRead, ); } @@ -2586,6 +2573,7 @@ class MangasSetIsReadStateFamily extends Family { ) { return call( mangaIds: provider.mangaIds, + markAsRead: provider.markAsRead, ); } @@ -2610,8 +2598,11 @@ class MangasSetIsReadStateProvider /// See also [MangasSetIsReadState]. MangasSetIsReadStateProvider({ required List mangaIds, + required bool markAsRead, }) : this._internal( - () => MangasSetIsReadState()..mangaIds = mangaIds, + () => MangasSetIsReadState() + ..mangaIds = mangaIds + ..markAsRead = markAsRead, from: mangasSetIsReadStateProvider, name: r'mangasSetIsReadStateProvider', debugGetCreateSourceHash: @@ -2622,6 +2613,7 @@ class MangasSetIsReadStateProvider allTransitiveDependencies: MangasSetIsReadStateFamily._allTransitiveDependencies, mangaIds: mangaIds, + markAsRead: markAsRead, ); MangasSetIsReadStateProvider._internal( @@ -2632,9 +2624,11 @@ class MangasSetIsReadStateProvider required super.debugGetCreateSourceHash, required super.from, required this.mangaIds, + required this.markAsRead, }) : super.internal(); final List mangaIds; + final bool markAsRead; @override void runNotifierBuild( @@ -2642,6 +2636,7 @@ class MangasSetIsReadStateProvider ) { return notifier.build( mangaIds: mangaIds, + markAsRead: markAsRead, ); } @@ -2650,13 +2645,16 @@ class MangasSetIsReadStateProvider return ProviderOverride( origin: this, override: MangasSetIsReadStateProvider._internal( - () => create()..mangaIds = mangaIds, + () => create() + ..mangaIds = mangaIds + ..markAsRead = markAsRead, from: from, name: null, dependencies: null, allTransitiveDependencies: null, debugGetCreateSourceHash: null, mangaIds: mangaIds, + markAsRead: markAsRead, ), ); } @@ -2669,13 +2667,16 @@ class MangasSetIsReadStateProvider @override bool operator ==(Object other) { - return other is MangasSetIsReadStateProvider && other.mangaIds == mangaIds; + return other is MangasSetIsReadStateProvider && + other.mangaIds == mangaIds && + other.markAsRead == markAsRead; } @override int get hashCode { var hash = _SystemHash.combine(0, runtimeType.hashCode); hash = _SystemHash.combine(hash, mangaIds.hashCode); + hash = _SystemHash.combine(hash, markAsRead.hashCode); return _SystemHash.finish(hash); } @@ -2686,6 +2687,9 @@ class MangasSetIsReadStateProvider mixin MangasSetIsReadStateRef on AutoDisposeNotifierProviderRef { /// The parameter `mangaIds` of this provider. List get mangaIds; + + /// The parameter `markAsRead` of this provider. + bool get markAsRead; } class _MangasSetIsReadStateProviderElement @@ -2695,153 +2699,8 @@ class _MangasSetIsReadStateProviderElement @override List get mangaIds => (origin as MangasSetIsReadStateProvider).mangaIds; -} - -String _$mangasSetUnReadStateHash() => - r'09ddd287b110fd76494f9f56bd5cf76f58936f1f'; - -abstract class _$MangasSetUnReadState - extends BuildlessAutoDisposeNotifier { - late final List mangaIds; - - void build({ - required List mangaIds, - }); -} - -/// See also [MangasSetUnReadState]. -@ProviderFor(MangasSetUnReadState) -const mangasSetUnReadStateProvider = MangasSetUnReadStateFamily(); - -/// See also [MangasSetUnReadState]. -class MangasSetUnReadStateFamily extends Family { - /// See also [MangasSetUnReadState]. - const MangasSetUnReadStateFamily(); - - /// See also [MangasSetUnReadState]. - MangasSetUnReadStateProvider call({ - required List mangaIds, - }) { - return MangasSetUnReadStateProvider( - mangaIds: mangaIds, - ); - } - @override - MangasSetUnReadStateProvider getProviderOverride( - covariant MangasSetUnReadStateProvider provider, - ) { - return call( - mangaIds: provider.mangaIds, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'mangasSetUnReadStateProvider'; -} - -/// See also [MangasSetUnReadState]. -class MangasSetUnReadStateProvider - extends AutoDisposeNotifierProviderImpl { - /// See also [MangasSetUnReadState]. - MangasSetUnReadStateProvider({ - required List mangaIds, - }) : this._internal( - () => MangasSetUnReadState()..mangaIds = mangaIds, - from: mangasSetUnReadStateProvider, - name: r'mangasSetUnReadStateProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$mangasSetUnReadStateHash, - dependencies: MangasSetUnReadStateFamily._dependencies, - allTransitiveDependencies: - MangasSetUnReadStateFamily._allTransitiveDependencies, - mangaIds: mangaIds, - ); - - MangasSetUnReadStateProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.mangaIds, - }) : super.internal(); - - final List mangaIds; - - @override - void runNotifierBuild( - covariant MangasSetUnReadState notifier, - ) { - return notifier.build( - mangaIds: mangaIds, - ); - } - - @override - Override overrideWith(MangasSetUnReadState Function() create) { - return ProviderOverride( - origin: this, - override: MangasSetUnReadStateProvider._internal( - () => create()..mangaIds = mangaIds, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - mangaIds: mangaIds, - ), - ); - } - - @override - AutoDisposeNotifierProviderElement - createElement() { - return _MangasSetUnReadStateProviderElement(this); - } - - @override - bool operator ==(Object other) { - return other is MangasSetUnReadStateProvider && other.mangaIds == mangaIds; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, mangaIds.hashCode); - - return _SystemHash.finish(hash); - } -} - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin MangasSetUnReadStateRef on AutoDisposeNotifierProviderRef { - /// The parameter `mangaIds` of this provider. - List get mangaIds; -} - -class _MangasSetUnReadStateProviderElement - extends AutoDisposeNotifierProviderElement - with MangasSetUnReadStateRef { - _MangasSetUnReadStateProviderElement(super.provider); - - @override - List get mangaIds => (origin as MangasSetUnReadStateProvider).mangaIds; + bool get markAsRead => (origin as MangasSetIsReadStateProvider).markAsRead; } // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/modules/library/widgets/library_gridview_widget.dart b/lib/modules/library/widgets/library_gridview_widget.dart index 5bec0f2e..75c891ad 100644 --- a/lib/modules/library/widgets/library_gridview_widget.dart +++ b/lib/modules/library/widgets/library_gridview_widget.dart @@ -9,6 +9,7 @@ import 'package:mangayomi/models/history.dart'; import 'package:mangayomi/modules/library/providers/isar_providers.dart'; import 'package:mangayomi/modules/library/providers/library_state_provider.dart'; import 'package:mangayomi/models/manga.dart'; +import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart'; import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart'; import 'package:mangayomi/utils/extensions/build_context_extensions.dart'; import 'package:mangayomi/utils/constant.dart'; @@ -52,7 +53,7 @@ class _LibraryGridViewWidgetState extends State { Widget build(BuildContext context) { return Consumer( builder: (context, ref, child) { - final isLongPressed = ref.watch(isLongPressedMangaStateProvider); + final isLongPressed = ref.watch(isLongPressedStateProvider); final itemType = widget.itemType; final gridSize = ref.watch( @@ -430,7 +431,7 @@ class _LibraryGridViewWidgetState extends State { ) { if (!isLongPressed) { ref.read(mangasListStateProvider.notifier).update(entry); - ref.read(isLongPressedMangaStateProvider.notifier).update(!isLongPressed); + ref.read(isLongPressedStateProvider.notifier).update(!isLongPressed); } else { ref.read(mangasListStateProvider.notifier).update(entry); } diff --git a/lib/modules/library/widgets/library_listview_widget.dart b/lib/modules/library/widgets/library_listview_widget.dart index 7306e15d..4dc2e22e 100644 --- a/lib/modules/library/widgets/library_listview_widget.dart +++ b/lib/modules/library/widgets/library_listview_widget.dart @@ -9,6 +9,7 @@ import 'package:mangayomi/models/history.dart'; import 'package:mangayomi/modules/library/providers/isar_providers.dart'; import 'package:mangayomi/modules/library/providers/library_state_provider.dart'; import 'package:mangayomi/models/manga.dart'; +import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart'; import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart'; import 'package:mangayomi/utils/extensions/build_context_extensions.dart'; import 'package:mangayomi/utils/constant.dart'; @@ -44,7 +45,7 @@ class LibraryListViewWidget extends StatelessWidget { bool isLocalArchive = entry.isLocalArchive ?? false; return Consumer( builder: (context, ref, child) { - final isLongPressed = ref.watch(isLongPressedMangaStateProvider); + final isLongPressed = ref.watch(isLongPressedStateProvider); return Material( borderRadius: BorderRadius.circular(5), color: Colors.transparent, @@ -80,7 +81,7 @@ class LibraryListViewWidget extends StatelessWidget { ref.read(mangasListStateProvider.notifier).update(entry); ref - .read(isLongPressedMangaStateProvider.notifier) + .read(isLongPressedStateProvider.notifier) .update(!isLongPressed); } else { ref.read(mangasListStateProvider.notifier).update(entry); @@ -91,7 +92,7 @@ class LibraryListViewWidget extends StatelessWidget { ref.read(mangasListStateProvider.notifier).update(entry); ref - .read(isLongPressedMangaStateProvider.notifier) + .read(isLongPressedStateProvider.notifier) .update(!isLongPressed); } else { ref.read(mangasListStateProvider.notifier).update(entry); diff --git a/lib/modules/main_view/main_screen.dart b/lib/modules/main_view/main_screen.dart index cf0c7f65..38c58e30 100644 --- a/lib/modules/main_view/main_screen.dart +++ b/lib/modules/main_view/main_screen.dart @@ -24,7 +24,7 @@ import 'package:mangayomi/router/router.dart'; import 'package:mangayomi/services/fetch_sources_list.dart'; import 'package:mangayomi/services/sync_server.dart'; import 'package:mangayomi/utils/extensions/build_context_extensions.dart'; -import 'package:mangayomi/modules/library/providers/library_state_provider.dart'; +import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart'; import 'package:mangayomi/modules/more/providers/incognito_mode_state_provider.dart'; final libLocationRegex = RegExp(r"^/(Manga|Anime|Novel)Library$"); @@ -241,7 +241,7 @@ class _MainScreenState extends ConsumerState { final incognitoMode = ref.watch(incognitoModeStateProvider); final downloadedOnly = ref.watch(downloadedOnlyStateProvider); - final isLongPressed = ref.watch(isLongPressedMangaStateProvider); + final isLongPressed = ref.watch(isLongPressedStateProvider); return Column( children: [ diff --git a/lib/modules/manga/detail/manga_detail_view.dart b/lib/modules/manga/detail/manga_detail_view.dart index d590013c..ce5aeaae 100644 --- a/lib/modules/manga/detail/manga_detail_view.dart +++ b/lib/modules/manga/detail/manga_detail_view.dart @@ -25,6 +25,7 @@ import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provi import 'package:mangayomi/modules/more/providers/algorithm_weights_state_provider.dart'; import 'package:mangayomi/modules/more/settings/appearance/providers/pure_black_dark_mode_state_provider.dart'; import 'package:mangayomi/modules/more/settings/track/widgets/track_listile.dart'; +import 'package:mangayomi/modules/widgets/bottom_select_bar.dart'; import 'package:mangayomi/modules/widgets/category_selection_dialog.dart'; import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart'; import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart'; @@ -827,8 +828,8 @@ class _MangaDetailViewState extends ConsumerState ], ), ), - bottomNavigationBar: Consumer( - builder: (context, ref, child) { + bottomNavigationBar: Builder( + builder: (context) { final chap = ref.watch(chaptersListStateProvider); bool getLength1 = chap.length == 1; bool checkFirstBookmarked = @@ -837,340 +838,241 @@ class _MangaDetailViewState extends ConsumerState chap.isNotEmpty && chap.first.isRead! && getLength1; final l10n = l10nLocalizations(context)!; final color = Theme.of(context).textTheme.bodyLarge!.color!; - return AnimatedContainer( - curve: Curves.easeIn, - decoration: BoxDecoration( - color: context.primaryColor.withValues(alpha: 0.2), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(20), - topRight: Radius.circular(20), + return BottomSelectBar( + isVisible: isLongPressed, + actions: [ + BottomSelectButton( + icon: Icon( + checkFirstBookmarked + ? Icons.bookmark_remove_outlined + : Icons.bookmark_add_outlined, + color: color, + ), + onPressed: () { + final chapters = ref.watch(chaptersListStateProvider); + final List updatedChapters = []; + final now = DateTime.now().millisecondsSinceEpoch; + for (var chapter in chapters) { + chapter.isBookmarked = !chapter.isBookmarked!; + chapter.updatedAt = now; + chapter.manga.value = widget.manga; + updatedChapters.add(chapter); + } + isar.writeTxnSync(() { + isar.chapters.putAllSync(updatedChapters); + }); + ref + .read(isLongPressedStateProvider.notifier) + .update(false); + ref.read(chaptersListStateProvider.notifier).clear(); + }, ), - ), - duration: const Duration(milliseconds: 100), - height: isLongPressed ? 70 : 0, - width: context.width(1), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 0, - backgroundColor: Colors.transparent, - shadowColor: Colors.transparent, - ), - onPressed: () { - final chapters = ref.watch( - chaptersListStateProvider, - ); - final List updatedChapters = []; - final now = DateTime.now().millisecondsSinceEpoch; - for (var chapter in chapters) { - chapter.isBookmarked = !chapter.isBookmarked!; - chapter.updatedAt = now; - chapter.manga.value = widget.manga; - updatedChapters.add(chapter); - } - isar.writeTxnSync(() { - isar.chapters.putAllSync(updatedChapters); - }); - ref - .read(isLongPressedStateProvider.notifier) - .update(false); - ref - .read(chaptersListStateProvider.notifier) - .clear(); - }, - child: Icon( - checkFirstBookmarked - ? Icons.bookmark_remove_outlined - : Icons.bookmark_add_outlined, - color: color, - ), - ), - ), + BottomSelectButton( + icon: Icon( + checkReadBookmarked + ? Icons.remove_done_sharp + : Icons.done_all_sharp, + color: color, ), - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 0, - backgroundColor: Colors.transparent, - shadowColor: Colors.transparent, - ), - onPressed: () { - final chapters = ref.watch( - chaptersListStateProvider, - ); - final List updatedChapters = []; - final now = DateTime.now().millisecondsSinceEpoch; - for (var chapter in chapters) { - chapter.isRead = !chapter.isRead!; - if (!chapter.isRead!) { - chapter.lastPageRead = "1"; - } - chapter.updatedAt = now; - chapter.manga.value = widget.manga; - updatedChapters.add(chapter); - if (chapter.isRead!) { - chapter.updateTrackChapterRead(ref); - } - } - isar.writeTxnSync(() { - isar.chapters.putAllSync(updatedChapters); - isar.mangas.putSync(widget.manga!); - }); - ref - .read(isLongPressedStateProvider.notifier) - .update(false); - ref - .read(chaptersListStateProvider.notifier) - .clear(); - }, - child: Icon( - checkReadBookmarked - ? Icons.remove_done_sharp - : Icons.done_all_sharp, - color: color, - ), - ), - ), - ), - if (getLength1) - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 0, - backgroundColor: Colors.transparent, - shadowColor: Colors.transparent, - ), - onPressed: () { - int index = chapters.indexOf(chap.first); - final List updatedChapters = []; - final now = DateTime.now().millisecondsSinceEpoch; - chapters[index + 1].updateTrackChapterRead(ref); - for ( - var i = index + 1; - i < chapters.length; - i++ - ) { - final chapter = chapters[i]; - if (!chapter.isRead!) { - chapter.isRead = true; - chapter.lastPageRead = "1"; - chapter.updatedAt = now; - chapter.manga.value = widget.manga; - updatedChapters.add(chapter); - } - } - isar.writeTxnSync(() { - isar.chapters.putAllSync(updatedChapters); - isar.mangas.putSync(widget.manga!); - }); - ref - .read(isLongPressedStateProvider.notifier) - .update(false); - ref - .read(chaptersListStateProvider.notifier) - .clear(); - }, - child: Stack( - children: [ - Icon(Icons.done_outlined, color: color), - Positioned( - bottom: 0, - right: 0, - child: Icon( - Icons.arrow_downward_outlined, - size: 11, - color: color, - ), - ), - ], - ), - ), - ), - ), - if (!isLocalArchive) - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 0, - backgroundColor: Colors.transparent, - shadowColor: Colors.transparent, - ), - onPressed: () { - for (var chapter in ref.watch( - chaptersListStateProvider, - )) { - final entries = isar.downloads - .filter() - .idEqualTo(chapter.id) - .findAllSync(); - if (entries.isEmpty || - !entries.first.isDownload!) { - ref.read( - addDownloadToQueueProvider( - chapter: chapter, - ), - ); - } - } - ref.watch(processDownloadsProvider()); - - ref - .read(isLongPressedStateProvider.notifier) - .update(false); - ref - .read(chaptersListStateProvider.notifier) - .clear(); - }, - child: Icon(Icons.download_outlined, color: color), - ), - ), - ), - if (isLocalArchive) - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 0, - backgroundColor: Colors.transparent, - shadowColor: Colors.transparent, - ), - onPressed: () { - final selectedChapters = ref.watch( - chaptersListStateProvider, - ); - final totalChapters = - widget.manga!.chapters.length; - final isLastChapters = - selectedChapters.length == totalChapters; - final isAnime = widget.itemType == ItemType.anime; - final entryType = isAnime - ? l10n.episode - : l10n.chapter; - final pluralEntryType = isAnime - ? l10n.episodes - : l10n.chapters; - final mediaType = isAnime - ? l10n.anime - : l10n.manga; - final warningMessage = l10n - .last_entry_delete_warning( - totalChapters, - entryType, - pluralEntryType, - mediaType, - ); - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text(l10n.delete_chapters), - content: isLastChapters - ? Row( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - const Icon( - Icons.warning_amber_rounded, - color: Colors.orange, - ), - const SizedBox(width: 12), - Expanded( - child: Text( - warningMessage, - style: TextStyle( - color: Colors.red, - ), - ), - ), - ], - ) - : null, - actions: [ - Row( - mainAxisAlignment: - MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: Text(l10n.cancel), - ), - const SizedBox(width: 15), - TextButton( - onPressed: () async { - final navigator = Navigator.of( - context, - ); - await isar.writeTxn(() async { - final idsToDelete = - selectedChapters - .map((c) => c.id!) - .toList(); - await isar.chapters.deleteAll( - idsToDelete, - ); - }); - if (!mounted) return; - ref - .read( - isLongPressedStateProvider - .notifier, - ) - .update(false); - ref - .read( - chaptersListStateProvider - .notifier, - ) - .clear(); - navigator.pop(); - if (isLastChapters) { - navigator.pop(); - Future.delayed( - const Duration( - milliseconds: 350, - ), - () { - isar.writeTxn( - () => isar.mangas.delete( - widget.manga!.id!, - ), - ); - }, - ); - } - }, - child: Text(l10n.delete), - ), - ], - ), - ], - ); - }, - ); - }, + onPressed: () { + final chapters = ref.watch(chaptersListStateProvider); + final List updatedChapters = []; + final now = DateTime.now().millisecondsSinceEpoch; + for (var chapter in chapters) { + chapter.isRead = !chapter.isRead!; + if (!chapter.isRead!) { + chapter.lastPageRead = "1"; + } + chapter.updatedAt = now; + chapter.manga.value = widget.manga; + updatedChapters.add(chapter); + if (chapter.isRead!) { + chapter.updateTrackChapterRead(ref); + } + } + isar.writeTxnSync(() { + isar.chapters.putAllSync(updatedChapters); + isar.mangas.putSync(widget.manga!); + }); + ref + .read(isLongPressedStateProvider.notifier) + .update(false); + ref.read(chaptersListStateProvider.notifier).clear(); + }, + ), + if (getLength1) + BottomSelectButton( + icon: Stack( + children: [ + Icon(Icons.done_outlined, color: color), + Positioned( + bottom: 0, + right: 0, child: Icon( - Icons.delete_outline_outlined, + Icons.arrow_downward_outlined, + size: 11, color: color, ), ), - ), + ], ), - ], - ), + onPressed: () { + int index = chapters.indexOf(chap.first); + final List updatedChapters = []; + final now = DateTime.now().millisecondsSinceEpoch; + chapters[index + 1].updateTrackChapterRead(ref); + for (var i = index + 1; i < chapters.length; i++) { + final chapter = chapters[i]; + if (!chapter.isRead!) { + chapter.isRead = true; + chapter.lastPageRead = "1"; + chapter.updatedAt = now; + chapter.manga.value = widget.manga; + updatedChapters.add(chapter); + } + } + isar.writeTxnSync(() { + isar.chapters.putAllSync(updatedChapters); + isar.mangas.putSync(widget.manga!); + }); + ref + .read(isLongPressedStateProvider.notifier) + .update(false); + ref.read(chaptersListStateProvider.notifier).clear(); + }, + ), + if (!isLocalArchive) + BottomSelectButton( + icon: Icon(Icons.download_outlined, color: color), + onPressed: () { + for (var chapter in ref.watch( + chaptersListStateProvider, + )) { + final entries = isar.downloads + .filter() + .idEqualTo(chapter.id) + .findAllSync(); + if (entries.isEmpty || !entries.first.isDownload!) { + ref.read( + addDownloadToQueueProvider(chapter: chapter), + ); + } + } + ref.watch(processDownloadsProvider()); + + ref + .read(isLongPressedStateProvider.notifier) + .update(false); + ref.read(chaptersListStateProvider.notifier).clear(); + }, + ), + if (isLocalArchive) + BottomSelectButton( + icon: Icon(Icons.delete_outline_outlined, color: color), + onPressed: () { + final selectedChapters = ref.watch( + chaptersListStateProvider, + ); + final totalChapters = widget.manga!.chapters.length; + final isLastChapters = + selectedChapters.length == totalChapters; + final isAnime = widget.itemType == ItemType.anime; + final entryType = isAnime ? l10n.episode : l10n.chapter; + final pluralEntryType = isAnime + ? l10n.episodes + : l10n.chapters; + final mediaType = isAnime ? l10n.anime : l10n.manga; + final warningMessage = l10n.last_entry_delete_warning( + totalChapters, + entryType, + pluralEntryType, + mediaType, + ); + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(l10n.delete_chapters), + content: isLastChapters + ? Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Icon( + Icons.warning_amber_rounded, + color: Colors.orange, + ), + const SizedBox(width: 12), + Expanded( + child: Text( + warningMessage, + style: TextStyle(color: Colors.red), + ), + ), + ], + ) + : null, + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text(l10n.cancel), + ), + const SizedBox(width: 15), + TextButton( + onPressed: () async { + final navigator = Navigator.of(context); + await isar.writeTxn(() async { + final idsToDelete = selectedChapters + .map((c) => c.id!) + .toList(); + await isar.chapters.deleteAll( + idsToDelete, + ); + }); + if (!mounted) return; + ref + .read( + isLongPressedStateProvider + .notifier, + ) + .update(false); + ref + .read( + chaptersListStateProvider + .notifier, + ) + .clear(); + navigator.pop(); + if (isLastChapters) { + navigator.pop(); + Future.delayed( + const Duration(milliseconds: 350), + () { + isar.writeTxn( + () => isar.mangas.delete( + widget.manga!.id!, + ), + ); + }, + ); + } + }, + child: Text(l10n.delete), + ), + ], + ), + ], + ); + }, + ); + }, + ), + ], ); }, ), diff --git a/lib/modules/widgets/bottom_select_bar.dart b/lib/modules/widgets/bottom_select_bar.dart new file mode 100644 index 00000000..0f2cc56b --- /dev/null +++ b/lib/modules/widgets/bottom_select_bar.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:mangayomi/utils/extensions/build_context_extensions.dart'; + +/// Bar, that appears at the bottom of the screen when long-pressing (selecting) +/// a Manga/Anime/Novel or Chapter/Episode +class BottomSelectBar extends StatelessWidget { + final bool isVisible; + final List actions; + + const BottomSelectBar({ + super.key, + required this.isVisible, + required this.actions, + }); + + @override + Widget build(BuildContext context) { + return AnimatedContainer( + curve: Curves.easeIn, + decoration: BoxDecoration( + color: context.primaryColor.withValues(alpha: 0.2), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + ), + duration: const Duration(milliseconds: 100), + height: isVisible ? 70 : 0, + width: context.width(1), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: actions, + ), + ); + } +} + +/// Button for the BottomSelectBar +class BottomSelectButton extends StatelessWidget { + final Widget icon; + final VoidCallback onPressed; + + const BottomSelectButton({ + super.key, + required this.icon, + required this.onPressed, + }); + + @override + Widget build(BuildContext context) { + return Expanded( + child: SizedBox( + height: 70, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0, + backgroundColor: Colors.transparent, + shadowColor: Colors.transparent, + ), + onPressed: onPressed, + child: icon, + ), + ), + ); + } +} diff --git a/lib/modules/widgets/category_selection_dialog.dart b/lib/modules/widgets/category_selection_dialog.dart index 1d79cf21..19719f92 100644 --- a/lib/modules/widgets/category_selection_dialog.dart +++ b/lib/modules/widgets/category_selection_dialog.dart @@ -7,6 +7,7 @@ import 'package:mangayomi/models/category.dart'; import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/modules/library/providers/library_state_provider.dart'; import 'package:mangayomi/modules/library/widgets/list_tile_manga_category.dart'; +import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart'; import 'package:mangayomi/modules/manga/detail/widgets/chapter_filter_list_tile_widget.dart'; import 'package:mangayomi/providers/l10n_providers.dart'; import 'package:mangayomi/utils/extensions/build_context_extensions.dart'; @@ -151,7 +152,7 @@ void showCategorySelectionDialog({ if (isBulk) { ref.read(mangasListStateProvider.notifier).clear(); ref - .read(isLongPressedMangaStateProvider.notifier) + .read(isLongPressedStateProvider.notifier) .update(false); } }); diff --git a/lib/services/aniskip.g.dart b/lib/services/aniskip.g.dart index c05db920..b2e1defb 100644 --- a/lib/services/aniskip.g.dart +++ b/lib/services/aniskip.g.dart @@ -6,7 +6,7 @@ part of 'aniskip.dart'; // RiverpodGenerator // ************************************************************************** -String _$aniSkipHash() => r'2e5d19b025a2207ff64da7bf7908450ea9e5ff8c'; +String _$aniSkipHash() => r'887869b54e2e151633efd46da83bde845e14f421'; /// See also [AniSkip]. @ProviderFor(AniSkip) diff --git a/lib/services/sync_server.g.dart b/lib/services/sync_server.g.dart index 30ccc0ed..af5cc986 100644 --- a/lib/services/sync_server.g.dart +++ b/lib/services/sync_server.g.dart @@ -6,7 +6,7 @@ part of 'sync_server.dart'; // RiverpodGenerator // ************************************************************************** -String _$syncServerHash() => r'97a778696e0cc8b8e4c706de50d60464bb7b2f03'; +String _$syncServerHash() => r'141ba3be28182e05480e06fbf3f1de68f868cb8e'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/services/trackers/anilist.g.dart b/lib/services/trackers/anilist.g.dart index 96c9f674..9eee8f76 100644 --- a/lib/services/trackers/anilist.g.dart +++ b/lib/services/trackers/anilist.g.dart @@ -6,7 +6,7 @@ part of 'anilist.dart'; // RiverpodGenerator // ************************************************************************** -String _$anilistHash() => r'fafb964252b3a5741e981cb8c2f0f2090b3b86ae'; +String _$anilistHash() => r'c786a526fdacc875e4a7e00886b2bda546eafeae'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/services/trackers/myanimelist.g.dart b/lib/services/trackers/myanimelist.g.dart index 7283560e..a3d8b6ee 100644 --- a/lib/services/trackers/myanimelist.g.dart +++ b/lib/services/trackers/myanimelist.g.dart @@ -6,7 +6,7 @@ part of 'myanimelist.dart'; // RiverpodGenerator // ************************************************************************** -String _$myAnimeListHash() => r'a612e9ce814268ac79dc86d810ca6bd3671812e6'; +String _$myAnimeListHash() => r'4391ad9446d14b1fb1ffdfbc5323ef04db5140f7'; /// Copied from Dart SDK class _SystemHash {