migration to isar database step 1

This commit is contained in:
kodjodevf 2023-05-04 23:55:55 +01:00
parent 752f6e6feb
commit f0f1d3a6e2
51 changed files with 6813 additions and 1977 deletions

View file

@ -15,21 +15,6 @@ migration:
- platform: root
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: android
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: ios
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: linux
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: macos
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: web
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: windows
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0

View file

@ -5,51 +5,77 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/models/categories.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/models/manga_history.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/router/router.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/views/manga/download/download_model.dart';
import 'package:mangayomi/views/manga/download/model/download_model.dart';
import 'package:mangayomi/views/manga/reader/providers/reader_controller_provider.dart';
import 'package:mangayomi/views/more/settings/appearance/providers/blend_level_state_provider.dart';
import 'views/more/settings/appearance/providers/flex_scheme_color_state_provider.dart';
import 'views/more/settings/appearance/providers/theme_mode_state_provider.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
late Isar isar;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid || Platform.isIOS) {
await Hive.initFlutter();
} else {
await Hive.initFlutter("Mangayomi/databases");
}
await FastCachedImageConfig.init();
Hive.registerAdapter(ModelMangaAdapter());
Hive.registerAdapter(MangaHistoryModelAdapter());
await FastCachedImageConfig.init(subDir: "Mangayomi/databases");
// Hive.registerAdapter(ModelMangaAdapter());
// Hive.registerAdapter(MangaHistoryModelAdapter());
Hive.registerAdapter(SourceModelAdapter());
Hive.registerAdapter(ReaderModeAdapter());
Hive.registerAdapter(TypeSourceAdapter());
Hive.registerAdapter(DownloadModelAdapter());
Hive.registerAdapter(ModelChaptersAdapter());
Hive.registerAdapter(CategoriesModelAdapter());
await Hive.openBox<ModelManga>(HiveConstant.hiveBoxManga);
await Hive.openBox<MangaHistoryModel>(HiveConstant.hiveBoxMangaHistory);
// Hive.registerAdapter(ModelChaptersAdapter());
// Hive.registerAdapter(CategoriesModelAdapter());
// await Hive.openBox<ModelManga>(HiveConstant.hiveBoxManga);
// await Hive.openBox<MangaHistoryModel>(HiveConstant.hiveBoxMangaHistory);
await Hive.openBox<ReaderMode>(HiveConstant.hiveBoxReaderMode);
await Hive.openBox<SourceModel>(HiveConstant.hiveBoxMangaSource);
await Hive.openBox<DownloadModel>(HiveConstant.hiveBoxDownloads);
await Hive.openBox(HiveConstant.hiveBoxMangaInfo);
// await Hive.openBox(HiveConstant.hiveBoxMangaInfo);
await Hive.openBox(HiveConstant.hiveBoxMangaFilter);
await Hive.openBox(HiveConstant.hiveBoxAppSettings);
await Hive.openBox<CategoriesModel>(HiveConstant.hiveBoxCategories);
await Hive.openBox(HiveConstant.hiveBoxMangaInfo);
// await Hive.openBox<CategoriesModel>(HiveConstant.hiveBoxCategories);
await initIsar();
runApp(const ProviderScope(child: MyApp()));
}
initIsar() async {
final dir = await getApplicationDocumentsDirectory();
if (Platform.isAndroid || Platform.isIOS) {
isar = Isar.openSync(
[ModelMangaSchema, ModelChaptersSchema, CategoriesModelSchema],
directory: dir.path,
);
} else {
String rootDir = path.join(Directory.current.path, '.dart_tool', 'isar');
await Directory(rootDir).create(recursive: true); // something like this
isar = await Isar.open(
[ModelMangaSchema, ModelChaptersSchema, CategoriesModelSchema],
directory: rootDir,
);
}
}
class MyApp extends ConsumerWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context, WidgetRef ref) {
// ref.read(isarDataProvider.notifier).set(isar);
final isThemeLight = ref.watch(themeModeStateProvider);
final blendLevel = ref.watch(blendLevelStateProvider);
ThemeData themeLight = FlexThemeData.light(

View file

@ -1,15 +1,13 @@
import 'package:hive/hive.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:isar/isar.dart';
part 'categories.g.dart';
@HiveType(typeId: 8)
class CategoriesModel extends HiveObject {
@HiveField(0)
final int id;
@HiveField(1)
final String name;
@collection
@Name("Category")
class CategoriesModel {
Id? id;
String? name;
CategoriesModel({
required this.id,
this.id = Isar.autoIncrement,
required this.name,
});
}

View file

@ -3,42 +3,486 @@
part of 'categories.dart';
// **************************************************************************
// TypeAdapterGenerator
// IsarCollectionGenerator
// **************************************************************************
class CategoriesModelAdapter extends TypeAdapter<CategoriesModel> {
@override
final int typeId = 8;
// coverage:ignore-file
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
@override
CategoriesModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return CategoriesModel(
id: fields[0] as int,
name: fields[1] as String,
);
}
@override
void write(BinaryWriter writer, CategoriesModel obj) {
writer
..writeByte(2)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.name);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is CategoriesModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
extension GetCategoriesModelCollection on Isar {
IsarCollection<CategoriesModel> get categoriesModels => this.collection();
}
const CategoriesModelSchema = CollectionSchema(
name: r'Category',
id: 5751694338128944171,
properties: {
r'name': PropertySchema(
id: 0,
name: r'name',
type: IsarType.string,
)
},
estimateSize: _categoriesModelEstimateSize,
serialize: _categoriesModelSerialize,
deserialize: _categoriesModelDeserialize,
deserializeProp: _categoriesModelDeserializeProp,
idName: r'id',
indexes: {},
links: {},
embeddedSchemas: {},
getId: _categoriesModelGetId,
getLinks: _categoriesModelGetLinks,
attach: _categoriesModelAttach,
version: '3.1.0+1',
);
int _categoriesModelEstimateSize(
CategoriesModel object,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
var bytesCount = offsets.last;
{
final value = object.name;
if (value != null) {
bytesCount += 3 + value.length * 3;
}
}
return bytesCount;
}
void _categoriesModelSerialize(
CategoriesModel object,
IsarWriter writer,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
writer.writeString(offsets[0], object.name);
}
CategoriesModel _categoriesModelDeserialize(
Id id,
IsarReader reader,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = CategoriesModel(
id: id,
name: reader.readStringOrNull(offsets[0]),
);
return object;
}
P _categoriesModelDeserializeProp<P>(
IsarReader reader,
int propertyId,
int offset,
Map<Type, List<int>> allOffsets,
) {
switch (propertyId) {
case 0:
return (reader.readStringOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
}
Id _categoriesModelGetId(CategoriesModel object) {
return object.id ?? Isar.autoIncrement;
}
List<IsarLinkBase<dynamic>> _categoriesModelGetLinks(CategoriesModel object) {
return [];
}
void _categoriesModelAttach(
IsarCollection<dynamic> col, Id id, CategoriesModel object) {
object.id = id;
}
extension CategoriesModelQueryWhereSort
on QueryBuilder<CategoriesModel, CategoriesModel, QWhere> {
QueryBuilder<CategoriesModel, CategoriesModel, QAfterWhere> anyId() {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(const IdWhereClause.any());
});
}
}
extension CategoriesModelQueryWhere
on QueryBuilder<CategoriesModel, CategoriesModel, QWhereClause> {
QueryBuilder<CategoriesModel, CategoriesModel, QAfterWhereClause> idEqualTo(
Id id) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(
lower: id,
upper: id,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterWhereClause>
idNotEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
)
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
);
} else {
return query
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
)
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
);
}
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterWhereClause>
idGreaterThan(Id id, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: include),
);
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterWhereClause> idLessThan(
Id id,
{bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: include),
);
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterWhereClause> idBetween(
Id lowerId,
Id upperId, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(
lower: lowerId,
includeLower: includeLower,
upper: upperId,
includeUpper: includeUpper,
));
});
}
}
extension CategoriesModelQueryFilter
on QueryBuilder<CategoriesModel, CategoriesModel, QFilterCondition> {
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
idIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'id',
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
idIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'id',
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
idEqualTo(Id? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'id',
value: value,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
idGreaterThan(
Id? value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'id',
value: value,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
idLessThan(
Id? value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'id',
value: value,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
idBetween(
Id? lower,
Id? upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'id',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'name',
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'name',
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameEqualTo(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'name',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameGreaterThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'name',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameLessThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'name',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameBetween(
String? lower,
String? upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'name',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'name',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'name',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameContains(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'name',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameMatches(String pattern, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'name',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'name',
value: '',
));
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterFilterCondition>
nameIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'name',
value: '',
));
});
}
}
extension CategoriesModelQueryObject
on QueryBuilder<CategoriesModel, CategoriesModel, QFilterCondition> {}
extension CategoriesModelQueryLinks
on QueryBuilder<CategoriesModel, CategoriesModel, QFilterCondition> {}
extension CategoriesModelQuerySortBy
on QueryBuilder<CategoriesModel, CategoriesModel, QSortBy> {
QueryBuilder<CategoriesModel, CategoriesModel, QAfterSortBy> sortByName() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'name', Sort.asc);
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterSortBy>
sortByNameDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'name', Sort.desc);
});
}
}
extension CategoriesModelQuerySortThenBy
on QueryBuilder<CategoriesModel, CategoriesModel, QSortThenBy> {
QueryBuilder<CategoriesModel, CategoriesModel, QAfterSortBy> thenById() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.asc);
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterSortBy> thenByIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.desc);
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterSortBy> thenByName() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'name', Sort.asc);
});
}
QueryBuilder<CategoriesModel, CategoriesModel, QAfterSortBy>
thenByNameDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'name', Sort.desc);
});
}
}
extension CategoriesModelQueryWhereDistinct
on QueryBuilder<CategoriesModel, CategoriesModel, QDistinct> {
QueryBuilder<CategoriesModel, CategoriesModel, QDistinct> distinctByName(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'name', caseSensitive: caseSensitive);
});
}
}
extension CategoriesModelQueryProperty
on QueryBuilder<CategoriesModel, CategoriesModel, QQueryProperty> {
QueryBuilder<CategoriesModel, int, QQueryOperations> idProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'id');
});
}
QueryBuilder<CategoriesModel, String?, QQueryOperations> nameProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'name');
});
}
}

View file

@ -1,12 +1,12 @@
import 'package:hive/hive.dart';
import 'package:mangayomi/models/model_manga.dart';
part 'manga_history.g.dart';
// import 'package:hive/hive.dart';
// import 'package:mangayomi/models/model_manga.dart';
// part 'manga_history.g.dart';
@HiveType(typeId: 2)
class MangaHistoryModel extends HiveObject {
@HiveField(0)
final ModelManga modelManga;
@HiveField(1)
final String date;
MangaHistoryModel({required this.date, required this.modelManga});
}
// @HiveType(typeId: 2)
// class MangaHistoryModel extends HiveObject {
// @HiveField(0)
// final ModelManga modelManga;
// @HiveField(1)
// final String date;
// MangaHistoryModel({required this.date, required this.modelManga});
// }

View file

