manga library diplay type, category **

This commit is contained in:
kodjodevf 2023-04-28 18:40:52 +01:00
parent 48b4c6b229
commit 088514dc17
28 changed files with 1428 additions and 274 deletions

View file

@ -8,8 +8,8 @@ class CategoriesModel extends HiveObject {
final int id;
@HiveField(1)
final String name;
@HiveField(2)
final List<ModelManga> listModelManga;
CategoriesModel(
{required this.id, required this.name, required this.listModelManga});
CategoriesModel({
required this.id,
required this.name,
});
}

View file

@ -19,20 +19,17 @@ class CategoriesModelAdapter extends TypeAdapter<CategoriesModel> {
return CategoriesModel(
id: fields[0] as int,
name: fields[1] as String,
listModelManga: (fields[2] as List).cast<ModelManga>(),
);
}
@override
void write(BinaryWriter writer, CategoriesModel obj) {
writer
..writeByte(3)
..writeByte(2)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.name)
..writeByte(2)
..write(obj.listModelManga);
..write(obj.name);
}
@override

View file

@ -47,7 +47,7 @@ class ModelManga extends HiveObject {
String? lastRead;
@HiveField(14)
int? category;
List<int>? categories;
ModelManga(
{required this.source,
@ -62,7 +62,7 @@ class ModelManga extends HiveObject {
required this.description,
required this.dateAdded,
required this.lastUpdate,
required this.category,
required this.categories,
required this.lastRead,
required this.chapters});
}

View file

@ -29,7 +29,7 @@ class ModelMangaAdapter extends TypeAdapter<ModelManga> {
description: fields[3] as String?,
dateAdded: fields[10] as int?,
lastUpdate: fields[11] as int?,
category: fields[14] as int?,
categories: (fields[14] as List?)?.cast<int>(),
lastRead: fields[13] as String?,
chapters: (fields[12] as List?)?.cast<ModelChapters>(),
);
@ -68,7 +68,7 @@ class ModelMangaAdapter extends TypeAdapter<ModelManga> {
..writeByte(13)
..write(obj.lastRead)
..writeByte(14)
..write(obj.category);
..write(obj.categories);
}
@override

View file

@ -7,7 +7,7 @@ part of 'get_manga_chapter_url.dart';
// **************************************************************************
String _$getMangaChapterUrlHash() =>
r'7645294b8966c7069d9c2e4528e1fee512bb1694';
r'af6919ddfaafdafc0e8c2fa0fa0f686dfccd934a';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -194,7 +194,7 @@ class _MangaGlobalImageCardState extends ConsumerState<MangaGlobalImageCard>
dateAdded: DateTime.now().microsecondsSinceEpoch,
lastUpdate: DateTime.now().microsecondsSinceEpoch,
chapters: data.chapters,
category: null,
categories: null,
lastRead: '');
if (mounted) {
context.push('/manga-reader/detail', extra: modelManga);

View file

@ -1,7 +1,10 @@
import 'dart:developer';
import 'package:draggable_menu/draggable_menu.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/categories.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/media_query.dart';
@ -24,124 +27,182 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
List<ModelManga> entries = [];
List<ModelManga> entriesFilter = [];
final _textEditingController = TextEditingController();
late TabController tabBarController;
int tabIndex = 0;
@override
Widget build(BuildContext context) {
final reverse = ref.watch(libraryReverseListStateProvider);
final showCategoryTabs = ref.watch(libraryShowCategoryTabsStateProvider);
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
.read(mangaFilterResultStateProvider(mangaList: entries).notifier)
.isNotFiltering();
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
title: isSearch
? null
: Text(
'Library',
style: TextStyle(color: Theme.of(context).hintColor),
),
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<int>(
value: 0, child: Text("Open random entry")),
];
},
onSelected: (value) {}),
],
),
body: ValueListenableBuilder<Box<ModelManga>>(
valueListenable: ref.watch(hiveBoxMangaProvider).listenable(),
return ValueListenableBuilder<Box<CategoriesModel>>(
valueListenable: ref.watch(hiveBoxCategoriesProvider).listenable(),
builder: (context, value, child) {
entries = value.values.where((element) => element.favorite).toList();
final data =
ref.watch(mangaFilterResultStateProvider(mangaList: entries));
entriesFilter = _textEditingController.text.isNotEmpty
? data
.where((element) => element.name!
.toLowerCase()
.contains(_textEditingController.text.toLowerCase()))
.toList()
: data;
final entriesManga =
reverse ? entriesFilter.reversed.toList() : entriesFilter;
final entr = value.values.toList();
if (entr.isNotEmpty && showCategoryTabs) {
tabBarController = TabController(length: entr.length, vsync: this);
tabBarController.animateTo(tabIndex);
tabBarController.addListener(() {
tabIndex = tabBarController.index;
});
return DefaultTabController(
length: entr.length,
child: Scaffold(
appBar:
_appBar(isNotFiltering, showNumbersOfItems, entries.length),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TabBar(
isScrollable: true,
controller: tabBarController,
tabs: entr.map((e) => Tab(text: e.name)).toList()),
Flexible(
child: TabBarView(
controller: tabBarController,
children: entr
.map(
(e) => Scaffold(
body: ValueListenableBuilder<Box<ModelManga>>(
valueListenable: ref
.watch(hiveBoxMangaProvider)
.listenable(),
builder: (context, value, child) {
entries = value.values
.where((element) =>
element.favorite &&
element.categories != null &&
element.categories!.contains(e.id))
.toList();
final data = ref.watch(
mangaFilterResultStateProvider(
mangaList: entries));
entriesFilter = _textEditingController
.text.isNotEmpty
? data
.where((element) => element.name!
.toLowerCase()
.contains(_textEditingController
.text
.toLowerCase()))
.toList()
: data;
final entriesManga = reverse
? entriesFilter.reversed.toList()
: entriesFilter;
if (entriesFilter.isNotEmpty) {
return displayType == DisplayType.list
? LibraryListViewWidget(
entriesManga: entriesManga,
)
: LibraryGridViewWidget(
entriesManga: entriesManga,
isCoverOnlyGrid:
displayType == DisplayType.compactGrid ? false : true,
isComfortableGrid:
displayType == DisplayType.comfortableGrid
? true
: false,
);
if (entriesFilter.isNotEmpty) {
return displayType == DisplayType.list
? LibraryListViewWidget(
entriesManga: entriesManga,
continueReaderBtn:
continueReaderBtn,
downloadedChapter:
downloadedChapter,
language: language,
)
: LibraryGridViewWidget(
entriesManga: entriesManga,
isCoverOnlyGrid: displayType ==
DisplayType.compactGrid
? false
: true,
isComfortableGrid: displayType ==
DisplayType.comfortableGrid
? true
: false,
continueReaderBtn:
continueReaderBtn,
downloadedChapter:
downloadedChapter,
language: language,
);
}
return const Center(
child: Text("Empty Library"));
},
),
),
)
.toList(),
))
],
),
),
);
}
return const Center(child: Text("Empty Library"));
},
),
);
return Scaffold(
appBar: _appBar(isNotFiltering, showNumbersOfItems, entries.length),
body: ValueListenableBuilder<Box<ModelManga>>(
valueListenable: ref.watch(hiveBoxMangaProvider).listenable(),
builder: (context, value, child) {
entries =
value.values.where((element) => element.favorite).toList();
final data = ref
.watch(mangaFilterResultStateProvider(mangaList: entries));
entriesFilter = _textEditingController.text.isNotEmpty
? data
.where((element) => element.name!
.toLowerCase()
.contains(
_textEditingController.text.toLowerCase()))
.toList()
: data;
final entriesManga =
reverse ? entriesFilter.reversed.toList() : entriesFilter;
if (entriesFilter.isNotEmpty) {
return displayType == DisplayType.list
? LibraryListViewWidget(
entriesManga: entriesManga,
continueReaderBtn: continueReaderBtn,
downloadedChapter: downloadedChapter,
language: language,
)
: LibraryGridViewWidget(
entriesManga: entriesManga,
isCoverOnlyGrid:
displayType == DisplayType.compactGrid
? false
: true,
isComfortableGrid:
displayType == DisplayType.comfortableGrid
? true
: false,
continueReaderBtn: continueReaderBtn,
downloadedChapter: downloadedChapter,
language: language,
);
}
return const Center(child: Text("Empty Library"));
},
),
);
});
}
_showDraggableMenu() {
late TabController tabBarController;
tabBarController = TabController(length: 3, vsync: this);
tabBarController.animateTo(0);
DraggableMenu.open(
context,
DraggableMenu(
barItem: Container(),
uiType: DraggableMenuUiType.classic,
expandable: false,
maxHeight: mediaHeight(context, 0.4),
fastDrag: false,
minimizeBeforeFastDrag: false,
uiType: DraggableMenuUiType.softModern,
expandable: true,
expandedHeight: mediaHeight(context, 0.8),
maxHeight: mediaHeight(context, 0.5),
minimizeBeforeFastDrag: true,
child: DefaultTabController(
length: 3,
child: Scaffold(
@ -220,8 +281,6 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
.notifier)
.update();
});
// _refreshData();
}),
],
);
@ -237,7 +296,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
ref
.read(libraryReverseListStateProvider
.notifier)
.setLibraryReverseList(!reverse);
.set(!reverse);
},
dense: true,
leading: Icon(reverse
@ -253,25 +312,160 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
ref.watch(libraryDisplayTypeStateProvider);
final displayV = ref
.read(libraryDisplayTypeStateProvider.notifier);
return Column(
children: [
for (var i = 0;
i < DisplayType.values.length;
i++)
RadioListTile<DisplayType>(
title: Text(
displayV.getLibraryDisplayTypeName(
DisplayType.values[i].name)),
value: DisplayType.values[i],
groupValue: displayV
.getLibraryDisplayTypeValue(display),
selected: true,
onChanged: (value) {
displayV.setLibraryDisplayType(value!);
},
final showCategoryTabs =
ref.watch(libraryShowCategoryTabsStateProvider);
final continueReaderBtn = ref.watch(
libraryShowContinueReadingButtonStateProvider);
final showNumbersOfItems = ref
.watch(libraryShowNumbersOfItemsStateProvider);
final downloadedChapter = ref
.watch(libraryDownloadedChaptersStateProvider);
final language =
ref.watch(libraryLanguageStateProvider);
return SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(
left: 20, top: 10),
child: Row(
children: const [
Text("Display mode"),
],
),
),
],
Column(
children: DisplayType.values
.map(
(e) => RadioListTile<DisplayType>(
title: Text(
displayV
.getLibraryDisplayTypeName(
e.name),
style: TextStyle(
color: Theme.of(context)
.textTheme
.bodyLarge!
.color,
fontSize: 14),
),
value: e,
groupValue: displayV
.getLibraryDisplayTypeValue(
display),
selected: true,
onChanged: (value) {
displayV.setLibraryDisplayType(
value!);
},
),
)
.toList()),
Padding(
padding: const EdgeInsets.only(
left: 20, top: 10),
child: Row(
children: const [
Text("Badges"),
],
),
),
Padding(
padding:
const EdgeInsets.only(left: 10, top: 5),
child: Column(
children: [
ListTileChapterFilter(
label: "Downloaded chapters",
type: downloadedChapter ? 1 : 0,
onTap: () {
ref
.read(
libraryDownloadedChaptersStateProvider
.notifier)
.set(!downloadedChapter);
}),
ListTileChapterFilter(
label: "Language",
type: language ? 1 : 0,
onTap: () {
ref
.read(
libraryLanguageStateProvider
.notifier)
.set(!language);
}),
],
),
),
Padding(
padding: const EdgeInsets.only(
left: 20, top: 10),
child: Row(
children: const [
Text("Tabs"),
],
),
),
Padding(
padding:
const EdgeInsets.only(left: 10, top: 5),
child: Column(
children: [
ListTileChapterFilter(
label: "Show category tabs",
type: showCategoryTabs ? 1 : 0,
onTap: () {
ref
.read(
libraryShowCategoryTabsStateProvider
.notifier)
.set(!showCategoryTabs);
}),
ListTileChapterFilter(
label: "Show numbers of items",
type: showNumbersOfItems ? 1 : 0,
onTap: () {
ref
.read(
libraryShowNumbersOfItemsStateProvider
.notifier)
.set(!showNumbersOfItems);
}),
],
),
),
Padding(
padding: const EdgeInsets.only(
left: 20, top: 10),
child: Row(
children: const [
Text("Others"),
],
),
),
Padding(
padding:
const EdgeInsets.only(left: 10, top: 5),
child: Column(
children: [
ListTileChapterFilter(
label:
"Show continue reading button",
type: continueReaderBtn ? 1 : 0,
onTap: () {
ref
.read(
libraryShowContinueReadingButtonStateProvider
.notifier)
.set(!continueReaderBtn);
}),
],
),
)
],
),
);
}),
]),
@ -280,4 +474,87 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
),
))));
}
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),
),
),
),
],
),
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<int>(
value: 0, child: Text("Open random entry")),
];
},
onSelected: (value) {}),
],
);
}
}

