wip backup and restore

This commit is contained in:
kodjomoustapha 2023-11-20 17:26:08 +01:00
parent fd8193b889
commit 4b359a7db0
36 changed files with 2230 additions and 351 deletions

View file

@ -7,9 +7,23 @@ class Category {
Id? id;
String? name;
bool? forManga;
Category({
this.id = Isar.autoIncrement,
required this.name,
required this.forManga
});
Category(
{this.id = Isar.autoIncrement,
required this.name,
required this.forManga});
Category.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
forManga = json['forManga'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['name'] = name;
data['forManga'] = forManga;
return data;
}
}

View file

@ -7,6 +7,8 @@ part 'chapter.g.dart';
class Chapter {
Id? id;
int? mangaId;
String? name;
String? url;
@ -28,6 +30,7 @@ class Chapter {
Chapter(
{this.id = Isar.autoIncrement,
required this.mangaId,
required this.name,
this.url = '',
this.dateUpload = '',
@ -36,4 +39,32 @@ class Chapter {
this.isRead = false,
this.lastPageRead = '',
this.archivePath = ''});
Chapter.fromJson(Map<String, dynamic> json) {
archivePath = json['archivePath'];
dateUpload = json['dateUpload'];
id = json['id'];
isBookmarked = json['isBookmarked'];
isRead = json['isRead'];
lastPageRead = json['lastPageRead'];
mangaId = json['mangaId'];
name = json['name'];
scanlator = json['scanlator'];
url = json['url'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['archivePath'] = archivePath;
data['dateUpload'] = dateUpload;
data['id'] = id;
data['isBookmarked'] = isBookmarked;
data['isRead'] = isRead;
data['lastPageRead'] = lastPageRead;
data['mangaId'] = mangaId;
data['name'] = name;
data['scanlator'] = scanlator;
data['url'] = url;
return data;
}
}

View file

@ -42,18 +42,23 @@ const ChapterSchema = CollectionSchema(
name: r'lastPageRead',
type: IsarType.string,
),
r'name': PropertySchema(
r'mangaId': PropertySchema(
id: 5,
name: r'mangaId',
type: IsarType.long,
),
r'name': PropertySchema(
id: 6,
name: r'name',
type: IsarType.string,
),
r'scanlator': PropertySchema(
id: 6,
id: 7,
name: r'scanlator',
type: IsarType.string,
),
r'url': PropertySchema(
id: 7,
id: 8,
name: r'url',
type: IsarType.string,
)
@ -135,9 +140,10 @@ void _chapterSerialize(
writer.writeBool(offsets[2], object.isBookmarked);
writer.writeBool(offsets[3], object.isRead);
writer.writeString(offsets[4], object.lastPageRead);
writer.writeString(offsets[5], object.name);
writer.writeString(offsets[6], object.scanlator);
writer.writeString(offsets[7], object.url);
writer.writeLong(offsets[5], object.mangaId);
writer.writeString(offsets[6], object.name);
writer.writeString(offsets[7], object.scanlator);
writer.writeString(offsets[8], object.url);
}
Chapter _chapterDeserialize(
@ -153,9 +159,10 @@ Chapter _chapterDeserialize(
isBookmarked: reader.readBoolOrNull(offsets[2]),
isRead: reader.readBoolOrNull(offsets[3]),
lastPageRead: reader.readStringOrNull(offsets[4]),
name: reader.readStringOrNull(offsets[5]),
scanlator: reader.readStringOrNull(offsets[6]),
url: reader.readStringOrNull(offsets[7]),
mangaId: reader.readLongOrNull(offsets[5]),
name: reader.readStringOrNull(offsets[6]),
scanlator: reader.readStringOrNull(offsets[7]),
url: reader.readStringOrNull(offsets[8]),
);
return object;
}
@ -178,11 +185,13 @@ P _chapterDeserializeProp<P>(
case 4:
return (reader.readStringOrNull(offset)) as P;
case 5:
return (reader.readStringOrNull(offset)) as P;
return (reader.readLongOrNull(offset)) as P;
case 6:
return (reader.readStringOrNull(offset)) as P;
case 7:
return (reader.readStringOrNull(offset)) as P;
case 8:
return (reader.readStringOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
@ -840,6 +849,75 @@ extension ChapterQueryFilter
});
}
QueryBuilder<Chapter, Chapter, QAfterFilterCondition> mangaIdIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'mangaId',
));
});
}
QueryBuilder<Chapter, Chapter, QAfterFilterCondition> mangaIdIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'mangaId',
));
});
}
QueryBuilder<Chapter, Chapter, QAfterFilterCondition> mangaIdEqualTo(
int? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'mangaId',
value: value,
));
});
}
QueryBuilder<Chapter, Chapter, QAfterFilterCondition> mangaIdGreaterThan(
int? value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'mangaId',
value: value,
));
});
}
QueryBuilder<Chapter, Chapter, QAfterFilterCondition> mangaIdLessThan(
int? value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'mangaId',
value: value,
));
});
}
QueryBuilder<Chapter, Chapter, QAfterFilterCondition> mangaIdBetween(
int? lower,
int? upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'mangaId',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
));
});
}
QueryBuilder<Chapter, Chapter, QAfterFilterCondition> nameIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
@ -1359,6 +1437,18 @@ extension ChapterQuerySortBy on QueryBuilder<Chapter, Chapter, QSortBy> {
});
}
QueryBuilder<Chapter, Chapter, QAfterSortBy> sortByMangaId() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'mangaId', Sort.asc);
});
}
QueryBuilder<Chapter, Chapter, QAfterSortBy> sortByMangaIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'mangaId', Sort.desc);
});
}
QueryBuilder<Chapter, Chapter, QAfterSortBy> sortByName() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'name', Sort.asc);
@ -1470,6 +1560,18 @@ extension ChapterQuerySortThenBy
});
}
QueryBuilder<Chapter, Chapter, QAfterSortBy> thenByMangaId() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'mangaId', Sort.asc);
});
}
QueryBuilder<Chapter, Chapter, QAfterSortBy> thenByMangaIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'mangaId', Sort.desc);
});
}
QueryBuilder<Chapter, Chapter, QAfterSortBy> thenByName() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'name', Sort.asc);
@ -1542,6 +1644,12 @@ extension ChapterQueryWhereDistinct
});
}
QueryBuilder<Chapter, Chapter, QDistinct> distinctByMangaId() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'mangaId');
});
}
QueryBuilder<Chapter, Chapter, QDistinct> distinctByName(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
@ -1602,6 +1710,12 @@ extension ChapterQueryProperty
});
}
QueryBuilder<Chapter, int?, QQueryOperations> mangaIdProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'mangaId');
});
}
QueryBuilder<Chapter, String?, QQueryOperations> nameProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'name');

View file

@ -10,6 +10,8 @@ class Download {
int? chapterId;
int? mangaId;
int? succeeded;
int? failed;
@ -27,6 +29,7 @@ class Download {
Download({
this.id = Isar.autoIncrement,
required this.chapterId,
required this.mangaId,
required this.succeeded,
required this.failed,
required this.total,
@ -34,4 +37,29 @@ class Download {
required this.taskIds,
required this.isStartDownload,
});
Download.fromJson(Map<String, dynamic> json) {
chapterId = json['chapterId'];
failed = json['failed'];
id = json['id'];
isDownload = json['isDownload'];
isStartDownload = json['isStartDownload'];
mangaId = json['mangaId'];
succeeded = json['succeeded'];
taskIds = json['taskIds'].cast<String>();
total = json['total'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['chapterId'] = chapterId;
data['failed'] = failed;
data['id'] = id;
data['isDownload'] = isDownload;
data['isStartDownload'] = isStartDownload;
data['mangaId'] = mangaId;
data['succeeded'] = succeeded;
data['taskIds'] = taskIds;
data['total'] = total;
return data;
}
}

View file

@ -37,18 +37,23 @@ const DownloadSchema = CollectionSchema(
name: r'isStartDownload',
type: IsarType.bool,
),
r'succeeded': PropertySchema(
r'mangaId': PropertySchema(
id: 4,
name: r'mangaId',
type: IsarType.long,
),
r'succeeded': PropertySchema(
id: 5,
name: r'succeeded',
type: IsarType.long,
),
r'taskIds': PropertySchema(
id: 5,
id: 6,
name: r'taskIds',
type: IsarType.stringList,
),
r'total': PropertySchema(
id: 6,
id: 7,
name: r'total',
type: IsarType.long,
)
@ -105,9 +110,10 @@ void _downloadSerialize(
writer.writeLong(offsets[1], object.failed);
writer.writeBool(offsets[2], object.isDownload);
writer.writeBool(offsets[3], object.isStartDownload);
writer.writeLong(offsets[4], object.succeeded);
writer.writeStringList(offsets[5], object.taskIds);
writer.writeLong(offsets[6], object.total);
writer.writeLong(offsets[4], object.mangaId);
writer.writeLong(offsets[5], object.succeeded);
writer.writeStringList(offsets[6], object.taskIds);
writer.writeLong(offsets[7], object.total);
}
Download _downloadDeserialize(
@ -122,9 +128,10 @@ Download _downloadDeserialize(
id: id,
isDownload: reader.readBoolOrNull(offsets[2]),
isStartDownload: reader.readBoolOrNull(offsets[3]),
succeeded: reader.readLongOrNull(offsets[4]),
taskIds: reader.readStringList(offsets[5]),
total: reader.readLongOrNull(offsets[6]),
mangaId: reader.readLongOrNull(offsets[4]),
succeeded: reader.readLongOrNull(offsets[5]),
taskIds: reader.readStringList(offsets[6]),
total: reader.readLongOrNull(offsets[7]),
);
return object;
}
@ -147,8 +154,10 @@ P _downloadDeserializeProp<P>(
case 4:
return (reader.readLongOrNull(offset)) as P;
case 5:
return (reader.readStringList(offset)) as P;
return (reader.readLongOrNull(offset)) as P;
case 6:
return (reader.readStringList(offset)) as P;
case 7:
return (reader.readLongOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
@ -506,6 +515,75 @@ extension DownloadQueryFilter
});
}
QueryBuilder<Download, Download, QAfterFilterCondition> mangaIdIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'mangaId',
));
});
}
QueryBuilder<Download, Download, QAfterFilterCondition> mangaIdIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'mangaId',
));
});
}
QueryBuilder<Download, Download, QAfterFilterCondition> mangaIdEqualTo(
int? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'mangaId',
value: value,
));
});
}
QueryBuilder<Download, Download, QAfterFilterCondition> mangaIdGreaterThan(
int? value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'mangaId',
value: value,
));
});
}
QueryBuilder<Download, Download, QAfterFilterCondition> mangaIdLessThan(
int? value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'mangaId',
value: value,
));
});
}
QueryBuilder<Download, Download, QAfterFilterCondition> mangaIdBetween(
int? lower,
int? upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'mangaId',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
));
});
}
QueryBuilder<Download, Download, QAfterFilterCondition> succeededIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
@ -949,6 +1027,18 @@ extension DownloadQuerySortBy on QueryBuilder<Download, Download, QSortBy> {
});
}
QueryBuilder<Download, Download, QAfterSortBy> sortByMangaId() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'mangaId', Sort.asc);
});
}
QueryBuilder<Download, Download, QAfterSortBy> sortByMangaIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'mangaId', Sort.desc);
});
}
QueryBuilder<Download, Download, QAfterSortBy> sortBySucceeded() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'succeeded', Sort.asc);
@ -1036,6 +1126,18 @@ extension DownloadQuerySortThenBy
});
}
QueryBuilder<Download, Download, QAfterSortBy> thenByMangaId() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'mangaId', Sort.asc);
});
}
QueryBuilder<Download, Download, QAfterSortBy> thenByMangaIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'mangaId', Sort.desc);
});
}
QueryBuilder<Download, Download, QAfterSortBy> thenBySucceeded() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'succeeded', Sort.asc);
@ -1087,6 +1189,12 @@ extension DownloadQueryWhereDistinct
});
}
QueryBuilder<Download, Download, QDistinct> distinctByMangaId() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'mangaId');
});
}
QueryBuilder<Download, Download, QDistinct> distinctBySucceeded() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'succeeded');
@ -1138,6 +1246,12 @@ extension DownloadQueryProperty
});
}
QueryBuilder<Download, int?, QQueryOperations> mangaIdProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'mangaId');
});
}
QueryBuilder<Download, int?, QQueryOperations> succeededProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'succeeded');