@ -1,44 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'manga_history.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class MangaHistoryModelAdapter extends TypeAdapter<MangaHistoryModel> {
@override
final int typeId = 2;
@override
MangaHistoryModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return MangaHistoryModel(
date: fields[1] as String,
modelManga: fields[0] as ModelManga,
);
}
@override
void write(BinaryWriter writer, MangaHistoryModel obj) {
writer
..writeByte(2)
..writeByte(0)
..write(obj.modelManga)
..writeByte(1)
..write(obj.date);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MangaHistoryModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View file

@ -1,97 +1,87 @@
import 'package:hive/hive.dart';
import 'package:isar/isar.dart';
part 'model_manga.g.dart';
@HiveType(typeId: 0)
class ModelManga extends HiveObject {
@HiveField(0)
@collection
@Name("Manga")
class ModelManga {
Id? id;
String? name;
@HiveField(1)
String? link;
@HiveField(2)
String? imageUrl;
@HiveField(3)
String? description;
@HiveField(4)
String? author;
@HiveField(5)
String? status;
@HiveField(6)
List<String>? genre;
@HiveField(7)
bool favorite;
@HiveField(8)
String? source;
@HiveField(9)
String? lang;
@HiveField(10)
int? dateAdded;
@HiveField(11)
int? lastUpdate;
@HiveField(12)
List<ModelChapters>? chapters;
@HiveField(13)
String? lastRead;
@HiveField(14)
List<int>? categories;
@Backlink(to: "manga")
final chapters = IsarLinks<ModelChapters>();
ModelManga(
{required this.source,
required this.author,
required this.favorite,
required this.genre,
required this.imageUrl,
required this.lang,
required this.link,
required this.name,
required this.status,
required this.description,
required this.dateAdded,
required this.lastUpdate,
required this.categories,
required this.lastRead,
required this.chapters});
ModelManga({
this.id = Isar.autoIncrement,
required this.source,
required this.author,
required this.favorite,
required this.genre,
required this.imageUrl,
required this.lang,
required this.link,
required this.name,
required this.status,
required this.description,
required this.dateAdded,
required this.lastUpdate,
required this.categories,
required this.lastRead,
});
}
@HiveType(typeId: 7)
class ModelChapters extends HiveObject {
@HiveField(0)
@collection
@Name("Chapters")
class ModelChapters {
Id? id;
int? mangaId;
String? name;
@HiveField(1)
String? url;
@HiveField(2)
String? dateUpload;
@HiveField(3)
String? scanlator;
@HiveField(4)
bool isBookmarked;
bool? isBookmarked;
@HiveField(5)
bool isRead;
bool? isRead;
@HiveField(6)
String lastPageRead;
String? lastPageRead;
final manga = IsarLink<ModelManga>();
ModelChapters(
{required this.name,
{this.id = Isar.autoIncrement,
required this.mangaId,
required this.name,
required this.url,
required this.dateUpload,
required this.isBookmarked,

File diff suppressed because it is too large Load diff

View file

@ -1,28 +1,30 @@
import 'dart:io';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/models/categories.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/models/manga_history.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/views/manga/download/download_model.dart';
import 'package:mangayomi/views/manga/download/model/download_model.dart';
import 'package:mangayomi/views/manga/reader/providers/reader_controller_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'hive_provider.g.dart';
@riverpod
Box<ModelManga> hiveBoxManga(HiveBoxMangaRef ref) {
return Hive.box<ModelManga>(HiveConstant.hiveBoxManga);
}
// @riverpod
// Box<ModelManga> hiveBoxManga(HiveBoxMangaRef ref) {
// return Hive.box<ModelManga>(HiveConstant.hiveBoxManga);
// }
@riverpod
Box hiveBoxMangaInfo(HiveBoxMangaInfoRef ref) {
return Hive.box(HiveConstant.hiveBoxMangaInfo);
}
@riverpod
Box<MangaHistoryModel> hiveBoxMangaHistory(HiveBoxMangaHistoryRef ref) {
return Hive.box<MangaHistoryModel>(HiveConstant.hiveBoxMangaHistory);
}
// @riverpod
// Box<MangaHistoryModel> hiveBoxMangaHistory(HiveBoxMangaHistoryRef ref) {
// return Hive.box<MangaHistoryModel>(HiveConstant.hiveBoxMangaHistory);
// }
@riverpod
Box<ReaderMode> hiveBoxReaderMode(HiveBoxReaderModeRef ref) {
@ -53,3 +55,4 @@ Box hiveBoxSettings(HiveBoxSettingsRef ref) {
Box<CategoriesModel> hiveBoxCategories(HiveBoxCategoriesRef ref) {
return Hive.box(HiveConstant.hiveBoxCategories);
}

View file

@ -6,20 +6,6 @@ part of 'hive_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$hiveBoxMangaHash() => r'63b8c649d7ac482b84fafa635626249294d4f92d';
/// See also [hiveBoxManga].
@ProviderFor(hiveBoxManga)
final hiveBoxMangaProvider = AutoDisposeProvider<Box<ModelManga>>.internal(
hiveBoxManga,
name: r'hiveBoxMangaProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$hiveBoxMangaHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef HiveBoxMangaRef = AutoDisposeProviderRef<Box<ModelManga>>;
String _$hiveBoxMangaInfoHash() => r'638c65c996c731a9764acc5911f9b0ec75b60273';
/// See also [hiveBoxMangaInfo].
@ -35,23 +21,6 @@ final hiveBoxMangaInfoProvider = AutoDisposeProvider<Box<dynamic>>.internal(
);
typedef HiveBoxMangaInfoRef = AutoDisposeProviderRef<Box<dynamic>>;
String _$hiveBoxMangaHistoryHash() =>
r'dd5c7a3cd8bfceb7b577c56f5ef8755914416c1e';
/// See also [hiveBoxMangaHistory].
@ProviderFor(hiveBoxMangaHistory)
final hiveBoxMangaHistoryProvider =
AutoDisposeProvider<Box<MangaHistoryModel>>.internal(
hiveBoxMangaHistory,
name: r'hiveBoxMangaHistoryProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$hiveBoxMangaHistoryHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef HiveBoxMangaHistoryRef = AutoDisposeProviderRef<Box<MangaHistoryModel>>;
String _$hiveBoxReaderModeHash() => r'fabc2f9e6b46c1ba0f1965630ff91f0bdccfeb99';
/// See also [hiveBoxReaderMode].

View file

@ -36,12 +36,12 @@ class StorageProvider {
Future<Directory?> getMangaChapterDirectory(
ModelManga modelManga, index) async {
String scanlator = modelManga.chapters![index].scanlator!.isNotEmpty
? "${modelManga.chapters![index].scanlator!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}_"
String scanlator = modelManga.chapters.toList()[index].scanlator!.isNotEmpty
? "${modelManga.chapters.toList()[index].scanlator!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}_"
: "";
final dir = await getDirectory();
return Directory(
"${dir!.path}/downloads/${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/$scanlator${modelManga.chapters![index].name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/");
"${dir!.path}/downloads/${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/$scanlator${modelManga.chapters.toList()[index].name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/");
}
Future<Directory?> getMangaMainDirectory(ModelManga modelManga, index) async {

View file

@ -106,23 +106,19 @@ class AsyncRouterNotifier extends ChangeNotifier {
GoRoute(
path: '/manga-reader/detail',
builder: (context, state) {
ModelManga? model;
model = state.extra as ModelManga;
int idManga = state.extra as int;
return MangaReaderDetail(
modelManga: model,
idManga: idManga,
);
},
pageBuilder: (context, state) {
ModelManga? model;
model = state.extra as ModelManga;
int idManga = state.extra as int;
return CustomTransition(
key: state.pageKey,
child: MangaReaderDetail(
modelManga: model,
idManga: idManga,
));
}),
GoRoute(

View file

@ -84,7 +84,7 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
List<bool> isLocaleList = [];
String source = modelManga.source!.toLowerCase();
List pagesUrl = ref.watch(hiveBoxMangaInfoProvider).get(
"${modelManga.lang}-${modelManga.source}/${modelManga.name}/${modelManga.chapters![index].name}-pageurl",
"${modelManga.lang}-${modelManga.source}/${modelManga.name}/${modelManga.chapters.toList()[index].name}-pageurl",
defaultValue: []);
final incognitoMode = ref.watch(incognitoModeStateProvider);
path = await StorageProvider().getMangaChapterDirectory(modelManga, index);
@ -98,7 +98,7 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
/********/
else if (getWpMangTypeSource(source) == TypeSource.comick) {
String mangaId =
modelManga.chapters![index].url!.split('/').last.split('-').first;
modelManga.chapters.toList()[index].url!.split('/').last.split('-').first;
final response = await httpGet(
url: 'https://api.comick.fun/chapter/$mangaId?tachiyomi=true',
@ -117,7 +117,7 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
else if (getWpMangTypeSource(source) == TypeSource.mangathemesia) {
final dom = await httpGet(
useUserAgent: true,
url: modelManga.chapters![index].url!,
url: modelManga.chapters.toList()[index].url!,
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('#readerarea').isNotEmpty) {
@ -165,7 +165,7 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
else if (source == 'mangakawaii') {
final response = await httpGet(
url: modelManga.chapters![index].url!,
url: modelManga.chapters.toList()[index].url!,
source: source,
resDom: false) as String?;
var chapterSlug = RegExp("""var chapter_slug = "([^"]*)";""")
@ -193,7 +193,7 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
else if (getWpMangTypeSource(source) == TypeSource.mmrcms) {
final dom = await httpGet(
useUserAgent: true,
url: modelManga.chapters![index].url!,
url: modelManga.chapters.toList()[index].url!,
source: source,
resDom: true) as Document?;
if (dom!.querySelectorAll('#all > .img-responsive').isNotEmpty) {
@ -242,7 +242,7 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
return secretKeyResultScript;
}
var link = "http://www.mangahere.cc${modelManga.chapters![index].url!}";
var link = "http://www.mangahere.cc${modelManga.chapters.toList()[index].url!}";
final response =
await httpGet(url: link, source: source, resDom: false) as String?;
@ -329,7 +329,7 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
} else if (source == 'japscan') {
final response = await httpGet(
useUserAgent: true,
url: modelManga.chapters![index].url!,
url: modelManga.chapters.toList()[index].url!,
source: source,
resDom: false) as String?;
RegExp regex = RegExp(r'<script src="/zjs/(.*?)"');
@ -349,7 +349,7 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
if (urll.isNotEmpty) {
if (!incognitoMode) {
ref.watch(hiveBoxMangaInfoProvider).put(
"${modelManga.lang}-${modelManga.source}/${modelManga.name}/${modelManga.chapters![index].name}-pageurl",
"${modelManga.lang}-${modelManga.source}/${modelManga.name}/${modelManga.chapters.toList()[index].name}-pageurl",
urll);
}
for (var i = 0; i < urll.length; i++) {

View file

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

View file

@ -5,6 +5,7 @@ import 'package:http/http.dart' as http;
import 'package:mangayomi/models/comick/manga_chapter_detail.dart';
import 'package:mangayomi/models/comick/manga_detail_comick.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/services/get_popular_manga.dart';
import 'package:mangayomi/services/http_service/http_res_to_dom_html.dart';
import 'package:mangayomi/services/http_service/http_service.dart';
@ -86,6 +87,7 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
String? description;
List<ModelChapters> chapters = [];
List<String> scanlators = [];
/********/
/*comick*/
/********/
@ -669,7 +671,8 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
isBookmarked: false,
scanlator: scanlators.isEmpty ? "" : scanlators[i],
isRead: false,
lastPageRead: ''));
lastPageRead: '',
mangaId: null));
}
}

View file

@ -6,7 +6,7 @@ part of 'get_manga_detail.dart';
// RiverpodGenerator
// **************************************************************************
String _$getMangaDetailHash() => r'ded3d41646d56c670699614c7ab01069ddc52b8d';
String _$getMangaDetailHash() => r'8a6b3c3948b179e4614ce45e293d11e0faf9a3ba';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -151,14 +151,14 @@ List<SourceModel> sourcesList = [
lang: "en",
typeSource: TypeSource.mangathemesia,
logoUrl: ''),
SourceModel(
sourceName: "ArenaScans",
url: "https://arenascans.net",
lang: "en",
typeSource: TypeSource.mangathemesia,
logoUrl:
'https://arenascans.net/wp-content/uploads/2023/02/arena-logo-1.png',
isCloudflare: true),
// SourceModel(
// sourceName: "ArenaScans",
// url: "https://arenascans.net",
// lang: "en",
// typeSource: TypeSource.mangathemesia,
// logoUrl:
// 'https://arenascans.net/wp-content/uploads/2023/02/arena-logo-1.png',
// isCloudflare: true),
SourceModel(
sourceName: "AzureScans",
url: "https://azuremanga.com",

View file

@ -1,14 +1,14 @@
class HiveConstant {
static String get hiveBoxManga => "manga_box";
static String get hiveBoxMangaInfo => "manga_box_info";
static String get hiveBoxMangaHistory => "_manga_box_history_";
static String get hiveBoxMangaSource => "_manga_box_source_";
static String get hiveBoxMangaFilter => "_manga_box_filter_";
static String get hiveBoxAppSettings => "_app_box_settings_";
static String get hiveBoxDownloads => "_manga_box_downloads_";
static String get hiveBoxReaderSettings => "_reader_box_settings_";
static String get hiveBoxReaderMode => "_readerMode_box_settings_";
static String get hiveBoxCategories => "_manga_box_categorie_";
static String get hiveBoxManga => "manga_box_";
static String get hiveBoxMangaInfo => "manga_box_info_";
static String get hiveBoxMangaHistory => "_manga_box_history__";
static String get hiveBoxMangaSource => "_manga_box_source__";
static String get hiveBoxMangaFilter => "_manga_box_filter__";
static String get hiveBoxAppSettings => "_app_box_settings__";
static String get hiveBoxDownloads => "_manga_box_downloads___";
static String get hiveBoxReaderSettings => "_reader_box_settings__";
static String get hiveBoxReaderMode => "_readerMode_box_settings__";
static String get hiveBoxCategories => "_manga_box_categorie__";
}
const defaultUserAgent =

View file

@ -193,7 +193,6 @@ class _MangaGlobalImageCardState extends ConsumerState<MangaGlobalImageCard>
lang: widget.lang,
dateAdded: DateTime.now().microsecondsSinceEpoch,
lastUpdate: DateTime.now().microsecondsSinceEpoch,
chapters: data.chapters,
categories: null,
lastRead: '');
if (mounted) {

View file

@ -22,283 +22,288 @@ class HistoryScreen extends ConsumerStatefulWidget {
class _HistoryScreenState extends ConsumerState<HistoryScreen> {
final _textEditingController = TextEditingController();
bool _isSearch = false;
List<MangaHistoryModel> entriesData = [];
List<MangaHistoryModel> entriesFilter = [];
// List<MangaHistoryModel> entriesData = [];
// List<MangaHistoryModel> entriesFilter = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
title: _isSearch
? null
: Text(
'History',
style: TextStyle(color: Theme.of(context).hintColor),
),
actions: [
_isSearch
? SeachFormTextField(
onChanged: (value) {
setState(() {
entriesFilter = entriesData
.where((element) => element.modelManga.name!
.toLowerCase()
.contains(value.toLowerCase()))
.toList();
});
},
onSuffixPressed: () {
_textEditingController.clear();
setState(() {});
},
onPressed: () {
setState(() {
_isSearch = false;
});
_textEditingController.clear();
},
controller: _textEditingController,
)
: IconButton(
splashRadius: 20,
onPressed: () {
setState(() {
_isSearch = true;
});
},
icon: Icon(Icons.search, color: Theme.of(context).hintColor)),
IconButton(
splashRadius: 20,
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text(
"Remove everything",
),
content: const Text(
'Are you sure? All history will be lost.'),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text("Cancel")),
const SizedBox(
width: 15,
),
TextButton(
onPressed: () {
ref
.watch(hiveBoxMangaHistoryProvider)
.clear();
Navigator.pop(context);
},
child: const Text("Ok")),
],
)
],
);
});
},
icon: Icon(Icons.delete_sweep_outlined,
color: Theme.of(context).hintColor)),
],
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: ValueListenableBuilder<Box<MangaHistoryModel>>(
valueListenable: ref.watch(hiveBoxMangaHistoryProvider).listenable(),
builder: (context, value, child) {
final entries = value.values.toList();
entriesData = value.values.toList();
final entriesHistory = _textEditingController.text.isNotEmpty
? entriesFilter
: entries;
if (entries.isNotEmpty) {
return GroupedListView<MangaHistoryModel, String>(
elements: entriesHistory,
groupBy: (element) => element.date.substring(0, 10),
groupSeparatorBuilder: (String groupByValue) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Text(dateFormat(DateTime.parse(groupByValue))),
],
),
),
itemBuilder: (context, MangaHistoryModel element) {
return SizedBox(
height: 105,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 60,
height: 90,
child: GestureDetector(
onTap: () {
context.push('/manga-reader/detail',
extra: element.modelManga);
},
child: ClipRRect(
borderRadius: BorderRadius.circular(7),
child: cachedNetworkImage(
headers: headers(element.modelManga.source!),
imageUrl: element.modelManga.imageUrl!,
width: 60,
height: 90,
fit: BoxFit.cover),
),
),
),
Flexible(
child: ValueListenableBuilder<Box>(
valueListenable: ref
.watch(hiveBoxMangaInfoProvider)
.listenable(),
builder: (context, value, child) {
final values = value.get(
"${element.modelManga.lang}-${element.modelManga.source}/${element.modelManga.name}-chapter_index",
defaultValue: '');
if (values.isNotEmpty) {
return Row(
children: [
Expanded(
child: GestureDetector(
onTap: () {
pushMangaReaderView(
context: context,
modelManga: element.modelManga,
index:
int.parse(values.toString()));
},
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
element.modelManga.name!,
style: const TextStyle(
fontSize: 14,
fontWeight:
FontWeight.bold),
textAlign: TextAlign.start,
),
Wrap(
crossAxisAlignment:
WrapCrossAlignment.end,
children: [
Text(
element
.modelManga
.chapters![int.parse(
values
.toString())]
.name!,
style: const TextStyle(
fontSize: 11,
),
),
const Text(' - '),
Text(
DateFormat.Hm().format(
DateTime.parse(
element.date)),
style: const TextStyle(
fontSize: 11,
fontWeight:
FontWeight.w400),
),
],
),
],
),
),
),
),
),
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text(
"Remove",
),
content: const Text(
'This will remove the read date of this chapter. Are you sure?'),
actions: [
Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(
context);
},
child: const Text(
"Cancel")),
const SizedBox(
width: 15,
),
TextButton(
onPressed: () {
ref
.watch(
hiveBoxMangaHistoryProvider)
.delete(
'${element.modelManga.lang}-${element.modelManga.link}');
Navigator.pop(
context);
},
child: const Text(
"Remove")),
],
)
],
);
});
},
icon: const Icon(
Icons.delete_outline,
size: 25,
)),
],
);
}
return Container();
},
),
)
],
),
);
},
itemComparator: (item1, item2) =>
item1.date.compareTo(item2.date),
order: GroupedListOrder.DESC,
);
}
return const Center(
child: Text('Nothing read recently'),
);
},
),
),
);
return Container();
// Scaffold(
// appBar: AppBar(
// elevation: 0,
// backgroundColor: Colors.transparent,
// title: _isSearch
// ? null
// : Text(
// 'History',
// style: TextStyle(color: Theme.of(context).hintColor),
// ),
// actions: [
// _isSearch
// ? SeachFormTextField(
// onChanged: (value) {
// setState(() {
// entriesFilter = entriesData
// .where((element) => element.modelManga.name!
// .toLowerCase()
// .contains(value.toLowerCase()))
// .toList();
// });
// },
// onSuffixPressed: () {
// _textEditingController.clear();
// setState(() {});
// },
// onPressed: () {
// setState(() {
// _isSearch = false;
// });
// _textEditingController.clear();
// },
// controller: _textEditingController,
// )
// : IconButton(
// splashRadius: 20,
// onPressed: () {
// setState(() {
// _isSearch = true;
// });
// },
// icon: Icon(Icons.search, color: Theme.of(context).hintColor)),
// IconButton(
// splashRadius: 20,
// onPressed: () {
// showDialog(
// context: context,
// builder: (context) {
// return AlertDialog(
// title: const Text(
// "Remove everything",
// ),
// content: const Text(
// 'Are you sure? All history will be lost.'),
// actions: [
// Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// TextButton(
// onPressed: () {
// Navigator.pop(context);
// },
// child: const Text("Cancel")),
// const SizedBox(
// width: 15,
// ),
// TextButton(
// onPressed: () {
// ref
// .watch(hiveBoxMangaHistoryProvider)
// .clear();
// Navigator.pop(context);
// },
// child: const Text("Ok")),
// ],
// )
// ],
// );
// });
// },
// icon: Icon(Icons.delete_sweep_outlined,
// color: Theme.of(context).hintColor)),
// ],
// ),
// body: Padding(
// padding: const EdgeInsets.symmetric(horizontal: 20),
// child: ValueListenableBuilder<Box<MangaHistoryModel>>(
// valueListenable: ref.watch(hiveBoxMangaHistoryProvider).listenable(),
// builder: (context, value, child) {
// final entries = value.values.toList();
// entriesData = value.values.toList();
// final entriesHistory = _textEditingController.text.isNotEmpty
// ? entriesFilter
// : entries;
// if (entries.isNotEmpty) {
// return GroupedListView<MangaHistoryModel, String>(
// elements: entriesHistory,
// groupBy: (element) => element.date.substring(0, 10),
// groupSeparatorBuilder: (String groupByValue) => Padding(
// padding: const EdgeInsets.only(bottom: 8),
// child: Row(
// children: [
// Text(dateFormat(DateTime.parse(groupByValue))),
// ],
// ),
// ),
// itemBuilder: (context, MangaHistoryModel element) {
// return SizedBox(
// height: 105,
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// SizedBox(
// width: 60,
// height: 90,
// child: GestureDetector(
// onTap: () {
// context.push('/manga-reader/detail',
// extra: element.modelManga);
// },
// child: ClipRRect(
// borderRadius: BorderRadius.circular(7),
// child: cachedNetworkImage(
// headers: headers(element.modelManga.source!),
// imageUrl: element.modelManga.imageUrl!,
// width: 60,
// height: 90,
// fit: BoxFit.cover),
// ),
// ),
// ),
// Flexible(
// child: ValueListenableBuilder<Box>(
// valueListenable: ref
// .watch(hiveBoxMangaInfoProvider)
// .listenable(),
// builder: (context, value, child) {
// final values = value.get(
// "${element.modelManga.lang}-${element.modelManga.source}/${element.modelManga.name}-chapter_index",
// defaultValue: '');
// if (values.isNotEmpty) {
// return Row(
// children: [
// Expanded(
// child: GestureDetector(
// onTap: () {
// pushMangaReaderView(
// context: context,
// modelManga: element.modelManga,
// index:
// int.parse(values.toString()));
// },
// child: Container(
// color: Colors.transparent,
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: Column(
// mainAxisAlignment:
// MainAxisAlignment.center,
// crossAxisAlignment:
// CrossAxisAlignment.start,
// children: [
// Text(
// element.modelManga.name!,
// style: const TextStyle(
// fontSize: 14,
// fontWeight:
// FontWeight.bold),
// textAlign: TextAlign.start,
// ),
// Wrap(
// crossAxisAlignment:
// WrapCrossAlignment.end,
// children: [
// Text(
// element
// .modelManga.chapters
// .toList()[int.parse(
// values
// .toString())]
// .name!,
// style: const TextStyle(
// fontSize: 11,
// ),
// ),
// const Text(' - '),
// Text(
// DateFormat.Hm().format(
// DateTime.parse(
// element.date)),
// style: const TextStyle(
// fontSize: 11,
// fontWeight:
// FontWeight.w400),
// ),
// ],
// ),
// ],
// ),
// ),
// ),
// ),
// ),
// IconButton(
// onPressed: () {
// showDialog(
// context: context,
// builder: (context) {
// return AlertDialog(
// title: const Text(
// "Remove",
// ),
// content: const Text(
// 'This will remove the read date of this chapter. Are you sure?'),
// actions: [
// Row(
// mainAxisAlignment:
// MainAxisAlignment.end,
// children: [
// TextButton(
// onPressed: () {
// Navigator.pop(
// context);
// },
// child: const Text(
// "Cancel")),
// const SizedBox(
// width: 15,
// ),
// TextButton(
// onPressed: () {
// ref
// .watch(
// hiveBoxMangaHistoryProvider)
// .delete(
// '${element.modelManga.lang}-${element.modelManga.link}');
// Navigator.pop(
// context);
// },
// child: const Text(
// "Remove")),
// ],
// )
// ],
// );
// });
// },
// icon: const Icon(
// Icons.delete_outline,
// size: 25,
// )),
// ],
// );
// }
// return Container();
// },
// ),
// )
// ],
// ),
// );
// },
// itemComparator: (item1, item2) =>
// item1.date.compareTo(item2.date),
// order: GroupedListOrder.DESC,
// );
// }
// return const Center(
// child: Text('Nothing read recently'),
// );
// },
// ),
// ),
// );
}
}