View file

@ -12,7 +12,7 @@ class LibraryReverseListState extends _$LibraryReverseListState {
.get('libraryReverseList', defaultValue: false)!;
}
void setLibraryReverseList(bool value) {
void set(bool value) {
state = value;
ref.watch(hiveBoxSettingsProvider).put('libraryReverseList', value);
}
@ -430,3 +430,82 @@ class MangaFilterResultState extends _$MangaFilterResultState {
bookmarkedFilterType == 0;
}
}
@riverpod
class LibraryShowCategoryTabsState extends _$LibraryShowCategoryTabsState {
@override
bool build() {
return ref
.watch(hiveBoxSettingsProvider)
.get('libraryShowCategoryTabs', defaultValue: false)!;
}
void set(bool value) {
state = value;
ref.watch(hiveBoxSettingsProvider).put('libraryShowCategoryTabs', value);
}
}
@riverpod
class LibraryDownloadedChaptersState extends _$LibraryDownloadedChaptersState {
@override
bool build() {
return ref
.watch(hiveBoxSettingsProvider)
.get('libraryDownloadedChapters', defaultValue: false)!;
}
void set(bool value) {
state = value;
ref.watch(hiveBoxSettingsProvider).put('libraryDownloadedChapters', value);
}
}
@riverpod
class LibraryLanguageState extends _$LibraryLanguageState {
@override
bool build() {
return ref
.watch(hiveBoxSettingsProvider)
.get('libraryLanguage', defaultValue: false)!;
}
void set(bool value) {
state = value;
ref.watch(hiveBoxSettingsProvider).put('libraryLanguage', value);
}
}
@riverpod
class LibraryShowNumbersOfItemsState extends _$LibraryShowNumbersOfItemsState {
@override
bool build() {
return ref
.watch(hiveBoxSettingsProvider)
.get('libraryShowNumbersOfItems', defaultValue: false)!;
}
void set(bool value) {
state = value;
ref.watch(hiveBoxSettingsProvider).put('libraryShowNumbersOfItems', value);
}
}
@riverpod
class LibraryShowContinueReadingButtonState
extends _$LibraryShowContinueReadingButtonState {
@override
bool build() {
return ref
.watch(hiveBoxSettingsProvider)
.get('libraryShowContinueReadingButton', defaultValue: false)!;
}
void set(bool value) {
state = value;
ref
.watch(hiveBoxSettingsProvider)
.put('libraryShowContinueReadingButton', value);
}
}