View file

@ -6,12 +6,40 @@ part 'history.g.dart';
@Name("History")
class History {
Id? id;
int? mangaId;
int? chapterId;
bool? isManga;
final chapter = IsarLink<Chapter>();
String? date;
History({
this.id = Isar.autoIncrement,
required this.isManga,
required this.chapterId,
required this.mangaId,
required this.date,
});
History.fromJson(Map<String, dynamic> json) {
chapterId = json['chapterId'];
date = json['date'];
id = json['id'];
isManga = json['isManga'];
mangaId = json['mangaId'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['chapterId'] = chapterId;
data['date'] = date;
data['id'] = id;
data['isManga'] = isManga;
data['mangaId'] = mangaId;
return data;
}
}

View file

@ -17,13 +17,23 @@ const HistorySchema = CollectionSchema(
name: r'History',
id: 1676981785059398080,
properties: {
r'date': PropertySchema(
r'chapterId': PropertySchema(
id: 0,
name: r'chapterId',
type: IsarType.long,
),
r'date': PropertySchema(
id: 1,
name: r'date',
type: IsarType.string,
),
r'isManga': PropertySchema(
id: 2,
name: r'isManga',
type: IsarType.bool,
),
r'mangaId': PropertySchema(
id: 1,
id: 3,
name: r'mangaId',
type: IsarType.long,
)
@ -70,8 +80,10 @@ void _historySerialize(
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
writer.writeString(offsets[0], object.date);
writer.writeLong(offsets[1], object.mangaId);
writer.writeLong(offsets[0], object.chapterId);
writer.writeString(offsets[1], object.date);
writer.writeBool(offsets[2], object.isManga);
writer.writeLong(offsets[3], object.mangaId);
}
History _historyDeserialize(
@ -81,9 +93,11 @@ History _historyDeserialize(
Map<Type, List<int>> allOffsets,
) {
final object = History(
date: reader.readStringOrNull(offsets[0]),
chapterId: reader.readLongOrNull(offsets[0]),
date: reader.readStringOrNull(offsets[1]),
id: id,
mangaId: reader.readLongOrNull(offsets[1]),
isManga: reader.readBoolOrNull(offsets[2]),
mangaId: reader.readLongOrNull(offsets[3]),
);
return object;
}
@ -96,8 +110,12 @@ P _historyDeserializeProp<P>(
) {
switch (propertyId) {
case 0:
return (reader.readStringOrNull(offset)) as P;
return (reader.readLongOrNull(offset)) as P;
case 1:
return (reader.readStringOrNull(offset)) as P;
case 2:
return (reader.readBoolOrNull(offset)) as P;
case 3:
return (reader.readLongOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
@ -194,6 +212,75 @@ extension HistoryQueryWhere on QueryBuilder<History, History, QWhereClause> {
extension HistoryQueryFilter
on QueryBuilder<History, History, QFilterCondition> {
QueryBuilder<History, History, QAfterFilterCondition> chapterIdIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'chapterId',
));
});
}
QueryBuilder<History, History, QAfterFilterCondition> chapterIdIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'chapterId',
));
});
}
QueryBuilder<History, History, QAfterFilterCondition> chapterIdEqualTo(
int? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'chapterId',
value: value,
));
});
}
QueryBuilder<History, History, QAfterFilterCondition> chapterIdGreaterThan(
int? value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'chapterId',
value: value,
));
});
}
QueryBuilder<History, History, QAfterFilterCondition> chapterIdLessThan(
int? value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'chapterId',
value: value,
));
});
}
QueryBuilder<History, History, QAfterFilterCondition> chapterIdBetween(
int? lower,
int? upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'chapterId',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
));
});
}
QueryBuilder<History, History, QAfterFilterCondition> dateIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
@ -408,6 +495,32 @@ extension HistoryQueryFilter
});
}
QueryBuilder<History, History, QAfterFilterCondition> isMangaIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'isManga',
));
});
}
QueryBuilder<History, History, QAfterFilterCondition> isMangaIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'isManga',
));
});
}
QueryBuilder<History, History, QAfterFilterCondition> isMangaEqualTo(
bool? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'isManga',
value: value,
));
});
}
QueryBuilder<History, History, QAfterFilterCondition> mangaIdIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
@ -498,6 +611,18 @@ extension HistoryQueryLinks
}
extension HistoryQuerySortBy on QueryBuilder<History, History, QSortBy> {
QueryBuilder<History, History, QAfterSortBy> sortByChapterId() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'chapterId', Sort.asc);
});
}
QueryBuilder<History, History, QAfterSortBy> sortByChapterIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'chapterId', Sort.desc);
});
}
QueryBuilder<History, History, QAfterSortBy> sortByDate() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'date', Sort.asc);
@ -510,6 +635,18 @@ extension HistoryQuerySortBy on QueryBuilder<History, History, QSortBy> {
});
}
QueryBuilder<History, History, QAfterSortBy> sortByIsManga() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isManga', Sort.asc);
});
}
QueryBuilder<History, History, QAfterSortBy> sortByIsMangaDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isManga', Sort.desc);
});
}
QueryBuilder<History, History, QAfterSortBy> sortByMangaId() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'mangaId', Sort.asc);
@ -525,6 +662,18 @@ extension HistoryQuerySortBy on QueryBuilder<History, History, QSortBy> {
extension HistoryQuerySortThenBy
on QueryBuilder<History, History, QSortThenBy> {
QueryBuilder<History, History, QAfterSortBy> thenByChapterId() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'chapterId', Sort.asc);
});
}
QueryBuilder<History, History, QAfterSortBy> thenByChapterIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'chapterId', Sort.desc);
});
}
QueryBuilder<History, History, QAfterSortBy> thenByDate() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'date', Sort.asc);
@ -549,6 +698,18 @@ extension HistoryQuerySortThenBy
});
}
QueryBuilder<History, History, QAfterSortBy> thenByIsManga() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isManga', Sort.asc);
});
}
QueryBuilder<History, History, QAfterSortBy> thenByIsMangaDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isManga', Sort.desc);
});
}
QueryBuilder<History, History, QAfterSortBy> thenByMangaId() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'mangaId', Sort.asc);
@ -564,6 +725,12 @@ extension HistoryQuerySortThenBy
extension HistoryQueryWhereDistinct
on QueryBuilder<History, History, QDistinct> {
QueryBuilder<History, History, QDistinct> distinctByChapterId() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'chapterId');
});
}
QueryBuilder<History, History, QDistinct> distinctByDate(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
@ -571,6 +738,12 @@ extension HistoryQueryWhereDistinct
});
}
QueryBuilder<History, History, QDistinct> distinctByIsManga() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'isManga');
});
}
QueryBuilder<History, History, QDistinct> distinctByMangaId() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'mangaId');
@ -586,12 +759,24 @@ extension HistoryQueryProperty
});
}
QueryBuilder<History, int?, QQueryOperations> chapterIdProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'chapterId');
});
}
QueryBuilder<History, String?, QQueryOperations> dateProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'date');
});
}
QueryBuilder<History, bool?, QQueryOperations> isMangaProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'isManga');
});
}
QueryBuilder<History, int?, QQueryOperations> mangaIdProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'mangaId');

View file