View file

@ -4,6 +4,8 @@ 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:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/categories.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
@ -45,12 +47,17 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
final isNotFiltering = ref
.read(mangaFilterResultStateProvider(mangaList: entries).notifier)
.isNotFiltering();
return ValueListenableBuilder<Box<CategoriesModel>>(
valueListenable: ref.watch(hiveBoxCategoriesProvider).listenable(),
builder: (context, value, child) {
final entr = value.values.toList();
if (entr.isNotEmpty && showCategoryTabs) {
tabBarController = TabController(length: entr.length, vsync: this);
return StreamBuilder(
stream: isar.categoriesModels
.filter()
.idIsNotNull()
.watch(fireImmediately: true),
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.data!.isNotEmpty &&
showCategoryTabs) {
final entr = snapshot.data;
tabBarController = TabController(length: entr!.length, vsync: this);
tabBarController.animateTo(tabIndex);
tabBarController.addListener(() {
tabIndex = tabBarController.index;
@ -73,35 +80,35 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
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;
body: StreamBuilder(
stream: isar.modelMangas
.filter()
.idIsNotNull()
.favoriteEqualTo(true)
.categoriesIsNotEmpty()
.categoriesElementEqualTo(e.id!)
.watch(fireImmediately: true),
builder: (context, snapshot) {
// final data = ref.watch(
// mangaFilterResultStateProvider(
// mangaList: entries));
// entriesFilter = _textEditingController
// .text.isNotEmpty
// ? data
// .where((element) => element.name!
// .toLowerCase()
// .contains(_textEditingController
// .text
// .toLowerCase()))
// .toList()
// : data;
if (entriesFilter.isNotEmpty) {
if (snapshot.hasData &&
snapshot.data!.isNotEmpty) {
final entries = snapshot.data!;
final entriesManga = reverse
? entries.reversed.toList()
: entries;
return displayType == DisplayType.list
? LibraryListViewWidget(
entriesManga: entriesManga,
@ -143,25 +150,17 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
}
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) {
body: StreamBuilder(
stream: isar.modelMangas
.filter()
.idIsNotNull()
.favoriteEqualTo(true)
.watch(fireImmediately: true),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
final entries = snapshot.data!;
final entriesManga =
reverse ? entries.reversed.toList() : entries;
return displayType == DisplayType.list
? LibraryListViewWidget(
entriesManga: entriesManga,

View file

@ -84,13 +84,13 @@ class MangaFilterDownloadedState extends _$MangaFilterDownloadedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloadsProvider)
.get(chap.name, defaultValue: null);
if (modelChapDownload != null &&
modelChapDownload.isDownload == true) {
list.add(true);
}
// final modelChapDownload = ref
// .watch(hiveBoxMangaDownloadsProvider)
// .get(chap.name, defaultValue: null);
// if (modelChapDownload != null &&
// modelChapDownload.isDownload == true) {
// list.add(true);
// }
}
return list.isNotEmpty;
}).toList();
@ -100,13 +100,13 @@ class MangaFilterDownloadedState extends _$MangaFilterDownloadedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloadsProvider)
.get(chap.name, defaultValue: null);
if (modelChapDownload == null ||
modelChapDownload.isDownload == false) {
list.add(true);
}
// final modelChapDownload = ref
// .watch(hiveBoxMangaDownloadsProvider)
// .get(chap.name, defaultValue: null);
// if (modelChapDownload == null ||
// modelChapDownload.isDownload == false) {
// list.add(true);
// }
}
return list.length == element.chapters!.length;
}).toList();
@ -121,13 +121,13 @@ class MangaFilterDownloadedState extends _$MangaFilterDownloadedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloadsProvider)
.get(chap.name, defaultValue: null);
if (modelChapDownload != null &&
modelChapDownload.isDownload == true) {
list.add(true);
}
// final modelChapDownload = ref
// .watch(hiveBoxMangaDownloadsProvider)
// .get(chap.name, defaultValue: null);
// if (modelChapDownload != null &&
// modelChapDownload.isDownload == true) {
// list.add(true);
// }
}
return list.isNotEmpty;
}).toList();
@ -137,13 +137,13 @@ class MangaFilterDownloadedState extends _$MangaFilterDownloadedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloadsProvider)
.get(chap.name, defaultValue: null);
if (modelChapDownload == null ||
modelChapDownload.isDownload == false) {
list.add(true);
}
// final modelChapDownload = ref
// .watch(hiveBoxMangaDownloadsProvider)
// .get(chap.name, defaultValue: null);
// if (modelChapDownload == null ||
// modelChapDownload.isDownload == false) {
// list.add(true);
// }
}
return list.length == element.chapters!.length;
}).toList();
@ -180,7 +180,7 @@ class MangaFilterUnreadState extends _$MangaFilterUnreadState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (!chap.isRead) {
if (!chap.isRead!) {
list.add(true);
}
}
@ -191,7 +191,7 @@ class MangaFilterUnreadState extends _$MangaFilterUnreadState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (chap.isRead) {
if (chap.isRead!) {
list.add(true);
}
}
@ -208,7 +208,7 @@ class MangaFilterUnreadState extends _$MangaFilterUnreadState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (!chap.isRead) {
if (!chap.isRead!) {
list.add(true);
}
}
@ -220,7 +220,7 @@ class MangaFilterUnreadState extends _$MangaFilterUnreadState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (chap.isRead) {
if (chap.isRead!) {
list.add(true);
}
}
@ -259,7 +259,7 @@ class MangaFilterStartedState extends _$MangaFilterStartedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (!chap.isRead) {
if (!chap.isRead!) {
list.add(true);
}
}
@ -270,7 +270,7 @@ class MangaFilterStartedState extends _$MangaFilterStartedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (chap.isRead) {
if (chap.isRead!) {
list.add(true);
}
}
@ -287,7 +287,7 @@ class MangaFilterStartedState extends _$MangaFilterStartedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (!chap.isRead) {
if (!chap.isRead!) {
list.add(true);
}
}
@ -299,7 +299,7 @@ class MangaFilterStartedState extends _$MangaFilterStartedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (chap.isRead) {
if (chap.isRead!) {
list.add(true);
}
}
@ -338,7 +338,7 @@ class MangaFilterBookmarkedState extends _$MangaFilterBookmarkedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (chap.isBookmarked) {
if (chap.isBookmarked!) {
list.add(true);
}
}
@ -349,7 +349,7 @@ class MangaFilterBookmarkedState extends _$MangaFilterBookmarkedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (!chap.isBookmarked) {
if (!chap.isBookmarked!) {
list.add(true);
}
}
@ -366,7 +366,7 @@ class MangaFilterBookmarkedState extends _$MangaFilterBookmarkedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (chap.isBookmarked) {
if (chap.isBookmarked!) {
list.add(true);
}
}
@ -378,7 +378,7 @@ class MangaFilterBookmarkedState extends _$MangaFilterBookmarkedState {
final data = mangaList.where((element) {
List list = [];
for (var chap in element.chapters!) {
if (!chap.isBookmarked) {
if (!chap.isBookmarked!) {
list.add(true);
}
}

View file

@ -41,7 +41,7 @@ final libraryDisplayTypeStateProvider =
typedef _$LibraryDisplayTypeState = AutoDisposeNotifier<String>;
String _$mangaFilterDownloadedStateHash() =>
r'18ed17c06f41084cbb92b0b3300025f4e65aa413';
r'3c09b61fc80e35711b308b2b0050879c37cdd299';
/// Copied from Dart SDK
class _SystemHash {
@ -161,7 +161,7 @@ class MangaFilterDownloadedStateProvider
}
String _$mangaFilterUnreadStateHash() =>
r'5eed6ec9f46f1562d48eb89a078f666ed5b466d8';
r'bb771edf600c96e1b9fe4ceb786f143fe7050bfb';
abstract class _$MangaFilterUnreadState
extends BuildlessAutoDisposeNotifier<int> {
@ -260,7 +260,7 @@ class MangaFilterUnreadStateProvider
}
String _$mangaFilterStartedStateHash() =>
r'cf5440f02e8454d75de4f311f945b33f73668ea2';
r'b8447b0eb414f15c4200bad461c260bdde3fe91c';
abstract class _$MangaFilterStartedState
extends BuildlessAutoDisposeNotifier<int> {
@ -359,7 +359,7 @@ class MangaFilterStartedStateProvider
}
String _$mangaFilterBookmarkedStateHash() =>
r'cdeeb68e7428e4856db3551443c70e28c3c7f95d';
r'777ac2ceb266d5bf6837ea270fd62ab8471add92';
abstract class _$MangaFilterBookmarkedState
extends BuildlessAutoDisposeNotifier<int> {

View file

@ -8,7 +8,6 @@ import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/utils/headers.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';
@ -38,7 +37,7 @@ class LibraryGridViewWidget extends StatelessWidget {
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
context.push('/manga-reader/detail', extra: entriesManga[index]);
context.push('/manga-reader/detail', extra: entriesManga[index].id);
},
child: CoverViewWidget(
bottomTextWidget: BottomTextWidget(
@ -76,7 +75,7 @@ class LibraryGridViewWidget extends StatelessWidget {
for (var i = 0;
i <
entriesManga[index]
.chapters!
.chapters
.length;
i++) {
final entries = ref
@ -84,12 +83,10 @@ class LibraryGridViewWidget extends StatelessWidget {
hiveBoxMangaDownloadsProvider)
.values
.where((element) =>
element
.modelManga
.chapters![element.index]
.name ==
element.chapterName ==
entriesManga[index]
.chapters![i]
.chapters
.toList()[i]
.name)
.toList();
if (entries.isNotEmpty &&
@ -127,7 +124,7 @@ class LibraryGridViewWidget extends StatelessWidget {
padding: const EdgeInsets.only(right: 3),
child: Text(
entriesManga[index]
.chapters!
.chapters
.length
.toString(),
style: const TextStyle(color: Colors.white),
@ -217,7 +214,7 @@ class LibraryGridViewWidget extends StatelessWidget {
context: context,
modelManga: entriesManga[index],
index: entriesManga[index]
.chapters!
.chapters
.length -
1);
},

View file

@ -8,7 +8,6 @@ import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/utils/headers.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';
@ -87,21 +86,16 @@ class LibraryListViewWidget extends StatelessWidget {
builder: (context, ref, child) {
List nbrDown = [];
for (var i = 0;
i <
entriesManga[index]
.chapters!
.length;
i < entriesManga[index].chapters.length;
i++) {
final entries = ref
.watch(hiveBoxMangaDownloadsProvider)
.values
.where((element) =>
element
.modelManga
.chapters![element.index]
.name ==
element.chapterName ==
entriesManga[index]
.chapters![i]
.chapters
.toList()[i]
.name)
.toList();
if (entries.isNotEmpty &&
@ -136,7 +130,7 @@ class LibraryListViewWidget extends StatelessWidget {
Padding(
padding: const EdgeInsets.only(right: 3),
child: Text(
entriesManga[index].chapters!.length.toString(),
entriesManga[index].chapters.length.toString(),
style: const TextStyle(color: Colors.white),
),
),
@ -208,9 +202,8 @@ class LibraryListViewWidget extends StatelessWidget {
pushMangaReaderView(
context: context,
modelManga: entriesManga[index],
index:
entriesManga[index].chapters!.length -
1);
index: entriesManga[index].chapters.length -
1);
},
child: Container(
decoration: BoxDecoration(

View file

@ -56,12 +56,12 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
bool _expanded = false;
ScrollController _scrollController = ScrollController();
List<ModelChapters>? _chapters;
ModelManga? _modelManga;
int? _pageLength;
@override
Widget build(BuildContext context) {
final isLongPressed = ref.watch(isLongPressedStateProvider);
final chapterNameList = ref.watch(chapterIdsListStateProvider);
bool reverse = ref.watch(
reverseChapterStateProvider(modelManga: widget.modelManga!))["reverse"];
return NotificationListener<UserScrollNotification>(
onNotification: (notification) {
if (notification.direction == ScrollDirection.forward) {
@ -85,7 +85,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
.isNotFiltering();
final isLongPressed = ref.watch(isLongPressedStateProvider);
final chapterNameList =
ref.watch(chapterNameListStateProvider);
ref.watch(chapterIdsListStateProvider);
return isLongPressed
? Container(
color: Theme.of(context).scaffoldBackgroundColor,
@ -96,7 +96,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
leading: IconButton(
onPressed: () {
ref
.read(chapterNameListStateProvider
.read(chapterIdsListStateProvider
.notifier)
.clear();
@ -109,49 +109,37 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
actions: [
IconButton(
onPressed: () {
for (var i = 0;
i <
widget
.modelManga!.chapters!.length;
i++) {
for (var chapter
in widget.modelManga!.chapters) {
ref
.read(chapterNameListStateProvider
.read(chapterIdsListStateProvider
.notifier)
.selectAll(
"${widget.modelManga!.chapters![i].name!}$i");
.selectAll(chapter);
}
},
icon: const Icon(Icons.select_all)),
IconButton(
onPressed: () {
if (widget.modelManga!.chapters!.length ==
if (widget.modelManga!.chapters.length ==
chapterNameList.length) {
for (var i = 0;
i <
widget.modelManga!.chapters!
.length;
i++) {
for (var chapter
in widget.modelManga!.chapters) {
ref
.read(chapterNameListStateProvider
.read(chapterIdsListStateProvider
.notifier)
.selectSome(
"${widget.modelManga!.chapters![i].name}$i");
.selectSome(chapter);
}
ref
.read(isLongPressedStateProvider
.notifier)
.update(false);
} else {
for (var i = 0;
i <
widget.modelManga!.chapters!
.length;
i++) {
for (var chapter
in widget.modelManga!.chapters) {
ref
.read(chapterNameListStateProvider
.read(chapterIdsListStateProvider
.notifier)
.selectSome(
"${widget.modelManga!.chapters![i].name}$i");
.selectSome(chapter);
}
}
},
@ -160,227 +148,201 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
],
),
)
: AppBar(
title: ref.watch(offetProvider) > 200
? Text(
widget.modelManga!.name!,
style: const TextStyle(fontSize: 17),
)
: null,
backgroundColor: ref.watch(offetProvider) == 0.0
? Colors.transparent
: Theme.of(context).scaffoldBackgroundColor,
actions: [
IconButton(
splashRadius: 20,
onPressed: () {},
icon: const Icon(
Icons.download_outlined,
: Stack(
children: [
Positioned(
top: 0,
child: Stack(
children: [
cachedNetworkImage(
headers: headers(
widget.modelManga!.source!),
imageUrl:
widget.modelManga!.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),
),
],
)),
IconButton(
splashRadius: 20,
onPressed: () {
_showDraggableMenu();
},
icon: Icon(
Icons.filter_list_sharp,
color:
isNotFiltering ? null : Colors.yellow,
)),
PopupMenuButton(
itemBuilder: (context) {
return [
if (widget.modelManga!.favorite)
const PopupMenuItem<int>(
value: 0,
child: Text("Edit categories")),
if (widget.modelManga!.favorite)
const PopupMenuItem<int>(
value: 0, child: Text("Migrate")),
const PopupMenuItem<int>(
value: 0, child: Text("Share")),
];
},
onSelected: (value) {}),
AppBar(
title: ref.watch(offetProvider) > 200
? Text(
widget.modelManga!.name!,
style: const TextStyle(fontSize: 17),
)
: null,
backgroundColor: ref.watch(offetProvider) == 0.0
? Colors.transparent
: Theme.of(context).scaffoldBackgroundColor,
actions: [
IconButton(
splashRadius: 20,
onPressed: () {},
icon: const Icon(
Icons.download_outlined,
)),
IconButton(
splashRadius: 20,
onPressed: () {
_showDraggableMenu();
},
icon: Icon(
Icons.filter_list_sharp,
color: isNotFiltering
? null
: Colors.yellow,
)),
PopupMenuButton(
itemBuilder: (context) {
return [
if (widget.modelManga!.favorite)
const PopupMenuItem<int>(
value: 0,
child: Text("Edit categories")),
if (widget.modelManga!.favorite)
const PopupMenuItem<int>(
value: 0,
child: Text("Migrate")),
const PopupMenuItem<int>(
value: 0, child: Text("Share")),
];
},
onSelected: (value) {}),
],
)
],
);
},
)),
body: Stack(
children: [
Positioned(
top: 0,
child: Stack(
children: [
cachedNetworkImage(
headers: headers(widget.modelManga!.source!),
imageUrl: widget.modelManga!.imageUrl!,
width: mediaWidth(context, 1),
height: 461,
fit: BoxFit.cover),
Container(
width: mediaWidth(context, 1),
height: 465,
color: Theme.of(context)
.scaffoldBackgroundColor
.withOpacity(0.9),
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,
),
SafeArea(
child: Container(
width: mediaWidth(context, 1),
height: mediaHeight(context, 1),
color: Theme.of(context).scaffoldBackgroundColor),
)
],
)),
SafeArea(child: Consumer(builder: (context, ref, child) {
_pageLength = ref
.watch(chapterFilterResultStateProvider(
modelManga: widget.modelManga!))
.chapters!
.length +
1;
_chapters = ref
.watch(chapterFilterResultStateProvider(
modelManga: widget.modelManga!))
.chapters;
_modelManga = ref.watch(chapterFilterResultStateProvider(
modelManga: widget.modelManga!));
bool reverse = ref.watch(reverseChapterStateProvider(
modelManga: widget.modelManga!))["reverse"];
return 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,
),
);
},
scrollbarTimeToFade: const Duration(seconds: 2),
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.only(top: 0, bottom: 60),
itemCount: _pageLength,
itemBuilder: (context, index) {
int finalIndex = index - 1;
if (index == 0) {
return _bodyContainer();
}
int reverseIndex = _chapters!.length -
_chapters!.reversed.toList().indexOf(
_chapters!.reversed.toList()[finalIndex]) -
1;
);
},
scrollbarTimeToFade: const Duration(seconds: 2),
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.only(top: 0, bottom: 60),
itemCount: widget.listLength,
itemBuilder: (context, index) {
int finalIndex = index - 1;
if (index == 0) {
return _bodyContainer();
}
List<ModelChapters> chapters = reverse
? _chapters!.reversed.toList()
: _chapters!;
int reverseIndex =
widget.modelManga!.chapters.length -
widget.modelManga!.chapters
.toList()
.reversed
.toList()
.indexOf(widget.modelManga!.chapters
.toList()
.reversed
.toList()[finalIndex]) -
1;
final indexx = reverse ? reverseIndex : finalIndex;
List<ModelChapters> chapters = reverse
? widget.modelManga!.chapters
.toList()
.reversed
.toList()
: widget.modelManga!.chapters.toList();
return ChapterListTileWidget(
chapters: chapters,
modelManga: _modelManga!,
reverse: reverse,
reverseIndex: reverseIndex,
finalIndex: finalIndex,
);
}));
})),
],
),
bottomNavigationBar: Consumer(
builder: (context, ref, child) {
final chapter = ref.watch(chapterModelStateProvider);
final isLongPressed = ref.watch(isLongPressedStateProvider);
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(
elevation: 0,
backgroundColor: Colors.transparent),
onPressed: () async {
ref
.read(chapterSetIsBookmarkStateProvider(
modelManga: widget.modelManga!)
.notifier)
.set();
ref
.read(chapterNameListStateProvider.notifier)
.clear();
},
child: Icon(chapter.isBookmarked
? Icons.bookmark_remove
: Icons.bookmark_add_outlined)),
),
),
Expanded(
child: SizedBox(
height: 70,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: Colors.transparent),
onPressed: () {
ref
.read(chapterSetIsReadStateProvider(
modelManga: widget.modelManga!)
.notifier)
.set();
ref
.read(chapterNameListStateProvider.notifier)
.clear();
},
child: Icon(chapter.isRead
? Icons.remove_done_sharp
: Icons.done_all_sharp)),
),
),
Expanded(
child: SizedBox(
height: 70,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: Colors.transparent),
onPressed: () {
ref
.read(chapterSetDownloadStateProvider(
modelManga: widget.modelManga!)
.notifier)
.set();
ref
.read(chapterNameListStateProvider.notifier)
.clear();
},
child: const Icon(Icons.download_outlined)),
),
)
],
return ChapterListTileWidget(
modelManga: widget.modelManga!,
chapter: chapters[indexx],
chapterNameList: chapterNameList,
chapterIndex: indexx,
);
}))),
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(
modelManga: widget.modelManga!)
.notifier)
.set();
},
child: const Icon(Icons.bookmark_add_outlined)),
),
),
);
},
Expanded(
child: SizedBox(
height: 70,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: Colors.transparent),
onPressed: () {
ref
.read(chapterSetIsReadStateProvider(
modelManga: widget.modelManga!)
.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(
modelManga: widget.modelManga!)
.notifier)
.set();
},
child: const Icon(Icons.download_outlined)),
),
)
],
),
)));
}
@ -659,7 +621,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
padding:
const EdgeInsets.symmetric(horizontal: 8),
child: Text(
'${(_pageLength! - 1)} chapters',
'${(widget.listLength - 1)} chapters',
style: const TextStyle(
fontWeight: FontWeight.bold),
),