View file

@ -7,7 +7,7 @@ part of 'library_state_provider.dart';
// **************************************************************************
String _$libraryReverseListStateHash() =>
r'9a9b8cc5bacc6e84a89847cc4e0ea6793c6b851e';
r'5d9037f95ffe332019dd1d3d08b0db06d798738c';
/// See also [LibraryReverseListState].
@ProviderFor(LibraryReverseListState)
@ -24,7 +24,7 @@ final libraryReverseListStateProvider =
typedef _$LibraryReverseListState = AutoDisposeNotifier<bool>;
String _$libraryDisplayTypeStateHash() =>
r'2743481e54668fe95294194b62014f9713de2cb8';
r'746bd6dac3600802c3ab5751b3c1def881274b3a';
/// See also [LibraryDisplayTypeState].
@ProviderFor(LibraryDisplayTypeState)
@ -41,7 +41,7 @@ final libraryDisplayTypeStateProvider =
typedef _$LibraryDisplayTypeState = AutoDisposeNotifier<String>;
String _$mangaFilterDownloadedStateHash() =>
r'12b1cb7b473e6556ea9c1a2f9c7bf44017076ff4';
r'18ed17c06f41084cbb92b0b3300025f4e65aa413';
/// Copied from Dart SDK
class _SystemHash {
@ -161,7 +161,7 @@ class MangaFilterDownloadedStateProvider
}
String _$mangaFilterUnreadStateHash() =>
r'4b9172bbb95ebca0759946328b1fbdacf07392b1';
r'5eed6ec9f46f1562d48eb89a078f666ed5b466d8';
abstract class _$MangaFilterUnreadState
extends BuildlessAutoDisposeNotifier<int> {
@ -260,7 +260,7 @@ class MangaFilterUnreadStateProvider
}
String _$mangaFilterStartedStateHash() =>
r'9b3c27078f42f624e3f3fdd255573a3279a4efed';
r'cf5440f02e8454d75de4f311f945b33f73668ea2';
abstract class _$MangaFilterStartedState
extends BuildlessAutoDisposeNotifier<int> {
@ -359,7 +359,7 @@ class MangaFilterStartedStateProvider
}
String _$mangaFilterBookmarkedStateHash() =>
r'88b9ef0b5a65735525a0141c6e7397d9aa7e27ff';
r'cdeeb68e7428e4856db3551443c70e28c3c7f95d';
abstract class _$MangaFilterBookmarkedState
extends BuildlessAutoDisposeNotifier<int> {
@ -458,7 +458,7 @@ class MangaFilterBookmarkedStateProvider
}
String _$mangaFilterResultStateHash() =>
r'5ec8cf6917ccc72d5eef7f2e7b308725563e4831';
r'fb5c27326f49a7e361ac19b97b511f1d8ab50920';
abstract class _$MangaFilterResultState
extends BuildlessAutoDisposeNotifier<List<ModelManga>> {
@ -555,4 +555,91 @@ class MangaFilterResultStateProvider extends AutoDisposeNotifierProviderImpl<
);
}
}
String _$libraryShowCategoryTabsStateHash() =>
r'0ee90372d42a11638479aadcf8ea5e688bb48369';
/// See also [LibraryShowCategoryTabsState].
@ProviderFor(LibraryShowCategoryTabsState)
final libraryShowCategoryTabsStateProvider =
AutoDisposeNotifierProvider<LibraryShowCategoryTabsState, bool>.internal(
LibraryShowCategoryTabsState.new,
name: r'libraryShowCategoryTabsStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$libraryShowCategoryTabsStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$LibraryShowCategoryTabsState = AutoDisposeNotifier<bool>;
String _$libraryDownloadedChaptersStateHash() =>
r'bdbb37edcd547e8f34df39d9221bb85051f765ae';
/// See also [LibraryDownloadedChaptersState].
@ProviderFor(LibraryDownloadedChaptersState)
final libraryDownloadedChaptersStateProvider =
AutoDisposeNotifierProvider<LibraryDownloadedChaptersState, bool>.internal(
LibraryDownloadedChaptersState.new,
name: r'libraryDownloadedChaptersStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$libraryDownloadedChaptersStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$LibraryDownloadedChaptersState = AutoDisposeNotifier<bool>;
String _$libraryLanguageStateHash() =>
r'b454724faeda5de41a67952cf9a80366fb72be9c';
/// See also [LibraryLanguageState].
@ProviderFor(LibraryLanguageState)
final libraryLanguageStateProvider =
AutoDisposeNotifierProvider<LibraryLanguageState, bool>.internal(
LibraryLanguageState.new,
name: r'libraryLanguageStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$libraryLanguageStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$LibraryLanguageState = AutoDisposeNotifier<bool>;
String _$libraryShowNumbersOfItemsStateHash() =>
r'f6eeb5df01cee601f05e442229830f64891a5fe9';
/// See also [LibraryShowNumbersOfItemsState].
@ProviderFor(LibraryShowNumbersOfItemsState)
final libraryShowNumbersOfItemsStateProvider =
AutoDisposeNotifierProvider<LibraryShowNumbersOfItemsState, bool>.internal(
LibraryShowNumbersOfItemsState.new,
name: r'libraryShowNumbersOfItemsStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$libraryShowNumbersOfItemsStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$LibraryShowNumbersOfItemsState = AutoDisposeNotifier<bool>;
String _$libraryShowContinueReadingButtonStateHash() =>
r'4d5553dc605e87714b3c23f54c52c1911910a8aa';
/// See also [LibraryShowContinueReadingButtonState].
@ProviderFor(LibraryShowContinueReadingButtonState)
final libraryShowContinueReadingButtonStateProvider =
AutoDisposeNotifierProvider<LibraryShowContinueReadingButtonState,
bool>.internal(
LibraryShowContinueReadingButtonState.new,
name: r'libraryShowContinueReadingButtonStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$libraryShowContinueReadingButtonStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$LibraryShowContinueReadingButtonState = AutoDisposeNotifier<bool>;
// 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

View file

@ -1,8 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/manga_reader.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/views/manga/download/download_model.dart';
import 'package:mangayomi/views/more/settings/providers/incognito_mode_state_provider.dart';
import 'package:mangayomi/views/widgets/bottom_text_widget.dart';
import 'package:mangayomi/views/widgets/cover_view_widget.dart';
import 'package:mangayomi/views/widgets/gridview_widget.dart';
@ -11,11 +17,17 @@ class LibraryGridViewWidget extends StatelessWidget {
final bool isCoverOnlyGrid;
final bool isComfortableGrid;
final List<ModelManga> entriesManga;
final bool language;
final bool downloadedChapter;
final bool continueReaderBtn;
const LibraryGridViewWidget(
{super.key,
required this.entriesManga,
required this.isCoverOnlyGrid,
this.isComfortableGrid = false});
this.isComfortableGrid = false,
required this.language,
required this.downloadedChapter,
required this.continueReaderBtn});
@override
Widget build(BuildContext context) {
@ -51,20 +63,177 @@ class LibraryGridViewWidget extends StatelessWidget {
borderRadius: BorderRadius.circular(3),
color: generalColor(context),
),
child: Padding(
padding: const EdgeInsets.all(1),
child: Text(
entriesManga[index].chapters!.length.toString(),
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
.modelManga
.chapters![element.index]
.name ==
entriesManga[index]
.chapters![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(
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!)
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) {
return ValueListenableBuilder<Box>(
valueListenable: ref
.watch(hiveBoxMangaInfoProvider)
.listenable(),
builder: (context, value, child) {
final entries = value.get(
"${entriesManga[index].lang}-${entriesManga[index].source}/${entriesManga[index].name}-chapter_index",
defaultValue: '');
final incognitoMode =
ref.watch(incognitoModeStateProvider);
if (entries.isNotEmpty && !incognitoMode) {
return GestureDetector(
onTap: () {
pushMangaReaderView(
context: context,
modelManga: entriesManga[index],
index: int.parse(entries.toString()));
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: generalColor(context)
.withOpacity(0.7),
),
child: const Padding(
padding: EdgeInsets.all(7),
child: Icon(
Icons.play_arrow,
size: 19,
color: Colors.white,
)),
),
);
}
return GestureDetector(
onTap: () {
pushMangaReaderView(
context: context,
modelManga: entriesManga[index],
index: entriesManga[index]
.chapters!
.length -
1);
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: generalColor(context)
.withOpacity(0.7),
),
child: const Padding(
padding: EdgeInsets.all(7),
child: Icon(
Icons.play_arrow,
size: 19,
color: Colors.white,
)),
),
);
},
);
},
)))
],
),
);