@ -18,13 +18,13 @@ class Manga {
String? author;
@enumerated
Status status;
late Status status;
bool? isManga;
List<String>? genre;
bool favorite;
bool? favorite;
String? source;
@ -64,6 +64,50 @@ class Manga {
this.lastRead = 0,
this.isLocalArchive = false,
this.customCoverImage});
Manga.fromJson(Map<String, dynamic> json) {
author = json['author'];
categories = json['categories']?.cast<int>();
customCoverImage = json['customCoverImage']?.cast<int>();
dateAdded = json['dateAdded'];
description = json['description'];
favorite = json['favorite']!;
genre = json['genre']?.cast<String>();
id = json['id'];
imageUrl = json['imageUrl'];
isLocalArchive = json['isLocalArchive'];
isManga = json['isManga'];
lang = json['lang'];
lastRead = json['lastRead'];
lastUpdate = json['lastUpdate'];
link = json['link'];
name = json['name'];
source = json['source'];
status = Status.values[json['status']];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['author'] = author;
data['categories'] = categories;
data['customCoverImage'] = customCoverImage;
data['dateAdded'] = dateAdded;
data['description'] = description;
data['favorite'] = favorite;
data['genre'] = genre;
data['id'] = id;
data['imageUrl'] = imageUrl;
data['isLocalArchive'] = isLocalArchive;
data['isManga'] = isManga;
data['lang'] = lang;
data['lastRead'] = lastRead;
data['lastUpdate'] = lastUpdate;
data['link'] = link;
data['name'] = name;
data['source'] = source;
data['status'] = status.index;
return data;
}
}
enum Status {

View file

@ -238,7 +238,7 @@ Manga _mangaDeserialize(
customCoverImage: reader.readByteList(offsets[2]),
dateAdded: reader.readLongOrNull(offsets[3]),
description: reader.readStringOrNull(offsets[4]),
favorite: reader.readBoolOrNull(offsets[5]) ?? false,
favorite: reader.readBoolOrNull(offsets[5]),
genre: reader.readStringList(offsets[6]),
id: id,
imageUrl: reader.readStringOrNull(offsets[7]),
@ -274,7 +274,7 @@ P _mangaDeserializeProp<P>(
case 4:
return (reader.readStringOrNull(offset)) as P;
case 5:
return (reader.readBoolOrNull(offset) ?? false) as P;
return (reader.readBoolOrNull(offset)) as P;
case 6:
return (reader.readStringList(offset)) as P;
case 7:
@ -1084,8 +1084,24 @@ extension MangaQueryFilter on QueryBuilder<Manga, Manga, QFilterCondition> {
});
}
QueryBuilder<Manga, Manga, QAfterFilterCondition> favoriteIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'favorite',
));
});
}
QueryBuilder<Manga, Manga, QAfterFilterCondition> favoriteIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'favorite',
));
});
}
QueryBuilder<Manga, Manga, QAfterFilterCondition> favoriteEqualTo(
bool value) {
bool? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'favorite',
@ -2919,7 +2935,7 @@ extension MangaQueryProperty on QueryBuilder<Manga, Manga, QQueryProperty> {
});
}
QueryBuilder<Manga, bool, QQueryOperations> favoriteProperty() {
QueryBuilder<Manga, bool?, QQueryOperations> favoriteProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'favorite');
});

View file

