From c7b910c807ec634f3c92b52ebd5da69057e5ea2f Mon Sep 17 00:00:00 2001 From: kodjomoustapha <107993382+kodjodevf@users.noreply.github.com> Date: Mon, 8 May 2023 22:14:49 +0100 Subject: [PATCH] add library category sort & selectable multiple for multiple action --- lib/models/manga.dart | 4 +- lib/models/manga.g.dart | 108 +- lib/views/browse/global_search_screen.dart | 43 +- lib/views/general/general_screen.dart | 167 +-- lib/views/library/library_screen.dart | 1096 ++++++++++++----- .../library/providers/isar_providers.g.dart | 2 +- .../providers/library_state_provider.dart | 150 ++- .../providers/library_state_provider.g.dart | 262 +++- .../widgets/library_gridview_widget.dart | 449 ++++--- .../widgets/library_listview_widget.dart | 382 +++--- .../widgets/list_tile_manga_category.dart | 46 + lib/views/manga/detail/manga_detail_view.dart | 592 +++++---- .../manga/detail/manga_details_view.dart | 52 +- .../manga/detail/manga_reader_detail.dart | 97 +- .../detail/providers/state_providers.dart | 132 +- .../detail/providers/state_providers.g.dart | 368 +----- .../widgets/chapter_list_tile_widget.dart | 7 +- .../providers/reader_controller_provider.dart | 13 +- .../reader_controller_provider.g.dart | 2 +- lib/views/widgets/cover_view_widget.dart | 2 +- .../widgets/manga_image_card_widget.dart | 20 +- 21 files changed, 2241 insertions(+), 1753 deletions(-) create mode 100644 lib/views/library/widgets/list_tile_manga_category.dart diff --git a/lib/models/manga.dart b/lib/models/manga.dart index b3d7955c..370ee5f9 100644 --- a/lib/models/manga.dart +++ b/lib/models/manga.dart @@ -31,7 +31,7 @@ class Manga { int? lastUpdate; - String? lastRead; + int? lastRead; List? categories; @@ -53,6 +53,6 @@ class Manga { this.dateAdded, this.lastUpdate, this.categories, - this.lastRead, + this.lastRead = 0, }); } diff --git a/lib/models/manga.g.dart b/lib/models/manga.g.dart index 559d54b5..e3848670 100644 --- a/lib/models/manga.g.dart +++ b/lib/models/manga.g.dart @@ -60,7 +60,7 @@ const MangaSchema = CollectionSchema( r'lastRead': PropertySchema( id: 8, name: r'lastRead', - type: IsarType.string, + type: IsarType.long, ), r'lastUpdate': PropertySchema( id: 9, @@ -158,12 +158,6 @@ int _mangaEstimateSize( bytesCount += 3 + value.length * 3; } } - { - final value = object.lastRead; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } { final value = object.link; if (value != null) { @@ -205,7 +199,7 @@ void _mangaSerialize( writer.writeStringList(offsets[5], object.genre); writer.writeString(offsets[6], object.imageUrl); writer.writeString(offsets[7], object.lang); - writer.writeString(offsets[8], object.lastRead); + writer.writeLong(offsets[8], object.lastRead); writer.writeLong(offsets[9], object.lastUpdate); writer.writeString(offsets[10], object.link); writer.writeString(offsets[11], object.name); @@ -229,7 +223,7 @@ Manga _mangaDeserialize( id: id, imageUrl: reader.readStringOrNull(offsets[6]), lang: reader.readStringOrNull(offsets[7]), - lastRead: reader.readStringOrNull(offsets[8]), + lastRead: reader.readLongOrNull(offsets[8]), lastUpdate: reader.readLongOrNull(offsets[9]), link: reader.readStringOrNull(offsets[10]), name: reader.readStringOrNull(offsets[11]), @@ -263,7 +257,7 @@ P _mangaDeserializeProp

( case 7: return (reader.readStringOrNull(offset)) as P; case 8: - return (reader.readStringOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 9: return (reader.readLongOrNull(offset)) as P; case 10: @@ -1497,54 +1491,46 @@ extension MangaQueryFilter on QueryBuilder { } QueryBuilder lastReadEqualTo( - String? value, { - bool caseSensitive = true, - }) { + int? value) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( property: r'lastRead', value: value, - caseSensitive: caseSensitive, )); }); } QueryBuilder lastReadGreaterThan( - String? value, { + int? value, { bool include = false, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( include: include, property: r'lastRead', value: value, - caseSensitive: caseSensitive, )); }); } QueryBuilder lastReadLessThan( - String? value, { + int? value, { bool include = false, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.lessThan( include: include, property: r'lastRead', value: value, - caseSensitive: caseSensitive, )); }); } QueryBuilder lastReadBetween( - String? lower, - String? upper, { + int? lower, + int? upper, { bool includeLower = true, bool includeUpper = true, - bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.between( @@ -1553,75 +1539,6 @@ extension MangaQueryFilter on QueryBuilder { includeLower: includeLower, upper: upper, includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder lastReadStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'lastRead', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder lastReadEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'lastRead', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder lastReadContains( - String value, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'lastRead', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder lastReadMatches( - String pattern, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'lastRead', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder lastReadIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lastRead', - value: '', - )); - }); - } - - QueryBuilder lastReadIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'lastRead', - value: '', )); }); } @@ -2691,10 +2608,9 @@ extension MangaQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByLastRead( - {bool caseSensitive = true}) { + QueryBuilder distinctByLastRead() { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'lastRead', caseSensitive: caseSensitive); + return query.addDistinctBy(r'lastRead'); }); } @@ -2788,7 +2704,7 @@ extension MangaQueryProperty on QueryBuilder { }); } - QueryBuilder lastReadProperty() { + QueryBuilder lastReadProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'lastRead'); }); diff --git a/lib/views/browse/global_search_screen.dart b/lib/views/browse/global_search_screen.dart index 017d434f..1fc1d755 100644 --- a/lib/views/browse/global_search_screen.dart +++ b/lib/views/browse/global_search_screen.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:isar/isar.dart'; +import 'package:mangayomi/main.dart'; +import 'package:mangayomi/models/chapter.dart'; import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/providers/hive_provider.dart'; import 'package:mangayomi/services/get_manga_detail.dart'; @@ -180,24 +183,48 @@ class _MangaGlobalImageCardState extends ConsumerState data: (data) { return GestureDetector( onTap: () async { - final modelManga = Manga( + final manga = Manga( imageUrl: data.imageUrl, name: data.name, genre: data.genre, author: data.author, status: data.status, description: data.description, - favorite: false, link: data.url, source: data.source, lang: widget.lang, - dateAdded: DateTime.now().microsecondsSinceEpoch, - lastUpdate: DateTime.now().microsecondsSinceEpoch, - categories: null, - lastRead: ''); - if (mounted) { - context.push('/manga-reader/detail', extra: modelManga); + lastUpdate: DateTime.now().millisecondsSinceEpoch); + + final empty = isar.mangas + .filter() + .langEqualTo(widget.lang) + .nameEqualTo(data.name) + .sourceEqualTo(data.source) + .isEmptySync(); + if (empty) { + isar.writeTxnSync(() { + isar.mangas.putSync(manga); + for (var i = 0; i < data.chapters.length; i++) { + final chapters = Chapter( + name: data.chapters[i].name, + url: data.chapters[i].url, + dateUpload: data.chapters[i].dateUpload, + scanlator: data.chapters[i].scanlator, + mangaId: manga.id) + ..manga.value = manga; + isar.chapters.putSync(chapters); + chapters.manga.saveSync(); + } + }); } + final mangaId = isar.mangas + .filter() + .langEqualTo(widget.lang) + .nameEqualTo(data.name) + .sourceEqualTo(data.source) + .findFirstSync()! + .id!; + context.push('/manga-reader/detail', extra: mangaId); }, child: SizedBox( width: 90, diff --git a/lib/views/general/general_screen.dart b/lib/views/general/general_screen.dart index 3fb52609..fb6730de 100644 --- a/lib/views/general/general_screen.dart +++ b/lib/views/general/general_screen.dart @@ -6,6 +6,7 @@ import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:mangayomi/utils/colors.dart'; import 'package:mangayomi/utils/media_query.dart'; +import 'package:mangayomi/views/library/providers/library_state_provider.dart'; import 'package:mangayomi/views/more/settings/providers/incognito_mode_state_provider.dart'; class GeneralScreen extends StatefulWidget { @@ -68,87 +69,93 @@ class _GeneralScreenState extends State { Flexible( child: Scaffold( body: widget.child, - bottomNavigationBar: SizedBox( - width: mediaWidth(context, 1), - height: route.location != '/library' && - route.location != '/updates' && - route.location != '/history' && - route.location != '/browse' && - route.location != '/more' - ? 0 - : 80, - child: NavigationBarTheme( - data: NavigationBarThemeData( - indicatorShape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30)), - height: 20, + bottomNavigationBar: Consumer(builder: (context, ref, child) { + final isLongPressed = ref.watch(isLongPressedMangaStateProvider); + return AnimatedContainer( + duration: const Duration(milliseconds: 300), + width: mediaWidth(context, 1), + height: isLongPressed + ? 0 + : route.location != '/library' && + route.location != '/updates' && + route.location != '/history' && + route.location != '/browse' && + route.location != '/more' + ? 0 + : 80, + child: NavigationBarTheme( + data: NavigationBarThemeData( + indicatorShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30)), + height: 20, + ), + child: NavigationBar( + animationDuration: const Duration(milliseconds: 500), + selectedIndex: currentIndex, + destinations: const [ + NavigationDestination( + selectedIcon: Icon( + Icons.collections_bookmark, + ), + icon: Icon( + Icons.collections_bookmark_outlined, + ), + label: 'Library'), + NavigationDestination( + selectedIcon: Icon( + Icons.new_releases, + ), + icon: Icon( + Icons.new_releases_outlined, + ), + label: 'Updates'), + NavigationDestination( + selectedIcon: Icon( + Icons.history, + ), + icon: Icon( + Icons.history_outlined, + ), + label: "History"), + NavigationDestination( + selectedIcon: Icon( + Icons.explore, + ), + icon: Icon( + Icons.explore_outlined, + ), + label: "Browse"), + NavigationDestination( + selectedIcon: Icon( + Icons.more_horiz, + ), + icon: Icon( + Icons.more_horiz_outlined, + ), + label: "More"), + ], + onDestinationSelected: (int newIndex) { + if (mounted) { + setState(() { + currentIndex = newIndex; + }); + } + if (newIndex == 0) { + route.go('/library'); + } else if (newIndex == 1) { + route.go('/updates'); + } else if (newIndex == 2) { + route.go('/history'); + } else if (newIndex == 3) { + route.go('/browse'); + } else if (newIndex == 4) { + route.go('/more'); + } + }, + ), ), - child: NavigationBar( - animationDuration: const Duration(milliseconds: 500), - selectedIndex: currentIndex, - destinations: const [ - NavigationDestination( - selectedIcon: Icon( - Icons.collections_bookmark, - ), - icon: Icon( - Icons.collections_bookmark_outlined, - ), - label: 'Library'), - NavigationDestination( - selectedIcon: Icon( - Icons.new_releases, - ), - icon: Icon( - Icons.new_releases_outlined, - ), - label: 'Updates'), - NavigationDestination( - selectedIcon: Icon( - Icons.history, - ), - icon: Icon( - Icons.history_outlined, - ), - label: "History"), - NavigationDestination( - selectedIcon: Icon( - Icons.explore, - ), - icon: Icon( - Icons.explore_outlined, - ), - label: "Browse"), - NavigationDestination( - selectedIcon: Icon( - Icons.more_horiz, - ), - icon: Icon( - Icons.more_horiz_outlined, - ), - label: "More"), - ], - onDestinationSelected: (int newIndex) { - if (mounted) { - setState(() { - currentIndex = newIndex; - }); - } - if (newIndex == 0) { - route.go('/library'); - } else if (newIndex == 1) { - route.go('/updates'); - } else if (newIndex == 2) { - route.go('/history'); - } else if (newIndex == 3) { - route.go('/browse'); - } else if (newIndex == 4) { - route.go('/more'); - } - }, - ), - ), - ), + ); + }), ), ), ], diff --git a/lib/views/library/library_screen.dart b/lib/views/library/library_screen.dart index 1a1b8f15..44ebd2a5 100644 --- a/lib/views/library/library_screen.dart +++ b/lib/views/library/library_screen.dart @@ -3,15 +3,23 @@ import 'dart:developer'; import 'package:draggable_menu/draggable_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:isar/isar.dart'; +import 'package:mangayomi/main.dart'; +import 'package:mangayomi/models/category.dart'; +import 'package:mangayomi/models/history.dart'; import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/providers/hive_provider.dart'; +import 'package:mangayomi/utils/colors.dart'; import 'package:mangayomi/utils/media_query.dart'; import 'package:mangayomi/views/library/providers/isar_providers.dart'; import 'package:mangayomi/views/library/providers/library_state_provider.dart'; import 'package:mangayomi/views/library/search_text_form_field.dart'; import 'package:mangayomi/views/library/widgets/library_gridview_widget.dart'; import 'package:mangayomi/views/library/widgets/library_listview_widget.dart'; +import 'package:mangayomi/views/library/widgets/list_tile_manga_category.dart'; import 'package:mangayomi/views/manga/detail/widgets/chapter_filter_list_tile_widget.dart'; +import 'package:mangayomi/views/manga/detail/widgets/chapter_sort_list_tile_widget.dart'; import 'package:mangayomi/views/more/settings/categoties/providers/isar_providers.dart'; import 'package:mangayomi/views/widgets/error_text.dart'; import 'package:mangayomi/views/widgets/progress_center.dart'; @@ -25,11 +33,11 @@ class LibraryScreen extends ConsumerStatefulWidget { class _LibraryScreenState extends ConsumerState with TickerProviderStateMixin { - bool isSearch = false; + bool _isSearch = false; final List _entries = []; final _textEditingController = TextEditingController(); late TabController tabBarController; - int tabIndex = 0; + int _tabIndex = 0; @override Widget build(BuildContext context) { final categories = ref.watch(getMangaCategorieStreamProvider); @@ -37,106 +45,160 @@ class _LibraryScreenState extends ConsumerState ref.watch(getAllMangaWithoutCategoriesStreamProvider); final showCategoryTabs = ref.watch(libraryShowCategoryTabsStateProvider); final mangaAll = ref.watch(getAllMangaStreamProvider(categoryId: null)); - return mangaAll.when( - data: (man) { - return withoutCategories.when( - data: (withoutCategory) { - return categories.when( - data: (data) { - if (data.isNotEmpty && showCategoryTabs) { - final entr = data; - tabBarController = TabController( - length: withoutCategory.isNotEmpty - ? entr.length + 1 - : entr.length, - vsync: this); - tabBarController.animateTo(tabIndex); - tabBarController.addListener(() { - tabIndex = tabBarController.index; - }); - return Consumer(builder: (context, ref, child) { - final reverse = ref.watch(libraryReverseListStateProvider); + return Scaffold( + body: mangaAll.when( + data: (man) { + return withoutCategories.when( + data: (withoutCategory) { + return categories.when( + data: (data) { + if (data.isNotEmpty && showCategoryTabs) { + final entr = data; + tabBarController = TabController( + length: withoutCategory.isNotEmpty + ? entr.length + 1 + : entr.length, + vsync: this); + tabBarController.animateTo(_tabIndex); + tabBarController.addListener(() { + _tabIndex = tabBarController.index; + }); - final continueReaderBtn = ref - .watch(libraryShowContinueReadingButtonStateProvider); - final showNumbersOfItems = - ref.watch(libraryShowNumbersOfItemsStateProvider); - final downloadedChapter = - ref.watch(libraryDownloadedChaptersStateProvider); - final language = ref.watch(libraryLanguageStateProvider); - final displayType = ref - .read(libraryDisplayTypeStateProvider.notifier) - .getLibraryDisplayTypeValue( - ref.watch(libraryDisplayTypeStateProvider)); - final isNotFiltering = ref.watch( - mangasFilterResultStateProvider(mangaList: _entries)); - final downloadFilterType = ref.watch( - mangaFilterDownloadedStateProvider( - mangaList: _entries)); - final unreadFilterType = ref.watch( - mangaFilterUnreadStateProvider(mangaList: _entries)); - final startedFilterType = ref.watch( - mangaFilterStartedStateProvider(mangaList: _entries)); - final bookmarkedFilterType = ref.watch( - mangaFilterBookmarkedStateProvider( - mangaList: _entries)); - final numberOfItemsList = _filterMangas( - data: man, - downloadFilterType: downloadFilterType, - unreadFilterType: unreadFilterType, - startedFilterType: startedFilterType, - bookmarkedFilterType: bookmarkedFilterType); - final withoutCateogoryNumberOfItemsList = _filterMangas( - data: withoutCategory, - downloadFilterType: downloadFilterType, - unreadFilterType: unreadFilterType, - startedFilterType: startedFilterType, - bookmarkedFilterType: bookmarkedFilterType); - return DefaultTabController( - length: entr.length, - child: Scaffold( - appBar: _appBar(isNotFiltering, showNumbersOfItems, - numberOfItemsList.length), - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TabBar( - isScrollable: true, - controller: tabBarController, - tabs: [ - if (withoutCategory.isNotEmpty) - for (var i = 0; i < entr.length + 1; i++) - Row( - children: [ - Tab( - text: i == 0 - ? "Default" - : entr[i - 1].name), - const SizedBox( - width: 4, - ), - if (showNumbersOfItems) - i == 0 - ? CircleAvatar( - backgroundColor: - Theme.of(context) - .focusColor, - radius: 8, - child: Text( - withoutCateogoryNumberOfItemsList - .length - .toString(), - style: TextStyle( - fontSize: 10, - color: - Theme.of(context) + return Consumer(builder: (context, ref, child) { + bool reverse = + ref.watch(sortLibraryMangaStateProvider)["reverse"]; + + final continueReaderBtn = ref.watch( + libraryShowContinueReadingButtonStateProvider); + final showNumbersOfItems = + ref.watch(libraryShowNumbersOfItemsStateProvider); + final downloadedChapter = + ref.watch(libraryDownloadedChaptersStateProvider); + final language = + ref.watch(libraryLanguageStateProvider); + final displayType = ref + .read(libraryDisplayTypeStateProvider.notifier) + .getLibraryDisplayTypeValue( + ref.watch(libraryDisplayTypeStateProvider)); + final isNotFiltering = ref.watch( + mangasFilterResultStateProvider( + mangaList: _entries)); + final downloadFilterType = ref.watch( + mangaFilterDownloadedStateProvider( + mangaList: _entries)); + final unreadFilterType = ref.watch( + mangaFilterUnreadStateProvider( + mangaList: _entries)); + final startedFilterType = ref.watch( + mangaFilterStartedStateProvider( + mangaList: _entries)); + final bookmarkedFilterType = ref.watch( + mangaFilterBookmarkedStateProvider( + mangaList: _entries)); + final sortType = + ref.watch(sortLibraryMangaStateProvider)['index'] + as int; + final numberOfItemsList = _filterAndSortMangas( + data: man, + downloadFilterType: downloadFilterType, + unreadFilterType: unreadFilterType, + startedFilterType: startedFilterType, + bookmarkedFilterType: bookmarkedFilterType, + sortType: sortType); + final withoutCategoryNumberOfItemsList = + _filterAndSortMangas( + data: withoutCategory, + downloadFilterType: downloadFilterType, + unreadFilterType: unreadFilterType, + startedFilterType: startedFilterType, + bookmarkedFilterType: bookmarkedFilterType, + sortType: sortType); + + return DefaultTabController( + length: entr.length, + child: Scaffold( + appBar: _appBar( + isNotFiltering, + showNumbersOfItems, + numberOfItemsList.length, + ref, + [], + true, + withoutCategory.isNotEmpty && _tabIndex == 0 + ? null + : entr[withoutCategory.isNotEmpty + ? _tabIndex - 1 + : _tabIndex] + .id!), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TabBar( + isScrollable: true, + controller: tabBarController, + tabs: [ + if (withoutCategory.isNotEmpty) + for (var i = 0; + i < entr.length + 1; + i++) + Row( + children: [ + Tab( + text: i == 0 + ? "Default" + : entr[i - 1].name), + const SizedBox( + width: 4, + ), + if (showNumbersOfItems) + i == 0 + ? CircleAvatar( + backgroundColor: + Theme.of(context) + .focusColor, + radius: 8, + child: Text( + withoutCategoryNumberOfItemsList + .length + .toString(), + style: TextStyle( + fontSize: 10, + color: Theme.of( + context) .textTheme .bodySmall! .color), - ), - ) - : _categoriNumberOfItems( + ), + ) + : _categoriesNumberOfItems( + downloadFilterType: + downloadFilterType, + unreadFilterType: + unreadFilterType, + startedFilterType: + startedFilterType, + bookmarkedFilterType: + bookmarkedFilterType, + reverse: reverse, + downloadedChapter: + downloadedChapter, + continueReaderBtn: + continueReaderBtn, + categoryId: + entr[i - 1].id!), + ], + ), + if (withoutCategory.isEmpty) + for (var i = 0; i < entr.length; i++) + Row( + children: [ + Tab(text: entr[i].name), + const SizedBox( + width: 4, + ), + if (showNumbersOfItems) + _categoriesNumberOfItems( downloadFilterType: downloadFilterType, unreadFilterType: @@ -150,43 +212,59 @@ class _LibraryScreenState extends ConsumerState downloadedChapter, continueReaderBtn: continueReaderBtn, - categoryId: - entr[i - 1].id!), - ], - ), - if (withoutCategory.isEmpty) - for (var i = 0; i < entr.length; i++) - Row( + categoryId: entr[i].id!), + ], + ) + ]), + Flexible( + child: TabBarView( + controller: tabBarController, children: [ - Tab(text: entr[i].name), - if (showNumbersOfItems) - _categoriNumberOfItems( - downloadFilterType: - downloadFilterType, - unreadFilterType: - unreadFilterType, - startedFilterType: - startedFilterType, - bookmarkedFilterType: - bookmarkedFilterType, - reverse: reverse, - downloadedChapter: - downloadedChapter, - continueReaderBtn: - continueReaderBtn, - categoryId: entr[i].id!), - ], - ) - ]), - Flexible( - child: TabBarView( - controller: tabBarController, - children: [ - if (withoutCategory.isNotEmpty) - for (var i = 0; i < entr.length + 1; i++) - i == 0 - ? _bodyWithoutCategories( - withouCategories: true, + if (withoutCategory.isNotEmpty) + for (var i = 0; + i < entr.length + 1; + i++) + i == 0 + ? _bodyWithoutCategories( + withouCategories: true, + downloadFilterType: + downloadFilterType, + unreadFilterType: + unreadFilterType, + startedFilterType: + startedFilterType, + bookmarkedFilterType: + bookmarkedFilterType, + reverse: reverse, + downloadedChapter: + downloadedChapter, + continueReaderBtn: + continueReaderBtn, + language: language, + displayType: displayType, + ref: ref) + : _bodyWithCatories( + categoryId: entr[i - 1].id!, + downloadFilterType: + downloadFilterType, + unreadFilterType: + unreadFilterType, + startedFilterType: + startedFilterType, + bookmarkedFilterType: + bookmarkedFilterType, + reverse: reverse, + downloadedChapter: + downloadedChapter, + continueReaderBtn: + continueReaderBtn, + language: language, + displayType: displayType, + ref: ref), + if (withoutCategory.isEmpty) + for (var i = 0; i < entr.length; i++) + _bodyWithCatories( + categoryId: entr[i].id!, downloadFilterType: downloadFilterType, unreadFilterType: @@ -203,91 +281,77 @@ class _LibraryScreenState extends ConsumerState language: language, displayType: displayType, ref: ref) - : _bodyWithCatories( - categoryId: entr[i - 1].id!, - downloadFilterType: - downloadFilterType, - unreadFilterType: - unreadFilterType, - startedFilterType: - startedFilterType, - bookmarkedFilterType: - bookmarkedFilterType, - reverse: reverse, - downloadedChapter: - downloadedChapter, - continueReaderBtn: - continueReaderBtn, - language: language, - displayType: displayType, - ref: ref), - if (withoutCategory.isEmpty) - for (var i = 0; i < entr.length; i++) - _bodyWithCatories( - categoryId: entr[i].id!, - downloadFilterType: - downloadFilterType, - unreadFilterType: unreadFilterType, - startedFilterType: startedFilterType, - bookmarkedFilterType: - bookmarkedFilterType, - reverse: reverse, - downloadedChapter: downloadedChapter, - continueReaderBtn: continueReaderBtn, - language: language, - displayType: displayType, - ref: ref) - ])) - ], - ), - ), - ); - }); - } - return Consumer(builder: (context, ref, child) { - final reverse = ref.watch(libraryReverseListStateProvider); - final continueReaderBtn = - ref.watch(libraryShowContinueReadingButtonStateProvider); - final showNumbersOfItems = - ref.watch(libraryShowNumbersOfItemsStateProvider); - final downloadedChapter = - ref.watch(libraryDownloadedChaptersStateProvider); - final language = ref.watch(libraryLanguageStateProvider); - final displayType = ref - .read(libraryDisplayTypeStateProvider.notifier) - .getLibraryDisplayTypeValue( - ref.watch(libraryDisplayTypeStateProvider)); - final isNotFiltering = ref.watch( - mangasFilterResultStateProvider(mangaList: _entries)); - final downloadFilterType = ref.watch( - mangaFilterDownloadedStateProvider(mangaList: _entries)); - final unreadFilterType = ref.watch( - mangaFilterUnreadStateProvider(mangaList: _entries)); - final startedFilterType = ref.watch( - mangaFilterStartedStateProvider(mangaList: _entries)); - final bookmarkedFilterType = ref.watch( - mangaFilterBookmarkedStateProvider(mangaList: _entries)); - final numberOfItemsList = _filterMangas( - data: man, - downloadFilterType: downloadFilterType, - unreadFilterType: unreadFilterType, - startedFilterType: startedFilterType, - bookmarkedFilterType: bookmarkedFilterType); - return Scaffold( - appBar: _appBar(isNotFiltering, showNumbersOfItems, - numberOfItemsList.length), - body: _bodyWithoutCategories( + ])) + ], + ), + ), + ); + }); + } + return Consumer(builder: (context, ref, child) { + bool reverse = + ref.watch(sortLibraryMangaStateProvider)["reverse"]; + final continueReaderBtn = ref + .watch(libraryShowContinueReadingButtonStateProvider); + final showNumbersOfItems = + ref.watch(libraryShowNumbersOfItemsStateProvider); + final downloadedChapter = + ref.watch(libraryDownloadedChaptersStateProvider); + final language = ref.watch(libraryLanguageStateProvider); + final displayType = ref + .read(libraryDisplayTypeStateProvider.notifier) + .getLibraryDisplayTypeValue( + ref.watch(libraryDisplayTypeStateProvider)); + final isNotFiltering = ref.watch( + mangasFilterResultStateProvider(mangaList: _entries)); + final downloadFilterType = ref.watch( + mangaFilterDownloadedStateProvider( + mangaList: _entries)); + final unreadFilterType = ref.watch( + mangaFilterUnreadStateProvider(mangaList: _entries)); + final startedFilterType = ref.watch( + mangaFilterStartedStateProvider(mangaList: _entries)); + final bookmarkedFilterType = ref.watch( + mangaFilterBookmarkedStateProvider( + mangaList: _entries)); + final sortType = ref + .watch(sortLibraryMangaStateProvider)['index'] as int; + final numberOfItemsList = _filterAndSortMangas( + data: man, downloadFilterType: downloadFilterType, unreadFilterType: unreadFilterType, startedFilterType: startedFilterType, bookmarkedFilterType: bookmarkedFilterType, - reverse: reverse, - downloadedChapter: downloadedChapter, - continueReaderBtn: continueReaderBtn, - language: language, - displayType: displayType, - ref: ref)); - }); + sortType: sortType); + return Scaffold( + appBar: _appBar( + isNotFiltering, + showNumbersOfItems, + numberOfItemsList.length, + ref, + numberOfItemsList, + false, + null), + body: _bodyWithoutCategories( + downloadFilterType: downloadFilterType, + unreadFilterType: unreadFilterType, + startedFilterType: startedFilterType, + bookmarkedFilterType: bookmarkedFilterType, + reverse: reverse, + downloadedChapter: downloadedChapter, + continueReaderBtn: continueReaderBtn, + language: language, + displayType: displayType, + ref: ref)); + }); + }, + error: (Object error, StackTrace stackTrace) { + return ErrorText(error); + }, + loading: () { + return const ProgressCenter(); + }, + ); }, error: (Object error, StackTrace stackTrace) { return ErrorText(error); @@ -303,18 +367,237 @@ class _LibraryScreenState extends ConsumerState loading: () { return const ProgressCenter(); }, - ); - }, - error: (Object error, StackTrace stackTrace) { - return ErrorText(error); - }, - loading: () { - return const ProgressCenter(); - }, - ); + ), + bottomNavigationBar: Consumer(builder: (context, ref, child) { + final isLongPressed = ref.watch(isLongPressedMangaStateProvider); + final color = Theme.of(context).textTheme.bodyLarge!.color!; + final mangaIds = ref.watch(mangasListStateProvider); + return AnimatedContainer( + curve: Curves.easeIn, + decoration: BoxDecoration( + color: primaryColor(context).withOpacity(0.2), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20))), + duration: const Duration(milliseconds: 100), + height: isLongPressed ? 70 : 0, + width: mediaWidth(context, 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: () { + _openCategory(); + }, + child: Icon( + Icons.label_outline_rounded, + color: color, + )), + ), + ), + 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(); + }, + child: Icon( + 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: () { + ref + .read(mangasSetUnReadStateProvider( + mangaIds: mangaIds) + .notifier) + .set(); + }, + child: Icon( + Icons.remove_done_sharp, + color: color, + )), + ), + ), + 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: () {}, + child: Icon( + Icons.delete_outline_outlined, + color: color, + )), + ), + ), + ], + ), + ); + })); } - Widget _categoriNumberOfItems( + _openCategory() { + List categoryIds = []; + showDialog( + context: context, + builder: (context) { + return Consumer(builder: (context, ref, child) { + final mangaIdsList = ref.watch(mangasListStateProvider); + final List mangasList = []; + for (var id in mangaIdsList) { + mangasList.add(isar.mangas.getSync(id)!); + } + + return StatefulBuilder( + builder: (context, setState) { + return AlertDialog( + title: const Text( + "Set categories", + ), + content: SizedBox( + width: mediaWidth(context, 0.8), + child: StreamBuilder( + stream: isar.categorys + .filter() + .idIsNotNull() + .watch(fireImmediately: true), + builder: (context, snapshot) { + if (snapshot.hasData && snapshot.data!.isNotEmpty) { + final entries = snapshot.data!; + return ListView.builder( + shrinkWrap: true, + itemCount: entries.length, + itemBuilder: (context, index) { + return ListTileMangaCategory( + category: entries[index], + categoryIds: categoryIds, + mangasList: mangasList, + onTap: () { + setState(() { + if (categoryIds + .contains(entries[index].id)) { + categoryIds.remove(entries[index].id); + } else { + categoryIds.add(entries[index].id!); + } + }); + }, + res: (res) { + if (res.isNotEmpty) { + categoryIds.add(entries[index].id!); + } + }, + ); + }, + ); + } + return Container(); + }), + ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextButton( + onPressed: () { + context.push("/categories"); + Navigator.pop(context); + }, + child: const Text("Edit")), + Row( + children: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text("Cancel")), + const SizedBox( + width: 15, + ), + TextButton( + onPressed: () { + isar.writeTxnSync(() { + for (var id in mangaIdsList) { + Manga? manga = isar.mangas.getSync(id); + manga!.categories = categoryIds; + isar.mangas.putSync(manga); + } + }); + ref + .read(mangasListStateProvider.notifier) + .clear(); + ref + .read(isLongPressedMangaStateProvider + .notifier) + .update(false); + if (mounted) { + Navigator.pop(context); + } + }, + child: const Text( + "OK", + )), + ], + ), + ], + ) + ], + ); + }, + ); + }); + }); + } + + Widget _categoriesNumberOfItems( {required int downloadFilterType, required int unreadFilterType, required int startedFilterType, @@ -324,14 +607,16 @@ class _LibraryScreenState extends ConsumerState required bool continueReaderBtn, required int categoryId}) { final mangas = ref.watch(getAllMangaStreamProvider(categoryId: categoryId)); + final sortType = ref.watch(sortLibraryMangaStateProvider)['index'] as int; return mangas.when( data: (data) { - final categoriNumberOfItemsList = _filterMangas( + final categoriNumberOfItemsList = _filterAndSortMangas( data: data, downloadFilterType: downloadFilterType, unreadFilterType: unreadFilterType, startedFilterType: startedFilterType, - bookmarkedFilterType: bookmarkedFilterType); + bookmarkedFilterType: bookmarkedFilterType, + sortType: sortType); return CircleAvatar( backgroundColor: Theme.of(context).focusColor, radius: 8, @@ -365,15 +650,18 @@ class _LibraryScreenState extends ConsumerState required WidgetRef ref, required DisplayType displayType}) { final mangas = ref.watch(getAllMangaStreamProvider(categoryId: categoryId)); + final sortType = ref.watch(sortLibraryMangaStateProvider)['index'] as int; + final mangaIdsList = ref.watch(mangasListStateProvider); return Scaffold( body: mangas.when( data: (data) { - final entries = _filterMangas( + final entries = _filterAndSortMangas( data: data, downloadFilterType: downloadFilterType, unreadFilterType: unreadFilterType, startedFilterType: startedFilterType, - bookmarkedFilterType: bookmarkedFilterType); + bookmarkedFilterType: bookmarkedFilterType, + sortType: sortType); if (entries.isNotEmpty) { final entriesManga = reverse ? entries.reversed.toList() : entries; return displayType == DisplayType.list @@ -382,6 +670,7 @@ class _LibraryScreenState extends ConsumerState continueReaderBtn: continueReaderBtn, downloadedChapter: downloadedChapter, language: language, + mangaIdsList: mangaIdsList, ) : LibraryGridViewWidget( entriesManga: entriesManga, @@ -392,6 +681,7 @@ class _LibraryScreenState extends ConsumerState continueReaderBtn: continueReaderBtn, downloadedChapter: downloadedChapter, language: language, + mangaIdsList: mangaIdsList, ); } return const Center(child: Text("Empty Library")); @@ -417,17 +707,20 @@ class _LibraryScreenState extends ConsumerState required DisplayType displayType, required WidgetRef ref, bool withouCategories = false}) { + final sortType = ref.watch(sortLibraryMangaStateProvider)['index'] as int; final manga = withouCategories ? ref.watch(getAllMangaWithoutCategoriesStreamProvider) : ref.watch(getAllMangaStreamProvider(categoryId: null)); + final mangaIdsList = ref.watch(mangasListStateProvider); return manga.when( data: (data) { - final entries = _filterMangas( + final entries = _filterAndSortMangas( data: data, downloadFilterType: downloadFilterType, unreadFilterType: unreadFilterType, startedFilterType: startedFilterType, - bookmarkedFilterType: bookmarkedFilterType); + bookmarkedFilterType: bookmarkedFilterType, + sortType: sortType); if (entries.isNotEmpty) { final entriesManga = reverse ? entries.reversed.toList() : entries; return displayType == DisplayType.list @@ -436,6 +729,7 @@ class _LibraryScreenState extends ConsumerState continueReaderBtn: continueReaderBtn, downloadedChapter: downloadedChapter, language: language, + mangaIdsList: mangaIdsList, ) : LibraryGridViewWidget( entriesManga: entriesManga, @@ -446,6 +740,7 @@ class _LibraryScreenState extends ConsumerState continueReaderBtn: continueReaderBtn, downloadedChapter: downloadedChapter, language: language, + mangaIdsList: mangaIdsList, ); } return const Center(child: Text("Empty Library")); @@ -459,13 +754,15 @@ class _LibraryScreenState extends ConsumerState ); } - List _filterMangas( + List _filterAndSortMangas( {required List data, required int downloadFilterType, required int unreadFilterType, required int startedFilterType, - required int bookmarkedFilterType}) { - return data + required int bookmarkedFilterType, + required int sortType}) { + List? mangas; + mangas = data .where((element) { List list = []; if (downloadFilterType == 1) { @@ -539,6 +836,59 @@ class _LibraryScreenState extends ConsumerState .contains(_textEditingController.text.toLowerCase()) : true) .toList(); + + if (sortType == 0) { + mangas.sort( + (a, b) { + return a.name!.compareTo(b.name!); + }, + ); + } else if (sortType == 1) { + mangas.sort( + (a, b) { + return a.lastRead!.compareTo(b.lastRead!); + }, + ); + } else if (sortType == 2) { + mangas.sort( + (a, b) { + return a.lastUpdate!.compareTo(b.lastUpdate!); + }, + ); + } else if (sortType == 3) { + mangas.sort( + (a, b) { + return a.lastUpdate!.compareTo(b.lastUpdate!); + }, + ); + } else if (sortType == 4) { + mangas.sort( + (a, b) { + return a.chapters + .where((element) => !element.isRead!) + .toList() + .length + .compareTo(b.chapters + .where((element) => !element.isRead!) + .toList() + .length); + }, + ); + } else if (sortType == 5) { + mangas.sort( + (a, b) { + return a.chapters.first.dateUpload! + .compareTo(b.chapters.first.dateUpload!); + }, + ); + } else if (sortType == 6) { + mangas.sort( + (a, b) { + return a.dateAdded!.compareTo(b.dateAdded!); + }, + ); + } + return mangas; } _showDraggableMenu() { @@ -551,7 +901,7 @@ class _LibraryScreenState extends ConsumerState uiType: DraggableMenuUiType.softModern, expandable: true, expandedHeight: mediaHeight(context, 0.8), - maxHeight: mediaHeight(context, 0.5), + maxHeight: mediaHeight(context, 0.6), minimizeBeforeFastDrag: true, child: DefaultTabController( length: 3, @@ -628,24 +978,25 @@ class _LibraryScreenState extends ConsumerState ); }), Consumer(builder: (context, ref, chil) { - final reverse = - ref.watch(libraryReverseListStateProvider); - + final reverse = ref + .read(sortLibraryMangaStateProvider.notifier) + .isReverse(); + final reverseChapter = + ref.watch(sortLibraryMangaStateProvider); return Column( children: [ - ListTile( - onTap: () { - ref - .read(libraryReverseListStateProvider - .notifier) - .set(!reverse); - }, - dense: true, - leading: Icon(reverse - ? Icons.arrow_downward_sharp - : Icons.arrow_upward_sharp), - title: const Text("Alphabetically"), - ), + for (var i = 0; i < 7; i++) + ListTileChapterSort( + label: _getSortNameByIndex(i), + reverse: reverse, + onTap: () { + ref + .read(sortLibraryMangaStateProvider + .notifier) + .set(i); + }, + showLeading: reverseChapter['index'] == i, + ), ], ); }), @@ -817,86 +1168,177 @@ class _LibraryScreenState extends ConsumerState )))); } - AppBar _appBar( - bool isNotFiltering, bool showNumbersOfItems, int numberOfItems) { - return AppBar( - elevation: 0, - backgroundColor: Colors.transparent, - title: isSearch - ? null - : Row( - children: [ - Text( - 'Library', - style: TextStyle(color: Theme.of(context).hintColor), - ), - const SizedBox( - width: 10, - ), - if (showNumbersOfItems) - Padding( - padding: const EdgeInsets.only(bottom: 3), - child: CircleAvatar( - backgroundColor: Theme.of(context).focusColor, - radius: 10, - child: Text( - numberOfItems.toString(), - style: TextStyle( - fontSize: 12, - color: - Theme.of(context).textTheme.bodySmall!.color), - ), - ), + String _getSortNameByIndex(int index) { + if (index == 0) { + return "AlphabeticalLy"; + } else if (index == 1) { + return "Last read"; + } else if (index == 2) { + return "Last update check"; + } else if (index == 3) { + return "Unread count"; + } else if (index == 4) { + return "Total chapters"; + } else if (index == 5) { + return "Latest chapter"; + } + return "Date added"; + } + + PreferredSize _appBar( + bool isNotFiltering, + bool showNumbersOfItems, + int numberOfItems, + WidgetRef ref, + List mangas, + bool isCategory, + int? categoryId) { + final isLongPressed = ref.watch(isLongPressedMangaStateProvider); + final mangaIdsList = ref.watch(mangasListStateProvider); + final manga = categoryId == null + ? ref.watch(getAllMangaWithoutCategoriesStreamProvider) + : ref.watch(getAllMangaStreamProvider(categoryId: categoryId)); + + return PreferredSize( + preferredSize: Size.fromHeight(AppBar().preferredSize.height), + child: isLongPressed + ? manga.when( + data: (data) => Container( + color: Theme.of(context).scaffoldBackgroundColor, + child: AppBar( + title: Text(mangaIdsList.length.toString()), + backgroundColor: primaryColor(context).withOpacity(0.2), + leading: IconButton( + onPressed: () { + ref.read(mangasListStateProvider.notifier).clear(); + + ref + .read(isLongPressedMangaStateProvider.notifier) + .update(!isLongPressed); + }, + icon: const Icon(Icons.clear)), + actions: [ + IconButton( + onPressed: () { + for (var manga in data) { + ref + .read(mangasListStateProvider.notifier) + .selectAll(manga); + } + }, + icon: const Icon(Icons.select_all)), + IconButton( + onPressed: () { + if (data.length == mangaIdsList.length) { + for (var manga in data) { + ref + .read(mangasListStateProvider.notifier) + .selectSome(manga); + } + ref + .read( + isLongPressedMangaStateProvider.notifier) + .update(false); + } else { + for (var manga in data) { + ref + .read(mangasListStateProvider.notifier) + .selectSome(manga); + } + } + }, + icon: const Icon(Icons.flip_to_back_rounded)), + ], ), - ], - ), - actions: [ - isSearch - ? SeachFormTextField( - onChanged: (value) { - setState(() {}); + ), + error: (Object error, StackTrace stackTrace) { + return ErrorText(error); }, - onPressed: () { - setState(() { - isSearch = false; - }); - _textEditingController.clear(); - }, - controller: _textEditingController, - onSuffixPressed: () { - _textEditingController.clear(); - setState(() {}); + loading: () { + return const ProgressCenter(); }, ) - : IconButton( - splashRadius: 20, - onPressed: () { - setState(() { - isSearch = true; - }); - _textEditingController.clear(); - }, - icon: const Icon( - Icons.search, - )), - IconButton( - splashRadius: 20, - onPressed: () { - _showDraggableMenu(); - }, - icon: Icon( - Icons.filter_list_sharp, - color: isNotFiltering ? null : Colors.yellow, - )), - PopupMenuButton( - itemBuilder: (context) { - return [ - const PopupMenuItem( - value: 0, child: Text("Open random entry")), - ]; - }, - onSelected: (value) {}), - ], - ); + : AppBar( + elevation: 0, + backgroundColor: Colors.transparent, + title: _isSearch + ? null + : Row( + children: [ + Text( + 'Library', + style: + TextStyle(color: Theme.of(context).hintColor), + ), + const SizedBox( + width: 10, + ), + if (showNumbersOfItems) + Padding( + padding: const EdgeInsets.only(bottom: 3), + child: CircleAvatar( + backgroundColor: Theme.of(context).focusColor, + radius: 10, + child: Text( + numberOfItems.toString(), + style: TextStyle( + fontSize: 12, + color: Theme.of(context) + .textTheme + .bodySmall! + .color), + ), + ), + ), + ], + ), + actions: [ + _isSearch + ? SeachFormTextField( + onChanged: (value) { + setState(() {}); + }, + onPressed: () { + setState(() { + _isSearch = false; + }); + _textEditingController.clear(); + }, + controller: _textEditingController, + onSuffixPressed: () { + _textEditingController.clear(); + setState(() {}); + }, + ) + : IconButton( + splashRadius: 20, + onPressed: () { + setState(() { + _isSearch = true; + }); + _textEditingController.clear(); + }, + icon: const Icon( + Icons.search, + )), + IconButton( + splashRadius: 20, + onPressed: () { + _showDraggableMenu(); + }, + icon: Icon( + Icons.filter_list_sharp, + color: isNotFiltering ? null : Colors.yellow, + )), + PopupMenuButton( + itemBuilder: (context) { + return [ + const PopupMenuItem( + value: 0, child: Text("Open random entry")), + ]; + }, + onSelected: (value) {}), + ], + )); } } diff --git a/lib/views/library/providers/isar_providers.g.dart b/lib/views/library/providers/isar_providers.g.dart index 068a47a9..8eb46d66 100644 --- a/lib/views/library/providers/isar_providers.g.dart +++ b/lib/views/library/providers/isar_providers.g.dart @@ -111,7 +111,7 @@ class GetAllMangaStreamProvider extends AutoDisposeStreamProvider> { } String _$getAllMangaWithoutCategoriesStreamHash() => - r'76230abaa3a6394c56b3ec90b6a4af812f6ebfb5'; + r'6fc216e2a14edb3a0323b358ef3749a75007e8b5'; /// See also [getAllMangaWithoutCategoriesStream]. @ProviderFor(getAllMangaWithoutCategoriesStream) diff --git a/lib/views/library/providers/library_state_provider.dart b/lib/views/library/providers/library_state_provider.dart index e50d9985..a6e8928b 100644 --- a/lib/views/library/providers/library_state_provider.dart +++ b/lib/views/library/providers/library_state_provider.dart @@ -1,25 +1,10 @@ -import 'dart:developer'; - +import 'package:mangayomi/main.dart'; +import 'package:mangayomi/models/chapter.dart'; import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/providers/hive_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'library_state_provider.g.dart'; -@riverpod -class LibraryReverseListState extends _$LibraryReverseListState { - @override - bool build() { - return ref - .watch(hiveBoxSettingsProvider) - .get('libraryReverseList', defaultValue: false)!; - } - - void set(bool value) { - state = value; - ref.watch(hiveBoxSettingsProvider).put('libraryReverseList', value); - } -} - @riverpod class LibraryDisplayTypeState extends _$LibraryDisplayTypeState { @override @@ -139,7 +124,7 @@ class MangaFilterUnreadState extends _$MangaFilterUnreadState { } } - update() { + update() { if (state == 0) { final data = mangaList.where((element) { List list = []; @@ -425,3 +410,132 @@ class LibraryShowContinueReadingButtonState .put('libraryShowContinueReadingButton', value); } } + +@riverpod +class SortLibraryMangaState extends _$SortLibraryMangaState { + @override + dynamic build() { + return ref.watch(hiveBoxSettingsProvider).get("sortLibraryMangaMap", + defaultValue: {"reverse": false, "index": 2}); + } + + void update(bool reverse, int index) { + var value = { + "reverse": state['index'] == index ? !reverse : reverse, + "index": index + }; + ref.watch(hiveBoxSettingsProvider).put("sortLibraryMangaMap", value); + state = value; + } + + void set(int index) { + final reverse = isReverse(); + update(reverse, index); + } + + bool isReverse() { + return state["reverse"]; + } +} + +@riverpod +class MangasListState extends _$MangasListState { + @override + List build() { + return []; + } + + void update(Manga value) { + var newList = state.reversed.toList(); + if (newList.contains(value.id)) { + newList.remove(value.id); + } else { + newList.add(value.id!); + } + if (newList.isEmpty) { + ref.read(isLongPressedMangaStateProvider.notifier).update(false); + } + state = newList; + } + + void selectAll(Manga value) { + var newList = state.reversed.toList(); + if (!newList.contains(value.id)) { + newList.add(value.id!); + } + + state = newList; + } + + void selectSome(Manga value) { + var newList = state.reversed.toList(); + if (newList.contains(value.id)) { + newList.remove(value.id); + } else { + newList.add(value.id!); + } + state = newList; + } + + void clear() { + state = []; + } +} + +@riverpod +class IsLongPressedMangaState extends _$IsLongPressedMangaState { + @override + bool build() { + return false; + } + + void update(bool value) { + state = value; + } +} + +@riverpod +class MangasSetIsReadState extends _$MangasSetIsReadState { + @override + build({required List mangaIds}) {} + + set() { + for (var mangaid in mangaIds) { + final manga = isar.mangas.getSync(mangaid)!; + final chapters = manga.chapters; + isar.writeTxnSync(() { + for (var chapter in chapters) { + chapter.isRead = true; + isar.chapters.putSync(chapter..manga.value = manga); + chapter.manga.saveSync(); + } + }); + } + + ref.read(isLongPressedMangaStateProvider.notifier).update(false); + ref.read(mangasListStateProvider.notifier).clear(); + } +} + +@riverpod +class MangasSetUnReadState extends _$MangasSetUnReadState { + @override + build({required List mangaIds}) {} + + set() { + for (var mangaid in mangaIds) { + final manga = isar.mangas.getSync(mangaid)!; + final chapters = manga.chapters; + isar.writeTxnSync(() { + for (var chapter in chapters) { + chapter.isRead = false; + isar.chapters.putSync(chapter..manga.value = manga); + chapter.manga.saveSync(); + } + }); + } + + ref.read(isLongPressedMangaStateProvider.notifier).update(false); + ref.read(mangasListStateProvider.notifier).clear(); + } +} diff --git a/lib/views/library/providers/library_state_provider.g.dart b/lib/views/library/providers/library_state_provider.g.dart index b80d7550..b1f364df 100644 --- a/lib/views/library/providers/library_state_provider.g.dart +++ b/lib/views/library/providers/library_state_provider.g.dart @@ -6,23 +6,6 @@ part of 'library_state_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$libraryReverseListStateHash() => - r'5d9037f95ffe332019dd1d3d08b0db06d798738c'; - -/// See also [LibraryReverseListState]. -@ProviderFor(LibraryReverseListState) -final libraryReverseListStateProvider = - AutoDisposeNotifierProvider.internal( - LibraryReverseListState.new, - name: r'libraryReverseListStateProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$libraryReverseListStateHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$LibraryReverseListState = AutoDisposeNotifier; String _$libraryDisplayTypeStateHash() => r'746bd6dac3600802c3ab5751b3c1def881274b3a'; @@ -642,4 +625,249 @@ final libraryShowContinueReadingButtonStateProvider = ); typedef _$LibraryShowContinueReadingButtonState = AutoDisposeNotifier; +String _$sortLibraryMangaStateHash() => + r'81abfe6c2841cf7b25301928d88f8af80cd480fd'; + +/// See also [SortLibraryMangaState]. +@ProviderFor(SortLibraryMangaState) +final sortLibraryMangaStateProvider = + AutoDisposeNotifierProvider.internal( + SortLibraryMangaState.new, + name: r'sortLibraryMangaStateProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$sortLibraryMangaStateHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$SortLibraryMangaState = AutoDisposeNotifier; +String _$mangasListStateHash() => r'ad1cc419dfd3793bfc8c90f3ce8b7726561dd9ad'; + +/// See also [MangasListState]. +@ProviderFor(MangasListState) +final mangasListStateProvider = + AutoDisposeNotifierProvider>.internal( + MangasListState.new, + name: r'mangasListStateProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mangasListStateHash, + dependencies: null, + allTransitiveDependencies: null, +); + +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'240a9a3e317e3b2a42fd5babe906699e8869ae0a'; + +abstract class _$MangasSetIsReadState + extends BuildlessAutoDisposeNotifier { + late final List mangaIds; + + dynamic build({ + required List mangaIds, + }); +} + +/// See also [MangasSetIsReadState]. +@ProviderFor(MangasSetIsReadState) +const mangasSetIsReadStateProvider = MangasSetIsReadStateFamily(); + +/// See also [MangasSetIsReadState]. +class MangasSetIsReadStateFamily extends Family { + /// See also [MangasSetIsReadState]. + const MangasSetIsReadStateFamily(); + + /// See also [MangasSetIsReadState]. + MangasSetIsReadStateProvider call({ + required List mangaIds, + }) { + return MangasSetIsReadStateProvider( + mangaIds: mangaIds, + ); + } + + @override + MangasSetIsReadStateProvider getProviderOverride( + covariant MangasSetIsReadStateProvider 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'mangasSetIsReadStateProvider'; +} + +/// See also [MangasSetIsReadState]. +class MangasSetIsReadStateProvider + extends AutoDisposeNotifierProviderImpl { + /// See also [MangasSetIsReadState]. + MangasSetIsReadStateProvider({ + required this.mangaIds, + }) : super.internal( + () => MangasSetIsReadState()..mangaIds = mangaIds, + from: mangasSetIsReadStateProvider, + name: r'mangasSetIsReadStateProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$mangasSetIsReadStateHash, + dependencies: MangasSetIsReadStateFamily._dependencies, + allTransitiveDependencies: + MangasSetIsReadStateFamily._allTransitiveDependencies, + ); + + final List mangaIds; + + @override + bool operator ==(Object other) { + return other is MangasSetIsReadStateProvider && other.mangaIds == mangaIds; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, mangaIds.hashCode); + + return _SystemHash.finish(hash); + } + + @override + dynamic runNotifierBuild( + covariant MangasSetIsReadState notifier, + ) { + return notifier.build( + mangaIds: mangaIds, + ); + } +} + +String _$mangasSetUnReadStateHash() => + r'0494afb9a17a0e4346e182cc2944341174348514'; + +abstract class _$MangasSetUnReadState + extends BuildlessAutoDisposeNotifier { + late final List mangaIds; + + dynamic 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 this.mangaIds, + }) : super.internal( + () => MangasSetUnReadState()..mangaIds = mangaIds, + from: mangasSetUnReadStateProvider, + name: r'mangasSetUnReadStateProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$mangasSetUnReadStateHash, + dependencies: MangasSetUnReadStateFamily._dependencies, + allTransitiveDependencies: + MangasSetUnReadStateFamily._allTransitiveDependencies, + ); + + final List mangaIds; + + @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); + } + + @override + dynamic runNotifierBuild( + covariant MangasSetUnReadState notifier, + ) { + return notifier.build( + mangaIds: mangaIds, + ); + } +} // ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions diff --git a/lib/views/library/widgets/library_gridview_widget.dart b/lib/views/library/widgets/library_gridview_widget.dart index 95e719d9..a0a28afb 100644 --- a/lib/views/library/widgets/library_gridview_widget.dart +++ b/lib/views/library/widgets/library_gridview_widget.dart @@ -3,6 +3,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:mangayomi/views/history/providers/isar_providers.dart'; +import 'package:mangayomi/views/library/providers/library_state_provider.dart'; +import 'package:mangayomi/views/manga/detail/providers/state_providers.dart'; import 'package:mangayomi/views/manga/reader/providers/push_router.dart'; import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/providers/hive_provider.dart'; @@ -19,6 +21,7 @@ import 'package:mangayomi/views/widgets/progress_center.dart'; class LibraryGridViewWidget extends StatelessWidget { final bool isCoverOnlyGrid; final bool isComfortableGrid; + final List mangaIdsList; final List entriesManga; final bool language; final bool downloadedChapter; @@ -30,7 +33,8 @@ class LibraryGridViewWidget extends StatelessWidget { this.isComfortableGrid = false, required this.language, required this.downloadedChapter, - required this.continueReaderBtn}); + required this.continueReaderBtn, + required this.mangaIdsList}); @override Widget build(BuildContext context) { @@ -38,214 +42,259 @@ class LibraryGridViewWidget extends StatelessWidget { mainAxisExtent: isComfortableGrid ? 310 : 280, itemCount: entriesManga.length, itemBuilder: (context, index) { - return GestureDetector( - onTap: () { - context.push('/manga-reader/detail', extra: entriesManga[index].id); - }, - child: CoverViewWidget( - bottomTextWidget: BottomTextWidget( - text: entriesManga[index].name!, - isComfortableGrid: isComfortableGrid, - ), - isComfortableGrid: isComfortableGrid, - children: [ - Stack( + return Consumer(builder: (context, ref, child) { + final isLongPressed = ref.watch(isLongPressedMangaStateProvider); + return Padding( + padding: const EdgeInsets.all(2), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.all(0), + backgroundColor: mangaIdsList.contains(entriesManga[index].id) + ? primaryColor(context).withOpacity(0.4) + : Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + elevation: 0, + shadowColor: Colors.transparent), + onPressed: () { + if (isLongPressed) { + ref + .read(mangasListStateProvider.notifier) + .update(entriesManga[index]); + } else { + context.push('/manga-reader/detail', + extra: entriesManga[index].id); + } + }, + onLongPress: () { + if (!isLongPressed) { + ref + .read(mangasListStateProvider.notifier) + .update(entriesManga[index]); + + ref + .read(isLongPressedMangaStateProvider.notifier) + .update(!isLongPressed); + } else { + ref + .read(mangasListStateProvider.notifier) + .update(entriesManga[index]); + } + }, + child: CoverViewWidget( + bottomTextWidget: BottomTextWidget( + text: entriesManga[index].name!, + isComfortableGrid: isComfortableGrid, + ), + isComfortableGrid: isComfortableGrid, children: [ - cachedNetworkImage( - headers: headers(entriesManga[index].source!), - imageUrl: entriesManga[index].imageUrl!, - width: 200, - height: 270, - fit: BoxFit.cover), - Positioned( - top: 0, - left: 0, - child: Padding( - padding: const EdgeInsets.all(5), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(3), - color: primaryColor(context), - ), - child: Row( - children: [ - if (downloadedChapter) - Padding( - padding: const EdgeInsets.only(right: 5), - child: Consumer( - builder: (context, ref, child) { - List nbrDown = []; - for (var i = 0; - i < - entriesManga[index] - .chapters - .length; - i++) { - final entries = ref - .watch( - hiveBoxMangaDownloadsProvider) - .values - .where((element) => - element.chapterName == - entriesManga[index] - .chapters - .toList()[i] - .name) - .toList(); - if (entries.isNotEmpty && - entries.first.isDownload) { - nbrDown.add(entries.first); - } - } - if (nbrDown.isNotEmpty) { - return Container( - decoration: BoxDecoration( - borderRadius: - const BorderRadius.only( - topLeft: Radius.circular(3), - bottomLeft: - Radius.circular(3)), - color: Theme.of(context).hintColor, - ), - child: Padding( - padding: const EdgeInsets.only( - left: 3, right: 3), - child: Text( - nbrDown.length.toString(), - style: const TextStyle( - color: Colors.white), - ), - ), - ); - } else { - return Container(); - } - }, - ), - ), - Padding( - padding: const EdgeInsets.only(right: 3), - child: Text( - entriesManga[index] - .chapters - .length - .toString(), - style: const TextStyle(color: Colors.white), - ), - ), - ], - ), - ), - )), - if (language) - Positioned( - top: 0, - right: 0, - child: Padding( - padding: const EdgeInsets.all(5), - child: Container( - color: primaryColor(context), + Stack( + children: [ + cachedNetworkImage( + headers: headers(entriesManga[index].source!), + imageUrl: entriesManga[index].imageUrl!, + width: 200, + height: 270, + fit: BoxFit.cover), + Positioned( + top: 0, + left: 0, + child: Padding( + padding: const EdgeInsets.all(5), child: Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(3), - bottomLeft: Radius.circular(3)), - color: Theme.of(context).hintColor, + borderRadius: BorderRadius.circular(3), + color: primaryColor(context), ), - child: Padding( - padding: - const EdgeInsets.only(left: 3, right: 3), - child: Text( - entriesManga[index].lang!.toUpperCase(), - style: const TextStyle(color: Colors.white), - ), + child: Row( + children: [ + if (downloadedChapter) + Padding( + padding: const EdgeInsets.only(right: 5), + child: Consumer( + builder: (context, ref, child) { + List nbrDown = []; + for (var i = 0; + i < + entriesManga[index] + .chapters + .length; + i++) { + final entries = ref + .watch( + hiveBoxMangaDownloadsProvider) + .values + .where((element) => + element.chapterName == + entriesManga[index] + .chapters + .toList()[i] + .name) + .toList(); + if (entries.isNotEmpty && + entries.first.isDownload) { + nbrDown.add(entries.first); + } + } + if (nbrDown.isNotEmpty) { + return Container( + decoration: BoxDecoration( + borderRadius: + const BorderRadius.only( + topLeft: + Radius.circular(3), + bottomLeft: + Radius.circular(3)), + color: + Theme.of(context).hintColor, + ), + child: Padding( + padding: const EdgeInsets.only( + left: 3, right: 3), + child: Text( + nbrDown.length.toString(), + style: const TextStyle( + color: Colors.white), + ), + ), + ); + } else { + return Container(); + } + }, + ), + ), + Padding( + padding: const EdgeInsets.only(right: 3), + child: Text( + entriesManga[index] + .chapters + .length + .toString(), + style: + const TextStyle(color: Colors.white), + ), + ), + ], ), ), - ), - )), - ], - ), - if (!isComfortableGrid) - if (!isCoverOnlyGrid) - BottomTextWidget(text: entriesManga[index].name!), - if (continueReaderBtn) - Positioned( - bottom: 0, - right: 0, - child: Padding( - padding: const EdgeInsets.all(9), - child: Consumer( - builder: (context, ref, child) { - final history = - ref.watch(getAllHistoryStreamProvider); - return history.when( - data: (data) { - final incognitoMode = - ref.watch(incognitoModeStateProvider); - final entries = data - .where((element) => - element.mangaId == - entriesManga[index].id) - .toList(); - if (entries.isNotEmpty && !incognitoMode) { - return GestureDetector( - onTap: () { - pushMangaReaderView( - context: context, - chapter: entries.first.chapter.value!, - ); - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: primaryColor(context) - .withOpacity(0.9), - ), - child: const Padding( - padding: EdgeInsets.all(7), - child: Icon( - Icons.play_arrow, - size: 19, - color: Colors.white, - )), - ), - ); - } - return GestureDetector( - onTap: () { - pushMangaReaderView( - context: context, - chapter: - entriesManga[index].chapters.last); - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: primaryColor(context) - .withOpacity(0.9), - ), - child: const Padding( - padding: EdgeInsets.all(7), - child: Icon( - Icons.play_arrow, - size: 19, - color: Colors.white, - )), + )), + if (language) + Positioned( + top: 0, + right: 0, + child: Padding( + padding: const EdgeInsets.all(5), + child: Container( + color: primaryColor(context), + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(3), + bottomLeft: Radius.circular(3)), + color: Theme.of(context).hintColor, ), + child: Padding( + padding: const EdgeInsets.only( + left: 3, right: 3), + child: Text( + entriesManga[index].lang!.toUpperCase(), + style: + const TextStyle(color: Colors.white), + ), + ), + ), + ), + )), + ], + ), + if (!isComfortableGrid) + if (!isCoverOnlyGrid) + BottomTextWidget(text: entriesManga[index].name!), + if (continueReaderBtn) + Positioned( + bottom: 0, + right: 0, + child: Padding( + padding: const EdgeInsets.all(9), + child: Consumer( + builder: (context, ref, child) { + final history = + ref.watch(getAllHistoryStreamProvider); + return history.when( + data: (data) { + final incognitoMode = + ref.watch(incognitoModeStateProvider); + final entries = data + .where((element) => + element.mangaId == + entriesManga[index].id) + .toList(); + if (entries.isNotEmpty && !incognitoMode) { + return GestureDetector( + onTap: () { + pushMangaReaderView( + context: context, + chapter: + entries.first.chapter.value!, + ); + }, + child: Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(5), + color: primaryColor(context) + .withOpacity(0.9), + ), + child: const Padding( + padding: EdgeInsets.all(7), + child: Icon( + Icons.play_arrow, + size: 19, + color: Colors.white, + )), + ), + ); + } + return GestureDetector( + onTap: () { + pushMangaReaderView( + context: context, + chapter: entriesManga[index] + .chapters + .last); + }, + child: Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(5), + color: primaryColor(context) + .withOpacity(0.9), + ), + child: const Padding( + padding: EdgeInsets.all(7), + child: Icon( + Icons.play_arrow, + size: 19, + color: Colors.white, + )), + ), + ); + }, + error: (Object error, StackTrace stackTrace) { + return ErrorText(error); + }, + loading: () { + return const ProgressCenter(); + }, ); }, - error: (Object error, StackTrace stackTrace) { - return ErrorText(error); - }, - loading: () { - return const ProgressCenter(); - }, - ); - }, - ))) - ], - ), - ); + ))) + ], + ), + ), + ); + }); }, ); } diff --git a/lib/views/library/widgets/library_listview_widget.dart b/lib/views/library/widgets/library_listview_widget.dart index 434108e2..8530c401 100644 --- a/lib/views/library/widgets/library_listview_widget.dart +++ b/lib/views/library/widgets/library_listview_widget.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:mangayomi/views/history/providers/isar_providers.dart'; +import 'package:mangayomi/views/library/providers/library_state_provider.dart'; import 'package:mangayomi/views/manga/reader/providers/push_router.dart'; import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/providers/hive_provider.dart'; @@ -18,170 +19,233 @@ class LibraryListViewWidget extends StatelessWidget { final List entriesManga; final bool language; final bool downloadedChapter; + final List mangaIdsList; final bool continueReaderBtn; const LibraryListViewWidget( {super.key, required this.entriesManga, required this.language, required this.downloadedChapter, - required this.continueReaderBtn}); + required this.continueReaderBtn, + required this.mangaIdsList}); @override Widget build(BuildContext context) { return ListViewWidget( itemCount: entriesManga.length, itemBuilder: (context, index) { - return ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(0)), - elevation: 0, - shadowColor: Colors.transparent), - onPressed: () { - context.push('/manga-reader/detail', extra: entriesManga[index].id); - }, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), - child: Container( - height: 45, - decoration: BoxDecoration(borderRadius: BorderRadius.circular(5)), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Row( - children: [ - ClipRRect( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(5), - bottomLeft: Radius.circular(5)), - child: cachedNetworkImage( - headers: headers(entriesManga[index].source!), - imageUrl: entriesManga[index].imageUrl!, - width: 40, - height: 40, - fit: BoxFit.cover), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Text(entriesManga[index].name!), + return Consumer(builder: (context, ref, child) { + final isLongPressed = ref.watch(isLongPressedMangaStateProvider); + return ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: mangaIdsList.contains(entriesManga[index].id) + ? primaryColor(context).withOpacity(0.4) + : Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(0)), + elevation: 0, + shadowColor: Colors.transparent), + onPressed: () { + if (isLongPressed) { + ref + .read(mangasListStateProvider.notifier) + .update(entriesManga[index]); + } else { + context.push('/manga-reader/detail', + extra: entriesManga[index].id); + } + }, + onLongPress: () { + if (!isLongPressed) { + ref + .read(mangasListStateProvider.notifier) + .update(entriesManga[index]); + + ref + .read(isLongPressedMangaStateProvider.notifier) + .update(!isLongPressed); + } else { + ref + .read(mangasListStateProvider.notifier) + .update(entriesManga[index]); + } + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: Container( + height: 45, + decoration: + BoxDecoration(borderRadius: BorderRadius.circular(5)), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + children: [ + ClipRRect( + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(5), + bottomLeft: Radius.circular(5)), + child: cachedNetworkImage( + headers: headers(entriesManga[index].source!), + imageUrl: entriesManga[index].imageUrl!, + width: 40, + height: 40, + fit: BoxFit.cover), ), - ) - ], + Expanded( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10), + child: Text(entriesManga[index].name!), + ), + ) + ], + ), ), - ), - Padding( - padding: const EdgeInsets.all(5), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(3), - color: primaryColor(context)), - child: SizedBox( - height: 22, - child: Row( - children: [ - if (downloadedChapter) - Padding( - padding: const EdgeInsets.only(right: 5), - child: Consumer( - builder: (context, ref, child) { - List nbrDown = []; - for (var i = 0; - i < entriesManga[index].chapters.length; - i++) { - final entries = ref - .watch(hiveBoxMangaDownloadsProvider) - .values - .where((element) => - element.chapterName == + Padding( + padding: const EdgeInsets.all(5), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(3), + color: primaryColor(context)), + child: SizedBox( + height: 22, + child: Row( + children: [ + if (downloadedChapter) + Padding( + padding: const EdgeInsets.only(right: 5), + child: Consumer( + builder: (context, ref, child) { + List nbrDown = []; + for (var i = 0; + i < entriesManga[index] .chapters - .toList()[i] - .name) - .toList(); - if (entries.isNotEmpty && - entries.first.isDownload) { - nbrDown.add(entries.first); + .length; + i++) { + final entries = ref + .watch( + hiveBoxMangaDownloadsProvider) + .values + .where((element) => + element.chapterName == + entriesManga[index] + .chapters + .toList()[i] + .name) + .toList(); + if (entries.isNotEmpty && + entries.first.isDownload) { + nbrDown.add(entries.first); + } } - } - if (nbrDown.isNotEmpty) { - return Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(3), - bottomLeft: Radius.circular(3)), - color: Theme.of(context).hintColor, - ), - child: Padding( - padding: const EdgeInsets.only( - left: 3, right: 3), - child: Text( - nbrDown.length.toString(), - style: const TextStyle( - color: Colors.white), + if (nbrDown.isNotEmpty) { + return Container( + decoration: BoxDecoration( + borderRadius: + const BorderRadius.only( + topLeft: Radius.circular(3), + bottomLeft: + Radius.circular(3)), + color: Theme.of(context).hintColor, ), - ), - ); - } else { - return Container(); - } - }, + child: Padding( + padding: const EdgeInsets.only( + left: 3, right: 3), + child: Text( + nbrDown.length.toString(), + style: const TextStyle( + color: Colors.white), + ), + ), + ); + } else { + return Container(); + } + }, + ), + ), + Padding( + padding: const EdgeInsets.only(right: 3), + child: Text( + entriesManga[index] + .chapters + .length + .toString(), + style: const TextStyle(color: Colors.white), ), ), - Padding( - padding: const EdgeInsets.only(right: 3), - child: Text( - entriesManga[index].chapters.length.toString(), - style: const TextStyle(color: Colors.white), - ), - ), - if (language) - Container( - color: primaryColor(context), - child: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topRight: Radius.circular(3), - bottomRight: Radius.circular(3)), - color: Theme.of(context).hintColor, - ), - child: Padding( - padding: const EdgeInsets.only( - left: 3, right: 3), - child: Text( - entriesManga[index].lang!.toUpperCase(), - style: - const TextStyle(color: Colors.white), + if (language) + Container( + color: primaryColor(context), + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.only( + topRight: Radius.circular(3), + bottomRight: Radius.circular(3)), + color: Theme.of(context).hintColor, + ), + child: Padding( + padding: const EdgeInsets.only( + left: 3, right: 3), + child: Text( + entriesManga[index].lang!.toUpperCase(), + style: const TextStyle( + color: Colors.white), + ), ), ), ), - ), - ], + ], + ), ), ), ), - ), - if (continueReaderBtn) - Consumer( - builder: (context, ref, child) { - final history = ref.watch(getAllHistoryStreamProvider); - return history.when( - data: (data) { - final incognitoMode = - ref.watch(incognitoModeStateProvider); - final entries = data - .where((element) => - element.mangaId == entriesManga[index].id) - .toList(); - if (entries.isNotEmpty && !incognitoMode) { + if (continueReaderBtn) + Consumer( + builder: (context, ref, child) { + final history = + ref.watch(getAllHistoryStreamProvider); + return history.when( + data: (data) { + final incognitoMode = + ref.watch(incognitoModeStateProvider); + final entries = data + .where((element) => + element.mangaId == entriesManga[index].id) + .toList(); + if (entries.isNotEmpty && !incognitoMode) { + return GestureDetector( + onTap: () { + pushMangaReaderView( + context: context, + chapter: entries.first.chapter.value!, + ); + }, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: primaryColor(context) + .withOpacity(0.9), + ), + child: const Padding( + padding: EdgeInsets.all(7), + child: Icon( + Icons.play_arrow, + size: 19, + color: Colors.white, + )), + ), + ); + } return GestureDetector( onTap: () { pushMangaReaderView( - context: context, - chapter: entries.first.chapter.value!, - ); + context: context, + chapter: + entriesManga[index].chapters.last); }, child: Container( decoration: BoxDecoration( @@ -198,42 +262,22 @@ class LibraryListViewWidget extends StatelessWidget { )), ), ); - } - return GestureDetector( - onTap: () { - pushMangaReaderView( - context: context, - chapter: entriesManga[index].chapters.last); - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: primaryColor(context).withOpacity(0.9), - ), - child: const Padding( - padding: EdgeInsets.all(7), - child: Icon( - Icons.play_arrow, - size: 19, - color: Colors.white, - )), - ), - ); - }, - error: (Object error, StackTrace stackTrace) { - return ErrorText(error); - }, - loading: () { - return const ProgressCenter(); - }, - ); - }, - ) - ], + }, + error: (Object error, StackTrace stackTrace) { + return ErrorText(error); + }, + loading: () { + return const ProgressCenter(); + }, + ); + }, + ) + ], + ), ), ), - ), - ); + ); + }); }, ); } diff --git a/lib/views/library/widgets/list_tile_manga_category.dart b/lib/views/library/widgets/list_tile_manga_category.dart new file mode 100644 index 00000000..bbe58693 --- /dev/null +++ b/lib/views/library/widgets/list_tile_manga_category.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:mangayomi/models/category.dart'; +import 'package:mangayomi/models/manga.dart'; +import 'package:mangayomi/views/manga/detail/widgets/chapter_filter_list_tile_widget.dart'; + +class ListTileMangaCategory extends StatefulWidget { + final Category category; + final List categoryIds; + final List mangasList; + final Function(List) res; + final VoidCallback onTap; + const ListTileMangaCategory( + {super.key, + required this.category, + required this.mangasList, + required this.res, + required this.onTap, + required this.categoryIds}); + + @override + State createState() => _ListTileMangaCategoryState(); +} + +class _ListTileMangaCategoryState extends State { + @override + void initState() { + final res = widget.mangasList.where( + (element) { + return element.categories == null + ? false + : element.categories!.contains(widget.category.id); + }, + ).toList(); + widget.res(res); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ListTileChapterFilter( + label: widget.category.name!, + onTap: widget.onTap, + type: widget.categoryIds.contains(widget.category.id) ? 1 : 0, + ); + } +} diff --git a/lib/views/manga/detail/manga_detail_view.dart b/lib/views/manga/detail/manga_detail_view.dart index dd42a4ed..72629642 100644 --- a/lib/views/manga/detail/manga_detail_view.dart +++ b/lib/views/manga/detail/manga_detail_view.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:cached_network_image/cached_network_image.dart'; import 'package:draggable_menu/draggable_menu.dart'; import 'package:draggable_scrollbar/draggable_scrollbar.dart'; @@ -7,8 +5,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; -import 'package:isar/isar.dart'; -import 'package:mangayomi/main.dart'; import 'package:mangayomi/models/chapter.dart'; import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/providers/hive_provider.dart'; @@ -23,9 +19,7 @@ import 'package:mangayomi/views/manga/detail/readmore.dart'; import 'package:mangayomi/views/manga/detail/widgets/chapter_filter_list_tile_widget.dart'; import 'package:mangayomi/views/manga/detail/widgets/chapter_list_tile_widget.dart'; import 'package:mangayomi/views/manga/detail/widgets/chapter_sort_list_tile_widget.dart'; -import 'package:mangayomi/views/manga/download/providers/download_provider.dart'; import 'package:mangayomi/views/widgets/error_text.dart'; -import 'package:mangayomi/views/widgets/progress_center.dart'; class MangaDetailView extends ConsumerStatefulWidget { final Function(bool) isExtended; @@ -66,17 +60,17 @@ class _MangaDetailViewState extends ConsumerState Widget build(BuildContext context) { final isLongPressed = ref.watch(isLongPressedStateProvider); final chapterNameList = ref.watch(chaptersListStateProvider); - bool reverse = ref.watch( - reverseChapterStateProvider(mangaId: widget.manga!.id!))["reverse"]; + bool reverse = ref + .watch(sortChapterStateProvider(mangaId: widget.manga!.id!))["reverse"]; final filterUnread = ref.watch(chapterFilterUnreadStateProvider(mangaId: widget.manga!.id!)); final filterBookmarked = ref.watch( chapterFilterBookmarkedStateProvider(mangaId: widget.manga!.id!)); final filterDownloaded = ref.watch( chapterFilterDownloadedStateProvider(mangaId: widget.manga!.id!)); - final sortChapter = ref.watch( - reverseChapterStateProvider(mangaId: widget.manga!.id!))['index'] - as int; + final sortChapter = + ref.watch(sortChapterStateProvider(mangaId: widget.manga!.id!))['index'] + as int; final chapters = ref.watch(getChaptersStreamProvider(mangaId: widget.manga!.id!)); return NotificationListener( @@ -91,45 +85,12 @@ class _MangaDetailViewState extends ConsumerState }, child: chapters.when( data: (data) { - List chapterList = data - .where((element) => filterUnread == 1 - ? element.isRead == false - : filterUnread == 2 - ? element.isRead == true - : true) - .where((element) => filterBookmarked == 1 - ? element.isBookmarked == true - : filterBookmarked == 2 - ? element.isBookmarked == false - : true) - .where((element) { - final modelChapDownload = ref - .watch(hiveBoxMangaDownloadsProvider) - .get("${element.mangaId}/${element.id}", defaultValue: null); - return filterDownloaded == 1 - ? modelChapDownload != null && - modelChapDownload.isDownload == true - : filterDownloaded == 2 - ? !(modelChapDownload != null && - modelChapDownload.isDownload == true) - : true; - }).toList(); - List chapters = - sortChapter == 1 ? chapterList.reversed.toList() : chapterList; - if (sortChapter == 0) { - chapters.sort( - (a, b) { - return a.scanlator!.compareTo(b.scanlator!) | - a.dateUpload!.compareTo(b.dateUpload!); - }, - ); - } else if (sortChapter == 2) { - chapters.sort( - (a, b) { - return a.dateUpload!.compareTo(b.dateUpload!); - }, - ); - } + List chapters = _filterAndSortChapter( + data: data, + filterUnread: filterUnread, + filterBookmarked: filterBookmarked, + filterDownloaded: filterDownloaded, + sortChapter: sortChapter); return _buildWidget( chapters: chapters, reverse: reverse, @@ -149,100 +110,158 @@ class _MangaDetailViewState extends ConsumerState )); } + List _filterAndSortChapter( + {required List data, + required int filterUnread, + required int filterBookmarked, + required int filterDownloaded, + required int sortChapter}) { + List? chapterList; + chapterList = data + .where((element) => filterUnread == 1 + ? element.isRead == false + : filterUnread == 2 + ? element.isRead == true + : true) + .where((element) => filterBookmarked == 1 + ? element.isBookmarked == true + : filterBookmarked == 2 + ? element.isBookmarked == false + : true) + .where((element) { + final modelChapDownload = ref + .watch(hiveBoxMangaDownloadsProvider) + .get("${element.mangaId}/${element.id}", defaultValue: null); + return filterDownloaded == 1 + ? modelChapDownload != null && modelChapDownload.isDownload == true + : filterDownloaded == 2 + ? !(modelChapDownload != null && + modelChapDownload.isDownload == true) + : true; + }).toList(); + List chapters = + sortChapter == 1 ? chapterList.reversed.toList() : chapterList; + if (sortChapter == 0) { + chapters.sort( + (a, b) { + return a.scanlator!.compareTo(b.scanlator!) | + a.dateUpload!.compareTo(b.dateUpload!); + }, + ); + } else if (sortChapter == 2) { + chapters.sort( + (a, b) { + return a.dateUpload!.compareTo(b.dateUpload!); + }, + ); + } + return chapterList; + } + Widget _buildWidget( {required List chapters, required bool reverse, required List chapterList, required bool isLongPressed}) { - return Scaffold( - extendBodyBehindAppBar: true, - appBar: PreferredSize( - preferredSize: Size.fromHeight(AppBar().preferredSize.height), - child: Consumer( - builder: (context, ref, child) { - final isNotFiltering = ref.watch( - chapterFilterResultStateProvider( - mangaId: widget.manga!.id!)); - final isLongPressed = ref.watch(isLongPressedStateProvider); - // final chapterList = ref.watch(chaptersListStateProvider); - return isLongPressed - ? Container( - color: Theme.of(context).scaffoldBackgroundColor, - child: AppBar( - title: Text(chapterList.length.toString()), - backgroundColor: - primaryColor(context).withOpacity(0.2), - leading: IconButton( - onPressed: () { - ref - .read(chaptersListStateProvider.notifier) - .clear(); - - ref - .read(isLongPressedStateProvider.notifier) - .update(!isLongPressed); - }, - icon: const Icon(Icons.clear)), - actions: [ - IconButton( - onPressed: () { - for (var chapter in chapters) { + return Stack( + children: [ + Consumer( + builder: (context, ref, child) { + return Positioned( + top: 0, + child: ref.watch(offetProvider) == 0.0 + ? Stack( + children: [ + cachedNetworkImage( + headers: headers(widget.manga!.source!), + imageUrl: widget.manga!.imageUrl!, + width: mediaWidth(context, 1), + height: 410, + fit: BoxFit.cover), + Container( + width: mediaWidth(context, 1), + height: 465, + color: Theme.of(context) + .scaffoldBackgroundColor + .withOpacity(0.9), + ), + ], + ) + : Container(), + ); + }, + ), + Scaffold( + backgroundColor: Colors.transparent, + extendBodyBehindAppBar: true, + appBar: PreferredSize( + preferredSize: Size.fromHeight(AppBar().preferredSize.height), + child: Consumer( + builder: (context, ref, child) { + final isNotFiltering = ref.watch( + chapterFilterResultStateProvider( + mangaId: widget.manga!.id!)); + final isLongPressed = ref.watch(isLongPressedStateProvider); + return isLongPressed + ? Container( + color: Theme.of(context).scaffoldBackgroundColor, + child: AppBar( + title: Text(chapterList.length.toString()), + backgroundColor: + primaryColor(context).withOpacity(0.2), + leading: IconButton( + onPressed: () { ref - .read(chaptersListStateProvider - .notifier) - .selectAll(chapter); - } - }, - icon: const Icon(Icons.select_all)), - IconButton( - onPressed: () { - if (chapters.length == - chapterList.length) { - for (var chapter in chapters) { - ref - .read(chaptersListStateProvider - .notifier) - .selectSome(chapter); - } + .read( + chaptersListStateProvider.notifier) + .clear(); + ref .read( isLongPressedStateProvider.notifier) - .update(false); - } else { - for (var chapter in chapters) { - ref - .read(chaptersListStateProvider - .notifier) - .selectSome(chapter); - } - } - }, - icon: const Icon(Icons.flip_to_back_rounded)), - ], - ), - ) - : Stack( - children: [ - Positioned( - top: 0, - child: Stack( - children: [ - cachedNetworkImage( - headers: headers(widget.manga!.source!), - imageUrl: widget.manga!.imageUrl!, - width: mediaWidth(context, 1), - height: 410, - fit: BoxFit.cover), - Container( - width: mediaWidth(context, 1), - height: 465, - color: Theme.of(context) - .scaffoldBackgroundColor - .withOpacity(0.9), - ), - ], - )), - AppBar( + .update(!isLongPressed); + }, + icon: const Icon(Icons.clear)), + actions: [ + IconButton( + onPressed: () { + for (var chapter in chapters) { + ref + .read(chaptersListStateProvider + .notifier) + .selectAll(chapter); + } + }, + icon: const Icon(Icons.select_all)), + IconButton( + onPressed: () { + if (chapters.length == + chapterList.length) { + for (var chapter in chapters) { + ref + .read(chaptersListStateProvider + .notifier) + .selectSome(chapter); + } + ref + .read(isLongPressedStateProvider + .notifier) + .update(false); + } else { + for (var chapter in chapters) { + ref + .read(chaptersListStateProvider + .notifier) + .selectSome(chapter); + } + } + }, + icon: + const Icon(Icons.flip_to_back_rounded)), + ], + ), + ) + : AppBar( title: ref.watch(offetProvider) > 200 ? Text( widget.manga!.name!, @@ -269,130 +288,155 @@ class _MangaDetailViewState extends ConsumerState color: isNotFiltering ? null : Colors.yellow, )), - PopupMenuButton( - itemBuilder: (context) { - return [ - if (widget.manga!.favorite) - const PopupMenuItem( - value: 0, - child: Text("Edit categories")), - if (widget.manga!.favorite) - const PopupMenuItem( - value: 0, child: Text("Migrate")), - const PopupMenuItem( - value: 0, child: Text("Share")), - ]; - }, - onSelected: (value) {}), + PopupMenuButton(itemBuilder: (context) { + return [ + if (widget.manga!.favorite) + const PopupMenuItem( + value: 0, + child: Text("Edit categories")), + if (widget.manga!.favorite) + const PopupMenuItem( + value: 0, child: Text("Migrate")), + const PopupMenuItem( + value: 0, child: Text("Share")), + ]; + }, onSelected: (value) { + if (value == 0) { + context.push("/categories"); + } + }), ], - ) - ], - ); - }, - )), - body: SafeArea( - child: DraggableScrollbar( - heightScrollThumb: 48.0, - backgroundColor: primaryColor(context), - scrollThumbBuilder: - (backgroundColor, thumbAnimation, labelAnimation, height, - {labelConstraints, labelText}) { - return FadeTransition( - opacity: thumbAnimation, - child: Container( - decoration: BoxDecoration( - color: backgroundColor, - borderRadius: BorderRadius.circular(20)), - height: height, - width: 8.0, + ); + }, + )), + body: SafeArea( + child: DraggableScrollbar( + padding: const EdgeInsets.only(right: 7), + heightScrollThumb: 48.0, + backgroundColor: primaryColor(context), + scrollThumbBuilder: + (backgroundColor, thumbAnimation, labelAnimation, height, + {labelConstraints, labelText}) { + return FadeTransition( + opacity: thumbAnimation, + child: Container( + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: BorderRadius.circular(20)), + height: height, + width: 8.0, + ), + ); + }, + scrollbarTimeToFade: const Duration(seconds: 2), + controller: _scrollController, + child: ListView.builder( + controller: _scrollController, + padding: const EdgeInsets.only(top: 0, bottom: 60), + itemCount: chapters.length + 1, + itemBuilder: (context, index) { + int finalIndex = index - 1; + if (index == 0) { + return _bodyContainer(chapterLength: chapters.length); + } + int reverseIndex = chapters.length - + chapters.reversed.toList().indexOf( + chapters.reversed.toList()[finalIndex]) - + 1; + final indexx = reverse ? reverseIndex : finalIndex; + return ChapterListTileWidget( + chapter: chapters[indexx], + chapterList: chapterList, + ); + })), + ), + bottomNavigationBar: AnimatedContainer( + curve: Curves.easeIn, + decoration: BoxDecoration( + color: primaryColor(context).withOpacity(0.2), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20))), + duration: const Duration(milliseconds: 100), + height: isLongPressed ? 70 : 0, + width: mediaWidth(context, 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: () { + ref + .read(chapterSetIsBookmarkStateProvider( + manga: widget.manga!) + .notifier) + .set(); + }, + child: Icon(Icons.bookmark_add_outlined, + color: Theme.of(context) + .textTheme + .bodyLarge! + .color)), ), - ); - }, - scrollbarTimeToFade: const Duration(seconds: 2), - controller: _scrollController, - child: ListView.builder( - controller: _scrollController, - padding: const EdgeInsets.only(top: 0, bottom: 60), - itemCount: chapters.length + 1, - itemBuilder: (context, index) { - int finalIndex = index - 1; - if (index == 0) { - return _bodyContainer(chapterLength: chapters.length); - } - int reverseIndex = chapters.length - - chapters.reversed - .toList() - .indexOf(chapters.reversed.toList()[finalIndex]) - - 1; - final indexx = reverse ? reverseIndex : finalIndex; - return ChapterListTileWidget( - chapter: chapters[indexx], - chapterList: chapterList, - ); - }))), - bottomNavigationBar: AnimatedContainer( - curve: Curves.easeIn, - decoration: BoxDecoration( - color: primaryColor(context).withOpacity(0.2), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(20), topRight: Radius.circular(20))), - duration: const Duration(milliseconds: 100), - height: isLongPressed ? 70 : 0, - width: mediaWidth(context, 1), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 0, backgroundColor: Colors.transparent), - onPressed: () { - ref - .read(chapterSetIsBookmarkStateProvider( - manga: widget.manga!) - .notifier) - .set(); - }, - child: const Icon(Icons.bookmark_add_outlined)), - ), + ), + Expanded( + child: SizedBox( + height: 70, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0, + backgroundColor: Colors.transparent, + shadowColor: Colors.transparent, + ), + onPressed: () { + ref + .read(chapterSetIsReadStateProvider( + manga: widget.manga!) + .notifier) + .set(); + }, + child: Icon(Icons.done_all_sharp, + color: Theme.of(context) + .textTheme + .bodyLarge! + .color!)), + ), + ), + Expanded( + child: SizedBox( + height: 70, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0, + backgroundColor: Colors.transparent, + shadowColor: Colors.transparent, + ), + onPressed: () { + ref + .read(chapterSetDownloadStateProvider( + manga: widget.manga!) + .notifier) + .set(); + }, + child: Icon( + Icons.download_outlined, + color: + Theme.of(context).textTheme.bodyLarge!.color!, + )), + ), + ) + ], ), - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 0, backgroundColor: Colors.transparent), - onPressed: () { - ref - .read(chapterSetIsReadStateProvider( - manga: widget.manga!) - .notifier) - .set(); - }, - child: const Icon(Icons.done_all_sharp)), - ), - ), - Expanded( - child: SizedBox( - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 0, backgroundColor: Colors.transparent), - onPressed: () { - ref - .read(chapterSetDownloadStateProvider( - manga: widget.manga!) - .notifier) - .set(); - }, - child: const Icon(Icons.download_outlined)), - ), - ) - ], - ), - )); + )), + ], + ); } _showDraggableMenu() { @@ -469,26 +513,22 @@ class _MangaDetailViewState extends ConsumerState }), Consumer(builder: (context, ref, chil) { final reverse = ref - .read(reverseChapterStateProvider( + .read(sortChapterStateProvider( mangaId: widget.manga!.id!) .notifier) .isReverse(); final reverseChapter = ref.watch( - reverseChapterStateProvider( + sortChapterStateProvider( mangaId: widget.manga!.id!)); return Column( children: [ for (var i = 0; i < 3; i++) ListTileChapterSort( - label: i == 0 - ? "By source" - : i == 1 - ? "By chapter number" - : "By upload date", + label: _getSortNameByIndex(i), reverse: reverse, onTap: () { ref - .read(reverseChapterStateProvider( + .read(sortChapterStateProvider( mangaId: widget.manga!.id!) .notifier) .set(i); @@ -502,6 +542,7 @@ class _MangaDetailViewState extends ConsumerState return Column( children: [ RadioListTile( + dense: true, title: const Text("Source title"), value: "e", groupValue: "e", @@ -509,6 +550,7 @@ class _MangaDetailViewState extends ConsumerState onChanged: (value) {}, ), RadioListTile( + dense: true, title: const Text("Chapter number"), value: "ej", groupValue: "e", @@ -528,6 +570,15 @@ class _MangaDetailViewState extends ConsumerState ); } + String _getSortNameByIndex(int index) { + if (index == 0) { + return "By source"; + } else if (index == 1) { + return "By chapter number"; + } + return "By upload date"; + } + Widget _bodyContainer({required int chapterLength}) { return Stack( children: [ @@ -537,7 +588,7 @@ class _MangaDetailViewState extends ConsumerState headers: headers(widget.manga!.source!), imageUrl: widget.manga!.imageUrl!, width: mediaWidth(context, 1), - height: 300, + height: 250, fit: BoxFit.cover)), Container( height: 300, @@ -565,7 +616,7 @@ class _MangaDetailViewState extends ConsumerState ], ), ), - _actionConstructor(), + _actionFavouriteAndWebview(), Container( color: Theme.of(context).scaffoldBackgroundColor, child: Column( @@ -655,7 +706,6 @@ class _MangaDetailViewState extends ConsumerState ], ), )), - // log Column( children: [ //Description @@ -690,7 +740,7 @@ class _MangaDetailViewState extends ConsumerState Widget _coverCard() { return Positioned( top: 20, - left: 20, + left: 13, child: GestureDetector( onTap: () {}, child: SizedBox( @@ -713,11 +763,11 @@ class _MangaDetailViewState extends ConsumerState Widget _titles() { return Positioned( top: 60, - left: 30, + left: 23, child: Container( alignment: Alignment.centerLeft, padding: const EdgeInsets.only(left: 100), - width: MediaQuery.of(context).size.width * 0.9, + width: MediaQuery.of(context).size.width * 1, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -732,7 +782,7 @@ class _MangaDetailViewState extends ConsumerState ); } - Widget _actionConstructor() { + Widget _actionFavouriteAndWebview() { return Container( color: Theme.of(context).scaffoldBackgroundColor, child: Row( diff --git a/lib/views/manga/detail/manga_details_view.dart b/lib/views/manga/detail/manga_details_view.dart index 016528cc..69e87718 100644 --- a/lib/views/manga/detail/manga_details_view.dart +++ b/lib/views/manga/detail/manga_details_view.dart @@ -6,7 +6,6 @@ import 'package:isar/isar.dart'; import 'package:mangayomi/main.dart'; import 'package:mangayomi/models/category.dart'; import 'package:mangayomi/views/history/providers/isar_providers.dart'; -import 'package:mangayomi/views/manga/detail/providers/isar_providers.dart'; import 'package:mangayomi/views/manga/reader/providers/push_router.dart'; import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/utils/colors.dart'; @@ -20,10 +19,8 @@ import 'package:mangayomi/views/widgets/progress_center.dart'; class MangaDetailsView extends ConsumerStatefulWidget { final Manga manga; - final Function(bool) isFavorite; const MangaDetailsView({ super.key, - required this.isFavorite, required this.manga, }); @@ -32,35 +29,9 @@ class MangaDetailsView extends ConsumerStatefulWidget { } class _MangaDetailsViewState extends ConsumerState { - bool isFavorite = false; - bool _isOk = false; - bool isGplay = false; - _checkFavorite(bool i) async { - if (!_isOk) { - await Future.delayed(const Duration(milliseconds: 30)); - if (mounted) { - setState(() { - widget.isFavorite(i); - isFavorite = i; - _isOk = true; - }); - } - } - } - - _setFavorite(bool i) async { - if (mounted) { - setState(() { - widget.isFavorite(i); - isFavorite = i; - }); - } - } - @override Widget build(BuildContext context) { final history = ref.watch(getAllHistoryStreamProvider); - _checkFavorite(widget.manga.favorite); return Scaffold( floatingActionButton: ref.watch(isLongPressedStateProvider) == true ? null @@ -113,7 +84,7 @@ class _MangaDetailsViewState extends ConsumerState { duration: const Duration(milliseconds: 200), child: const Text( - "Continue", + "Resume", overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 14, color: Colors.white), @@ -224,12 +195,12 @@ class _MangaDetailsViewState extends ConsumerState { backgroundColor: Theme.of(context).scaffoldBackgroundColor, elevation: 0), - onPressed: () async { - _setFavorite(false); + onPressed: () { final model = widget.manga; - await isar.writeTxn(() async { + isar.writeTxnSync(() { model.favorite = false; - await isar.mangas.put(model); + model.dateAdded = 0; + isar.mangas.putSync(model); }); }, child: Column( @@ -264,11 +235,11 @@ class _MangaDetailsViewState extends ConsumerState { if (checkCategoryList) { _openCategory(widget.manga); } else { - _setFavorite(true); final model = widget.manga; - await isar.writeTxn(() async { + isar.writeTxnSync(() { model.favorite = true; - await isar.mangas.put(model); + model.dateAdded = DateTime.now().millisecondsSinceEpoch; + isar.mangas.putSync(model); }); } }, @@ -367,13 +338,12 @@ class _MangaDetailsViewState extends ConsumerState { width: 15, ), TextButton( - onPressed: () async { - _setFavorite(true); + onPressed: () { final model = widget.manga; - await isar.writeTxn(() async { + isar.writeTxnSync(() { model.favorite = true; model.categories = categoryIds; - await isar.mangas.put(model); + isar.mangas.putSync(model); }); if (mounted) { Navigator.pop(context); diff --git a/lib/views/manga/detail/manga_reader_detail.dart b/lib/views/manga/detail/manga_reader_detail.dart index bc3f71cf..03d2b41e 100644 --- a/lib/views/manga/detail/manga_reader_detail.dart +++ b/lib/views/manga/detail/manga_reader_detail.dart @@ -25,7 +25,6 @@ class MangaReaderDetail extends ConsumerStatefulWidget { } class _MangaReaderDetailState extends ConsumerState { - bool _isFavorite = false; @override void initState() { SystemChrome.setPreferredOrientations([ @@ -51,60 +50,54 @@ class _MangaReaderDetailState extends ConsumerState { data: (modelManga) { return RefreshIndicator( onRefresh: () async { - if (_isFavorite) { - bool isOk = false; - ref - .watch(getMangaDetailProvider( - imageUrl: modelManga.imageUrl!, - lang: modelManga.lang!, - title: modelManga.name!, - source: modelManga.source!, - url: modelManga.link!) - .future) - .then((value) async { - if (value.chapters.isNotEmpty && - value.chapters.length > modelManga.chapters.length) { - await isar.writeTxn(() async { - int newChapsIndex = - value.chapters.length - modelManga.chapters.length; - for (var i = 0; i < newChapsIndex; i++) { - final chapters = Chapter( - name: value.chapters[i].name, - url: value.chapters[i].url, - dateUpload: value.chapters[i].dateUpload, - isBookmarked: false, - scanlator: value.chapters[i].scanlator, - isRead: false, - lastPageRead: '', - mangaId: modelManga.id) - ..manga.value = modelManga; - await isar.chapters.put(chapters); - await chapters.manga.save(); - } - }); - } - if (mounted) { - setState(() { - isOk = true; - }); - } - }); - await Future.doWhile(() async { - await Future.delayed(const Duration(seconds: 1)); - if (isOk == true) { - return false; - } - return true; - }); - } + bool isOk = false; + ref + .watch(getMangaDetailProvider( + imageUrl: modelManga.imageUrl!, + lang: modelManga.lang!, + title: modelManga.name!, + source: modelManga.source!, + url: modelManga.link!) + .future) + .then((value) async { + if (value.chapters.isNotEmpty && + value.chapters.length > modelManga.chapters.length) { + await isar.writeTxn(() async { + int newChapsIndex = + value.chapters.length - modelManga.chapters.length; + modelManga.lastUpdate = DateTime.now().millisecondsSinceEpoch; + for (var i = 0; i < newChapsIndex; i++) { + final chapters = Chapter( + name: value.chapters[i].name, + url: value.chapters[i].url, + dateUpload: value.chapters[i].dateUpload, + isBookmarked: false, + scanlator: value.chapters[i].scanlator, + isRead: false, + lastPageRead: '', + mangaId: modelManga.id) + ..manga.value = modelManga; + await isar.chapters.put(chapters); + await chapters.manga.save(); + } + }); + } + if (mounted) { + setState(() { + isOk = true; + }); + } + }); + await Future.doWhile(() async { + await Future.delayed(const Duration(seconds: 1)); + if (isOk == true) { + return false; + } + return true; + }); }, child: MangaDetailsView( manga: modelManga!, - isFavorite: (value) { - setState(() { - _isFavorite = value; - }); - }, ), ); }, diff --git a/lib/views/manga/detail/providers/state_providers.dart b/lib/views/manga/detail/providers/state_providers.dart index 3540a88b..cc852328 100644 --- a/lib/views/manga/detail/providers/state_providers.dart +++ b/lib/views/manga/detail/providers/state_providers.dart @@ -1,6 +1,3 @@ -import 'dart:convert'; -import 'dart:developer'; - import 'package:mangayomi/main.dart'; import 'package:mangayomi/models/chapter.dart'; import 'package:mangayomi/models/manga.dart'; @@ -9,26 +6,6 @@ import 'package:mangayomi/views/manga/download/providers/download_provider.dart' import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'state_providers.g.dart'; -@riverpod -class ChapterModelState extends _$ChapterModelState { - @override - Chapter build() { - return Chapter( - name: "", - url: "", - dateUpload: "", - isBookmarked: false, - scanlator: "", - isRead: false, - lastPageRead: "", - mangaId: null); - } - - void update(Chapter chapters) { - state = chapters; - } -} - @riverpod class ChaptersListState extends _$ChaptersListState { @override @@ -98,10 +75,10 @@ class IsExtendedState extends _$IsExtendedState { } @riverpod -class ReverseChapterState extends _$ReverseChapterState { +class SortChapterState extends _$SortChapterState { @override dynamic build({required int mangaId}) { - return ref.watch(hiveBoxSettingsProvider).get("$mangaId-reverseChapterMap", + return ref.watch(hiveBoxSettingsProvider).get("$mangaId-sortChapterMap", defaultValue: {"reverse": false, "index": 2}); } @@ -110,33 +87,13 @@ class ReverseChapterState extends _$ReverseChapterState { "reverse": state['index'] == index ? !reverse : reverse, "index": index }; - ref.watch(hiveBoxSettingsProvider).put("$mangaId-reverseChapterMap", value); + ref.watch(hiveBoxSettingsProvider).put("$mangaId-sortChapterMap", value); state = value; } void set(int index) { - final reverse = ref - .read(reverseChapterStateProvider(mangaId: mangaId).notifier) - .isReverse(); - final sortBySource = ref.watch(sortBySourceStateProvider(mangaId: mangaId)); - final sortByChapterNumber = - ref.watch(sortByChapterNumberStateProvider(mangaId: mangaId)); - final sortByUploadDate = - ref.watch(sortByUploadDateStateProvider(mangaId: mangaId)); + final reverse = isReverse(); update(reverse, index); - if (index == 0) { - ref - .read(sortBySourceStateProvider(mangaId: mangaId).notifier) - .update(!sortBySource); - } else if (index == 1) { - ref - .read(sortByChapterNumberStateProvider(mangaId: mangaId).notifier) - .update(!sortByChapterNumber); - } else { - ref - .read(sortByUploadDateStateProvider(mangaId: mangaId).notifier) - .update(!sortByUploadDate); - } } bool isReverse() { @@ -260,20 +217,20 @@ class ChapterFilterResultState extends _$ChapterFilterResultState { Manga mangaWithNewChapValue( {required Manga manga, required List? chapters}) { return Manga( - imageUrl: manga.imageUrl, - name: manga.name, - genre: manga.genre, - author: manga.author, - description: manga.description, - status: manga.status, - favorite: manga.favorite, - link: manga.link, - source: manga.source, - lang: manga.lang, - dateAdded: manga.dateAdded, - lastUpdate: manga.lastUpdate, - categories: manga.categories, - lastRead: manga.lastRead); + imageUrl: manga.imageUrl, + name: manga.name, + genre: manga.genre, + author: manga.author, + description: manga.description, + status: manga.status, + favorite: manga.favorite, + link: manga.link, + source: manga.source, + lang: manga.lang, + dateAdded: manga.dateAdded, + lastUpdate: manga.lastUpdate, + categories: manga.categories, + ); } @riverpod @@ -302,7 +259,7 @@ class ChapterSetIsReadState extends _$ChapterSetIsReadState { set() { final chapters = ref.watch(chaptersListStateProvider); - isar.writeTxnSync(() async { + isar.writeTxnSync(() { for (var chapter in chapters) { chapter.isRead = !chapter.isRead!; isar.chapters.putSync(chapter..manga.value = manga); @@ -336,54 +293,3 @@ class ChapterSetDownloadState extends _$ChapterSetDownloadState { ref.read(chaptersListStateProvider.notifier).clear(); } } - -@riverpod -class SortByUploadDateState extends _$SortByUploadDateState { - @override - bool build({required int mangaId}) { - return ref - .watch(hiveBoxSettingsProvider) - .get("$mangaId-sortByUploadDateChapter", defaultValue: false); - } - - void update(bool value) { - ref - .watch(hiveBoxSettingsProvider) - .put("$mangaId-sortByUploadDateChapter", value); - state = value; - } -} - -@riverpod -class SortBySourceState extends _$SortBySourceState { - @override - bool build({required int mangaId}) { - return ref - .watch(hiveBoxSettingsProvider) - .get("$mangaId-sortBySourceChapter", defaultValue: false); - } - - void update(bool value) { - ref - .watch(hiveBoxSettingsProvider) - .put("$mangaId-sortBySourceChapter", value); - state = value; - } -} - -@riverpod -class SortByChapterNumberState extends _$SortByChapterNumberState { - @override - bool build({required int mangaId}) { - return ref - .watch(hiveBoxSettingsProvider) - .get("$mangaId-sortByChapterNumberChapter", defaultValue: false); - } - - void update(bool value) { - ref - .watch(hiveBoxSettingsProvider) - .put("$mangaId-sortByChapterNumberChapter", value); - state = value; - } -} diff --git a/lib/views/manga/detail/providers/state_providers.g.dart b/lib/views/manga/detail/providers/state_providers.g.dart index 6ad8745a..05e03b62 100644 --- a/lib/views/manga/detail/providers/state_providers.g.dart +++ b/lib/views/manga/detail/providers/state_providers.g.dart @@ -6,22 +6,6 @@ part of 'state_providers.dart'; // RiverpodGenerator // ************************************************************************** -String _$chapterModelStateHash() => r'e1b191e6176992d1fc5f04dcb1e25e211f8a69a7'; - -/// See also [ChapterModelState]. -@ProviderFor(ChapterModelState) -final chapterModelStateProvider = - AutoDisposeNotifierProvider.internal( - ChapterModelState.new, - name: r'chapterModelStateProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$chapterModelStateHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$ChapterModelState = AutoDisposeNotifier; String _$chaptersListStateHash() => r'251609214d127964e84d4616d2c3a7afa4fd80b4'; /// See also [ChaptersListState]. @@ -71,8 +55,7 @@ final isExtendedStateProvider = ); typedef _$IsExtendedState = AutoDisposeNotifier; -String _$reverseChapterStateHash() => - r'8e3db99ef54d27d37e9af35ef9d822a4f5dc6eaf'; +String _$sortChapterStateHash() => r'38b241e06866613a0c34d306da0d855f57af3862'; /// Copied from Dart SDK class _SystemHash { @@ -95,7 +78,7 @@ class _SystemHash { } } -abstract class _$ReverseChapterState +abstract class _$SortChapterState extends BuildlessAutoDisposeNotifier { late final int mangaId; @@ -104,27 +87,27 @@ abstract class _$ReverseChapterState }); } -/// See also [ReverseChapterState]. -@ProviderFor(ReverseChapterState) -const reverseChapterStateProvider = ReverseChapterStateFamily(); +/// See also [SortChapterState]. +@ProviderFor(SortChapterState) +const sortChapterStateProvider = SortChapterStateFamily(); -/// See also [ReverseChapterState]. -class ReverseChapterStateFamily extends Family { - /// See also [ReverseChapterState]. - const ReverseChapterStateFamily(); +/// See also [SortChapterState]. +class SortChapterStateFamily extends Family { + /// See also [SortChapterState]. + const SortChapterStateFamily(); - /// See also [ReverseChapterState]. - ReverseChapterStateProvider call({ + /// See also [SortChapterState]. + SortChapterStateProvider call({ required int mangaId, }) { - return ReverseChapterStateProvider( + return SortChapterStateProvider( mangaId: mangaId, ); } @override - ReverseChapterStateProvider getProviderOverride( - covariant ReverseChapterStateProvider provider, + SortChapterStateProvider getProviderOverride( + covariant SortChapterStateProvider provider, ) { return call( mangaId: provider.mangaId, @@ -143,33 +126,33 @@ class ReverseChapterStateFamily extends Family { _allTransitiveDependencies; @override - String? get name => r'reverseChapterStateProvider'; + String? get name => r'sortChapterStateProvider'; } -/// See also [ReverseChapterState]. -class ReverseChapterStateProvider - extends AutoDisposeNotifierProviderImpl { - /// See also [ReverseChapterState]. - ReverseChapterStateProvider({ +/// See also [SortChapterState]. +class SortChapterStateProvider + extends AutoDisposeNotifierProviderImpl { + /// See also [SortChapterState]. + SortChapterStateProvider({ required this.mangaId, }) : super.internal( - () => ReverseChapterState()..mangaId = mangaId, - from: reverseChapterStateProvider, - name: r'reverseChapterStateProvider', + () => SortChapterState()..mangaId = mangaId, + from: sortChapterStateProvider, + name: r'sortChapterStateProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') ? null - : _$reverseChapterStateHash, - dependencies: ReverseChapterStateFamily._dependencies, + : _$sortChapterStateHash, + dependencies: SortChapterStateFamily._dependencies, allTransitiveDependencies: - ReverseChapterStateFamily._allTransitiveDependencies, + SortChapterStateFamily._allTransitiveDependencies, ); final int mangaId; @override bool operator ==(Object other) { - return other is ReverseChapterStateProvider && other.mangaId == mangaId; + return other is SortChapterStateProvider && other.mangaId == mangaId; } @override @@ -182,7 +165,7 @@ class ReverseChapterStateProvider @override dynamic runNotifierBuild( - covariant ReverseChapterState notifier, + covariant SortChapterState notifier, ) { return notifier.build( mangaId: mangaId, @@ -687,7 +670,7 @@ class ChapterSetIsBookmarkStateProvider extends AutoDisposeNotifierProviderImpl< } String _$chapterSetIsReadStateHash() => - r'a8436a6cdac49c241483b4260f41c7b4045c060d'; + r'b60dfd136e52743fdde9067d3e366d90d49dd9b4'; abstract class _$ChapterSetIsReadState extends BuildlessAutoDisposeNotifier { @@ -881,297 +864,4 @@ class ChapterSetDownloadStateProvider ); } } - -String _$sortByUploadDateStateHash() => - r'5dd31fd2ee8fbaa5f3f5c8e2397842fa60af8b46'; - -abstract class _$SortByUploadDateState - extends BuildlessAutoDisposeNotifier { - late final int mangaId; - - bool build({ - required int mangaId, - }); -} - -/// See also [SortByUploadDateState]. -@ProviderFor(SortByUploadDateState) -const sortByUploadDateStateProvider = SortByUploadDateStateFamily(); - -/// See also [SortByUploadDateState]. -class SortByUploadDateStateFamily extends Family { - /// See also [SortByUploadDateState]. - const SortByUploadDateStateFamily(); - - /// See also [SortByUploadDateState]. - SortByUploadDateStateProvider call({ - required int mangaId, - }) { - return SortByUploadDateStateProvider( - mangaId: mangaId, - ); - } - - @override - SortByUploadDateStateProvider getProviderOverride( - covariant SortByUploadDateStateProvider provider, - ) { - return call( - mangaId: provider.mangaId, - ); - } - - 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'sortByUploadDateStateProvider'; -} - -/// See also [SortByUploadDateState]. -class SortByUploadDateStateProvider - extends AutoDisposeNotifierProviderImpl { - /// See also [SortByUploadDateState]. - SortByUploadDateStateProvider({ - required this.mangaId, - }) : super.internal( - () => SortByUploadDateState()..mangaId = mangaId, - from: sortByUploadDateStateProvider, - name: r'sortByUploadDateStateProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$sortByUploadDateStateHash, - dependencies: SortByUploadDateStateFamily._dependencies, - allTransitiveDependencies: - SortByUploadDateStateFamily._allTransitiveDependencies, - ); - - final int mangaId; - - @override - bool operator ==(Object other) { - return other is SortByUploadDateStateProvider && other.mangaId == mangaId; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, mangaId.hashCode); - - return _SystemHash.finish(hash); - } - - @override - bool runNotifierBuild( - covariant SortByUploadDateState notifier, - ) { - return notifier.build( - mangaId: mangaId, - ); - } -} - -String _$sortBySourceStateHash() => r'b254edd8c935aafd1cb41e4841134d177b58ba62'; - -abstract class _$SortBySourceState extends BuildlessAutoDisposeNotifier { - late final int mangaId; - - bool build({ - required int mangaId, - }); -} - -/// See also [SortBySourceState]. -@ProviderFor(SortBySourceState) -const sortBySourceStateProvider = SortBySourceStateFamily(); - -/// See also [SortBySourceState]. -class SortBySourceStateFamily extends Family { - /// See also [SortBySourceState]. - const SortBySourceStateFamily(); - - /// See also [SortBySourceState]. - SortBySourceStateProvider call({ - required int mangaId, - }) { - return SortBySourceStateProvider( - mangaId: mangaId, - ); - } - - @override - SortBySourceStateProvider getProviderOverride( - covariant SortBySourceStateProvider provider, - ) { - return call( - mangaId: provider.mangaId, - ); - } - - 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'sortBySourceStateProvider'; -} - -/// See also [SortBySourceState]. -class SortBySourceStateProvider - extends AutoDisposeNotifierProviderImpl { - /// See also [SortBySourceState]. - SortBySourceStateProvider({ - required this.mangaId, - }) : super.internal( - () => SortBySourceState()..mangaId = mangaId, - from: sortBySourceStateProvider, - name: r'sortBySourceStateProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$sortBySourceStateHash, - dependencies: SortBySourceStateFamily._dependencies, - allTransitiveDependencies: - SortBySourceStateFamily._allTransitiveDependencies, - ); - - final int mangaId; - - @override - bool operator ==(Object other) { - return other is SortBySourceStateProvider && other.mangaId == mangaId; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, mangaId.hashCode); - - return _SystemHash.finish(hash); - } - - @override - bool runNotifierBuild( - covariant SortBySourceState notifier, - ) { - return notifier.build( - mangaId: mangaId, - ); - } -} - -String _$sortByChapterNumberStateHash() => - r'c3f088a3235a0c0305b8c33376fa430950902fc6'; - -abstract class _$SortByChapterNumberState - extends BuildlessAutoDisposeNotifier { - late final int mangaId; - - bool build({ - required int mangaId, - }); -} - -/// See also [SortByChapterNumberState]. -@ProviderFor(SortByChapterNumberState) -const sortByChapterNumberStateProvider = SortByChapterNumberStateFamily(); - -/// See also [SortByChapterNumberState]. -class SortByChapterNumberStateFamily extends Family { - /// See also [SortByChapterNumberState]. - const SortByChapterNumberStateFamily(); - - /// See also [SortByChapterNumberState]. - SortByChapterNumberStateProvider call({ - required int mangaId, - }) { - return SortByChapterNumberStateProvider( - mangaId: mangaId, - ); - } - - @override - SortByChapterNumberStateProvider getProviderOverride( - covariant SortByChapterNumberStateProvider provider, - ) { - return call( - mangaId: provider.mangaId, - ); - } - - 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'sortByChapterNumberStateProvider'; -} - -/// See also [SortByChapterNumberState]. -class SortByChapterNumberStateProvider - extends AutoDisposeNotifierProviderImpl { - /// See also [SortByChapterNumberState]. - SortByChapterNumberStateProvider({ - required this.mangaId, - }) : super.internal( - () => SortByChapterNumberState()..mangaId = mangaId, - from: sortByChapterNumberStateProvider, - name: r'sortByChapterNumberStateProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$sortByChapterNumberStateHash, - dependencies: SortByChapterNumberStateFamily._dependencies, - allTransitiveDependencies: - SortByChapterNumberStateFamily._allTransitiveDependencies, - ); - - final int mangaId; - - @override - bool operator ==(Object other) { - return other is SortByChapterNumberStateProvider && - other.mangaId == mangaId; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, mangaId.hashCode); - - return _SystemHash.finish(hash); - } - - @override - bool runNotifierBuild( - covariant SortByChapterNumberState notifier, - ) { - return notifier.build( - mangaId: mangaId, - ); - } -} // ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions diff --git a/lib/views/manga/detail/widgets/chapter_list_tile_widget.dart b/lib/views/manga/detail/widgets/chapter_list_tile_widget.dart index 70caec11..27d2d2d7 100644 --- a/lib/views/manga/detail/widgets/chapter_list_tile_widget.dart +++ b/lib/views/manga/detail/widgets/chapter_list_tile_widget.dart @@ -40,19 +40,17 @@ class ChapterListTileWidget extends ConsumerWidget { onLongPress: () { if (!isLongPressed) { ref.read(chaptersListStateProvider.notifier).update(chapter); - ref.read(chapterModelStateProvider.notifier).update(chapter); + ref .read(isLongPressedStateProvider.notifier) .update(!isLongPressed); } else { ref.read(chaptersListStateProvider.notifier).update(chapter); - ref.read(chapterModelStateProvider.notifier).update(chapter); } }, onTap: () async { if (isLongPressed) { ref.read(chaptersListStateProvider.notifier).update(chapter); - ref.read(chapterModelStateProvider.notifier).update(chapter); } else { pushMangaReaderView(context: context, chapter: chapter); } @@ -62,7 +60,7 @@ class ChapterListTileWidget extends ConsumerWidget { chapter.isBookmarked! ? Icon( Icons.bookmark, - size: 15, + size: 16, color: primaryColor(context), ) : Container(), @@ -81,6 +79,7 @@ class ChapterListTileWidget extends ConsumerWidget { dateFormat(chapter.dateUpload!, ref: ref), style: const TextStyle(fontSize: 11), ), + if(!chapter.isRead!) if (chapter.lastPageRead!.isNotEmpty && chapter.lastPageRead != "1") Row( children: [ diff --git a/lib/views/manga/reader/providers/reader_controller_provider.dart b/lib/views/manga/reader/providers/reader_controller_provider.dart index 6b40510a..a96d7b0f 100644 --- a/lib/views/manga/reader/providers/reader_controller_provider.dart +++ b/lib/views/manga/reader/providers/reader_controller_provider.dart @@ -106,6 +106,11 @@ class ReaderController extends _$ReaderController { void setMangaHistoryUpdate() { final incognitoMode = ref.watch(incognitoModeStateProvider); if (!incognitoMode) { + isar.writeTxnSync(() { + Manga? manga = chapter.manga.value; + manga!.lastRead = DateTime.now().millisecondsSinceEpoch; + isar.mangas.putSync(manga); + }); History? history; final empty = @@ -205,9 +210,11 @@ class ReaderController extends _$ReaderController { int getPageIndex() { final incognitoMode = ref.watch(incognitoModeStateProvider); if (!incognitoMode) { - return ref.watch(hiveBoxMangaProvider).get( - "${getSourceName()}/${getMangaName()}/${getChapterTitle()}-page_index", - defaultValue: 0); + return chapter.isRead! + ? 0 + : ref.watch(hiveBoxMangaProvider).get( + "${getSourceName()}/${getMangaName()}/${getChapterTitle()}-page_index", + defaultValue: 0); } return 0; } diff --git a/lib/views/manga/reader/providers/reader_controller_provider.g.dart b/lib/views/manga/reader/providers/reader_controller_provider.g.dart index dbbb2d88..ec74e432 100644 --- a/lib/views/manga/reader/providers/reader_controller_provider.g.dart +++ b/lib/views/manga/reader/providers/reader_controller_provider.g.dart @@ -181,7 +181,7 @@ class CurrentIndexProvider } } -String _$readerControllerHash() => r'232cc4cde708015a3b4cdac481c61cff82076182'; +String _$readerControllerHash() => r'cd0b65db9c027393d7b0f6cf1b7000847623364d'; abstract class _$ReaderController extends BuildlessAutoDisposeNotifier { late final Chapter chapter; diff --git a/lib/views/widgets/cover_view_widget.dart b/lib/views/widgets/cover_view_widget.dart index 4353d1db..c562696d 100644 --- a/lib/views/widgets/cover_view_widget.dart +++ b/lib/views/widgets/cover_view_widget.dart @@ -13,7 +13,7 @@ class CoverViewWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(5), child: isComfortableGrid ? Column( children: [ diff --git a/lib/views/widgets/manga_image_card_widget.dart b/lib/views/widgets/manga_image_card_widget.dart index 3f940027..51d44a6d 100644 --- a/lib/views/widgets/manga_image_card_widget.dart +++ b/lib/views/widgets/manga_image_card_widget.dart @@ -28,16 +28,16 @@ class MangaImageCardWidget extends StatelessWidget { return GestureDetector( onTap: () { final manga = Manga( - imageUrl: getMangaDetailModel!.imageUrl, - name: getMangaDetailModel!.name, - genre: getMangaDetailModel!.genre, - author: getMangaDetailModel!.author, - status: getMangaDetailModel!.status, - description: getMangaDetailModel!.description, - link: getMangaDetailModel!.url, - source: getMangaDetailModel!.source, - lang: lang, - ); + imageUrl: getMangaDetailModel!.imageUrl, + name: getMangaDetailModel!.name, + genre: getMangaDetailModel!.genre, + author: getMangaDetailModel!.author, + status: getMangaDetailModel!.status, + description: getMangaDetailModel!.description, + link: getMangaDetailModel!.url, + source: getMangaDetailModel!.source, + lang: lang, + lastUpdate: DateTime.now().millisecondsSinceEpoch); final empty = isar.mangas .filter()