View file

@ -1,14 +1,27 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/manga_reader.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/utils/media_query.dart';
import 'package:mangayomi/views/more/settings/providers/incognito_mode_state_provider.dart';
import 'package:mangayomi/views/widgets/listview_widget.dart';
class LibraryListViewWidget extends StatelessWidget {
final List<ModelManga> entriesManga;
const LibraryListViewWidget({super.key, required this.entriesManga});
final bool language;
final bool downloadedChapter;
final bool continueReaderBtn;
const LibraryListViewWidget(
{super.key,
required this.entriesManga,
required this.language,
required this.downloadedChapter,
required this.continueReaderBtn});
@override
Widget build(BuildContext context) {
@ -27,25 +40,27 @@ class LibraryListViewWidget extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(5),
bottomLeft: Radius.circular(5)),
child: cachedNetworkImage(
imageUrl: entriesManga[index].imageUrl!,
width: 40,
height: 40,
fit: BoxFit.cover),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: SizedBox(
width: mediaWidth(context, 0.7),
child: Text(entriesManga[index].name!)),
),
],
Expanded(
child: Row(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(5),
bottomLeft: Radius.circular(5)),
child: cachedNetworkImage(
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),
@ -53,15 +68,162 @@ class LibraryListViewWidget extends StatelessWidget {
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
color: generalColor(context)),
child: Padding(
padding: const EdgeInsets.all(1),
child: Text(
entriesManga[index].chapters!.length.toString(),
style: const TextStyle(color: Colors.white),
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
.modelManga
.chapters![element.index]
.name ==
entriesManga[index]
.chapters![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)
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)
Positioned(
bottom: 0,
right: 0,
child: Consumer(
builder: (context, ref, child) {
return ValueListenableBuilder<Box>(
valueListenable: ref
.watch(hiveBoxMangaInfoProvider)
.listenable(),
builder: (context, value, child) {
final entries = value.get(
"${entriesManga[index].lang}-${entriesManga[index].source}/${entriesManga[index].name}-chapter_index",
defaultValue: '');
final incognitoMode =
ref.watch(incognitoModeStateProvider);
if (entries.isNotEmpty && !incognitoMode) {
return GestureDetector(
onTap: () {
pushMangaReaderView(
context: context,
modelManga: entriesManga[index],
index: int.parse(entries.toString()));
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: generalColor(context)
.withOpacity(0.7),
),
child: const Padding(
padding: EdgeInsets.all(7),
child: Icon(
Icons.play_arrow,
size: 19,
color: Colors.white,
)),
),
);
}
return GestureDetector(
onTap: () {
pushMangaReaderView(
context: context,
modelManga: entriesManga[index],
index: entriesManga[index]
.chapters!
.length -
1);
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: generalColor(context)
.withOpacity(0.7),
),
child: const Padding(
padding: EdgeInsets.all(7),
child: Icon(
Icons.play_arrow,
size: 19,
color: Colors.white,
)),
),
);
},
);
},
))
],
),
),