View file

@ -3,6 +3,8 @@ 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:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/categories.dart';
import 'package:mangayomi/models/manga_reader.dart';
import 'package:mangayomi/models/model_manga.dart';
@ -55,132 +57,132 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
@override
Widget build(BuildContext context) {
final manga = ref.watch(hiveBoxMangaProvider);
_checkFavorite(widget.modelManga.favorite);
return Scaffold(
floatingActionButton: ref.watch(isLongPressedStateProvider) == true
? null
: widget.modelManga.chapters!.isNotEmpty
? ValueListenableBuilder<Box>(
valueListenable:
ref.watch(hiveBoxMangaInfoProvider).listenable(),
builder: (context, value, child) {
final entries = value.get(
"${widget.modelManga.lang}-${widget.modelManga.source}/${widget.modelManga.name}-chapter_index",
defaultValue: '');
final incognitoMode = ref.watch(incognitoModeStateProvider);
// floatingActionButton: ref.watch(isLongPressedStateProvider) == true
// ? null
// : widget.modelManga.chapters!.isNotEmpty
// ? ValueListenableBuilder<Box>(
// valueListenable:
// ref.watch(hiveBoxMangaInfoProvider).listenable(),
// builder: (context, value, child) {
// final entries = value.get(
// "${widget.modelManga.lang}-${widget.modelManga.source}/${widget.modelManga.name}-chapter_index",
// defaultValue: '');
// final incognitoMode = ref.watch(incognitoModeStateProvider);
if (entries.isNotEmpty && !incognitoMode) {
return Consumer(builder: (context, ref, child) {
final isExtended = ref.watch(isExtendedStateProvider);
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedContainer(
height: 55,
width: !isExtended ? 63 : 130,
duration: const Duration(milliseconds: 200),
curve: Curves.easeIn,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor(context),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(15))),
onPressed: () {
pushMangaReaderView(
context: context,
modelManga: widget.modelManga,
index: int.parse(entries.toString()));
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.play_arrow,
color: Colors.white,
),
AnimatedContainer(
curve: Curves.easeIn,
width: !isExtended ? 0 : 8,
duration:
const Duration(milliseconds: 500),
),
AnimatedContainer(
curve: Curves.easeIn,
width: !isExtended ? 0 : 60,
duration:
const Duration(milliseconds: 200),
child: const Text(
"Continue",
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14, color: Colors.white),
),
),
],
),
),
),
],
);
});
}
return Consumer(builder: (context, ref, child) {
final isExtended = ref.watch(isExtendedStateProvider);
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedContainer(
height: 55,
width: !isExtended ? 60 : 105,
duration: const Duration(milliseconds: 300),
curve: Curves.easeIn,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor(context),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15))),
onPressed: () {
pushMangaReaderView(
context: context,
modelManga: widget.modelManga,
index:
widget.modelManga.chapters!.length - 1);
},
child: Row(
children: [
const Icon(
Icons.play_arrow,
color: Colors.white,
),
AnimatedContainer(
curve: Curves.easeIn,
width: !isExtended ? 0 : 5,
duration: const Duration(milliseconds: 300),
),
AnimatedContainer(
curve: Curves.easeIn,
width: !isExtended ? 0 : 40,
duration: const Duration(milliseconds: 300),
child: const Text(
"Read",
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
color: Colors.white,
),
),
),
],
),
),
),
],
);
});
},
)
: null,
// if (entries.isNotEmpty && !incognitoMode) {
// return Consumer(builder: (context, ref, child) {
// final isExtended = ref.watch(isExtendedStateProvider);
// return Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// AnimatedContainer(
// height: 55,
// width: !isExtended ? 63 : 130,
// duration: const Duration(milliseconds: 200),
// curve: Curves.easeIn,
// child: ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: primaryColor(context),
// shape: RoundedRectangleBorder(
// borderRadius:
// BorderRadius.circular(15))),
// onPressed: () {
// pushMangaReaderView(
// context: context,
// modelManga: widget.modelManga,
// index: int.parse(entries.toString()));
// },
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// const Icon(
// Icons.play_arrow,
// color: Colors.white,
// ),
// AnimatedContainer(
// curve: Curves.easeIn,
// width: !isExtended ? 0 : 8,
// duration:
// const Duration(milliseconds: 500),
// ),
// AnimatedContainer(
// curve: Curves.easeIn,
// width: !isExtended ? 0 : 60,
// duration:
// const Duration(milliseconds: 200),
// child: const Text(
// "Continue",
// overflow: TextOverflow.ellipsis,
// style: TextStyle(
// fontSize: 14, color: Colors.white),
// ),
// ),
// ],
// ),
// ),
// ),
// ],
// );
// });
// }
// return Consumer(builder: (context, ref, child) {
// final isExtended = ref.watch(isExtendedStateProvider);
// return Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// AnimatedContainer(
// height: 55,
// width: !isExtended ? 60 : 105,
// duration: const Duration(milliseconds: 300),
// curve: Curves.easeIn,
// child: ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: primaryColor(context),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(15))),
// onPressed: () {
// pushMangaReaderView(
// context: context,
// modelManga: widget.modelManga,
// index:
// widget.modelManga.chapters!.length - 1);
// },
// child: Row(
// children: [
// const Icon(
// Icons.play_arrow,
// color: Colors.white,
// ),
// AnimatedContainer(
// curve: Curves.easeIn,
// width: !isExtended ? 0 : 5,
// duration: const Duration(milliseconds: 300),
// ),
// AnimatedContainer(
// curve: Curves.easeIn,
// width: !isExtended ? 0 : 40,
// duration: const Duration(milliseconds: 300),
// child: const Text(
// "Read",
// overflow: TextOverflow.ellipsis,
// style: TextStyle(
// fontSize: 14,
// color: Colors.white,
// ),
// ),
// ),
// ],
// ),
// ),
// ),
// ],
// );
// });
// },
// )
// : null,
body: MangaDetailView(
titleDescription: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -210,185 +212,160 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
)
],
),
action: ValueListenableBuilder<Box<ModelManga>>(
valueListenable: ref.watch(hiveBoxMangaProvider).listenable(),
builder: (context, value, child) {
final entries = value.values
.where((element) =>
'${element.lang}-${element.link}' ==
'${widget.modelManga.lang}-${widget.modelManga.link}')
.toList();
if (entries.isNotEmpty) {
if (entries[0].favorite == true) {
_checkFavorite(true);
return SizedBox(
width: mediaWidth(context, 0.4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
elevation: 0),
onPressed: () {
_setFavorite(false);
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: false,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang,
dateAdded: widget.modelManga.dateAdded,
lastUpdate: widget.modelManga.lastUpdate,
chapters: widget.modelManga.chapters,
categories: [],
lastRead: widget.modelManga.lastRead);
manga.put(
'${widget.modelManga.lang}-${widget.modelManga.link}',
model);
},
child: Column(
children: const [
Icon(
Icons.favorite,
size: 22,
),
SizedBox(
height: 4,
),
Text(
'In library',
style: TextStyle(fontSize: 13),
)
],
),
action: widget.modelManga.favorite
? SizedBox(
width: mediaWidth(context, 0.4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
elevation: 0),
onPressed: () async {
_setFavorite(false);
final model = widget.modelManga;
await isar.writeTxn(() async {
model.favorite = false;
await isar.modelMangas.put(model);
});
},
child: Column(
children: const [
Icon(
Icons.favorite,
size: 22,
),
SizedBox(
height: 4,
),
Text(
'In library',
style: TextStyle(fontSize: 13),
)
],
),
);
} else {
_checkFavorite(false);
return SizedBox(
width: mediaWidth(context, 0.4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
elevation: 0),
onPressed: () {
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: [
Icon(
Icons.favorite_border_rounded,
size: 22,
color: secondaryColor(context),
),
const SizedBox(
height: 4,
),
Text(
'Add to library',
style: TextStyle(
color: secondaryColor(context), fontSize: 13),
)
],
),
),
)
: SizedBox(
width: mediaWidth(context, 0.4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
elevation: 0),
onPressed: () async {
final checkCategoryList = await isar.categoriesModels
.filter()
.idIsNotNull()
.isNotEmpty();
if (checkCategoryList) {
_openCategory(widget.modelManga);
} else {
_setFavorite(true);
final model = widget.modelManga;
await isar.writeTxn(() async {
model.favorite = true;
await isar.modelMangas.put(model);
});
}
},
child: Column(
children: [
Icon(
Icons.favorite_border_rounded,
size: 22,
color: secondaryColor(context),
),
const SizedBox(
height: 4,
),
Text(
'Add to library',
style: TextStyle(
color: secondaryColor(context), fontSize: 13),
)
],
),
);
}
}
return SizedBox(
width: mediaWidth(context, 0.4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
elevation: 0),
onPressed: () {
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: [
Icon(
Icons.favorite_border_rounded,
size: 22,
color: secondaryColor(context),
),
const SizedBox(
height: 4,
),
Text(
'Add to library',
style: TextStyle(
color: secondaryColor(context), fontSize: 13),
)
],
),
),
);
},
),
// ValueListenableBuilder<Box<ModelManga>>(
// valueListenable: ref.watch(hiveBoxMangaProvider).listenable(),
// builder: (context, value, child) {
// final entries = value.values
// .where((element) =>
// '${element.lang}-${element.link}' ==
// '${widget.modelManga.lang}-${widget.modelManga.link}')
// .toList();
// if (entries.isNotEmpty) {
// if (entries[0].favorite == true) {
// _checkFavorite(true);
// return ;
// } else {
// _checkFavorite(false);
// return ;
// }
// }
// return SizedBox(
// width: mediaWidth(context, 0.4),
// child: ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: Theme.of(context).scaffoldBackgroundColor,
// elevation: 0),
// onPressed: () {
// 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: [
// Icon(
// Icons.favorite_border_rounded,
// size: 22,
// color: secondaryColor(context),
// ),
// const SizedBox(
// height: 4,
// ),
// Text(
// 'Add to library',
// style: TextStyle(
// color: secondaryColor(context), fontSize: 13),
// )
// ],
// ),
// ),
// );
// },
// ),
modelManga: widget.modelManga,
listLength: widget.modelManga.chapters!.length + 1,
listLength: widget.modelManga.chapters.length + 1,
isExtended: (value) {
ref.read(isExtendedStateProvider.notifier).update(value);
},
@ -396,7 +373,7 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
);
}
_openCategory(Box<ModelManga> manga) {
_openCategory(ModelManga manga) {
List<int> categoryIds = [];
showDialog(
context: context,
@ -409,32 +386,38 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
),
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,
);
},
);
child: StreamBuilder(
stream: isar.categoriesModels
.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 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,
);
},
);
}
return Container();
}),
),
actions: [
@ -458,30 +441,17 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
width: 15,
),
TextButton(
onPressed: () {
onPressed: () async {
_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);
final model = widget.modelManga;
await isar.writeTxn(() async {
model.favorite = true;
model.categories = categoryIds;
await isar.modelMangas.put(model);
});
if (mounted) {
Navigator.pop(context);
}
},
child: const Text(
"OK",

View file

@ -1,22 +1,33 @@
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/services/get_manga_detail.dart';
import 'package:mangayomi/views/manga/detail/manga_details_view.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
class MangaReaderDetail extends ConsumerStatefulWidget {
final ModelManga modelManga;
const MangaReaderDetail({super.key, required this.modelManga});
final int idManga;
const MangaReaderDetail({super.key, required this.idManga});
@override
ConsumerState<MangaReaderDetail> createState() => _MangaReaderDetailState();
}
final mangaa = StreamProvider.family<ModelManga?, int>((ref, id) async* {
// final ddd = isar.modelMangas.filter().chapters((q) => q.isReadEqualTo(true)).build().watch();
yield* isar.modelMangas.watchObject(id, fireImmediately: true);
});
class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
bool _isFavorite = false;
@override
@ -25,6 +36,7 @@ class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
super.initState();
}
@ -36,95 +48,118 @@ class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
@override
Widget build(BuildContext context) {
final mm = ref.watch(mangaa(widget.idManga));
return Scaffold(
body: RefreshIndicator(
onRefresh: () async {
if (_isFavorite) {
bool isOk = false;
ref
.watch(getMangaDetailProvider(
imageUrl: widget.modelManga.imageUrl!,
lang: widget.modelManga.lang!,
title: widget.modelManga.name!,
source: widget.modelManga.source!,
url: widget.modelManga.link!)
.future)
.then((value) {
if (value.chapters.isNotEmpty &&
value.chapters.length > widget.modelManga.chapters!.length) {
List<ModelChapters>? chapters = [];
for (var chap in widget.modelManga.chapters!) {
chapters.add(chap);
}
int newChapsSize =
value.chapters.length - widget.modelManga.chapters!.length;
for (var i = 0; i < newChapsSize; i++) {
chapters.insert(i, value.chapters[i]);
}
final model = ModelManga(
imageUrl: widget.modelManga.imageUrl,
name: widget.modelManga.name,
genre: widget.modelManga.genre,
author: widget.modelManga.author,
description: widget.modelManga.description,
status: value.status,
favorite: _isFavorite,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang,
dateAdded: widget.modelManga.dateAdded,
lastUpdate: DateTime.now().microsecondsSinceEpoch,
chapters: chapters,
categories: widget.modelManga.categories,
lastRead: widget.modelManga.lastRead);
ref.watch(hiveBoxMangaProvider).put(
'${widget.modelManga.lang}-${widget.modelManga.link}',
model);
}
if (mounted) {
setState(() {
isOk = true;
});
}
body: mm.when(
data: (data) {
// final mang = data.where((element) => element.id==widget.idManga).toList().first;
return MangaDetailsView(
modelManga: data!,
isFavorite: (value) {
setState(() {
_isFavorite = value;
});
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
if (isOk == true) {
return false;
}
return true;
});
}
},
child: ValueListenableBuilder<Box<ModelManga>>(
valueListenable: ref.watch(hiveBoxMangaProvider).listenable(),
builder: (context, value, child) {
final entries = value.values
.where((element) =>
'${element.lang}-${element.link}' ==
'${widget.modelManga.lang}-${widget.modelManga.link}')
.toList();
if (entries.isNotEmpty) {
return MangaDetailsView(
modelManga: entries[0],
isFavorite: (value) {
setState(() {
_isFavorite = value;
});
},
);
}
return MangaDetailsView(
modelManga: widget.modelManga,
isFavorite: (value) {
setState(() {
_isFavorite = value;
});
},
);
},
),
),
);
);
},
error: (Object error, StackTrace stackTrace) {
return Text("data");
},
loading: () {
return Text("eeee");
},
)
// RefreshIndicator(
// onRefresh: () async {
// if (_isFavorite) {
// // bool isOk = false;
// // ref
// // .watch(getMangaDetailProvider(
// // imageUrl: widget.modelManga.imageUrl!,
// // lang: widget.modelManga.lang!,
// // title: widget.modelManga.name!,
// // source: widget.modelManga.source!,
// // url: widget.modelManga.link!)
// // .future)
// // .then((value) {
// // if (value.chapters.isNotEmpty &&
// // value.chapters.length > widget.modelManga.chapters!.length) {
// // List<ModelChapters>? chapters = [];
// // for (var chap in widget.modelManga.chapters!) {
// // chapters.add(chap);
// // }
// // int newChapsSize =
// // value.chapters.length - widget.modelManga.chapters!.length;
// // for (var i = 0; i < newChapsSize; i++) {
// // chapters.insert(i, value.chapters[i]);
// // }
// // final model = ModelManga(
// // imageUrl: widget.modelManga.imageUrl,
// // name: widget.modelManga.name,
// // genre: widget.modelManga.genre,
// // author: widget.modelManga.author,
// // description: widget.modelManga.description,
// // status: value.status,
// // favorite: _isFavorite,
// // link: widget.modelManga.link,
// // source: widget.modelManga.source,
// // lang: widget.modelManga.lang,
// // dateAdded: widget.modelManga.dateAdded,
// // lastUpdate: DateTime.now().microsecondsSinceEpoch,
// // chapters: chapters,
// // categories: widget.modelManga.categories,
// // lastRead: widget.modelManga.lastRead);
// // ref.watch(hiveBoxMangaProvider).put(
// // '${widget.modelManga.lang}-${widget.modelManga.link}',
// // model);
// // }
// // if (mounted) {
// // setState(() {
// // isOk = true;
// // });
// // }
// // });
// // await Future.doWhile(() async {
// // await Future.delayed(const Duration(seconds: 1));
// // if (isOk == true) {
// // return false;
// // }
// // return true;
// // });
// }
// },
// child:
// // ValueListenableBuilder<ModelManga>(
// // valueListenable: ref.watch(hiveBoxMangaProvider).listenable(),
// // builder: (context, value, child) {
// // final entries = value.values
// // .where((element) =>
// // '${element.lang}-${element.link}' ==
// // '${widget.modelManga.lang}-${widget.modelManga.link}')
// // .toList();
// // if (entries.isNotEmpty) {
// // return MangaDetailsView(
// // modelManga: entries[0],
// // isFavorite: (value) {
// // setState(() {
// // _isFavorite = value;
// // });
// // },
// // );
// // }
// // return MangaDetailsView(
// // modelManga: widget.modelManga,
// // isFavorite: (value) {
// // setState(() {
// // _isFavorite = value;
// // });
// // },
// // );
// // },
// // ),
// ),
);
}
}