@ -9,7 +9,7 @@ class Settings {
Id? id;
@enumerated
DisplayType displayType;
late DisplayType displayType;
int? libraryFilterMangasDownloadType;
@ -64,7 +64,7 @@ class Settings {
List<Cookie>? cookiesList;
@enumerated
ReaderMode defaultReaderMode;
late ReaderMode defaultReaderMode;
List<PersonalReaderMode>? personalReaderModeList;
@ -95,7 +95,7 @@ class Settings {
L10nLocale? locale;
@enumerated
DisplayType animeDisplayType;
late DisplayType animeDisplayType;
int? libraryFilterAnimeDownloadType;
@ -117,17 +117,17 @@ class Settings {
bool? animeLibraryLocalSource;
SortLibraryManga? sortLibraryAnime;
late SortLibraryManga? sortLibraryAnime;
int? pagePreloadAmount;
bool? checkForExtensionUpdates;
@enumerated
ScaleType scaleType;
late ScaleType scaleType;
@enumerated
BackgroundColor backgroundColor;
late BackgroundColor backgroundColor;
List<PersonalPageMode>? personalPageModeList;
@ -187,6 +187,208 @@ class Settings {
this.checkForExtensionUpdates = true,
this.backgroundColor = BackgroundColor.black,
this.personalPageModeList});
Settings.fromJson(Map<String, dynamic> json) {
animatePageTransitions = json['animatePageTransitions'];
animeDisplayType = DisplayType.values[json['animeDisplayType']];
animeLibraryDownloadedChapters = json['animeLibraryDownloadedChapters'];
animeLibraryLocalSource = json['animeLibraryLocalSource'];
animeLibraryShowCategoryTabs = json['animeLibraryShowCategoryTabs'];
animeLibraryShowContinueReadingButton =
json['animeLibraryShowContinueReadingButton'];
animeLibraryShowLanguage = json['animeLibraryShowLanguage'];
animeLibraryShowNumbersOfItems = json['animeLibraryShowNumbersOfItems'];
autoExtensionsUpdates = json['autoExtensionsUpdates'];
backgroundColor = BackgroundColor.values[json['backgroundColor']];
if (json['chapterFilterBookmarkedList'] != null) {
chapterFilterBookmarkedList =
(json['chapterFilterBookmarkedList'] as List)
.map((e) => ChapterFilterBookmarked.fromJson(e))
.toList();
}
if (json['chapterFilterDownloadedList'] != null) {
chapterFilterDownloadedList =
(json['chapterFilterDownloadedList'] as List)
.map((e) => ChapterFilterDownloaded.fromJson(e))
.toList();
}
if (json['chapterFilterUnreadList'] != null) {
chapterFilterUnreadList = (json['chapterFilterUnreadList'] as List)
.map((e) => ChapterFilterUnread.fromJson(e))
.toList();
}
if (json['chapterPageIndexList'] != null) {
chapterPageIndexList = (json['chapterPageIndexList'] as List)
.map((e) => ChapterPageIndex.fromJson(e))
.toList();
}
if (json['chapterPageUrlsList'] != null) {
chapterPageUrlsList = (json['chapterPageUrlsList'] as List)
.map((e) => ChapterPageurls.fromJson(e))
.toList();
}
checkForExtensionUpdates = json['checkForExtensionUpdates'];
if (json['cookiesList'] != null) {
cookiesList =
(json['cookiesList'] as List).map((e) => Cookie.fromJson(e)).toList();
}
cropBorders = json['cropBorders'];
dateFormat = json['dateFormat'];
defaultReaderMode = ReaderMode.values[json['defaultReaderMode']];
displayType = DisplayType.values[json['displayType']];
doubleTapAnimationSpeed = json['doubleTapAnimationSpeed'];
downloadLocation = json['downloadLocation'];
downloadOnlyOnWifi = json['downloadOnlyOnWifi'];
if (json['filterScanlatorList'] != null) {
filterScanlatorList = (json['filterScanlatorList'] as List)
.map((e) => FilterScanlator.fromJson(e))
.toList();
}
flexColorSchemeBlendLevel = json['flexColorSchemeBlendLevel'];
flexSchemeColorIndex = json['flexSchemeColorIndex'];
id = json['id'];
incognitoMode = json['incognitoMode'];
libraryDownloadedChapters = json['libraryDownloadedChapters'];
libraryFilterAnimeBookMarkedType = json['libraryFilterAnimeBookMarkedType'];
libraryFilterAnimeDownloadType = json['libraryFilterAnimeDownloadType'];
libraryFilterAnimeStartedType = json['libraryFilterAnimeStartedType'];
libraryFilterAnimeUnreadType = json['libraryFilterAnimeUnreadType'];
libraryFilterMangasBookMarkedType =
json['libraryFilterMangasBookMarkedType'];
libraryFilterMangasDownloadType = json['libraryFilterMangasDownloadType'];
libraryFilterMangasStartedType = json['libraryFilterMangasStartedType'];
libraryFilterMangasUnreadType = json['libraryFilterMangasUnreadType'];
libraryLocalSource = json['libraryLocalSource'];
libraryShowCategoryTabs = json['libraryShowCategoryTabs'];
libraryShowContinueReadingButton = json['libraryShowContinueReadingButton'];
libraryShowLanguage = json['libraryShowLanguage'];
libraryShowNumbersOfItems = json['libraryShowNumbersOfItems'];
locale =
json['locale'] != null ? L10nLocale.fromJson(json['locale']) : null;
onlyIncludePinnedSources = json['onlyIncludePinnedSources'];
pagePreloadAmount = json['pagePreloadAmount'];
if (json['personalPageModeList'] != null) {
personalPageModeList = (json['personalPageModeList'] as List)
.map((e) => PersonalPageMode.fromJson(e))
.toList();
}
if (json['personalReaderModeList'] != null) {
personalReaderModeList = (json['personalReaderModeList'] as List)
.map((e) => PersonalReaderMode.fromJson(e))
.toList();
}
pureBlackDarkMode = json['pureBlackDarkMode'];
relativeTimesTamps = json['relativeTimesTamps'];
saveAsCBZArchive = json['saveAsCBZArchive'];
scaleType = ScaleType.values[json['scaleType']];
showNSFW = json['showNSFW'];
showPagesNumber = json['showPagesNumber'];
if (json['sortChapterList'] != null) {
sortChapterList = (json['sortChapterList'] as List)
.map((e) => SortChapter.fromJson(e))
.toList();
}
sortLibraryAnime = json['sortLibraryAnime'];
sortLibraryManga = json['sortLibraryManga'] != null
? SortLibraryManga.fromJson(json['sortLibraryManga'])
: null;
themeIsDark = json['themeIsDark'];
userAgent = json['userAgent'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['animatePageTransitions'] = animatePageTransitions;
data['animeDisplayType'] = animeDisplayType.index;
data['animeLibraryDownloadedChapters'] = animeLibraryDownloadedChapters;
data['animeLibraryLocalSource'] = animeLibraryLocalSource;
data['animeLibraryShowCategoryTabs'] = animeLibraryShowCategoryTabs;
data['animeLibraryShowContinueReadingButton'] =
animeLibraryShowContinueReadingButton;
data['animeLibraryShowLanguage'] = animeLibraryShowLanguage;
data['animeLibraryShowNumbersOfItems'] = animeLibraryShowNumbersOfItems;
data['autoExtensionsUpdates'] = autoExtensionsUpdates;
data['backgroundColor'] = backgroundColor.index;
if (chapterFilterBookmarkedList != null) {
data['chapterFilterBookmarkedList'] =
chapterFilterBookmarkedList!.map((v) => v.toJson()).toList();
}
if (chapterFilterDownloadedList != null) {
data['chapterFilterDownloadedList'] =
chapterFilterDownloadedList!.map((v) => v.toJson()).toList();
}
if (chapterFilterUnreadList != null) {
data['chapterFilterUnreadList'] =
chapterFilterUnreadList!.map((v) => v.toJson()).toList();
}
if (chapterPageIndexList != null) {
data['chapterPageIndexList'] =
chapterPageIndexList!.map((v) => v.toJson()).toList();
}
if (chapterPageUrlsList != null) {
data['chapterPageUrlsList'] =
chapterPageUrlsList!.map((v) => v.toJson()).toList();
}
data['checkForExtensionUpdates'] = checkForExtensionUpdates;
data['cookiesList'] = cookiesList;
data['cropBorders'] = cropBorders;
data['dateFormat'] = dateFormat;
data['defaultReaderMode'] = defaultReaderMode.index;
data['displayType'] = displayType.index;
data['doubleTapAnimationSpeed'] = doubleTapAnimationSpeed;
data['downloadLocation'] = downloadLocation;
data['downloadOnlyOnWifi'] = downloadOnlyOnWifi;
data['filterScanlatorList'] = filterScanlatorList;
data['flexColorSchemeBlendLevel'] = flexColorSchemeBlendLevel;
data['flexSchemeColorIndex'] = flexSchemeColorIndex;
data['id'] = id;
data['incognitoMode'] = incognitoMode;
data['libraryDownloadedChapters'] = libraryDownloadedChapters;
data['libraryFilterAnimeBookMarkedType'] = libraryFilterAnimeBookMarkedType;
data['libraryFilterAnimeDownloadType'] = libraryFilterAnimeDownloadType;
data['libraryFilterAnimeStartedType'] = libraryFilterAnimeStartedType;
data['libraryFilterAnimeUnreadType'] = libraryFilterAnimeUnreadType;
data['libraryFilterMangasBookMarkedType'] =
libraryFilterMangasBookMarkedType;
data['libraryFilterMangasDownloadType'] = libraryFilterMangasDownloadType;
data['libraryFilterMangasStartedType'] = libraryFilterMangasStartedType;
data['libraryFilterMangasUnreadType'] = libraryFilterMangasUnreadType;
data['libraryLocalSource'] = libraryLocalSource;
data['libraryShowCategoryTabs'] = libraryShowCategoryTabs;
data['libraryShowContinueReadingButton'] = libraryShowContinueReadingButton;
data['libraryShowLanguage'] = libraryShowLanguage;
data['libraryShowNumbersOfItems'] = libraryShowNumbersOfItems;
if (locale != null) {
data['locale'] = locale!.toJson();
}
data['onlyIncludePinnedSources'] = onlyIncludePinnedSources;
data['pagePreloadAmount'] = pagePreloadAmount;
if (personalPageModeList != null) {
data['personalPageModeList'] =
personalPageModeList!.map((v) => v.toJson()).toList();
}
if (personalReaderModeList != null) {
data['personalReaderModeList'] =
personalReaderModeList!.map((v) => v.toJson()).toList();
}
data['pureBlackDarkMode'] = pureBlackDarkMode;
data['relativeTimesTamps'] = relativeTimesTamps;
data['saveAsCBZArchive'] = saveAsCBZArchive;
data['scaleType'] = scaleType.index;
data['showNSFW'] = showNSFW;
data['showPagesNumber'] = showPagesNumber;
if (sortChapterList != null) {
data['sortChapterList'] =
sortChapterList!.map((v) => v.toJson()).toList();
}
data['sortLibraryAnime'] = sortLibraryAnime;
if (sortLibraryManga != null) {
data['sortLibraryManga'] = sortLibraryManga!.toJson();
}
data['themeIsDark'] = themeIsDark;
data['userAgent'] = userAgent;
return data;
}
}
enum DisplayType {
@ -212,6 +414,17 @@ class SortLibraryManga {
bool? reverse;
int? index;
SortLibraryManga({this.reverse = false, this.index = 0});
SortLibraryManga.fromJson(Map<String, dynamic> json) {
index = json['index'];
reverse = json['reverse'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['index'] = index;
data['reverse'] = reverse;
return data;
}
}
@embedded
@ -220,6 +433,19 @@ class SortChapter {
bool? reverse;
int? index;
SortChapter({this.mangaId, this.reverse = false, this.index = 1});
SortChapter.fromJson(Map<String, dynamic> json) {
index = json['index'];
mangaId = json['mangaId'];
reverse = json['reverse'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['index'] = index;
data['mangaId'] = mangaId;
data['reverse'] = reverse;
return data;
}
}
@embedded
@ -227,6 +453,17 @@ class ChapterFilterDownloaded {
int? mangaId;
int? type;
ChapterFilterDownloaded({this.mangaId, this.type = 0});
ChapterFilterDownloaded.fromJson(Map<String, dynamic> json) {
mangaId = json['mangaId'];
type = json['type'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['mangaId'] = mangaId;
data['type'] = type;
return data;
}
}
@embedded
@ -234,6 +471,17 @@ class ChapterFilterUnread {
int? mangaId;
int? type;
ChapterFilterUnread({this.mangaId, this.type = 0});
ChapterFilterUnread.fromJson(Map<String, dynamic> json) {
mangaId = json['mangaId'];
type = json['type'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['mangaId'] = mangaId;
data['type'] = type;
return data;
}
}
@embedded
@ -241,24 +489,74 @@ class ChapterFilterBookmarked {
int? mangaId;
int? type;
ChapterFilterBookmarked({this.mangaId, this.type = 0});
ChapterFilterBookmarked.fromJson(Map<String, dynamic> json) {
mangaId = json['mangaId'];
type = json['type'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['mangaId'] = mangaId;
data['type'] = type;
return data;
}
}
@embedded
class ChapterPageurls {
int? chapterId;
List<String>? urls;
ChapterPageurls({this.chapterId, this.urls});
ChapterPageurls.fromJson(Map<String, dynamic> json) {
chapterId = json['chapterId'];
urls = json['urls'].cast<String>();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['chapterId'] = chapterId;
data['urls'] = urls;
return data;
}
}
@embedded
class ChapterPageIndex {
int? chapterId;
int? index;
ChapterPageIndex({this.chapterId, this.index});
ChapterPageIndex.fromJson(Map<String, dynamic> json) {
chapterId = json['chapterId'];
index = json['index'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['chapterId'] = chapterId;
data['index'] = index;
return data;
}
}
@embedded
class Cookie {
String? idSource;
String? cookie;
Cookie({this.idSource, this.cookie});
Cookie.fromJson(Map<String, dynamic> json) {
idSource = json['idSource'];
cookie = json['cookie'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['idSource'] = idSource;
data['cookie'] = cookie;
return data;
}
}
@embedded
@ -267,6 +565,19 @@ class PersonalReaderMode {
@enumerated
ReaderMode readerMode = ReaderMode.vertical;
PersonalReaderMode({this.mangaId, this.readerMode = ReaderMode.vertical});
PersonalReaderMode.fromJson(Map<String, dynamic> json) {
mangaId = json['mangaId'];
readerMode = ReaderMode.values[json['readerMode']];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['mangaId'] = mangaId;
data['readerMode'] = readerMode.index;
return data;
}
}
@embedded
@ -275,6 +586,19 @@ class PersonalPageMode {
@enumerated
PageMode pageMode = PageMode.onePage;
PersonalPageMode({this.mangaId, this.pageMode = PageMode.onePage});
PersonalPageMode.fromJson(Map<String, dynamic> json) {
mangaId = json['mangaId'];
pageMode = PageMode.values[json['pageMode']];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['mangaId'] = mangaId;
data['pageMode'] = pageMode.index;
return data;
}
}
enum ReaderMode { vertical, ltr, rtl, verticalContinuous, webtoon }
@ -285,6 +609,19 @@ enum PageMode { onePage, doubleColumm }
class FilterScanlator {
int? mangaId;
List<String>? scanlators;
FilterScanlator({this.mangaId, this.scanlators});
FilterScanlator.fromJson(Map<String, dynamic> json) {
mangaId = json['mangaId'];
scanlators = json['scanlators'].cast<String>();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['mangaId'] = mangaId;
data['scanlators'] = scanlators;
return data;
}
}
@embedded
@ -292,4 +629,16 @@ class L10nLocale {
String? languageCode;
String? countryCode;
L10nLocale({this.languageCode, this.countryCode});
L10nLocale.fromJson(Map<String, dynamic> json) {
countryCode = json['countryCode'];
languageCode = json['languageCode'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['countryCode'] = countryCode;
data['languageCode'] = languageCode;
return data;
}
}

View file

@ -7973,9 +7973,10 @@ ChapterPageurls _chapterPageurlsDeserialize(
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = ChapterPageurls();
object.chapterId = reader.readLongOrNull(offsets[0]);
object.urls = reader.readStringList(offsets[1]);
final object = ChapterPageurls(
chapterId: reader.readLongOrNull(offsets[0]),
urls: reader.readStringList(offsets[1]),
);
return object;
}
@ -8367,9 +8368,10 @@ ChapterPageIndex _chapterPageIndexDeserialize(
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = ChapterPageIndex();
object.chapterId = reader.readLongOrNull(offsets[0]);
object.index = reader.readLongOrNull(offsets[1]);
final object = ChapterPageIndex(
chapterId: reader.readLongOrNull(offsets[0]),
index: reader.readLongOrNull(offsets[1]),
);
return object;
}
@ -8604,9 +8606,10 @@ Cookie _cookieDeserialize(
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = Cookie();
object.cookie = reader.readStringOrNull(offsets[0]);
object.idSource = reader.readStringOrNull(offsets[1]);
final object = Cookie(
cookie: reader.readStringOrNull(offsets[0]),
idSource: reader.readStringOrNull(offsets[1]),
);
return object;
}
@ -8972,11 +8975,12 @@ PersonalReaderMode _personalReaderModeDeserialize(
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = PersonalReaderMode();
object.mangaId = reader.readLongOrNull(offsets[0]);
object.readerMode = _PersonalReaderModereaderModeValueEnumMap[
reader.readByteOrNull(offsets[1])] ??
ReaderMode.vertical;
final object = PersonalReaderMode(
mangaId: reader.readLongOrNull(offsets[0]),
readerMode: _PersonalReaderModereaderModeValueEnumMap[
reader.readByteOrNull(offsets[1])] ??
ReaderMode.vertical,
);
return object;
}
@ -9199,11 +9203,12 @@ PersonalPageMode _personalPageModeDeserialize(
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = PersonalPageMode();
object.mangaId = reader.readLongOrNull(offsets[0]);
object.pageMode = _PersonalPageModepageModeValueEnumMap[
reader.readByteOrNull(offsets[1])] ??
PageMode.onePage;
final object = PersonalPageMode(
mangaId: reader.readLongOrNull(offsets[0]),
pageMode: _PersonalPageModepageModeValueEnumMap[
reader.readByteOrNull(offsets[1])] ??
PageMode.onePage,
);
return object;
}
@ -9431,9 +9436,10 @@ FilterScanlator _filterScanlatorDeserialize(
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = FilterScanlator();
object.mangaId = reader.readLongOrNull(offsets[0]);
object.scanlators = reader.readStringList(offsets[1]);
final object = FilterScanlator(
mangaId: reader.readLongOrNull(offsets[0]),
scanlators: reader.readStringList(offsets[1]),
);
return object;
}

View file

@ -75,24 +75,61 @@ class Source {
this.headers = '',
this.isManga = true,
this.appMinVerReq = ""});
Source.fromJson(Map<String, dynamic> json) {
name = json['name'];
id = json['id'];
apiUrl = json['apiUrl'];
appMinVerReq = json['appMinVerReq'];
baseUrl = json['baseUrl'];
lang = json['lang'];
typeSource = json['typeSource'];
iconUrl = json['iconUrl'];
dateFormat = json['dateFormat'];
dateFormatLocale = json['dateFormatLocale'];
isNsfw = json['isNsfw'];
hasCloudflare = json['hasCloudflare'];
headers = json['headers'];
iconUrl = json['iconUrl'];
id = json['id'];
isActive = json['isActive'];
isAdded = json['isAdded'];
isFullData = json['isFullData'];
isManga = json['isManga'];
isNsfw = json['isNsfw'];
isPinned = json['isPinned'];
lang = json['lang'];
lastUsed = json['lastUsed'];
name = json['name'];
sourceCode = json['sourceCode'];
sourceCodeUrl = json['sourceCodeUrl'];
apiUrl = json['apiUrl'];
typeSource = json['typeSource'];
version = json['version'];
isManga = json['isManga'] ?? true;
isFullData = json['isFullData'] ?? false;
appMinVerReq = json['appMinVerReq'];
versionLast = json['versionLast'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['apiUrl'] = apiUrl;
data['appMinVerReq'] = appMinVerReq;
data['baseUrl'] = baseUrl;
data['dateFormat'] = dateFormat;
data['dateFormatLocale'] = dateFormatLocale;
data['hasCloudflare'] = hasCloudflare;
data['headers'] = headers;
data['iconUrl'] = iconUrl;
data['id'] = id;
data['isActive'] = isActive;
data['isAdded'] = isAdded;
data['isFullData'] = isFullData;
data['isManga'] = isManga;
data['isNsfw'] = isNsfw;
data['isPinned'] = isPinned;
data['lang'] = lang;
data['lastUsed'] = lastUsed;
data['name'] = name;
data['sourceCode'] = sourceCode;
data['sourceCodeUrl'] = sourceCodeUrl;
data['typeSource'] = typeSource;
data['version'] = version;
data['versionLast'] = versionLast;
return data;
}
MSource toMSource() {
return MSource(
id: id,

View file

@ -23,7 +23,7 @@ class Track {
int? score;
@enumerated
TrackStatus status;
late TrackStatus status;
int? startedReadingDate;
@ -45,6 +45,39 @@ class Track {
this.startedReadingDate,
this.finishedReadingDate,
this.trackingUrl});
Track.fromJson(Map<String, dynamic> json) {
finishedReadingDate = json['finishedReadingDate'];
id = json['id'];
lastChapterRead = json['lastChapterRead'];
libraryId = json['libraryId'];
mangaId = json['mangaId'];
mediaId = json['mediaId'];
score = json['score'];
startedReadingDate = json['startedReadingDate'];
status = TrackStatus.values[json['status']];
syncId = json['syncId'];
title = json['title'];
totalChapter = json['totalChapter'];
trackingUrl = json['trackingUrl'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['finishedReadingDate'] = finishedReadingDate;
data['id'] = id;
data['lastChapterRead'] = lastChapterRead;
data['libraryId'] = libraryId;
data['mangaId'] = mangaId;
data['mediaId'] = mediaId;
data['score'] = score;
data['startedReadingDate'] = startedReadingDate;
data['status'] = status.index;
data['syncId'] = syncId;
data['title'] = title;
data['totalChapter'] = totalChapter;
data['trackingUrl'] = trackingUrl;
return data;
}
}
enum TrackStatus {

View file

@ -18,4 +18,21 @@ class TrackPreference {
this.oAuth,
this.prefs,
});
TrackPreference.fromJson(Map<String, dynamic> json) {
syncId = json['syncId'];
username = json['username'];
oAuth = json['oAuth'];
prefs = json['prefs'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['syncId'] = syncId;
data['username'] = username;
data['oAuth'] = oAuth;
data['prefs'] = prefs;
return data;
}
}

View file

@ -98,7 +98,9 @@ class AnimeStreamController {
if (empty) {
history = History(
mangaId: getAnime().id,
date: DateTime.now().millisecondsSinceEpoch.toString())
date: DateTime.now().millisecondsSinceEpoch.toString(),
isManga: getAnime().isManga,
chapterId: episode.id)
..chapter.value = episode;
} else {
history = (isar.historys

View file

@ -253,13 +253,13 @@ class _MangaGlobalImageCardState extends ConsumerState<MangaGlobalImageCard>
height: 150,
color: snapshot.hasData &&
snapshot.data!.isNotEmpty &&
snapshot.data!.first.favorite
snapshot.data!.first.favorite!
? Colors.black.withOpacity(0.7)
: null,
),
if (snapshot.hasData &&
snapshot.data!.isNotEmpty &&
snapshot.data!.first.favorite)
snapshot.data!.first.favorite!)
Positioned(
top: 0,
left: 0,

View file

@ -15,10 +15,7 @@ Future importArchivesFromFile(ImportArchivesFromFileRef ref, Manga? mManga,
allowMultiple: true,
type: FileType.custom,
allowedExtensions: isManga
? [
'cbz',
'zip',
]
? ['cbz', 'zip']
: ['mp4', 'mov', 'avi', 'flv', 'wmv', 'mpeg', 'mkv']);
if (result != null) {
final dateNow = DateTime.now().millisecondsSinceEpoch;
@ -53,7 +50,8 @@ Future importArchivesFromFile(ImportArchivesFromFileRef ref, Manga? mManga,
isar.mangas.putSync(manga);
final chapters = Chapter(
name: isManga ? data!.$1 : name,
archivePath: isManga ? data!.$4 : file.path)
archivePath: isManga ? data!.$4 : file.path,
mangaId: manga.id)
..manga.value = manga;
isar.chapters.putSync(chapters);
chapters.manga.saveSync();

View file

@ -7,7 +7,7 @@ part of 'local_archive.dart';
// **************************************************************************
String _$importArchivesFromFileHash() =>
r'f911dfd50c20ebbfec97322dd0bf44261a025341';
r'cbc93a01e89564117369b764d226f4943bf7ee49';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -7,8 +7,11 @@ import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/browse/extension/providers/fetch_manga_sources.dart';
import 'package:mangayomi/modules/main_view/providers/migration.dart';
import 'package:mangayomi/modules/more/about/providers/check_for_update.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:mangayomi/modules/widgets/error_text.dart';
import 'package:mangayomi/modules/widgets/progress_center.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/router/router.dart';
import 'package:mangayomi/utils/colors.dart';
@ -26,236 +29,246 @@ class MainScreen extends ConsumerWidget {
final l10n = l10nLocalizations(context)!;
final route = GoRouter.of(context);
ref.watch(checkForUpdateProvider(context: context));
return Consumer(builder: (context, ref, chuld) {
final location = ref.watch(
routerCurrentLocationStateProvider(context),
);
bool isReadingScreen =
location == '/mangareaderview' || location == '/animePlayerView';
int currentIndex = switch (location) {
null => 0,
'/MangaLibrary' => 0,
'/AnimeLibrary' => 1,
'/history' => 2,
'/browse' => 3,
_ => 4,
};
return ref.watch(migrationProvider).when(data: (_) {
return Consumer(builder: (context, ref, chuld) {
final location = ref.watch(
routerCurrentLocationStateProvider(context),
);
bool isReadingScreen =
location == '/mangareaderview' || location == '/animePlayerView';
int currentIndex = switch (location) {
null => 0,
'/MangaLibrary' => 0,
'/AnimeLibrary' => 1,
'/history' => 2,
'/browse' => 3,
_ => 4,
};
final incognitoMode = ref.watch(incognitoModeStateProvider);
final isLongPressed = ref.watch(isLongPressedMangaStateProvider);
return Column(
children: [
if (!isReadingScreen)
Material(
child: AnimatedContainer(
height: incognitoMode
? Platform.isAndroid || Platform.isIOS
? MediaQuery.of(context).padding.top * 2
: 50
: 0,
curve: Curves.easeIn,
duration: const Duration(milliseconds: 150),
color: primaryColor(context),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
l10n.incognito_mode,
style: TextStyle(
color: Colors.white,
fontFamily: GoogleFonts.aBeeZee().fontFamily,
),
),
)
],
),
),
),
Flexible(
child: Scaffold(
body: isTablet(context)
? Row(
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 0),
width: switch (isLongPressed) {
true => 0,
_ => switch (location) {
null => 100,
!= '/MangaLibrary' &&
!= '/AnimeLibrary' &&
!= '/history' &&
!= '/browse' &&
!= '/more' =>
0,
_ => 100,
},
},
child: Stack(
children: [
NavigationRailTheme(
data: NavigationRailThemeData(
indicatorShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30)),
),
child: NavigationRail(
labelType: NavigationRailLabelType.all,
useIndicator: true,
destinations: [
NavigationRailDestination(
selectedIcon: const Icon(
Icons.collections_bookmark),
icon: const Icon(Icons
.collections_bookmark_outlined),
label: Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(l10n.manga))),
NavigationRailDestination(
selectedIcon:
const Icon(Icons.video_collection),
icon: const Icon(
Icons.video_collection_outlined),
label: Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(l10n.anime))),
NavigationRailDestination(
selectedIcon: const Icon(Icons.history),
icon:
const Icon(Icons.history_outlined),
label: Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(l10n.history))),
NavigationRailDestination(
selectedIcon: const Icon(Icons.explore),
icon:
const Icon(Icons.explore_outlined),
label: Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(l10n.browse))),
NavigationRailDestination(
selectedIcon:
const Icon(Icons.more_horiz),
icon: const Icon(
Icons.more_horiz_outlined),
label: Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(l10n.more))),
],
selectedIndex: currentIndex,
onDestinationSelected: (newIndex) {
if (newIndex == 0) {
route.go('/MangaLibrary');
} else if (newIndex == 1) {
route.go('/AnimeLibrary');
} else if (newIndex == 2) {
route.go('/history');
} else if (newIndex == 3) {
route.go('/browse');
} else if (newIndex == 4) {
route.go('/more');
}
},
),
),
Positioned(
right: 7,
top: 210,
child: _extensionUpdateTotalNumbers(ref)),
],
final incognitoMode = ref.watch(incognitoModeStateProvider);
final isLongPressed = ref.watch(isLongPressedMangaStateProvider);
return Column(
children: [
if (!isReadingScreen)
Material(
child: AnimatedContainer(
height: incognitoMode
? Platform.isAndroid || Platform.isIOS
? MediaQuery.of(context).padding.top * 2
: 50
: 0,
curve: Curves.easeIn,
duration: const Duration(milliseconds: 150),
color: primaryColor(context),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
l10n.incognito_mode,
style: TextStyle(
color: Colors.white,
fontFamily: GoogleFonts.aBeeZee().fontFamily,
),
),
Expanded(child: child)
],
)
: child,
bottomNavigationBar: isTablet(context)
? null
: AnimatedContainer(
duration: const Duration(milliseconds: 0),
width: mediaWidth(context, 1),
height: switch (isLongPressed) {
true => 0,
_ => switch (location) {
null => null,
!= '/MangaLibrary' &&
!= '/AnimeLibrary' &&
!= '/history' &&
!= '/browse' &&
!= '/more' =>
0,
_ => null,
},
},
child: NavigationBarTheme(
data: NavigationBarThemeData(
indicatorShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30)),
),
child: NavigationBar(
animationDuration: const Duration(milliseconds: 500),
selectedIndex: currentIndex,
destinations: [
NavigationDestination(
selectedIcon:
const Icon(Icons.collections_bookmark),
icon: const Icon(
Icons.collections_bookmark_outlined),
label: l10n.manga),
NavigationDestination(
selectedIcon:
const Icon(Icons.video_collection),
icon:
const Icon(Icons.video_collection_outlined),
label: l10n.anime),
NavigationDestination(
selectedIcon: const Icon(Icons.history),
icon: const Icon(Icons.history_outlined),
label: l10n.history),
Stack(
)
],
),
),
),
Flexible(
child: Scaffold(
body: isTablet(context)
? Row(
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 0),
width: switch (isLongPressed) {
true => 0,
_ => switch (location) {
null => 100,
!= '/MangaLibrary' &&
!= '/AnimeLibrary' &&
!= '/history' &&
!= '/browse' &&
!= '/more' =>
0,
_ => 100,
},
},
child: Stack(
children: [
NavigationDestination(
selectedIcon: const Icon(Icons.explore),
icon: const Icon(Icons.explore_outlined),
label: l10n.browse),
NavigationRailTheme(
data: NavigationRailThemeData(
indicatorShape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(30)),
),
child: NavigationRail(
labelType: NavigationRailLabelType.all,
useIndicator: true,
destinations: [
NavigationRailDestination(
selectedIcon: const Icon(
Icons.collections_bookmark),
icon: const Icon(Icons
.collections_bookmark_outlined),
label: Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(l10n.manga))),
NavigationRailDestination(
selectedIcon: const Icon(
Icons.video_collection),
icon: const Icon(
Icons.video_collection_outlined),
label: Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(l10n.anime))),
NavigationRailDestination(
selectedIcon:
const Icon(Icons.history),
icon: const Icon(
Icons.history_outlined),
label: Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(l10n.history))),
NavigationRailDestination(
selectedIcon:
const Icon(Icons.explore),
icon: const Icon(
Icons.explore_outlined),
label: Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(l10n.browse))),
NavigationRailDestination(
selectedIcon:
const Icon(Icons.more_horiz),
icon: const Icon(
Icons.more_horiz_outlined),
label: Padding(
padding:
const EdgeInsets.only(top: 5),
child: Text(l10n.more))),
],
selectedIndex: currentIndex,
onDestinationSelected: (newIndex) {
if (newIndex == 0) {
route.go('/MangaLibrary');
} else if (newIndex == 1) {
route.go('/AnimeLibrary');
} else if (newIndex == 2) {
route.go('/history');
} else if (newIndex == 3) {
route.go('/browse');
} else if (newIndex == 4) {
route.go('/more');
}
},
),
),
Positioned(
right: 14,
top: 3,
right: 7,
top: 210,
child: _extensionUpdateTotalNumbers(ref)),
],
),
NavigationDestination(
selectedIcon: const Icon(Icons.more_horiz),
icon: const Icon(Icons.more_horiz_outlined),
label: l10n.more),
],
onDestinationSelected: (newIndex) {
if (newIndex == 0) {
route.go('/MangaLibrary');
} else if (newIndex == 1) {
route.go('/AnimeLibrary');
} else if (newIndex == 2) {
route.go('/history');
} else if (newIndex == 3) {
route.go('/browse');
} else if (newIndex == 4) {
route.go('/more');
}
},
),
Expanded(child: child)
],
)
: child,
bottomNavigationBar: isTablet(context)
? null
: AnimatedContainer(
duration: const Duration(milliseconds: 0),
width: mediaWidth(context, 1),
height: switch (isLongPressed) {
true => 0,
_ => switch (location) {
null => null,
!= '/MangaLibrary' &&
!= '/AnimeLibrary' &&
!= '/history' &&
!= '/browse' &&
!= '/more' =>
0,
_ => null,
},
},
child: NavigationBarTheme(
data: NavigationBarThemeData(
indicatorShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30)),
),
child: NavigationBar(
animationDuration:
const Duration(milliseconds: 500),
selectedIndex: currentIndex,
destinations: [
NavigationDestination(
selectedIcon:
const Icon(Icons.collections_bookmark),
icon: const Icon(
Icons.collections_bookmark_outlined),
label: l10n.manga),
NavigationDestination(
selectedIcon:
const Icon(Icons.video_collection),
icon: const Icon(
Icons.video_collection_outlined),
label: l10n.anime),
NavigationDestination(
selectedIcon: const Icon(Icons.history),
icon: const Icon(Icons.history_outlined),
label: l10n.history),
Stack(
children: [
NavigationDestination(
selectedIcon: const Icon(Icons.explore),
icon: const Icon(Icons.explore_outlined),
label: l10n.browse),
Positioned(
right: 14,
top: 3,
child: _extensionUpdateTotalNumbers(ref)),
],
),
NavigationDestination(
selectedIcon: const Icon(Icons.more_horiz),
icon: const Icon(Icons.more_horiz_outlined),
label: l10n.more),
],
onDestinationSelected: (newIndex) {
if (newIndex == 0) {
route.go('/MangaLibrary');
} else if (newIndex == 1) {
route.go('/AnimeLibrary');
} else if (newIndex == 2) {
route.go('/history');
} else if (newIndex == 3) {
route.go('/browse');
} else if (newIndex == 4) {
route.go('/more');
}
},
),
),
),
),
),
),
),
],
);
],
);
});
}, error: (Object error, StackTrace stackTrace) {
return ErrorText(error);
}, loading: () {
return const ProgressCenter();
});
}
}