View file

@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/categories.dart';
import 'package:mangayomi/models/manga_reader.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
@ -9,6 +11,7 @@ import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/utils/media_query.dart';
import 'package:mangayomi/views/manga/detail/manga_detail_view.dart';
import 'package:mangayomi/views/manga/detail/providers/state_providers.dart';
import 'package:mangayomi/views/manga/detail/widgets/chapter_filter_list_tile_widget.dart';
import 'package:mangayomi/views/more/settings/providers/incognito_mode_state_provider.dart';
class MangaDetailsView extends ConsumerStatefulWidget {
@ -242,7 +245,7 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
dateAdded: widget.modelManga.dateAdded,
lastUpdate: widget.modelManga.lastUpdate,
chapters: widget.modelManga.chapters,
category: widget.modelManga.category,
categories: [],
lastRead: widget.modelManga.lastRead);
manga.put(
'${widget.modelManga.lang}-${widget.modelManga.link}',
@ -275,26 +278,35 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
Theme.of(context).scaffoldBackgroundColor,
elevation: 0),
onPressed: () {
_setFavorite(true);
final model = ModelManga(
imageUrl: widget.modelManga.imageUrl,
name: widget.modelManga.name,
genre: widget.modelManga.genre,
author: widget.modelManga.author,
status: widget.modelManga.status,
description: widget.modelManga.description,
favorite: true,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang,
dateAdded: DateTime.now().microsecondsSinceEpoch,
lastUpdate: DateTime.now().microsecondsSinceEpoch,
chapters: widget.modelManga.chapters,
category: null,
lastRead: '');
manga.put(
'${widget.modelManga.lang}-${widget.modelManga.link}',
model);
final checkCategoryList = ref
.watch(hiveBoxCategoriesProvider)
.values
.toList()
.isNotEmpty;
if (checkCategoryList) {
_openCategory(manga);
} else {
_setFavorite(true);
final model = ModelManga(
imageUrl: widget.modelManga.imageUrl,
name: widget.modelManga.name,
genre: widget.modelManga.genre,
author: widget.modelManga.author,
status: widget.modelManga.status,
description: widget.modelManga.description,
favorite: true,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang,
dateAdded: DateTime.now().microsecondsSinceEpoch,
lastUpdate: DateTime.now().microsecondsSinceEpoch,
chapters: widget.modelManga.chapters,
categories: [],
lastRead: '');
manga.put(
'${widget.modelManga.lang}-${widget.modelManga.link}',
model);
}
},
child: Column(
children: [
@ -324,26 +336,35 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
elevation: 0),
onPressed: () {
_setFavorite(true);
final model = ModelManga(
imageUrl: widget.modelManga.imageUrl,
name: widget.modelManga.name,
genre: widget.modelManga.genre,
author: widget.modelManga.author,
status: widget.modelManga.status,
description: widget.modelManga.description,
favorite: true,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang,
dateAdded: DateTime.now().microsecondsSinceEpoch,
lastUpdate: DateTime.now().microsecondsSinceEpoch,
chapters: widget.modelManga.chapters,
category: null,
lastRead: '');
manga.put(
'${widget.modelManga.lang}-${widget.modelManga.link}',
model);
final checkCategoryList = ref
.watch(hiveBoxCategoriesProvider)
.values
.toList()
.isNotEmpty;
if (checkCategoryList) {
_openCategory(manga);
} else {
_setFavorite(true);
final model = ModelManga(
imageUrl: widget.modelManga.imageUrl,
name: widget.modelManga.name,
genre: widget.modelManga.genre,
author: widget.modelManga.author,
status: widget.modelManga.status,
description: widget.modelManga.description,
favorite: true,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang,
dateAdded: DateTime.now().microsecondsSinceEpoch,
lastUpdate: DateTime.now().microsecondsSinceEpoch,
chapters: widget.modelManga.chapters,
categories: [],
lastRead: '');
manga.put(
'${widget.modelManga.lang}-${widget.modelManga.link}',
model);
}
},
child: Column(
children: [
@ -374,4 +395,105 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
),
);
}
_openCategory(Box<ModelManga> manga) {
List<int> categoryIds = [];
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text(
"Set categories",
),
content: SizedBox(
width: mediaWidth(context, 0.8),
child: ValueListenableBuilder<Box<CategoriesModel>>(
valueListenable:
ref.watch(hiveBoxCategoriesProvider).listenable(),
builder: (context, value, child) {
final entries = value.values.toList();
return ListView.builder(
shrinkWrap: true,
itemCount: entries.length,
itemBuilder: (context, index) {
return ListTileChapterFilter(
label: entries[index].name,
onTap: () {
setState(() {
if (categoryIds.contains(entries[index].id)) {
categoryIds.remove(entries[index].id);
} else {
categoryIds.add(entries[index].id);
}
});
},
type: categoryIds.contains(entries[index].id)
? 1
: 0,
);
},
);
}),
),
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: () {
_setFavorite(true);
final model = ModelManga(
imageUrl: widget.modelManga.imageUrl,
name: widget.modelManga.name,
genre: widget.modelManga.genre,
author: widget.modelManga.author,
status: widget.modelManga.status,
description: widget.modelManga.description,
favorite: true,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang,
dateAdded:
DateTime.now().microsecondsSinceEpoch,
lastUpdate:
DateTime.now().microsecondsSinceEpoch,
chapters: widget.modelManga.chapters,
categories: categoryIds,
lastRead: '');
manga.put(
'${widget.modelManga.lang}-${widget.modelManga.link}',
model);
Navigator.pop(context);
},
child: const Text(
"OK",
)),
],
),
],
)
],
);
},
);
});
}
}