View file

@ -1,5 +1,7 @@
import 'dart:convert';
import 'dart:developer';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/views/manga/download/providers/download_provider.dart';
@ -17,7 +19,8 @@ class ChapterModelState extends _$ChapterModelState {
isBookmarked: false,
scanlator: "",
isRead: false,
lastPageRead: "");
lastPageRead: "",
mangaId: null);
}
void update(ModelChapters chapters) {
@ -26,13 +29,13 @@ class ChapterModelState extends _$ChapterModelState {
}
@riverpod
class ChapterNameListState extends _$ChapterNameListState {
class ChapterIdsListState extends _$ChapterIdsListState {
@override
List<String> build() {
List<ModelChapters> build() {
return [];
}
void update(String value) {
void update(ModelChapters value) {
var newList = state.reversed.toList();
if (newList.contains(value)) {
newList.remove(value);
@ -45,7 +48,7 @@ class ChapterNameListState extends _$ChapterNameListState {
state = newList;
}
void selectAll(String value) {
void selectAll(ModelChapters value) {
var newList = state.reversed.toList();
if (!newList.contains(value)) {
newList.add(value);
@ -54,7 +57,7 @@ class ChapterNameListState extends _$ChapterNameListState {
state = newList;
}
void selectSome(String value) {
void selectSome(ModelChapters value) {
var newList = state.reversed.toList();
if (newList.contains(value)) {
newList.remove(value);
@ -167,26 +170,26 @@ class ChapterFilterDownloadedState extends _$ChapterFilterDownloadedState {
ModelManga getData() {
if (getType() == 1) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!.where((element) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloadsProvider)
.get(element.name, defaultValue: null);
return modelChapDownload != null &&
modelChapDownload.isDownload == true;
}).toList();
// chap = modelManga.chapters.where((element) {
// final modelChapDownload = ref
// .watch(hiveBoxMangaDownloadsProvider)
// .get(element.name, defaultValue: null);
// return modelChapDownload != null &&
// modelChapDownload.isDownload == true;
// }).toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
return model;
} else if (getType() == 2) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!.where((element) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloadsProvider)
.get(element.name, defaultValue: null);
return !(modelChapDownload != null &&
modelChapDownload.isDownload == true);
}).toList();
// chap = modelManga.chapters.where((element) {
// final modelChapDownload = ref
// .watch(hiveBoxMangaDownloadsProvider)
// .get(element.name, defaultValue: null);
// return !(modelChapDownload != null &&
// modelChapDownload.isDownload == true);
// }).toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
@ -199,26 +202,26 @@ class ChapterFilterDownloadedState extends _$ChapterFilterDownloadedState {
ModelManga update() {
if (state == 0) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!.where((element) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloadsProvider)
.get(element.name, defaultValue: null);
return modelChapDownload != null &&
modelChapDownload.isDownload == true;
}).toList();
// chap = modelManga.chapters!.where((element) {
// final modelChapDownload = ref
// .watch(hiveBoxMangaDownloadsProvider)
// .get(element.name, defaultValue: null);
// return modelChapDownload != null &&
// modelChapDownload.isDownload == true;
// }).toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
setType(1);
return model;
} else if (state == 1) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!.where((element) {
final modelChapDownload = ref
.watch(hiveBoxMangaDownloadsProvider)
.get(element.name, defaultValue: null);
return !(modelChapDownload != null &&
modelChapDownload.isDownload == true);
}).toList();
// chap = modelManga.chapters!.where((element) {
// final modelChapDownload = ref
// .watch(hiveBoxMangaDownloadsProvider)
// .get(element.name, defaultValue: null);
// return !(modelChapDownload != null &&
// modelChapDownload.isDownload == true);
// }).toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
setType(2);
@ -253,14 +256,18 @@ class ChapterFilterUnreadState extends _$ChapterFilterUnreadState {
ModelManga getData() {
if (getType() == 1) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!.where((element) => !element.isRead).toList();
chap = modelManga.chapters
.where((element) => element.isRead == false)
.toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
return model;
} else if (getType() == 2) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!.where((element) => element.isRead).toList();
chap = modelManga.chapters
.where((element) => element.isRead == true)
.toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
@ -273,14 +280,18 @@ class ChapterFilterUnreadState extends _$ChapterFilterUnreadState {
ModelManga update() {
if (state == 0) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!.where((element) => !element.isRead).toList();
chap = modelManga.chapters
.where((element) => element.isRead == false)
.toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
setType(1);
return model;
} else if (state == 1) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!.where((element) => element.isRead).toList();
chap = modelManga.chapters
.where((element) => element.isRead == false)
.toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
setType(2);
@ -315,16 +326,16 @@ class ChapterFilterBookmarkedState extends _$ChapterFilterBookmarkedState {
ModelManga getData() {
if (getType() == 1) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!
.where((element) => element.isBookmarked)
chap = modelManga.chapters
.where((element) => element.isBookmarked == true)
.toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
return model;
} else if (getType() == 2) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!
.where((element) => !element.isBookmarked)
chap = modelManga.chapters
.where((element) => element.isBookmarked == false)
.toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
@ -338,8 +349,8 @@ class ChapterFilterBookmarkedState extends _$ChapterFilterBookmarkedState {
ModelManga update() {
if (state == 0) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!
.where((element) => element.isBookmarked)
chap = modelManga.chapters
.where((element) => element.isBookmarked == true)
.toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
@ -347,8 +358,8 @@ class ChapterFilterBookmarkedState extends _$ChapterFilterBookmarkedState {
return model;
} else if (state == 1) {
List<ModelChapters> chap = [];
chap = modelManga.chapters!
.where((element) => !element.isBookmarked)
chap = modelManga.chapters
.where((element) => element.isBookmarked == false)
.toList();
final model =
modelMangaWithNewChapValue(modelManga: modelManga, chapters: chap);
@ -380,7 +391,7 @@ class ChapterFilterResultState extends _$ChapterFilterResultState {
.read(chapterFilterBookmarkedStateProvider(modelManga: data2).notifier)
.getData();
if (indexSelected == 0) {
data3.chapters!.sort(
data3.chapters.toList().sort(
(a, b) {
return b.scanlator!.compareTo(a.scanlator!);
},
@ -392,7 +403,7 @@ class ChapterFilterResultState extends _$ChapterFilterResultState {
// },
// );
} else {
data3.chapters!.sort(
data3.chapters.toList().sort(
(a, b) {
return a.dateUpload!.compareTo(b.dateUpload!);
},
@ -430,7 +441,6 @@ ModelManga modelMangaWithNewChapValue(
lang: modelManga.lang,
dateAdded: modelManga.dateAdded,
lastUpdate: modelManga.lastUpdate,
chapters: chapters,
categories: modelManga.categories,
lastRead: modelManga.lastRead);
}
@ -440,17 +450,17 @@ class ChapterSetIsBookmarkState extends _$ChapterSetIsBookmarkState {
@override
build({required ModelManga modelManga}) {}
set() {
ref.read(isLongPressedStateProvider.notifier).update(false);
for (var name in ref.watch(chapterNameListStateProvider)) {
for (var i = 0; i < modelManga.chapters!.length; i++) {
modelManga.chapters![i].isBookmarked =
name == "${modelManga.chapters![i].name}$i"
? !modelManga.chapters![i].isBookmarked
: modelManga.chapters![i].isBookmarked;
set() async {
final chapters = ref.watch(chapterIdsListStateProvider);
await isar.writeTxn(() async {
for (var chapter in chapters) {
chapter.isBookmarked = !chapter.isBookmarked!;
await isar.modelChapters.put(chapter..manga.value = modelManga);
await chapter.manga.save();
}
modelManga.save();
}
});
ref.read(isLongPressedStateProvider.notifier).update(false);
ref.read(chapterIdsListStateProvider.notifier).clear();
}
}
@ -459,17 +469,17 @@ class ChapterSetIsReadState extends _$ChapterSetIsReadState {
@override
build({required ModelManga modelManga}) {}
set() {
ref.read(isLongPressedStateProvider.notifier).update(false);
for (var name in ref.watch(chapterNameListStateProvider)) {
for (var i = 0; i < modelManga.chapters!.length; i++) {
modelManga.chapters![i].isRead =
name == "${modelManga.chapters![i].name}$i"
? !modelManga.chapters![i].isRead
: modelManga.chapters![i].isRead;
set() async {
final chapters = ref.watch(chapterIdsListStateProvider);
await isar.writeTxn(() async {
for (var chapter in chapters) {
chapter.isRead = !chapter.isRead!;
await isar.modelChapters.put(chapter..manga.value = modelManga);
await chapter.manga.save();
}
modelManga.save();
}
});
ref.read(isLongPressedStateProvider.notifier).update(false);
ref.read(chapterIdsListStateProvider.notifier).clear();
}
}
@ -481,30 +491,31 @@ class ChapterSetDownloadState extends _$ChapterSetDownloadState {
set() {
ref.read(isLongPressedStateProvider.notifier).update(false);
List<int> indexList = [];
for (var name in ref.watch(chapterNameListStateProvider)) {
for (var i = 0; i < modelManga.chapters!.length; i++) {
if ("${modelManga.chapters![i].name}$i" == name) {
for (var name in ref.watch(chapterIdsListStateProvider)) {
for (var i = 0; i < modelManga.chapters.length; i++) {
if ("$i" == name) {
indexList.add(i);
}
}
}
for (var idx in indexList) {
final entries = ref
.watch(hiveBoxMangaDownloadsProvider)
.values
.where((element) =>
element.modelManga.chapters![element.index].name ==
modelManga.chapters![idx].name)
.toList();
if (entries.isEmpty) {
ref.watch(downloadChapterProvider(modelManga: modelManga, index: idx));
} else {
if (!entries.first.isDownload) {
ref.watch(
downloadChapterProvider(modelManga: modelManga, index: idx));
}
}
// final entries = ref
// .watch(hiveBoxMangaDownloadsProvider)
// .values
// .where((element) =>
// element.modelManga.chapters.toList()[element.index].name ==
// modelManga.chapters.toList()[idx].name)
// .toList();
// if (entries.isEmpty) {
// // ref.watch(downloadChapterProvider(modelManga: modelManga, index: idx));
// } else {
// if (!entries.first.isDownload) {
// // ref.watch(
// // downloadChapterProvider(modelManga: modelManga, index: idx));
// }
// }
}
ref.read(chapterIdsListStateProvider.notifier).clear();
}
}

View file

@ -6,7 +6,7 @@ part of 'state_providers.dart';
// RiverpodGenerator
// **************************************************************************
String _$chapterModelStateHash() => r'39804350aba3fc457cb2ddcda25c1ca41069a537';
String _$chapterModelStateHash() => r'd36dd66381770f0e710929ae95ed100828c8d9ae';
/// See also [ChapterModelState].
@ProviderFor(ChapterModelState)
@ -22,23 +22,23 @@ final chapterModelStateProvider =
);
typedef _$ChapterModelState = AutoDisposeNotifier<ModelChapters>;
String _$chapterNameListStateHash() =>
r'7ad81711d912271910489528b88b8d473c1d9c60';
String _$chapterIdsListStateHash() =>
r'0cfdc515b7c2086eea593eba19ccd0b1f2ecdcd6';
/// See also [ChapterNameListState].
@ProviderFor(ChapterNameListState)
final chapterNameListStateProvider =
AutoDisposeNotifierProvider<ChapterNameListState, List<String>>.internal(
ChapterNameListState.new,
name: r'chapterNameListStateProvider',
/// See also [ChapterIdsListState].
@ProviderFor(ChapterIdsListState)
final chapterIdsListStateProvider = AutoDisposeNotifierProvider<
ChapterIdsListState, List<ModelChapters>>.internal(
ChapterIdsListState.new,
name: r'chapterIdsListStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$chapterNameListStateHash,
: _$chapterIdsListStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ChapterNameListState = AutoDisposeNotifier<List<String>>;
typedef _$ChapterIdsListState = AutoDisposeNotifier<List<ModelChapters>>;
String _$isLongPressedStateHash() =>
r'26fe435e8381046a30e3f6c4495303946aa3aaa7';
@ -73,7 +73,7 @@ final isExtendedStateProvider =
typedef _$IsExtendedState = AutoDisposeNotifier<bool>;
String _$reverseChapterStateHash() =>
r'7fb5d8f60f32377ca365eb328f989e31569c8b19';
r'd7f4d869e7c4bbceaee361bf790d4e8bba7df30e';
/// Copied from Dart SDK
class _SystemHash {
@ -193,7 +193,7 @@ class ReverseChapterStateProvider
}
String _$chapterFilterDownloadedStateHash() =>
r'6b433670cf840fe1e8166ae5b7a4fe17c9b56b5d';
r'33e96291f58f4ddfb1271c44ab06032da80e7c58';
abstract class _$ChapterFilterDownloadedState
extends BuildlessAutoDisposeNotifier<int> {
@ -293,7 +293,7 @@ class ChapterFilterDownloadedStateProvider
}
String _$chapterFilterUnreadStateHash() =>
r'ee96d8e6b3f145096e00ff3c09fda07d5f968047';
r'bcd0a645901a80401affe5bf08d92244e3324832';
abstract class _$ChapterFilterUnreadState
extends BuildlessAutoDisposeNotifier<int> {
@ -392,7 +392,7 @@ class ChapterFilterUnreadStateProvider
}
String _$chapterFilterBookmarkedStateHash() =>
r'f9761f9a32d0d6af53f513f862476a7125054ce7';
r'2f7d04afd075d55b6d7849ed312bc40fcfd35e05';
abstract class _$ChapterFilterBookmarkedState
extends BuildlessAutoDisposeNotifier<int> {
@ -492,7 +492,7 @@ class ChapterFilterBookmarkedStateProvider
}
String _$chapterFilterResultStateHash() =>
r'a0c0bccb457db8ccfba52e2b7e36a1f6e2b6afe3';
r'2efadc2a20b1b7ffde1ce95da77e52b9930a5543';
abstract class _$ChapterFilterResultState
extends BuildlessAutoDisposeNotifier<ModelManga> {
@ -591,7 +591,7 @@ class ChapterFilterResultStateProvider extends AutoDisposeNotifierProviderImpl<
}
String _$chapterSetIsBookmarkStateHash() =>
r'c12ed82216c7a649b9937226d841e413209ec704';
r'd52ed51da1434f4192f99e316279414011a51200';
abstract class _$ChapterSetIsBookmarkState
extends BuildlessAutoDisposeNotifier<dynamic> {
@ -690,7 +690,7 @@ class ChapterSetIsBookmarkStateProvider extends AutoDisposeNotifierProviderImpl<
}
String _$chapterSetIsReadStateHash() =>
r'2976cee969928c8ce433e173e53ba67e6ee49d6d';
r'88a5f68d8d24924f6399cb86347150669a725211';
abstract class _$ChapterSetIsReadState
extends BuildlessAutoDisposeNotifier<dynamic> {
@ -789,7 +789,7 @@ class ChapterSetIsReadStateProvider
}
String _$chapterSetDownloadStateHash() =>
r'cc003f376668ea8b1c8f3f6d03190f72d172f55e';
r'b74fbabed0abb3abbebd50c6f1af789b13af3f97';
abstract class _$ChapterSetDownloadState
extends BuildlessAutoDisposeNotifier<dynamic> {

View file

@ -10,79 +10,57 @@ import 'package:mangayomi/views/manga/detail/providers/state_providers.dart';
import 'package:mangayomi/views/manga/download/download_page_widget.dart';
class ChapterListTileWidget extends ConsumerWidget {
final List<ModelChapters> chapters;
final ModelManga modelManga;
final bool reverse;
final int reverseIndex;
final int finalIndex;
final ModelChapters chapter;
final int chapterIndex;
final List<ModelChapters> chapterNameList;
const ChapterListTileWidget({
required this.chapterNameList,
required this.chapter,
required this.chapterIndex,
super.key,
required this.chapters,
required this.modelManga,
required this.reverse,
required this.reverseIndex,
required this.finalIndex,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isLongPressed = ref.watch(isLongPressedStateProvider);
final idx = reverse ? reverseIndex : finalIndex;
final chapterNameList = ref.watch(chapterNameListStateProvider);
log(chapterNameList.toString());
final chapterName = modelManga.chapters![idx].name;
return Container(
color: chapterNameList.contains("$chapterName$idx")
color: chapterNameList.contains(chapter)
? primaryColor(context).withOpacity(0.4)
: null,
child: ListTile(
textColor: chapters[finalIndex].isRead
textColor: chapter.isRead!
? isLight(context)
? Colors.black.withOpacity(0.4)
: Colors.white.withOpacity(0.3)
: null,
selectedColor: chapters[finalIndex].isRead
? Colors.white.withOpacity(0.3)
: Colors.white,
selectedColor:
chapter.isRead! ? Colors.white.withOpacity(0.3) : Colors.white,
onLongPress: () {
if (!isLongPressed) {
ref
.read(chapterNameListStateProvider.notifier)
.update("$chapterName$idx");
ref
.read(chapterModelStateProvider.notifier)
.update(chapters[finalIndex]);
ref.read(chapterIdsListStateProvider.notifier).update(chapter);
ref.read(chapterModelStateProvider.notifier).update(chapter);
ref
.read(isLongPressedStateProvider.notifier)
.update(!isLongPressed);
} else {
ref
.read(chapterNameListStateProvider.notifier)
.update("$chapterName$idx");
ref
.read(chapterModelStateProvider.notifier)
.update(chapters[finalIndex]);
ref.read(chapterIdsListStateProvider.notifier).update(chapter);
ref.read(chapterModelStateProvider.notifier).update(chapter);
}
},
onTap: () async {
if (isLongPressed) {
ref
.read(chapterNameListStateProvider.notifier)
.update("$chapterName$idx");
ref
.read(chapterModelStateProvider.notifier)
.update(chapters[finalIndex]);
ref.read(chapterIdsListStateProvider.notifier).update(chapter);
ref.read(chapterModelStateProvider.notifier).update(chapter);
} else {
pushMangaReaderView(
context: context,
modelManga: modelManga,
index: reverse ? reverseIndex : finalIndex);
context: context, modelManga: modelManga, index: chapterIndex);
}
},
title: Row(
children: [
chapters[finalIndex].isBookmarked
chapter.isBookmarked!
? Icon(
Icons.bookmark,
size: 15,
@ -91,7 +69,7 @@ class ChapterListTileWidget extends ConsumerWidget {
: Container(),
Flexible(
child: Text(
chapters[finalIndex].name!,
chapter.name!,
style: const TextStyle(fontSize: 13),
overflow: TextOverflow.ellipsis,
),
@ -101,16 +79,15 @@ class ChapterListTileWidget extends ConsumerWidget {
subtitle: Row(
children: [
Text(
chapters[finalIndex].dateUpload!,
chapter.dateUpload!,
style: const TextStyle(fontSize: 11),
),
if (chapters[finalIndex].lastPageRead.isNotEmpty &&
chapters[finalIndex].lastPageRead != "1")
if (chapter.lastPageRead!.isNotEmpty && chapter.lastPageRead != "1")
Row(
children: [
const Text(''),
Text(
"Page ${chapters[finalIndex].lastPageRead}",
"Page ${chapter.lastPageRead}",
style: TextStyle(
fontSize: 11,
color: isLight(context)
@ -119,15 +96,15 @@ class ChapterListTileWidget extends ConsumerWidget {
),
],
),
if (chapters[finalIndex].scanlator!.isNotEmpty)
if (chapter.scanlator!.isNotEmpty)
Row(
children: [
const Text(''),
Text(
chapters[finalIndex].scanlator!,
chapter.scanlator!,
style: TextStyle(
fontSize: 11,
color: chapters[finalIndex].isRead
color: chapter.isRead!
? isLight(context)
? Colors.black.withOpacity(0.4)
: Colors.white.withOpacity(0.3)
@ -138,8 +115,9 @@ class ChapterListTileWidget extends ConsumerWidget {
],
),
trailing: ref.watch(ChapterPageDownloadsProvider(
index: reverse ? reverseIndex : finalIndex,
modelManga: modelManga)),
chapterIndex: chapterIndex,
modelManga: modelManga,
chapterId: chapter.id!)),
),
);
}

View file

@ -1,32 +0,0 @@
import 'package:hive/hive.dart';
import 'package:mangayomi/models/model_manga.dart';
part 'download_model.g.dart';
@HiveType(typeId: 6)
class DownloadModel {
@HiveField(0)
final ModelManga modelManga;
@HiveField(1)
final int index;
@HiveField(2)
final int succeeded;
@HiveField(3)
final int failed;
@HiveField(4)
final int total;
@HiveField(6)
final bool isDownload;
@HiveField(7)
final List taskIds;
@HiveField(8)
final bool isStartDownload;
DownloadModel(
{required this.modelManga,
required this.succeeded,
required this.failed,
required this.index,
required this.total,
required this.isDownload,
required this.taskIds,
required this.isStartDownload});
}

View file

@ -7,7 +7,7 @@ import 'package:mangayomi/providers/storage_provider.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/views/manga/download/download_model.dart';
import 'package:mangayomi/views/manga/download/model/download_model.dart';
import 'package:mangayomi/views/manga/download/providers/download_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'download_page_widget.g.dart';
@ -15,21 +15,27 @@ part 'download_page_widget.g.dart';
@riverpod
class ChapterPageDownloads extends _$ChapterPageDownloads {
@override
Widget build({required ModelManga modelManga, required int index}) {
Widget build(
{required ModelManga modelManga,
required int chapterIndex,
required int chapterId}) {
return ChapterPageDownload(
index: index,
chapterId: chapterId,
chapterIndex: chapterIndex,
modelManga: modelManga,
);
}
// ...
}
class ChapterPageDownload extends ConsumerStatefulWidget {
final ModelManga modelManga;
final int index;
final int chapterId;
final int chapterIndex;
const ChapterPageDownload(
{super.key, required this.modelManga, required this.index});
{super.key,
required this.modelManga,
required this.chapterId,
required this.chapterIndex});
@override
ConsumerState createState() => _ChapterPageDownloadState();
@ -42,7 +48,9 @@ class _ChapterPageDownloadState extends ConsumerState<ChapterPageDownload>
final StorageProvider _storageProvider = StorageProvider();
_startDownload() async {
final data = await ref.watch(downloadChapterProvider(
modelManga: widget.modelManga, index: widget.index)
modelManga: widget.modelManga,
chapterId: widget.chapterId,
chapterIndex: widget.chapterIndex)
.future);
if (mounted) {
setState(() {
@ -53,16 +61,16 @@ class _ChapterPageDownloadState extends ConsumerState<ChapterPageDownload>
_deleteFile(List pageUrl) async {
final path = await _storageProvider.getMangaChapterDirectory(
widget.modelManga, widget.index);
widget.modelManga, widget.chapterIndex);
try {
path!.deleteSync(recursive: true);
ref.watch(hiveBoxMangaDownloadsProvider).delete(
widget.modelManga.chapters![widget.index].name!,
widget.modelManga.chapters.toList()[widget.chapterIndex].name!,
);
} catch (e) {
ref.watch(hiveBoxMangaDownloadsProvider).delete(
widget.modelManga.chapters![widget.index].name!,
widget.modelManga.chapters.toList()[widget.chapterIndex].name!,
);
}
}
@ -82,8 +90,8 @@ class _ChapterPageDownloadState extends ConsumerState<ChapterPageDownload>
builder: (context, val, child) {
final entries = val.values
.where((element) =>
"${element.modelManga.chapters![element.index].name}${element.index}" ==
"${widget.modelManga.chapters![widget.index].name}${widget.index}")
"${element.chapterName}${element.chapterIndex}${element.chapterId}" ==
"${widget.modelManga.chapters.toList()[widget.chapterIndex].name!}${widget.chapterIndex}${widget.chapterId}")
.toList();
if (entries.isNotEmpty) {
@ -216,7 +224,7 @@ class _ChapterPageDownloadState extends ConsumerState<ChapterPageDownload>
.watch(
hiveBoxMangaDownloadsProvider)
.delete(
"${widget.modelManga.chapters![widget.index].name}${widget.index}",
"${widget.modelManga.chapters.toList()[widget.chapterIndex].name}${widget.chapterIndex}${widget.chapterId}",
);
_startDownload();
setState(() {
@ -274,7 +282,7 @@ class _ChapterPageDownloadState extends ConsumerState<ChapterPageDownload>
FileDownloader().cancelTasksWithIds(taskIds).then((value) async {
await Future.delayed(const Duration(seconds: 1));
ref.watch(hiveBoxMangaDownloadsProvider).delete(
"${widget.modelManga.chapters![widget.index].name}${widget.index}",
"${widget.modelManga.chapters.toList()[widget.chapterIndex].name}${widget.chapterIndex}${widget.chapterId}",
);
});
}

View file

@ -7,7 +7,7 @@ part of 'download_page_widget.dart';
// **************************************************************************
String _$chapterPageDownloadsHash() =>
r'0b3eaf9a3ca4786287616a87e5de62af24259b68';
r'682bf480c3b8e3ccda792661746e5fba24755526';
/// Copied from Dart SDK
class _SystemHash {
@ -33,11 +33,13 @@ class _SystemHash {
abstract class _$ChapterPageDownloads
extends BuildlessAutoDisposeNotifier<Widget> {
late final ModelManga modelManga;
late final int index;
late final int chapterIndex;
late final int chapterId;
Widget build({
required ModelManga modelManga,
required int index,
required int chapterIndex,
required int chapterId,
});
}
@ -53,11 +55,13 @@ class ChapterPageDownloadsFamily extends Family<Widget> {
/// See also [ChapterPageDownloads].
ChapterPageDownloadsProvider call({
required ModelManga modelManga,
required int index,
required int chapterIndex,
required int chapterId,
}) {
return ChapterPageDownloadsProvider(
modelManga: modelManga,
index: index,
chapterIndex: chapterIndex,
chapterId: chapterId,
);
}
@ -67,7 +71,8 @@ class ChapterPageDownloadsFamily extends Family<Widget> {
) {
return call(
modelManga: provider.modelManga,
index: provider.index,
chapterIndex: provider.chapterIndex,
chapterId: provider.chapterId,
);
}
@ -92,11 +97,13 @@ class ChapterPageDownloadsProvider
/// See also [ChapterPageDownloads].
ChapterPageDownloadsProvider({
required this.modelManga,
required this.index,
required this.chapterIndex,
required this.chapterId,
}) : super.internal(
() => ChapterPageDownloads()
..modelManga = modelManga
..index = index,
..chapterIndex = chapterIndex
..chapterId = chapterId,
from: chapterPageDownloadsProvider,
name: r'chapterPageDownloadsProvider',
debugGetCreateSourceHash:
@ -109,20 +116,23 @@ class ChapterPageDownloadsProvider
);
final ModelManga modelManga;
final int index;
final int chapterIndex;
final int chapterId;
@override
bool operator ==(Object other) {
return other is ChapterPageDownloadsProvider &&
other.modelManga == modelManga &&
other.index == index;
other.chapterIndex == chapterIndex &&
other.chapterId == chapterId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, modelManga.hashCode);
hash = _SystemHash.combine(hash, index.hashCode);
hash = _SystemHash.combine(hash, chapterIndex.hashCode);
hash = _SystemHash.combine(hash, chapterId.hashCode);
return _SystemHash.finish(hash);
}
@ -133,7 +143,8 @@ class ChapterPageDownloadsProvider
) {
return notifier.build(
modelManga: modelManga,
index: index,
chapterIndex: chapterIndex,
chapterId: chapterId,
);
}
}

View file

@ -0,0 +1,42 @@
import 'package:hive_flutter/hive_flutter.dart';
part 'download_model.g.dart';
@HiveType(typeId: 6)
class DownloadModel extends HiveObject {
@HiveField(0)
final int chapterIndex;
@HiveField(1)
final int succeeded;
@HiveField(2)
final int failed;
@HiveField(3)
final int total;
@HiveField(4)
final bool isDownload;
@HiveField(5)
final List taskIds;
@HiveField(6)
final bool isStartDownload;
@HiveField(7)
final int? chapterId;
@HiveField(9)
final String? mangaSource;
@HiveField(10)
final String? chapterName;
@HiveField(11)
final String? mangaName;
DownloadModel({
required this.chapterId,
required this.succeeded,
required this.failed,
required this.chapterIndex,
required this.total,
required this.isDownload,
required this.taskIds,
required this.isStartDownload,
required this.mangaSource,
required this.chapterName,
required this.mangaName,
});
}

View file

@ -17,37 +17,46 @@ class DownloadModelAdapter extends TypeAdapter<DownloadModel> {
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return DownloadModel(
modelManga: fields[0] as ModelManga,
succeeded: fields[2] as int,
failed: fields[3] as int,
index: fields[1] as int,
total: fields[4] as int,
isDownload: fields[6] as bool,
taskIds: (fields[7] as List).cast<dynamic>(),
isStartDownload: fields[8] as bool,
chapterId: fields[7] as int?,
succeeded: fields[1] as int,
failed: fields[2] as int,
chapterIndex: fields[0] as int,
total: fields[3] as int,
isDownload: fields[4] as bool,
taskIds: (fields[5] as List).cast<dynamic>(),
isStartDownload: fields[6] as bool,
mangaSource: fields[9] as String?,
chapterName: fields[10] as String?,
mangaName: fields[11] as String?,
);
}
@override
void write(BinaryWriter writer, DownloadModel obj) {
writer
..writeByte(8)
..writeByte(11)
..writeByte(0)
..write(obj.modelManga)
..write(obj.chapterIndex)
..writeByte(1)
..write(obj.index)
..writeByte(2)
..write(obj.succeeded)
..writeByte(3)
..writeByte(2)
..write(obj.failed)
..writeByte(4)
..writeByte(3)
..write(obj.total)
..writeByte(6)
..writeByte(4)
..write(obj.isDownload)
..writeByte(7)
..writeByte(5)
..write(obj.taskIds)
..writeByte(8)
..write(obj.isStartDownload);
..writeByte(6)
..write(obj.isStartDownload)
..writeByte(7)
..write(obj.chapterId)
..writeByte(9)
..write(obj.mangaSource)
..writeByte(10)
..write(obj.chapterName)
..writeByte(11)
..write(obj.mangaName);
}
@override

View file

@ -1,3 +1,4 @@
import 'dart:developer';
import 'dart:io';
import 'package:background_downloader/background_downloader.dart';
@ -9,13 +10,17 @@ import 'package:mangayomi/services/get_manga_chapter_url.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:mangayomi/views/manga/download/download_model.dart';
import 'package:mangayomi/views/manga/download/model/download_model.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'download_provider.g.dart';
@riverpod
Future<List<dynamic>> downloadChapter(DownloadChapterRef ref,
{required ModelManga modelManga, required int index}) async {
Future<List<dynamic>> downloadChapter(
DownloadChapterRef ref, {
required ModelManga modelManga,
required int chapterIndex,
required int chapterId,
}) async {
List urll = [];
List<DownloadTask> tasks = [];
final StorageProvider storageProvider = StorageProvider();
@ -23,16 +28,20 @@ Future<List<dynamic>> downloadChapter(DownloadChapterRef ref,
Directory? path;
bool isOk = false;
final path1 = await storageProvider.getDirectory();
String scanlator = modelManga.chapters![index].scanlator!.isNotEmpty
? "${modelManga.chapters![index].scanlator!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}_"
String scanlator = modelManga.chapters
.toList()[chapterIndex]
.scanlator!
.isNotEmpty
? "${modelManga.chapters.toList()[chapterIndex].scanlator!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}_"
: "";
final finalPath =
"downloads/${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/$scanlator${modelManga.chapters![index].name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}";
"downloads/${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/$scanlator${modelManga.chapters.toList()[chapterIndex].name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}";
path = Directory("${path1!.path}$finalPath/");
log(scanlator);
ref
.read(getMangaChapterUrlProvider(
modelManga: modelManga,
index: index,
index: chapterIndex,
).future)
.then((value) {
if (value.urll.isNotEmpty) {
@ -55,8 +64,6 @@ Future<List<dynamic>> downloadChapter(DownloadChapterRef ref,
"${path2.path}${modelManga.source} (${modelManga.lang!.toUpperCase()})/");
final path3 = Directory(
"${path2.path}${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/");
final path5 = Directory(
"${path2.path}${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/${modelManga.chapters![index].name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}");
if (!(await path1.exists())) {
path1.create();
@ -66,7 +73,7 @@ Future<List<dynamic>> downloadChapter(DownloadChapterRef ref,
File("${path1.path}" ".nomedia").create();
}
}
log(path2.path);
if (!(await path2.exists())) {
path2.create();
}
@ -76,9 +83,7 @@ Future<List<dynamic>> downloadChapter(DownloadChapterRef ref,
if (!(await path3.exists())) {
path3.create();
}
if (!(await path5.exists())) {
path5.create();
}
if ((await path.exists())) {
if (await File("${path.path}" "${padIndex(index + 1)}.jpg").exists()) {
} else {
@ -124,34 +129,41 @@ Future<List<dynamic>> downloadChapter(DownloadChapterRef ref,
}
if (tasks.isEmpty && urll.isNotEmpty) {
final model = DownloadModel(
modelManga: modelManga,
chapterId: chapterId,
mangaName: modelManga.name,
chapterIndex: chapterIndex,
succeeded: 0,
failed: 0,
index: index,
chapterName: modelManga.chapters.toList()[chapterIndex].name!,
mangaSource: modelManga.source,
total: 0,
isDownload: true,
taskIds: urll,
isStartDownload: false);
ref
.watch(hiveBoxMangaDownloadsProvider)
.put("${modelManga.chapters![index].name!}$index", model);
ref.watch(hiveBoxMangaDownloadsProvider).put(
"${modelManga.chapters.toList()[chapterIndex].name!}$chapterIndex$chapterId",
model);
} else {
await FileDownloader().downloadBatch(
tasks,
batchProgressCallback: (succeeded, failed) {
final model = DownloadModel(
modelManga: modelManga,
succeeded: succeeded,
failed: failed,
index: index,
total: tasks.length,
isDownload: (succeeded == tasks.length) ? true : false,
taskIds: urll,
isStartDownload: true);
Hive.box<DownloadModel>(HiveConstant.hiveBoxDownloads)
.put("${modelManga.chapters![index].name!}$index", model);
chapterIndex: chapterIndex,
mangaName: modelManga.name,
succeeded: succeeded,
failed: failed,
chapterId: chapterId,
total: tasks.length,
isDownload: (succeeded == tasks.length) ? true : false,
taskIds: urll,
isStartDownload: true,
chapterName: modelManga.chapters.toList()[chapterIndex].name!,
mangaSource: modelManga.source,
);
Hive.box<DownloadModel>(HiveConstant.hiveBoxDownloads).put(
"${modelManga.chapters.toList()[chapterIndex].name!}$chapterIndex$chapterId",
model);
},
taskProgressCallback: (task, progress) async {
if (progress == 1.0) {

View file

@ -6,7 +6,7 @@ part of 'download_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$downloadChapterHash() => r'982e5db78e716894f63b97598709e29098c3eb8f';
String _$downloadChapterHash() => r'19ec35b1bc0db5ebbc91f5ddb456dcac93b840ab';
/// Copied from Dart SDK
class _SystemHash {
@ -43,11 +43,13 @@ class DownloadChapterFamily extends Family<AsyncValue<List<dynamic>>> {
/// See also [downloadChapter].
DownloadChapterProvider call({
required ModelManga modelManga,
required int index,
required int chapterIndex,
required int chapterId,
}) {
return DownloadChapterProvider(
modelManga: modelManga,
index: index,
chapterIndex: chapterIndex,
chapterId: chapterId,
);
}
@ -57,7 +59,8 @@ class DownloadChapterFamily extends Family<AsyncValue<List<dynamic>>> {
) {
return call(
modelManga: provider.modelManga,
index: provider.index,
chapterIndex: provider.chapterIndex,
chapterId: provider.chapterId,
);
}
@ -81,12 +84,14 @@ class DownloadChapterProvider extends AutoDisposeFutureProvider<List<dynamic>> {
/// See also [downloadChapter].
DownloadChapterProvider({
required this.modelManga,
required this.index,
required this.chapterIndex,
required this.chapterId,
}) : super.internal(
(ref) => downloadChapter(
ref,
modelManga: modelManga,
index: index,
chapterIndex: chapterIndex,
chapterId: chapterId,
),
from: downloadChapterProvider,
name: r'downloadChapterProvider',
@ -100,20 +105,23 @@ class DownloadChapterProvider extends AutoDisposeFutureProvider<List<dynamic>> {
);
final ModelManga modelManga;
final int index;
final int chapterIndex;
final int chapterId;
@override
bool operator ==(Object other) {
return other is DownloadChapterProvider &&
other.modelManga == modelManga &&
other.index == index;
other.chapterIndex == chapterIndex &&
other.chapterId == chapterId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, modelManga.hashCode);
hash = _SystemHash.combine(hash, index.hashCode);
hash = _SystemHash.combine(hash, chapterIndex.hashCode);
hash = _SystemHash.combine(hash, chapterId.hashCode);
return _SystemHash.finish(hash);
}

View file

@ -469,7 +469,7 @@ class _MangaChapterPageGalleryState
1 !=
widget.readerController
.getModelManga()
.chapters!
.chapters
.length
? Theme.of(context)
.textTheme

View file

@ -101,39 +101,41 @@ class ReaderController extends _$ReaderController {
void setMangaHistoryUpdate() {
final incognitoMode = ref.watch(incognitoModeStateProvider);
if (!incognitoMode) {
ref.watch(hiveBoxMangaHistoryProvider).put(
'${getModelManga().lang}-${getModelManga().link}',
MangaHistoryModel(
date: DateTime.now().toString(), modelManga: getModelManga()));
// ref.watch(hiveBoxMangaHistoryProvider).put(
// '${getModelManga().lang}-${getModelManga().link}',
// MangaHistoryModel(
// date: DateTime.now().toString(), modelManga: getModelManga()));
}
}
void setChapterPageLastRead(int pageIndex) async {
final incognitoMode = ref.watch(incognitoModeStateProvider);
if (!incognitoMode) {
List<ModelChapters> chapter = getModelManga().chapters!;
chapter[getChapterIndex()].lastPageRead = (pageIndex + 1).toString();
getModelManga().save();
}
// if (!incognitoMode) {
// List<ModelChapters> chapter = getModelManga().chapters!;
// chapter[getChapterIndex()].lastPageRead = (pageIndex + 1).toString();
// getModelManga().save();
// }
}
void setChapterBookmarked() {
final incognitoMode = ref.watch(incognitoModeStateProvider);
if (!incognitoMode) {
final isBookmarked = getChapterBookmarked();
List<ModelChapters> chapter = getModelManga().chapters!;
chapter[getChapterIndex()].isBookmarked = !isBookmarked;
getModelManga().save();
}
// if (!incognitoMode) {
// final isBookmarked = getChapterBookmarked();
// List<ModelChapters> chapter = getModelManga().chapters!;
// chapter[getChapterIndex()].isBookmarked = !isBookmarked;
// getModelManga().save();
// }
}
bool getChapterBookmarked() {
return ref
.watch(hiveBoxMangaProvider)
.get('${getModelManga().lang}-${getModelManga().link}',
defaultValue: getModelManga())!
.chapters![getChapterIndex()]
.isBookmarked;
return true;
// ref
// .watch(hiveBoxMangaProvider)
// .get('${getModelManga().lang}-${getModelManga().link}',
// defaultValue: getModelManga())!
// .chapters![getChapterIndex()]
// .isBookmarked;
}
int getChapterIndex() {
@ -188,6 +190,6 @@ class ReaderController extends _$ReaderController {
}
String getChapterTitle() {
return getModelManga().chapters![mangaReaderModel.index].name!;
return getModelManga().chapters.toList()[mangaReaderModel.index].name!;
}
}

View file

@ -182,7 +182,7 @@ class CurrentIndexProvider
}
}
String _$readerControllerHash() => r'8999132e4d6b64b2c652d641cbef9ae6e9175d0e';
String _$readerControllerHash() => r'255b727a186844a75ea4b71f724142f3f09a1e9a';
abstract class _$ReaderController extends BuildlessAutoDisposeNotifier<void> {
late final MangaReaderModel mangaReaderModel;

View file

@ -4,7 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:grouped_list/grouped_list.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/views/manga/download/download_model.dart';
import 'package:mangayomi/views/manga/download/model/download_model.dart';
class DownloadQueueScreen extends ConsumerWidget {
const DownloadQueueScreen({super.key});
@ -50,11 +50,10 @@ class DownloadQueueScreen extends ConsumerWidget {
),
body: GroupedListView<DownloadModel, String>(
elements: entries,
groupBy: (element) => element.modelManga.source!,
groupBy: (element) => element.mangaSource!,
groupSeparatorBuilder: (String groupByValue) {
final sourceQueueLength = entries
.where(
(element) => element.modelManga.source == groupByValue)
.where((element) => element.mangaSource! == groupByValue)
.toList()
.length;
return Padding(
@ -80,7 +79,7 @@ class DownloadQueueScreen extends ConsumerWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
element.modelManga.name!,
element.mangaName!,
style: const TextStyle(fontSize: 16),
),
Text(
@ -90,7 +89,7 @@ class DownloadQueueScreen extends ConsumerWidget {
],
),
Text(
element.modelManga.chapters![element.index].name!,
element.chapterName!,
style: const TextStyle(fontSize: 13),
),
const SizedBox(
@ -126,7 +125,7 @@ class DownloadQueueScreen extends ConsumerWidget {
await Future.delayed(
const Duration(seconds: 1));
ref.watch(hiveBoxMangaDownloadsProvider).delete(
"${element.modelManga.chapters![element.index].name}${element.index}",
"${element.chapterName}${element.chapterIndex}${element.chapterId}",
);
});
}
@ -142,7 +141,7 @@ class DownloadQueueScreen extends ConsumerWidget {
);
},
itemComparator: (item1, item2) =>
item1.modelManga.source!.compareTo(item2.modelManga.source!),
item1.mangaSource!.compareTo(item2.mangaSource!),
order: GroupedListOrder.DESC,
),
);

View file

@ -1,10 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/main.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});
@ -14,145 +13,153 @@ class CategoriesScreen extends ConsumerStatefulWidget {
}
class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
List<CategoriesModel> entries = [];
List<CategoriesModel> _entries = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Edit categories"),
),
body: ValueListenableBuilder<Box<CategoriesModel>>(
valueListenable: ref.watch(hiveBoxCategoriesProvider).listenable(),
builder: (context, value, child) {
entries = value.values.toList();
if (entries.isNotEmpty) {
return ListView.builder(
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[
body: StreamBuilder(
stream: isar.categoriesModels
.filter()
.idIsNotNull()
.watch(fireImmediately: true),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
_entries = snapshot.data!;
return ListView.builder(
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:
() async {
await isar
.writeTxn(
() async {
await isar
.categoriesModels
.delete(_entries[
index]
.id
.toString());
.id!);
});
if (mounted) {
Navigator.pop(
context);
},
child: const Text(
"OK",
)),
],
)
],
);
},
);
});
},
icon: const Icon(Icons.delete_outlined))
],
),
],
)
],
),
}
},
child: const Text(
"OK",
)),
],
)
],
);
},
);
});
},
icon: const Icon(Icons.delete_outlined))
],
),
],
)
],
),
);
},
);
} else {
return const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text(
"You have no categories. Tap the plus button to create one for organizing your library",
textAlign: TextAlign.center,
),
),
);
}
}),
);
},
);
}
_entries = [];
return const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text(
"You have no categories. Tap the plus button to create one for organizing your library",
textAlign: TextAlign.center,
),
),
);
},
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
bool isExist = false;
@ -160,65 +167,67 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text(
"Add category",
),
content: CustomTextFormField(
controller: controller,
entries: entries,
context: context,
exist: (value) {
setState(() {
isExist = value;
});
},
isExist: isExist,
val: (val) {}),
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
? null
: () {
String randomId = randomNumeric(10);
ref
.watch(hiveBoxCategoriesProvider)
.put(
randomId,
CategoriesModel(
id: int.parse(randomId),
return SizedBox(
child: StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text(
"Add category",
),
content: CustomTextFormField(
controller: controller,
entries: _entries,
context: context,
exist: (value) {
setState(() {
isExist = value;
});
},
isExist: isExist,
val: (val) {}),
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
? null
: () async {
await isar.writeTxn(() async {
await isar.categoriesModels
.put(CategoriesModel(
name: controller.text,
));
Navigator.pop(context);
},
child: Text(
"Add",
style: TextStyle(
color:
controller.text.isEmpty || isExist
? Theme.of(context)
.primaryColor
.withOpacity(0.2)
: null),
)),
],
)
],
);
},
});
if (mounted) {
Navigator.pop(context);
}
},
child: Text(
"Add",
style: TextStyle(
color:
controller.text.isEmpty || isExist
? Theme.of(context)
.primaryColor
.withOpacity(0.2)
: null),
)),
],
)
],
);
},
),
);
});
},
@ -249,7 +258,7 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
),
content: CustomTextFormField(
controller: controller,
entries: entries,
entries: _entries,
context: context,
exist: (value) {
setState(() {
@ -257,7 +266,7 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
});
},
isExist: isExist,
name: category.name,
name: category.name!,
val: (val) {
setState(() {
isSameName = controller.text == category.name;
@ -276,18 +285,19 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
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);
},
onPressed: controller.text.isEmpty ||
isExist ||
isSameName
? null
: () async {
await isar.writeTxn(() async {
category.name = controller.text;
await isar.categoriesModels.put(category);
});
if (mounted) {
Navigator.pop(context);
}
},
child: Text(
"OK",
style: TextStyle(

View file

@ -1,6 +1,10 @@
import 'dart:developer';
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/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/services/get_manga_detail.dart';
@ -24,7 +28,6 @@ class MangaImageCardWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final manga = ref.watch(hiveBoxMangaProvider);
return GestureDetector(
onTap: () async {
final modelManga = ModelManga(
@ -40,16 +43,42 @@ class MangaImageCardWidget extends ConsumerWidget {
lang: lang,
dateAdded: null,
lastUpdate: null,
chapters: getMangaDetailModel!.chapters,
categories: [],
lastRead: '');
if (manga.get('$lang-${getMangaDetailModel!.url}',
defaultValue: null) ==
null) {
manga.put('$lang-${getMangaDetailModel!.url}', modelManga);
}
context.push('/manga-reader/detail', extra: modelManga);
final empty = await isar.modelMangas
.filter()
.langEqualTo(lang)
.nameEqualTo(getMangaDetailModel!.name)
.sourceEqualTo(getMangaDetailModel!.source)
.isEmpty();
if (empty) {
await isar.writeTxn(() async {
await isar.modelMangas.put(modelManga);
for (var i = 0; i < getMangaDetailModel!.chapters.length; i++) {
final chapters = ModelChapters(
name: getMangaDetailModel!.chapters[i].name,
url: getMangaDetailModel!.chapters[i].url,
dateUpload: getMangaDetailModel!.chapters[i].dateUpload,
isBookmarked: false,
scanlator: getMangaDetailModel!.chapters[i].scanlator,
isRead: false,
lastPageRead: '',
mangaId: modelManga.id)
..manga.value = modelManga;
await isar.modelChapters.put(chapters);
await chapters.manga.save();
}
});
}
final getMangaId = await isar.modelMangas
.filter()
.langEqualTo(lang)
.nameEqualTo(getMangaDetailModel!.name)
.sourceEqualTo(getMangaDetailModel!.source)
.findFirst();
context.push('/manga-reader/detail', extra: getMangaId!.id);
log("${getMangaId.id}");
},
child: CoverViewWidget(children: [
cachedNetworkImage(

View file

@ -7,12 +7,16 @@
#include "generated_plugin_registrant.h"
#include <flutter_js/flutter_js_plugin.h>
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_js_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterJsPlugin");
flutter_js_plugin_register_with_registrar(flutter_js_registrar);
g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin");
isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);

View file

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
flutter_js
isar_flutter_libs
url_launcher_linux
)

View file

@ -7,6 +7,7 @@ import Foundation
import flutter_inappwebview
import flutter_js
import isar_flutter_libs
import package_info_plus
import path_provider_foundation
import sqflite
@ -15,6 +16,7 @@ import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
FlutterJsPlugin.register(with: registry.registrar(forPlugin: "FlutterJsPlugin"))
IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))

View file

@ -266,6 +266,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
dartx:
dependency: transitive
description:
name: dartx
sha256: "45d7176701f16c5a5e00a4798791c1964bc231491b879369c818dd9a9c764871"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
dio:
dependency: transitive
description:
@ -602,6 +610,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
isar:
dependency: "direct main"
description:
name: isar
sha256: "99165dadb2cf2329d3140198363a7e7bff9bbd441871898a87e26914d25cf1ea"
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
isar_flutter_libs:
dependency: "direct main"
description:
name: isar_flutter_libs
sha256: bc6768cc4b9c61aabff77152e7f33b4b17d2fc93134f7af1c3dd51500fe8d5e8
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
isar_generator:
dependency: "direct dev"
description:
name: isar_generator
sha256: "76c121e1295a30423604f2f819bc255bc79f852f3bc8743a24017df6068ad133"
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
js:
dependency: transitive
description:
@ -642,14 +674,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
m_toast:
dependency: "direct main"
description:
name: m_toast
sha256: "9b9096625a58da18341ba7d46f38b23a21a0be723aab008e1b5cfff71980f881"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
matcher:
dependency: transitive
description:
@ -1079,6 +1103,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.4.16"
time:
dependency: transitive
description:
name: time
sha256: "83427e11d9072e038364a5e4da559e85869b227cf699a541be0da74f14140124"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
timing:
dependency: transitive
description:
@ -1215,6 +1247,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.2.2"
xxh3:
dependency: transitive
description:
name: xxh3
sha256: a92b30944a9aeb4e3d4f3c3d4ddb3c7816ca73475cd603682c4f8149690f56d7
url: "https://pub.dev"
source: hosted
version: "1.0.1"
yaml:
dependency: transitive
description:

View file

@ -63,6 +63,8 @@ dependencies:
draggable_menu: ^0.3.0
fast_cached_network_image: ^1.2.0
random_string: ^2.3.1
isar: 3.1.0+1
isar_flutter_libs: 3.1.0+1
# The following adds the Cupertino Icons font to your application.
@ -75,6 +77,7 @@ dev_dependencies:
build_runner: ^2.3.3
riverpod_generator: ^2.1.4
flutter_launcher_icons: ^0.13.1
isar_generator: 3.1.0+1
flutter_lints: ^2.0.1
flutter_launcher_icons:

View file

@ -7,12 +7,15 @@
#include "generated_plugin_registrant.h"
#include <flutter_js/flutter_js_plugin.h>
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterJsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterJsPlugin"));
IsarFlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(

View file

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
flutter_js
isar_flutter_libs
permission_handler_windows
url_launcher_windows
)