View file

@ -0,0 +1,44 @@
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/download.dart';
import 'package:mangayomi/models/history.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'migration.g.dart';
@riverpod
Future<void> migration(MigrationRef ref) async {
final chapters =
isar.chapters.filter().idIsNotNull().mangaIdIsNull().findAllSync();
final downloads =
isar.downloads.filter().idIsNotNull().mangaIdIsNull().findAllSync();
final histories = isar.historys
.filter()
.idIsNotNull()
.chapterIdIsNull()
.or()
.idIsNotNull()
.isMangaIsNull()
.findAllSync();
isar.writeTxnSync(() {
//mangaId in chapter
for (var chapter in chapters) {
final mangaId = chapter.manga.value!.id;
isar.chapters.putSync(chapter..mangaId = mangaId);
}
//mangaId in Download
for (var download in downloads) {
final mangaId = download.chapter.value!.manga.value!.id;
isar.downloads.putSync(download..mangaId = mangaId);
}
//chapterId and isManga in History
for (var history in histories) {
final chapterId = history.chapter.value!.id;
final isManga = history.chapter.value!.manga.value!.isManga;
isar.historys.putSync(history
..chapterId = chapterId
..isManga = isManga);
}
});
}

View file

@ -0,0 +1,24 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'migration.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$migrationHash() => r'c37210dc71317d8be30be363f78793ff5c9c6fd1';
/// See also [migration].
@ProviderFor(migration)
final migrationProvider = AutoDisposeFutureProvider<void>.internal(
migration,
name: r'migrationProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$migrationHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef MigrationRef = AutoDisposeFutureProviderRef<void>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View file

@ -373,12 +373,12 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
)),
PopupMenuButton(itemBuilder: (context) {
return [
if (widget.manga!.favorite)
if (widget.manga!.favorite!)
PopupMenuItem<int>(
value: 0,
child: Text(l10n.edit_categories)),
if (!isLocalArchive)
if (widget.manga!.favorite)
if (widget.manga!.favorite!)
PopupMenuItem<int>(
value: 1, child: Text(l10n.migrate)),
if (!isLocalArchive)

View file

@ -143,7 +143,7 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
)
],
),
action: widget.manga.favorite
action: widget.manga.favorite!
? SizedBox(
child: ElevatedButton(
style: ElevatedButton.styleFrom(

View file

@ -68,6 +68,7 @@ Future<dynamic> updateMangaDetail(UpdateMangaDetailRef ref,
? DateTime.now().millisecondsSinceEpoch.toString()
: chaps[i].dateUpload.toString(),
scanlator: chaps[i].scanlator ?? '',
mangaId: mangaId,
)..manga.value = manga;
chapters.add(chapter);
}

View file

@ -6,7 +6,7 @@ part of 'update_manga_detail_providers.dart';
// RiverpodGenerator
// **************************************************************************
String _$updateMangaDetailHash() => r'634581c5e6d7c887695eaed86e920c6d8904e5f1';
String _$updateMangaDetailHash() => r'7c528575751a4c88f3ea80edc87973dd668fba6c';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -206,19 +206,22 @@ Future<List<String>> downloadChapter(
}
if (tasks.isEmpty && pageUrls.isNotEmpty) {
final model = Download(
savePageUrls();
final download = Download(
succeeded: 0,
failed: 0,
total: 0,
isDownload: true,
taskIds: pageUrls,
isStartDownload: false,
chapterId: chapter.id);
chapterId: chapter.id,
mangaId: manga.id);
isar.writeTxnSync(() {
isar.downloads.putSync(model..chapter.value = chapter);
isar.downloads.putSync(download..chapter.value = chapter);
});
} else {
savePageUrls();
if (isManga) {
await FileDownloader().downloadBatch(
tasks,
@ -238,25 +241,25 @@ Future<List<String>> downloadChapter(
.chapterIdEqualTo(chapter.id!)
.isEmptySync();
if (isEmpty) {
final model = Download(
succeeded: succeeded,
failed: failed,
total: tasks.length,
isDownload: (succeeded == tasks.length),
taskIds: pageUrls,
isStartDownload: true,
chapterId: chapter.id,
);
final download = Download(
succeeded: succeeded,
failed: failed,
total: tasks.length,
isDownload: (succeeded == tasks.length),
taskIds: pageUrls,
isStartDownload: true,
chapterId: chapter.id,
mangaId: manga.id);
isar.writeTxnSync(() {
isar.downloads.putSync(model..chapter.value = chapter);
isar.downloads.putSync(download..chapter.value = chapter);
});
} else {
final model = isar.downloads
final download = isar.downloads
.filter()
.chapterIdEqualTo(chapter.id!)
.findFirstSync()!;
isar.writeTxnSync(() {
isar.downloads.putSync(model
isar.downloads.putSync(download
..succeeded = succeeded
..failed = failed
..isDownload = (succeeded == tasks.length));
@ -283,33 +286,30 @@ Future<List<String>> downloadChapter(
.chapterIdEqualTo(chapter.id!)
.isEmptySync();
if (isEmpty) {
final model = Download(
succeeded: (progress * 100).toInt(),
failed: 0,
total: 100,
isDownload: (progress == 1.0),
taskIds: pageUrls,
isStartDownload: true,
chapterId: chapter.id,
);
final download = Download(
succeeded: (progress * 100).toInt(),
failed: 0,
total: 100,
isDownload: (progress == 1.0),
taskIds: pageUrls,
isStartDownload: true,
chapterId: chapter.id,
mangaId: manga.id);
isar.writeTxnSync(() {
isar.downloads.putSync(model..chapter.value = chapter);
isar.downloads.putSync(download..chapter.value = chapter);
});
} else {
final model = isar.downloads
final download = isar.downloads
.filter()
.chapterIdEqualTo(chapter.id!)
.findFirstSync()!;
isar.writeTxnSync(() {
isar.downloads.putSync(model
isar.downloads.putSync(download
..succeeded = (progress * 100).toInt()
..failed = 0
..isDownload = (progress == 1.0));
});
}
if (progress == 1.0) {
savePageUrls();
}
},
onStatus: (status) async {
if (status == TaskStatus.complete) {

View file

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

View file

@ -134,7 +134,9 @@ class ReaderController {
if (empty) {
history = History(
mangaId: getManga().id,
date: DateTime.now().millisecondsSinceEpoch.toString())
date: DateTime.now().millisecondsSinceEpoch.toString(),
isManga: getManga().isManga,
chapterId: chapter.id)
..chapter.value = chapter;
} else {
history = (isar.historys

View file

@ -0,0 +1,136 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/modules/manga/detail/widgets/chapter_filter_list_tile_widget.dart';
import 'package:mangayomi/modules/more/backup_and_restore/providers/backup.dart';
import 'package:mangayomi/modules/more/backup_and_restore/providers/restore.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/utils/media_query.dart';
class BackupAndRestore extends ConsumerWidget {
const BackupAndRestore({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final l10n = l10nLocalizations(context)!;
return Scaffold(
appBar: AppBar(
title: const Text("Backup and restore"),
),
body: Column(
children: [
ListTile(
onTap: () {
final list = _getList();
List<int> indexList = [];
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text(
"What do you want to backup?",
),
content: SizedBox(
width: mediaWidth(context, 0.8),
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: (context, index) {
return ListTileChapterFilter(
label: list[index],
type: indexList.contains(index) ? 1 : 0,
onTap: () {
if (indexList.contains(index)) {
setState(() {
indexList.remove(index);
});
} else {
setState(() {
indexList.add(index);
});
}
});
},
)),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () async {
Navigator.pop(context);
},
child: Text(
l10n.cancel,
style: TextStyle(
color: primaryColor(context)),
)),
TextButton(
onPressed: () async {
String? result = await FilePicker.platform
.getDirectoryPath();
if (result != null && context.mounted) {
ref.watch(doBackUpProvider(
list: indexList,
path: result,
context: context));
}
},
child: Text(
l10n.ok,
style: TextStyle(
color: primaryColor(context)),
)),
],
)
],
);
},
);
});
},
title: const Text("Create backup"),
subtitle: Text(
"Can be used to restore current library",
style: TextStyle(fontSize: 11, color: secondaryColor(context)),
),
),
ListTile(
onTap: () async {
FilePickerResult? result = await FilePicker.platform.pickFiles(
allowMultiple: false,
type: FileType.custom,
allowedExtensions: ['backup']);
if (result != null && context.mounted) {
ref.watch(doRestoreProvider(
path: result.files.first.path!, context: context));
}
},
title: const Text("Restore backup"),
subtitle: Text(
"Restore library from backup file",
style: TextStyle(fontSize: 11, color: secondaryColor(context)),
),
),
],
),
);
}
}
List<String> _getList() {
return [
"Library entries",
"Categories",
"Chapters and episode",
"Tracking",
"History",
"Settings",
"Extensions"
];
}

View file

@ -0,0 +1,135 @@
import 'dart:convert';
import 'dart:io';
import 'package:archive/archive_io.dart';
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/category.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/history.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/models/track.dart';
import 'package:mangayomi/models/track_preference.dart';
import 'package:mangayomi/utils/extensions.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:share_plus/share_plus.dart';
part 'backup.g.dart';
@riverpod
void doBackUp(DoBackUpRef ref,
{required List<int> list,
required String path,
required BuildContext context}) {
Map<String, dynamic> datas = {};
datas.addAll({"version": "1"});
if (list.contains(0)) {
final res = isar.mangas
.filter()
.idIsNotNull()
.favoriteEqualTo(true)
.isLocalArchiveEqualTo(false)
.findAllSync()
.map((e) => e.toJson())
.toList();
datas.addAll({"manga": res});
}
if (list.contains(1)) {
final res = isar.categorys
.filter()
.idIsNotNull()
.findAllSync()
.map((e) => e.toJson())
.toList();
datas.addAll({"categories": res});
}
if (list.contains(2)) {
final res = isar.chapters
.filter()
.idIsNotNull()
.findAllSync()
.map((e) => e.toJson())
.toList();
datas.addAll({"chapters": res});
}
if (list.contains(3)) {
final res = isar.tracks
.filter()
.idIsNotNull()
.findAllSync()
.map((e) => e.toJson())
.toList();
datas.addAll({"tracks": res});
final res_ = isar.trackPreferences
.filter()
.syncIdIsNotNull()
.findAllSync()
.map((e) => e.toJson())
.toList();
datas.addAll({"trackPreferences": res_});
}
if (list.contains(4)) {
final res = isar.historys
.filter()
.idIsNotNull()
.findAllSync()
.map((e) => e.toJson())
.toList();
datas.addAll({"history": res});
}
if (list.contains(5)) {
final res = isar.settings
.filter()
.idIsNotNull()
.findAllSync()
.map((e) => e.toJson())
.toList();
datas.addAll({"settings": res});
}
if (list.contains(6)) {
final res = isar.sources
.filter()
.idIsNotNull()
.findAllSync()
.map((e) => e.toJson())
.toList();
datas.addAll({"extensions": res});
}
final name =
'mangayomi_${DateTime.now().toString().substringBefore(' ').replaceAll('-', '_')}';
final backupFilePath = '$path/$name.backup.db';
final file = File(backupFilePath);
file.writeAsStringSync(jsonEncode(datas));
var encoder = ZipFileEncoder();
encoder.create('$path/$name.backup');
encoder.addFile(File(backupFilePath));
encoder.close();
Directory(backupFilePath).deleteSync(recursive: true);
Navigator.pop(context);
BotToast.showNotification(
animationDuration: const Duration(milliseconds: 200),
animationReverseDuration: const Duration(milliseconds: 200),
duration: const Duration(seconds: 5),
backButtonBehavior: BackButtonBehavior.none,
leading: (cancel) =>
Image.asset('assets/app_icons/icon-red.png', height: 40),
title: (_) => const Text(
"Backup created!",
style: TextStyle(fontWeight: FontWeight.bold),
),
trailing: (_) => UnconstrainedBox(
alignment: Alignment.topLeft,
child: ElevatedButton(
onPressed: () {
Share.shareXFiles([XFile('$path/$name.backup')],
text: '$name.backup');
},
child: const Text('Share')),
),
enableSlideOff: true,
onlyOne: true,
crossPage: true);
}

View file

@ -0,0 +1,190 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'backup.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$doBackUpHash() => r'7fd1956fc443633587e33bf38efe7279518c4aa0';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [doBackUp].
@ProviderFor(doBackUp)
const doBackUpProvider = DoBackUpFamily();
/// See also [doBackUp].
class DoBackUpFamily extends Family<void> {
/// See also [doBackUp].
const DoBackUpFamily();
/// See also [doBackUp].
DoBackUpProvider call({
required List<int> list,
required String path,
required BuildContext context,
}) {
return DoBackUpProvider(
list: list,
path: path,
context: context,
);
}
@override
DoBackUpProvider getProviderOverride(
covariant DoBackUpProvider provider,
) {
return call(
list: provider.list,
path: provider.path,
context: provider.context,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'doBackUpProvider';
}
/// See also [doBackUp].
class DoBackUpProvider extends AutoDisposeProvider<void> {
/// See also [doBackUp].
DoBackUpProvider({
required List<int> list,
required String path,
required BuildContext context,
}) : this._internal(
(ref) => doBackUp(
ref as DoBackUpRef,
list: list,
path: path,
context: context,
),
from: doBackUpProvider,
name: r'doBackUpProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$doBackUpHash,
dependencies: DoBackUpFamily._dependencies,
allTransitiveDependencies: DoBackUpFamily._allTransitiveDependencies,
list: list,
path: path,
context: context,
);
DoBackUpProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.list,
required this.path,
required this.context,
}) : super.internal();
final List<int> list;
final String path;
final BuildContext context;
@override
Override overrideWith(
void Function(DoBackUpRef provider) create,
) {
return ProviderOverride(
origin: this,
override: DoBackUpProvider._internal(
(ref) => create(ref as DoBackUpRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
list: list,
path: path,
context: context,
),
);
}
@override
AutoDisposeProviderElement<void> createElement() {
return _DoBackUpProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is DoBackUpProvider &&
other.list == list &&
other.path == path &&
other.context == context;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, list.hashCode);
hash = _SystemHash.combine(hash, path.hashCode);
hash = _SystemHash.combine(hash, context.hashCode);
return _SystemHash.finish(hash);
}
}
mixin DoBackUpRef on AutoDisposeProviderRef<void> {
/// The parameter `list` of this provider.
List<int> get list;
/// The parameter `path` of this provider.
String get path;
/// The parameter `context` of this provider.
BuildContext get context;
}
class _DoBackUpProviderElement extends AutoDisposeProviderElement<void>
with DoBackUpRef {
_DoBackUpProviderElement(super.provider);
@override
List<int> get list => (origin as DoBackUpProvider).list;
@override
String get path => (origin as DoBackUpProvider).path;
@override
BuildContext get context => (origin as DoBackUpProvider).context;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View file

@ -0,0 +1,130 @@
import 'dart:convert';
import 'package:archive/archive_io.dart';
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart';
import 'package:mangayomi/eval/model/m_bridge.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/category.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/history.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/models/track.dart';
import 'package:mangayomi/models/track_preference.dart';
import 'package:mangayomi/modules/more/settings/appearance/providers/blend_level_state_provider.dart';
import 'package:mangayomi/modules/more/settings/appearance/providers/flex_scheme_color_state_provider.dart';
import 'package:mangayomi/modules/more/settings/appearance/providers/pure_black_dark_mode_state_provider.dart';
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_mode_state_provider.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'restore.g.dart';
@riverpod
void doRestore(DoRestoreRef ref,
{required String path, required BuildContext context}) {
final inputStream = InputFileStream(path);
final archive = ZipDecoder().decodeBuffer(inputStream);
final backup = jsonDecode(utf8.decode(archive.files.first.content))
as Map<String, dynamic>;
if (backup['version'] == "1") {
try {
final manga =
(backup["manga"] as List?)?.map((e) => Manga.fromJson(e)).toList();
final chapters = (backup["chapters"] as List?)
?.map((e) => Chapter.fromJson(e))
.toList();
final categories = (backup["categories"] as List?)
?.map((e) => Category.fromJson(e))
.toList();
final track =
(backup["tracks"] as List?)?.map((e) => Track.fromJson(e)).toList();
final trackPreferences = (backup["trackPreferences"] as List?)
?.map((e) => TrackPreference.fromJson(e))
.toList();
final history = (backup["history"] as List?)
?.map((e) => History.fromJson(e))
.toList();
final settings = (backup["settings"] as List?)
?.map((e) => Settings.fromJson(e))
.toList();
final extensions = (backup["extensions"] as List?)
?.map((e) => Source.fromJson(e))
.toList();
isar.writeTxnSync(() {
isar.mangas.clearSync();
if (manga != null) {
isar.mangas.putAllSync(manga);
if (chapters != null) {
isar.chapters.clearSync();
for (var chapter in chapters) {
final manga = isar.mangas.getSync(chapter.mangaId!);
if (manga != null) {
isar.chapters.putSync(chapter..manga.value = manga);
chapter.manga.saveSync();
}
}
isar.historys.clearSync();
if (history != null) {
for (var element in history) {
final chapter = isar.chapters.getSync(element.chapterId!);
if (chapter != null) {
isar.historys.putSync(element..chapter.value = chapter);
element.chapter.saveSync();
}
}
}
}
isar.categorys.clearSync();
if (categories != null) {
isar.categorys.putAllSync(categories);
}
}
isar.tracks.clearSync();
if (track != null) {
isar.tracks.putAllSync(track);
}
isar.trackPreferences.clearSync();
if (trackPreferences != null) {
isar.trackPreferences.putAllSync(trackPreferences);
}
isar.sources.clearSync();
if (extensions != null) {
isar.sources.putAllSync(extensions);
}
isar.settings.clearSync();
if (settings != null) {
isar.settings.putAllSync(settings);
}
ref.invalidate(themeModeStateProvider);
ref.invalidate(blendLevelStateProvider);
ref.invalidate(flexSchemeColorStateProvider);
ref.invalidate(pureBlackDarkModeStateProvider);
ref.invalidate(l10nLocaleStateProvider);
});
} catch (e) {
botToast(e.toString());
}
BotToast.showNotification(
animationDuration: const Duration(milliseconds: 200),
animationReverseDuration: const Duration(milliseconds: 200),
duration: const Duration(seconds: 5),
backButtonBehavior: BackButtonBehavior.none,
leading: (_) =>
Image.asset('assets/app_icons/icon-red.png', height: 40),
title: (_) => const Text(
"Backup restored!",
style: TextStyle(fontWeight: FontWeight.bold),
),
enableSlideOff: true,
onlyOne: true,
crossPage: true);
}
}

View file

@ -0,0 +1,174 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'restore.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$doRestoreHash() => r'd98ebbe829a033b9092896a76d59bf0fcf422928';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [doRestore].
@ProviderFor(doRestore)
const doRestoreProvider = DoRestoreFamily();
/// See also [doRestore].
class DoRestoreFamily extends Family<void> {
/// See also [doRestore].
const DoRestoreFamily();
/// See also [doRestore].
DoRestoreProvider call({
required String path,
required BuildContext context,
}) {
return DoRestoreProvider(
path: path,
context: context,
);
}
@override
DoRestoreProvider getProviderOverride(
covariant DoRestoreProvider provider,
) {
return call(
path: provider.path,
context: provider.context,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'doRestoreProvider';
}
/// See also [doRestore].
class DoRestoreProvider extends AutoDisposeProvider<void> {
/// See also [doRestore].
DoRestoreProvider({
required String path,
required BuildContext context,
}) : this._internal(
(ref) => doRestore(
ref as DoRestoreRef,
path: path,
context: context,
),
from: doRestoreProvider,
name: r'doRestoreProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$doRestoreHash,
dependencies: DoRestoreFamily._dependencies,
allTransitiveDependencies: DoRestoreFamily._allTransitiveDependencies,
path: path,
context: context,
);
DoRestoreProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.path,
required this.context,
}) : super.internal();
final String path;
final BuildContext context;
@override
Override overrideWith(
void Function(DoRestoreRef provider) create,
) {
return ProviderOverride(
origin: this,
override: DoRestoreProvider._internal(
(ref) => create(ref as DoRestoreRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
path: path,
context: context,
),
);
}
@override
AutoDisposeProviderElement<void> createElement() {
return _DoRestoreProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is DoRestoreProvider &&
other.path == path &&
other.context == context;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, path.hashCode);
hash = _SystemHash.combine(hash, context.hashCode);
return _SystemHash.finish(hash);
}
}
mixin DoRestoreRef on AutoDisposeProviderRef<void> {
/// The parameter `path` of this provider.
String get path;
/// The parameter `context` of this provider.
BuildContext get context;
}
class _DoRestoreProviderElement extends AutoDisposeProviderElement<void>
with DoRestoreRef {
_DoRestoreProviderElement(super.provider);
@override
String get path => (origin as DoRestoreProvider).path;
@override
BuildContext get context => (origin as DoRestoreProvider).context;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View file

@ -31,7 +31,7 @@ class MoreScreen extends StatelessWidget {
const Divider(),
// ListTile(
// onTap: () {},
// leading:
// leading:
// const SizedBox(height: 40, child: Icon(Icons.cloud_off)),
// subtitle: const Text('Filter all entries in your library'),
// title: const Text('Donloaded only'),
@ -64,13 +64,13 @@ class MoreScreen extends StatelessWidget {
// icon: Icons.history_outlined,
// title: l10n.history,
// ),
// ListTile(
// onTap: () {},
// leading: const SizedBox(
// height: 40,
// child: Icon(Icons.settings_backup_restore_sharp)),
// title: const Text('Backup and restore'),
// ),
ListTileWidget(
onTap: () {
context.push('/backupAndRestore');
},
icon: Icons.settings_backup_restore_sharp,
title: 'Backup and restore',
),
// const Divider(
// color: Colors.grey,
// ),

View file

@ -69,13 +69,13 @@ class MangaImageCardWidget extends ConsumerWidget {
Container(
color: snapshot.hasData &&
snapshot.data!.isNotEmpty &&
snapshot.data!.first.favorite
snapshot.data!.first.favorite!
? Colors.black.withOpacity(0.7)
: null,
),
if (snapshot.hasData &&
snapshot.data!.isNotEmpty &&
snapshot.data!.first.favorite)
snapshot.data!.first.favorite!)
Positioned(
top: 0,
left: 0,

View file

@ -6,6 +6,7 @@ import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/anime/anime_player_view.dart';
import 'package:mangayomi/modules/browse/extension/extension_detail.dart';
import 'package:mangayomi/modules/browse/sources/sources_filter_screen.dart';
import 'package:mangayomi/modules/more/backup_and_restore/backup_and_restore.dart';
import 'package:mangayomi/modules/more/categories/categories_screen.dart';
import 'package:mangayomi/modules/more/settings/downloads/downloads_screen.dart';
import 'package:mangayomi/modules/more/settings/track/track.dart';
@ -439,6 +440,19 @@ class RouterNotifier extends ChangeNotifier {
);
},
),
GoRoute(
path: "/backupAndRestore",
name: "backupAndRestore",
builder: (context, state) {
return const BackupAndRestore();
},
pageBuilder: (context, state) {
return CustomTransition(
key: state.pageKey,
child: const BackupAndRestore(),
);
},
),
];
}