View file

@ -75,7 +75,7 @@ class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
dateAdded: widget.modelManga.dateAdded,
lastUpdate: DateTime.now().microsecondsSinceEpoch,
chapters: chapters,
category: widget.modelManga.category,
categories: widget.modelManga.categories,
lastRead: widget.modelManga.lastRead);
ref.watch(hiveBoxMangaProvider).put(
'${widget.modelManga.lang}-${widget.modelManga.link}',

View file

@ -502,7 +502,7 @@ ModelManga modelMangaWithNewChapValue(
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chapters,
category: modelManga.category,
categories: modelManga.categories,
lastRead: modelManga.lastRead);
}

View file

@ -72,7 +72,7 @@ final isExtendedStateProvider =
);
typedef _$IsExtendedState = AutoDisposeNotifier<bool>;
String _$reverseMangaStateHash() => r'ba21cdabf4a5e60e9c31b09a93080ccae84e3bd4';
String _$reverseMangaStateHash() => r'27a74f99810dac3d27d428a107a397e03eb2835d';
/// Copied from Dart SDK
class _SystemHash {
@ -190,7 +190,7 @@ class ReverseMangaStateProvider
}
String _$chapterFilterDownloadedStateHash() =>
r'46dca943e064d5f70968c711812091783e0a4039';
r'ea2313a3f81e408cdea77e98d82510e824ddd6a4';
abstract class _$ChapterFilterDownloadedState
extends BuildlessAutoDisposeNotifier<int> {
@ -290,7 +290,7 @@ class ChapterFilterDownloadedStateProvider
}
String _$chapterFilterUnreadStateHash() =>
r'48076800725c22f04493653deec4e946cb71d195';
r'54a6bd0ace5db2262298ec51a7a99149aeaff047';
abstract class _$ChapterFilterUnreadState
extends BuildlessAutoDisposeNotifier<int> {
@ -389,7 +389,7 @@ class ChapterFilterUnreadStateProvider
}
String _$chapterFilterBookmarkedStateHash() =>
r'4772ce2506d0c939b549c8661cd3b62cae853e20';
r'316ae7f6d11556927aa160ab950585e3e74fc8e1';
abstract class _$ChapterFilterBookmarkedState
extends BuildlessAutoDisposeNotifier<int> {
@ -489,7 +489,7 @@ class ChapterFilterBookmarkedStateProvider
}
String _$chapterFilterResultStateHash() =>
r'85ab18048c228f674211c8254d7efedb92152b8f';
r'a0c0bccb457db8ccfba52e2b7e36a1f6e2b6afe3';
abstract class _$ChapterFilterResultState
extends BuildlessAutoDisposeNotifier<ModelManga> {
@ -588,7 +588,7 @@ class ChapterFilterResultStateProvider extends AutoDisposeNotifierProviderImpl<
}
String _$chapterSetIsBookmarkStateHash() =>
r'a53f9acaea333287e229c391ba01c9da22a59b0f';
r'1be8afbfd3ebd0a519922f315d204d60409bed57';
abstract class _$ChapterSetIsBookmarkState
extends BuildlessAutoDisposeNotifier<dynamic> {
@ -687,7 +687,7 @@ class ChapterSetIsBookmarkStateProvider extends AutoDisposeNotifierProviderImpl<
}
String _$chapterSetIsReadStateHash() =>
r'9435f9d17d5d1fdcfab0279e93d964bd2e4d1fbb';
r'fafd4503e4e65d48f157893de2b3a2234f4b20c7';
abstract class _$ChapterSetIsReadState
extends BuildlessAutoDisposeNotifier<dynamic> {
@ -786,7 +786,7 @@ class ChapterSetIsReadStateProvider
}
String _$chapterSetDownloadStateHash() =>
r'9c27161d2eedd2e8d55281d9f5640e4459926d6f';
r'299986f635cf64cf09aafbd7da373fa3e93ac8fc';
abstract class _$ChapterSetDownloadState
extends BuildlessAutoDisposeNotifier<dynamic> {

View file

@ -6,7 +6,7 @@ part of 'download_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$downloadChapterHash() => r'39625ed8cba923709fe7c76788e334bec1d8bcc3';
String _$downloadChapterHash() => r'982e5db78e716894f63b97598709e29098c3eb8f';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -138,7 +138,7 @@ class ReaderController extends _$ReaderController {
dateAdded: getModelManga().dateAdded,
lastUpdate: getModelManga().lastUpdate,
chapters: chap,
category: getModelManga().category,
categories: getModelManga().categories,
lastRead: getModelManga().lastRead);
ref
.watch(hiveBoxMangaProvider)
@ -177,7 +177,7 @@ class ReaderController extends _$ReaderController {
dateAdded: getModelManga().dateAdded,
lastUpdate: getModelManga().lastUpdate,
chapters: chap,
category: getModelManga().category,
categories: getModelManga().categories,
lastRead: getModelManga().lastRead);
ref
.watch(hiveBoxMangaProvider)

View file

@ -64,7 +64,7 @@ class ReaderModeAdapter extends TypeAdapter<ReaderMode> {
// RiverpodGenerator
// **************************************************************************
String _$currentIndexHash() => r'287404f12bc984052a83e97beb3ff335e820e8de';
String _$currentIndexHash() => r'b25073058aa2f1a392a4591d8812a61e752061ef';
/// Copied from Dart SDK
class _SystemHash {
@ -182,7 +182,7 @@ class CurrentIndexProvider
}
}
String _$readerControllerHash() => r'56c832982f94aab78b30439b025177b7f3851cec';
String _$readerControllerHash() => r'a389f9b08001c6863a651f6c2ed3d1b18588d5b0';
abstract class _$ReaderController extends BuildlessAutoDisposeNotifier<void> {
late final MangaReaderModel mangaReaderModel;

View file

@ -41,7 +41,7 @@ class MoreScreen extends StatelessWidget {
onTap: () {
context.push('/categories');
},
icon: Icons.label_rounded,
icon: Icons.label_outline_rounded,
title: 'Categories',
),
const Divider(),

View file

@ -6,7 +6,7 @@ part of 'blend_level_state_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$blendLevelStateHash() => r'20a7e4e5cb5ff9d60bcf60c2c7e9b36584c3acb0';
String _$blendLevelStateHash() => r'b1a14a5ff8ddf89164aec31c92c24ec9585ce0f5';
/// See also [BlendLevelState].
@ProviderFor(BlendLevelState)

View file

@ -7,7 +7,7 @@ part of 'flex_scheme_color_state_provider.dart';
// **************************************************************************
String _$flexSchemeColorStateHash() =>
r'825680702e419e5eb921e251312ffd4ba3303a2c';
r'84c6afce63762e5704c548caf9059f922a9936b8';
/// See also [FlexSchemeColorState].
@ProviderFor(FlexSchemeColorState)

View file

@ -6,7 +6,7 @@ part of 'theme_mode_state_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$themeModeStateHash() => r'ae86c33659103122399492e5409a0e947b0c6292';
String _$themeModeStateHash() => r'ffb06bbf255e51a6f14424280f7874b1a11062ec';
/// See also [ThemeModeState].
@ProviderFor(ThemeModeState)

View file

@ -3,6 +3,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/categories.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/views/more/settings/categoties/widgets/custom_textfield.dart';
import 'package:random_string/random_string.dart';
class CategoriesScreen extends ConsumerStatefulWidget {
const CategoriesScreen({super.key});
@ -12,6 +14,7 @@ class CategoriesScreen extends ConsumerStatefulWidget {
}
class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
List<CategoriesModel> entries = [];
@override
Widget build(BuildContext context) {
return Scaffold(
@ -21,10 +24,122 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
body: ValueListenableBuilder<Box<CategoriesModel>>(
valueListenable: ref.watch(hiveBoxCategoriesProvider).listenable(),
builder: (context, value, child) {
final entries = value.values.toList();
entries = value.values.toList();
if (entries.isNotEmpty) {
return ListView.builder(
itemBuilder: (context, index) {},
itemCount: entries.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Card(
child: Column(
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
elevation: 0,
shadowColor: Colors.transparent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(0),
bottomRight: Radius.circular(0),
topRight: Radius.circular(10),
topLeft: Radius.circular(10)))),
onPressed: () {
_renameCategory(entries[index]);
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const Icon(Icons.label_outline_rounded),
const SizedBox(
width: 10,
),
Expanded(child: Text(entries[index].name))
],
)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: const [
SizedBox(width: 10),
Icon(Icons.arrow_drop_up_outlined),
SizedBox(width: 10),
Icon(Icons.arrow_drop_down_outlined)
],
),
Row(
children: [
IconButton(
onPressed: () {
_renameCategory(entries[index]);
},
icon: const Icon(
Icons.mode_edit_outline_outlined)),
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text(
"Delete category",
),
content: Text(
"Do you wish to delete the category"
' "${entries[index].name}"?'),
actions: [
Row(
mainAxisAlignment:
MainAxisAlignment
.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(
context);
},
child: const Text(
"Cancel")),
const SizedBox(
width: 15,
),
TextButton(
onPressed: () {
ref
.watch(
hiveBoxCategoriesProvider)
.delete(entries[
index]
.id
.toString());
Navigator.pop(
context);
},
child: const Text(
"OK",
)),
],
)
],
);
},
);
});
},
icon: const Icon(Icons.delete_outlined))
],
),
],
)
],
),
),
);
},
);
} else {
return const Center(
@ -40,41 +155,28 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
}),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
bool isExist = false;
final controller = TextEditingController();
showDialog(
context: context,
builder: (context) {
final controller = TextEditingController();
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text(
"Add category",
),
content: TextFormField(
autofocus: true,
controller: controller,
keyboardType: TextInputType.text,
onChanged: (s) {
setState(() {});
},
onFieldSubmitted: (s) {
setState(() {});
},
decoration: InputDecoration(
isDense: true,
labelText: "Name",
filled: true,
fillColor: Colors.transparent,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).primaryColor)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).primaryColor)),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).primaryColor))),
),
content: CustomTextFormField(
controller: controller,
entries: entries,
context: context,
exist: (value) {
setState(() {
isExist = value;
});
},
isExist: isExist,
val: (val) {}),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
@ -88,19 +190,29 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
width: 15,
),
TextButton(
onPressed: controller.text.isEmpty
onPressed: controller.text.isEmpty || isExist
? null
: () {
String randomId = randomNumeric(10);
ref
.watch(hiveBoxCategoriesProvider)
.put(
randomId,
CategoriesModel(
id: int.parse(randomId),
name: controller.text,
));
Navigator.pop(context);
},
child: Text(
"Add",
style: TextStyle(
color: controller.text.isEmpty
? Theme.of(context)
.primaryColor
.withOpacity(0.2)
: null),
color:
controller.text.isEmpty || isExist
? Theme.of(context)
.primaryColor
.withOpacity(0.2)
: null),
)),
],
)
@ -121,4 +233,78 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
)),
);
}
_renameCategory(CategoriesModel category) {
bool isExist = false;
final controller = TextEditingController(text: category.name);
bool isSameName = controller.text == category.name;
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text(
"Rename category",
),
content: CustomTextFormField(
controller: controller,
entries: entries,
context: context,
exist: (value) {
setState(() {
isExist = value;
});
},
isExist: isExist,
name: category.name,
val: (val) {
setState(() {
isSameName = controller.text == category.name;
});
}),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text("Cancel")),
const SizedBox(
width: 15,
),
TextButton(
onPressed:
controller.text.isEmpty || isExist || isSameName
? null
: () {
ref.watch(hiveBoxCategoriesProvider).put(
category.id.toString(),
CategoriesModel(
id: category.id,
name: controller.text,
));
Navigator.pop(context);
},
child: Text(
"OK",
style: TextStyle(
color: controller.text.isEmpty ||
isExist ||
isSameName
? Theme.of(context)
.primaryColor
.withOpacity(0.2)
: null),
)),
],
)
],
);
},
);
});
}
}

View file

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:mangayomi/models/categories.dart';
class CustomTextFormField extends StatelessWidget {
final TextEditingController controller;
final List<CategoriesModel> entries;
final BuildContext context;
final Function(bool) exist;
final bool isExist;
final String name;
final Function(String) val;
const CustomTextFormField({
super.key,
required this.controller,
required this.entries,
required this.context,
required this.exist,
required this.isExist,
this.name = "",
required this.val,
});
@override
Widget build(BuildContext context) {
return TextFormField(
autofocus: true,
controller: controller,
keyboardType: TextInputType.text,
onChanged: (value) {
if (name != controller.text) {
exist(entries
.where((element) => element.name == controller.text)
.toList()
.isNotEmpty);
}
val(value);
},
onFieldSubmitted: (s) {},
decoration: InputDecoration(
helperText: isExist == true
? "A category with this name already exist!"
: "*required",
helperStyle: TextStyle(color: isExist == true ? Colors.red : null),
isDense: true,
label: Text("Name",
style: TextStyle(color: isExist == true ? Colors.red : null)),
filled: true,
fillColor: Colors.transparent,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: isExist == true
? Colors.red
: Theme.of(context).primaryColor)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: isExist == true
? Colors.red
: Theme.of(context).primaryColor)),
border: OutlineInputBorder(
borderSide: BorderSide(
color: isExist == true
? Colors.red
: Theme.of(context).primaryColor))),
);
}
}

View file

@ -7,7 +7,7 @@ part of 'incognito_mode_state_provider.dart';
// **************************************************************************
String _$incognitoModeStateHash() =>
r'614663075117c18594fda5884b79451ec77c3f1f';
r'004cbf630f6431dcbaf87f4ac5dca0e2c96e689e';
/// See also [IncognitoModeState].
@ProviderFor(IncognitoModeState)

View file

@ -42,10 +42,10 @@ class _MangaImageCardWidgetState extends ConsumerState<MangaImageCardWidget> {
link: widget.getMangaDetailModel!.url,
source: widget.getMangaDetailModel!.source,
lang: widget.lang,
dateAdded: DateTime.now().microsecondsSinceEpoch,
lastUpdate: DateTime.now().microsecondsSinceEpoch,
dateAdded: null,
lastUpdate: null,
chapters: widget.getMangaDetailModel!.chapters,
category: null,
categories: [],
lastRead: '');
if (mounted) {
context.push('/manga-reader/detail', extra: modelManga);

View file

@ -881,6 +881,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.2"
random_string:
dependency: "direct main"
description:
name: random_string
sha256: "03b52435aae8cbdd1056cf91bfc5bf845e9706724dd35ae2e99fa14a1ef79d02"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
riverpod:
dependency: transitive
description:

View file

@ -60,6 +60,7 @@ dependencies:
flutter_cache_manager: ^3.3.0
draggable_menu: ^0.3.0
fast_cached_network_image: ^1.2.0
random_string: ^2.3.1
# The following adds the Cupertino Icons font to your application.