mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-03-11 17:25:32 +00:00
enhanced sync feature
This commit is contained in:
parent
011f62e157
commit
aa946c9d51
44 changed files with 1923 additions and 118 deletions
55
lib/models/changed.dart
Normal file
55
lib/models/changed.dart
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import 'package:isar/isar.dart';
|
||||
part 'changed.g.dart';
|
||||
|
||||
@collection
|
||||
@Name("ChangedPart")
|
||||
class ChangedPart {
|
||||
Id? id;
|
||||
@enumerated
|
||||
late ActionType actionType;
|
||||
int? isarId;
|
||||
String data;
|
||||
int clientDate;
|
||||
|
||||
ChangedPart(
|
||||
{this.id = Isar.autoIncrement,
|
||||
required this.actionType,
|
||||
this.isarId,
|
||||
required this.data,
|
||||
required this.clientDate});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'action': actionType.name,
|
||||
'isarId': isarId,
|
||||
'data': data,
|
||||
'clientDate': clientDate
|
||||
};
|
||||
}
|
||||
|
||||
enum ActionType {
|
||||
addItem(name: "ADD_ITEM"),
|
||||
removeItem(name: "REMOVE_ITEM"),
|
||||
updateItem(name: "UPDATE_ITEM"),
|
||||
addCategory(name: "ADD_CATEGORY"),
|
||||
removeCategory(name: "REMOVE_CATEGORY"),
|
||||
renameCategory(name: "RENAME_CATEGORY"),
|
||||
addChapter(name: "ADD_CHAPTER"),
|
||||
removeChapter(name: "REMOVE_CHAPTER"),
|
||||
updateChapter(name: "UPDATE_CHAPTER"),
|
||||
clearHistory(name: "CLEAR_HISTORY"),
|
||||
addHistory(name: "ADD_HISTORY"),
|
||||
removeHistory(name: "REMOVE_HISTORY"),
|
||||
updateHistory(name: "UPDATE_HISTORY"),
|
||||
clearUpdates(name: "CLEAR_UPDATES"),
|
||||
addUpdate(name: "ADD_UPDATE"),
|
||||
addExtension(name: "ADD_EXTENSION"),
|
||||
removeExtension(name: "REMOVE_EXTENSION"),
|
||||
updateExtension(name: "UPDATE_EXTENSION"),
|
||||
addTrack(name: "ADD_TRACK"),
|
||||
removeTrack(name: "REMOVE_TRACK"),
|
||||
updateTrack(name: "UPDATE_TRACK");
|
||||
|
||||
final String name;
|
||||
|
||||
const ActionType({required this.name});
|
||||
}
|
||||
821
lib/models/changed.g.dart
Normal file
821
lib/models/changed.g.dart
Normal file
|
|
@ -0,0 +1,821 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'changed.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// IsarCollectionGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// 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
|
||||
|
||||
extension GetChangedPartCollection on Isar {
|
||||
IsarCollection<ChangedPart> get changedParts => this.collection();
|
||||
}
|
||||
|
||||
const ChangedPartSchema = CollectionSchema(
|
||||
name: r'ChangedPart',
|
||||
id: 984304309479278230,
|
||||
properties: {
|
||||
r'actionType': PropertySchema(
|
||||
id: 0,
|
||||
name: r'actionType',
|
||||
type: IsarType.byte,
|
||||
enumMap: _ChangedPartactionTypeEnumValueMap,
|
||||
),
|
||||
r'clientDate': PropertySchema(
|
||||
id: 1,
|
||||
name: r'clientDate',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'data': PropertySchema(
|
||||
id: 2,
|
||||
name: r'data',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'isarId': PropertySchema(
|
||||
id: 3,
|
||||
name: r'isarId',
|
||||
type: IsarType.long,
|
||||
)
|
||||
},
|
||||
estimateSize: _changedPartEstimateSize,
|
||||
serialize: _changedPartSerialize,
|
||||
deserialize: _changedPartDeserialize,
|
||||
deserializeProp: _changedPartDeserializeProp,
|
||||
idName: r'id',
|
||||
indexes: {},
|
||||
links: {},
|
||||
embeddedSchemas: {},
|
||||
getId: _changedPartGetId,
|
||||
getLinks: _changedPartGetLinks,
|
||||
attach: _changedPartAttach,
|
||||
version: '3.1.0+1',
|
||||
);
|
||||
|
||||
int _changedPartEstimateSize(
|
||||
ChangedPart object,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
bytesCount += 3 + object.data.length * 3;
|
||||
return bytesCount;
|
||||
}
|
||||
|
||||
void _changedPartSerialize(
|
||||
ChangedPart object,
|
||||
IsarWriter writer,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeByte(offsets[0], object.actionType.index);
|
||||
writer.writeLong(offsets[1], object.clientDate);
|
||||
writer.writeString(offsets[2], object.data);
|
||||
writer.writeLong(offsets[3], object.isarId);
|
||||
}
|
||||
|
||||
ChangedPart _changedPartDeserialize(
|
||||
Id id,
|
||||
IsarReader reader,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
final object = ChangedPart(
|
||||
actionType:
|
||||
_ChangedPartactionTypeValueEnumMap[reader.readByteOrNull(offsets[0])] ??
|
||||
ActionType.addItem,
|
||||
clientDate: reader.readLong(offsets[1]),
|
||||
data: reader.readString(offsets[2]),
|
||||
id: id,
|
||||
isarId: reader.readLongOrNull(offsets[3]),
|
||||
);
|
||||
return object;
|
||||
}
|
||||
|
||||
P _changedPartDeserializeProp<P>(
|
||||
IsarReader reader,
|
||||
int propertyId,
|
||||
int offset,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
switch (propertyId) {
|
||||
case 0:
|
||||
return (_ChangedPartactionTypeValueEnumMap[
|
||||
reader.readByteOrNull(offset)] ??
|
||||
ActionType.addItem) as P;
|
||||
case 1:
|
||||
return (reader.readLong(offset)) as P;
|
||||
case 2:
|
||||
return (reader.readString(offset)) as P;
|
||||
case 3:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
}
|
||||
}
|
||||
|
||||
const _ChangedPartactionTypeEnumValueMap = {
|
||||
'addItem': 0,
|
||||
'removeItem': 1,
|
||||
'updateItem': 2,
|
||||
'addCategory': 3,
|
||||
'removeCategory': 4,
|
||||
'renameCategory': 5,
|
||||
'addChapter': 6,
|
||||
'removeChapter': 7,
|
||||
'updateChapter': 8,
|
||||
'clearHistory': 9,
|
||||
'addHistory': 10,
|
||||
'removeHistory': 11,
|
||||
'updateHistory': 12,
|
||||
'clearUpdates': 13,
|
||||
'addUpdate': 14,
|
||||
'addExtension': 15,
|
||||
'removeExtension': 16,
|
||||
'updateExtension': 17,
|
||||
'addTrack': 18,
|
||||
'removeTrack': 19,
|
||||
'updateTrack': 20,
|
||||
};
|
||||
const _ChangedPartactionTypeValueEnumMap = {
|
||||
0: ActionType.addItem,
|
||||
1: ActionType.removeItem,
|
||||
2: ActionType.updateItem,
|
||||
3: ActionType.addCategory,
|
||||
4: ActionType.removeCategory,
|
||||
5: ActionType.renameCategory,
|
||||
6: ActionType.addChapter,
|
||||
7: ActionType.removeChapter,
|
||||
8: ActionType.updateChapter,
|
||||
9: ActionType.clearHistory,
|
||||
10: ActionType.addHistory,
|
||||
11: ActionType.removeHistory,
|
||||
12: ActionType.updateHistory,
|
||||
13: ActionType.clearUpdates,
|
||||
14: ActionType.addUpdate,
|
||||
15: ActionType.addExtension,
|
||||
16: ActionType.removeExtension,
|
||||
17: ActionType.updateExtension,
|
||||
18: ActionType.addTrack,
|
||||
19: ActionType.removeTrack,
|
||||
20: ActionType.updateTrack,
|
||||
};
|
||||
|
||||
Id _changedPartGetId(ChangedPart object) {
|
||||
return object.id ?? Isar.autoIncrement;
|
||||
}
|
||||
|
||||
List<IsarLinkBase<dynamic>> _changedPartGetLinks(ChangedPart object) {
|
||||
return [];
|
||||
}
|
||||
|
||||
void _changedPartAttach(
|
||||
IsarCollection<dynamic> col, Id id, ChangedPart object) {
|
||||
object.id = id;
|
||||
}
|
||||
|
||||
extension ChangedPartQueryWhereSort
|
||||
on QueryBuilder<ChangedPart, ChangedPart, QWhere> {
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterWhere> anyId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(const IdWhereClause.any());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension ChangedPartQueryWhere
|
||||
on QueryBuilder<ChangedPart, ChangedPart, QWhereClause> {
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterWhereClause> idEqualTo(Id id) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IdWhereClause.between(
|
||||
lower: id,
|
||||
upper: id,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, 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<ChangedPart, ChangedPart, QAfterWhereClause> idGreaterThan(Id id,
|
||||
{bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterWhereClause> idLessThan(Id id,
|
||||
{bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, 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 ChangedPartQueryFilter
|
||||
on QueryBuilder<ChangedPart, ChangedPart, QFilterCondition> {
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition>
|
||||
actionTypeEqualTo(ActionType value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'actionType',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition>
|
||||
actionTypeGreaterThan(
|
||||
ActionType value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'actionType',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition>
|
||||
actionTypeLessThan(
|
||||
ActionType value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'actionType',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition>
|
||||
actionTypeBetween(
|
||||
ActionType lower,
|
||||
ActionType upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'actionType',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition>
|
||||
clientDateEqualTo(int value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'clientDate',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition>
|
||||
clientDateGreaterThan(
|
||||
int value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'clientDate',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition>
|
||||
clientDateLessThan(
|
||||
int value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'clientDate',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition>
|
||||
clientDateBetween(
|
||||
int lower,
|
||||
int upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'clientDate',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> dataEqualTo(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'data',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> dataGreaterThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'data',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> dataLessThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'data',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> dataBetween(
|
||||
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'data',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> dataStartsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.startsWith(
|
||||
property: r'data',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> dataEndsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.endsWith(
|
||||
property: r'data',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> dataContains(
|
||||
String value,
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.contains(
|
||||
property: r'data',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> dataMatches(
|
||||
String pattern,
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.matches(
|
||||
property: r'data',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> dataIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'data',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition>
|
||||
dataIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
property: r'data',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> idIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
property: r'id',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> idIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||
property: r'id',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> idEqualTo(
|
||||
Id? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'id',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, 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<ChangedPart, ChangedPart, 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<ChangedPart, ChangedPart, 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<ChangedPart, ChangedPart, QAfterFilterCondition> isarIdIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
property: r'isarId',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition>
|
||||
isarIdIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||
property: r'isarId',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> isarIdEqualTo(
|
||||
int? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'isarId',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition>
|
||||
isarIdGreaterThan(
|
||||
int? value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'isarId',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> isarIdLessThan(
|
||||
int? value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'isarId',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterFilterCondition> isarIdBetween(
|
||||
int? lower,
|
||||
int? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'isarId',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension ChangedPartQueryObject
|
||||
on QueryBuilder<ChangedPart, ChangedPart, QFilterCondition> {}
|
||||
|
||||
extension ChangedPartQueryLinks
|
||||
on QueryBuilder<ChangedPart, ChangedPart, QFilterCondition> {}
|
||||
|
||||
extension ChangedPartQuerySortBy
|
||||
on QueryBuilder<ChangedPart, ChangedPart, QSortBy> {
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> sortByActionType() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'actionType', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> sortByActionTypeDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'actionType', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> sortByClientDate() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'clientDate', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> sortByClientDateDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'clientDate', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> sortByData() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'data', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> sortByDataDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'data', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> sortByIsarId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'isarId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> sortByIsarIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'isarId', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension ChangedPartQuerySortThenBy
|
||||
on QueryBuilder<ChangedPart, ChangedPart, QSortThenBy> {
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> thenByActionType() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'actionType', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> thenByActionTypeDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'actionType', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> thenByClientDate() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'clientDate', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> thenByClientDateDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'clientDate', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> thenByData() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'data', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> thenByDataDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'data', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> thenById() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> thenByIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> thenByIsarId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'isarId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QAfterSortBy> thenByIsarIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'isarId', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension ChangedPartQueryWhereDistinct
|
||||
on QueryBuilder<ChangedPart, ChangedPart, QDistinct> {
|
||||
QueryBuilder<ChangedPart, ChangedPart, QDistinct> distinctByActionType() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'actionType');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QDistinct> distinctByClientDate() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'clientDate');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QDistinct> distinctByData(
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'data', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ChangedPart, QDistinct> distinctByIsarId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'isarId');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension ChangedPartQueryProperty
|
||||
on QueryBuilder<ChangedPart, ChangedPart, QQueryProperty> {
|
||||
QueryBuilder<ChangedPart, int, QQueryOperations> idProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'id');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, ActionType, QQueryOperations> actionTypeProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'actionType');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, int, QQueryOperations> clientDateProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'clientDate');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, String, QQueryOperations> dataProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'data');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<ChangedPart, int?, QQueryOperations> isarIdProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'isarId');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,8 @@ class SyncPreference {
|
|||
|
||||
String? server;
|
||||
|
||||
bool syncOn = false;
|
||||
|
||||
SyncPreference({
|
||||
this.syncId,
|
||||
this.email,
|
||||
|
|
@ -26,6 +28,7 @@ class SyncPreference {
|
|||
this.lastUpload,
|
||||
this.lastDownload,
|
||||
this.server,
|
||||
this.syncOn = false,
|
||||
});
|
||||
|
||||
SyncPreference.fromJson(Map<String, dynamic> json) {
|
||||
|
|
@ -36,6 +39,7 @@ class SyncPreference {
|
|||
lastUpload = json['lastUpload'];
|
||||
lastDownload = json['lastDownload'];
|
||||
server = json['server'];
|
||||
syncOn = json['syncOn'] ?? false;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
|
@ -45,6 +49,6 @@ class SyncPreference {
|
|||
'lastSync': lastSync,
|
||||
'lastUpload': lastUpload,
|
||||
'lastDownload': lastDownload,
|
||||
'server': server
|
||||
'syncOn': syncOn
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ const SyncPreferenceSchema = CollectionSchema(
|
|||
id: 5,
|
||||
name: r'server',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'syncOn': PropertySchema(
|
||||
id: 6,
|
||||
name: r'syncOn',
|
||||
type: IsarType.bool,
|
||||
)
|
||||
},
|
||||
estimateSize: _syncPreferenceEstimateSize,
|
||||
|
|
@ -101,6 +106,7 @@ void _syncPreferenceSerialize(
|
|||
writer.writeLong(offsets[3], object.lastSync);
|
||||
writer.writeLong(offsets[4], object.lastUpload);
|
||||
writer.writeString(offsets[5], object.server);
|
||||
writer.writeBool(offsets[6], object.syncOn);
|
||||
}
|
||||
|
||||
SyncPreference _syncPreferenceDeserialize(
|
||||
|
|
@ -117,6 +123,7 @@ SyncPreference _syncPreferenceDeserialize(
|
|||
lastUpload: reader.readLongOrNull(offsets[4]),
|
||||
server: reader.readStringOrNull(offsets[5]),
|
||||
syncId: id,
|
||||
syncOn: reader.readBoolOrNull(offsets[6]) ?? false,
|
||||
);
|
||||
return object;
|
||||
}
|
||||
|
|
@ -140,6 +147,8 @@ P _syncPreferenceDeserializeProp<P>(
|
|||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 5:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 6:
|
||||
return (reader.readBoolOrNull(offset) ?? false) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
}
|
||||
|
|
@ -996,6 +1005,16 @@ extension SyncPreferenceQueryFilter
|
|||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterFilterCondition>
|
||||
syncOnEqualTo(bool value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'syncOn',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncPreferenceQueryObject
|
||||
|
|
@ -1084,6 +1103,19 @@ extension SyncPreferenceQuerySortBy
|
|||
return query.addSortBy(r'server', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterSortBy> sortBySyncOn() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'syncOn', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterSortBy>
|
||||
sortBySyncOnDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'syncOn', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncPreferenceQuerySortThenBy
|
||||
|
|
@ -1179,6 +1211,19 @@ extension SyncPreferenceQuerySortThenBy
|
|||
return query.addSortBy(r'syncId', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterSortBy> thenBySyncOn() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'syncOn', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterSortBy>
|
||||
thenBySyncOnDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'syncOn', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncPreferenceQueryWhereDistinct
|
||||
|
|
@ -1223,6 +1268,12 @@ extension SyncPreferenceQueryWhereDistinct
|
|||
return query.addDistinctBy(r'server', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QDistinct> distinctBySyncOn() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'syncOn');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncPreferenceQueryProperty
|
||||
|
|
@ -1268,4 +1319,10 @@ extension SyncPreferenceQueryProperty
|
|||
return query.addPropertyName(r'server');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, bool, QQueryOperations> syncOnProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'syncOn');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/history.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
|
|
@ -7,6 +8,7 @@ import 'package:mangayomi/models/settings.dart';
|
|||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/player/providers/player_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/services/aniskip.dart';
|
||||
import 'package:mangayomi/utils/chapter_recognition.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
|
@ -121,6 +123,8 @@ class AnimeStreamController extends _$AnimeStreamController {
|
|||
Manga? anime = episode.manga.value;
|
||||
anime!.lastRead = DateTime.now().millisecondsSinceEpoch;
|
||||
isar.mangas.putSync(anime);
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateItem, anime.id, anime.toJson(), false);
|
||||
});
|
||||
History? history;
|
||||
|
||||
|
|
@ -146,6 +150,13 @@ class AnimeStreamController extends _$AnimeStreamController {
|
|||
isar.writeTxnSync(() {
|
||||
isar.historys.putSync(history!);
|
||||
history.chapter.saveSync();
|
||||
if (empty) {
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.addHistory, null, history.toJson(), false);
|
||||
} else {
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateHistory, history.id, history.toJson(), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -166,6 +177,8 @@ class AnimeStreamController extends _$AnimeStreamController {
|
|||
ep.isRead = isWatch;
|
||||
ep.lastPageRead = (duration.inMilliseconds).toString();
|
||||
isar.chapters.putSync(ep);
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateChapter, ep.id, ep.toJson(), false);
|
||||
});
|
||||
if (isWatch) {
|
||||
episode.updateTrackChapterRead(ref);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ part of 'anime_player_controller_provider.dart';
|
|||
// **************************************************************************
|
||||
|
||||
String _$animeStreamControllerHash() =>
|
||||
r'57ebd35f033d51fd213763173c26cd887f5c42d7';
|
||||
r'e0217071ae7b908a12bbba2dcdc4a6da8828e1c5';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ class _MangaGlobalImageCardState extends ConsumerState<MangaGlobalImageCard>
|
|||
return GestureDetector(
|
||||
onTap: () async {
|
||||
pushToMangaReaderDetail(
|
||||
ref: ref,
|
||||
context: context,
|
||||
getManga: getMangaDetail,
|
||||
lang: widget.source.lang!,
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import 'package:grouped_list/sliver_grouped_list.dart';
|
|||
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/history.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/history/providers/isar_providers.dart';
|
||||
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/cached_network.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
|
|
@ -424,6 +426,17 @@ class _HistoryTabState extends ConsumerState<HistoryTab> {
|
|||
.delete(manga
|
||||
.id!);
|
||||
});
|
||||
await ref
|
||||
.read(synchingProvider(
|
||||
syncId:
|
||||
1)
|
||||
.notifier)
|
||||
.addChangedPartAsync(
|
||||
ActionType
|
||||
.removeItem,
|
||||
manga.id,
|
||||
"{}",
|
||||
true);
|
||||
if (context
|
||||
.mounted) {
|
||||
Navigator.pop(
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import 'package:isar/isar.dart';
|
|||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/category.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/download.dart';
|
||||
import 'package:mangayomi/models/history.dart';
|
||||
|
|
@ -21,6 +22,7 @@ import 'package:mangayomi/modules/library/providers/add_torrent.dart';
|
|||
import 'package:mangayomi/modules/library/providers/local_archive.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/update_manga_detail_providers.dart';
|
||||
import 'package:mangayomi/modules/more/categories/providers/isar_providers.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart';
|
||||
import 'package:mangayomi/modules/widgets/manga_image_card_widget.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
|
|
@ -1030,6 +1032,15 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
manga!.categories =
|
||||
categoryIds;
|
||||
isar.mangas.putSync(manga);
|
||||
ref
|
||||
.read(synchingProvider(
|
||||
syncId: 1)
|
||||
.notifier)
|
||||
.addChangedPart(
|
||||
ActionType.updateItem,
|
||||
manga.id,
|
||||
manga.toJson(),
|
||||
false);
|
||||
}
|
||||
});
|
||||
ref
|
||||
|
|
@ -1041,6 +1052,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
isLongPressedMangaStateProvider
|
||||
.notifier)
|
||||
.update(false);
|
||||
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
|
@ -1164,9 +1176,19 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
isar.chapters.deleteSync(chapter.id!);
|
||||
}
|
||||
isar.mangas.deleteSync(manga.id!);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1)
|
||||
.notifier)
|
||||
.addChangedPart(ActionType.removeItem,
|
||||
manga.id, "{}", false);
|
||||
} else {
|
||||
manga.favorite = false;
|
||||
isar.mangas.putSync(manga);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1)
|
||||
.notifier)
|
||||
.addChangedPart(ActionType.updateItem,
|
||||
manga.id, manga.toJson(), false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -1804,6 +1826,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
manga.whenData((value) {
|
||||
var randomManga = (value..shuffle()).first;
|
||||
pushToMangaReaderDetail(
|
||||
ref: ref,
|
||||
archiveId: randomManga.isLocalArchive ?? false
|
||||
? randomManga.id
|
||||
: null,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/services/torrent_server.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
|
@ -45,6 +47,11 @@ Future addTorrentFromUrlOrFromFile(Ref ref, Manga? mManga,
|
|||
isLocalArchive: true,
|
||||
artist: '',
|
||||
);
|
||||
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.addItem, null, manga.toJson(), true);
|
||||
|
||||
if (url != null) {
|
||||
manga.customCoverImage = null;
|
||||
isar.writeTxnSync(() {
|
||||
|
|
@ -53,6 +60,8 @@ Future addTorrentFromUrlOrFromFile(Ref ref, Manga? mManga,
|
|||
..manga.value = manga;
|
||||
isar.chapters.putSync(chapters);
|
||||
chapters.manga.saveSync();
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.addChapter, null, chapters.toJson(), false);
|
||||
});
|
||||
} else {
|
||||
for (var file in result!.files.reversed.toList()) {
|
||||
|
|
@ -69,6 +78,8 @@ Future addTorrentFromUrlOrFromFile(Ref ref, Manga? mManga,
|
|||
..manga.value = manga;
|
||||
isar.chapters.putSync(chapters);
|
||||
chapters.manga.saveSync();
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.addChapter, null, chapters.toJson(), false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ part of 'add_torrent.dart';
|
|||
// **************************************************************************
|
||||
|
||||
String _$addTorrentFromUrlOrFromFileHash() =>
|
||||
r'11cc239bb8b517326f9a005b0c89dd5eb1127099';
|
||||
r'd12f901b675ecbf4a29c496cf99da17f219745f7';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'library_state_provider.g.dart';
|
||||
|
|
@ -788,6 +790,8 @@ class MangasSetIsReadState extends _$MangasSetIsReadState {
|
|||
chapter.lastPageRead = "1";
|
||||
isar.chapters.putSync(chapter..manga.value = manga);
|
||||
chapter.manga.saveSync();
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateChapter, chapter.id, chapter.toJson(), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -812,6 +816,8 @@ class MangasSetUnReadState extends _$MangasSetUnReadState {
|
|||
chapter.isRead = false;
|
||||
isar.chapters.putSync(chapter..manga.value = manga);
|
||||
chapter.manga.saveSync();
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateChapter, chapter.id, chapter.toJson(), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2551,7 +2551,7 @@ final isLongPressedMangaStateProvider =
|
|||
|
||||
typedef _$IsLongPressedMangaState = AutoDisposeNotifier<bool>;
|
||||
String _$mangasSetIsReadStateHash() =>
|
||||
r'8f86296f588a48747de625e0471048978ee9bdeb';
|
||||
r'b599664aed8cc00d35a683fa6660bf79b66c555d';
|
||||
|
||||
abstract class _$MangasSetIsReadState
|
||||
extends BuildlessAutoDisposeNotifier<void> {
|
||||
|
|
@ -2698,7 +2698,7 @@ class _MangasSetIsReadStateProviderElement
|
|||
}
|
||||
|
||||
String _$mangasSetUnReadStateHash() =>
|
||||
r'3413e731b2fd8476a4032d3e47b943ca12f25090';
|
||||
r'03906113f5e5878909a5a6399ead997eaa2c1204';
|
||||
|
||||
abstract class _$MangasSetUnReadState
|
||||
extends BuildlessAutoDisposeNotifier<void> {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/manga/archive_reader/models/models.dart';
|
||||
import 'package:mangayomi/modules/manga/archive_reader/providers/archive_reader_providers.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
part 'local_archive.g.dart';
|
||||
|
|
@ -38,6 +40,11 @@ Future importArchivesFromFile(Ref ref, Manga? mManga,
|
|||
isLocalArchive: true,
|
||||
artist: '',
|
||||
);
|
||||
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.addItem, null, manga.toJson(), true);
|
||||
|
||||
for (var file in result.files.reversed.toList()) {
|
||||
(String, LocalExtensionType, Uint8List, String)? data = itemType ==
|
||||
ItemType.manga
|
||||
|
|
@ -58,6 +65,8 @@ Future importArchivesFromFile(Ref ref, Manga? mManga,
|
|||
..manga.value = manga;
|
||||
isar.chapters.putSync(chapters);
|
||||
chapters.manga.saveSync();
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.addChapter, null, chapters.toJson(), false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ part of 'local_archive.dart';
|
|||
// **************************************************************************
|
||||
|
||||
String _$importArchivesFromFileHash() =>
|
||||
r'8e6e592c927ad080e93d54dac1144ef8637a7e52';
|
||||
r'49cd5455a5ff601e4b7b3fccd2fd5f6463c35fb3';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ class _LibraryGridViewWidgetState extends State<LibraryGridViewWidget> {
|
|||
ref.read(mangasListStateProvider.notifier).update(entry);
|
||||
} else {
|
||||
await pushToMangaReaderDetail(
|
||||
ref: ref,
|
||||
archiveId: isLocalArchive ? entry.id : null,
|
||||
context: context,
|
||||
lang: entry.lang!,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class LibraryListViewWidget extends StatelessWidget {
|
|||
ref.read(mangasListStateProvider.notifier).update(entry);
|
||||
} else {
|
||||
await pushToMangaReaderDetail(
|
||||
ref: ref,
|
||||
archiveId: isLocalArchive ? entry.id : null,
|
||||
context: context,
|
||||
lang: entry.lang!,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import 'package:go_router/go_router.dart';
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/download.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
|
|
@ -22,6 +23,7 @@ import 'package:mangayomi/modules/manga/detail/widgets/tracker_search_widget.dar
|
|||
import 'package:mangayomi/modules/manga/detail/widgets/tracker_widget.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/appearance/providers/pure_black_dark_mode_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/widgets/track_listile.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
|
||||
|
|
@ -729,6 +731,11 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
isar.chapters.putSync(
|
||||
chapter..manga.value = widget.manga);
|
||||
chapter.manga.saveSync();
|
||||
ref
|
||||
.read(
|
||||
synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.updateChapter,
|
||||
chapter.id, chapter.toJson(), false);
|
||||
}
|
||||
});
|
||||
ref
|
||||
|
|
@ -772,6 +779,11 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
if (chapter.isRead!) {
|
||||
chapter.updateTrackChapterRead(ref);
|
||||
}
|
||||
ref
|
||||
.read(
|
||||
synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.updateChapter,
|
||||
chapter.id, chapter.toJson(), false);
|
||||
}
|
||||
});
|
||||
ref
|
||||
|
|
@ -814,6 +826,14 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
isar.chapters.putSync(chapters[i]
|
||||
..manga.value = widget.manga);
|
||||
chapters[i].manga.saveSync();
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1)
|
||||
.notifier)
|
||||
.addChangedPart(
|
||||
ActionType.updateChapter,
|
||||
chapters[i].id,
|
||||
chapters[i].toJson(),
|
||||
false);
|
||||
}
|
||||
}
|
||||
ref
|
||||
|
|
@ -1666,6 +1686,15 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
..customCoverImage = null
|
||||
..customCoverFromTracker =
|
||||
trackSearch.coverUrl);
|
||||
ref
|
||||
.read(synchingProvider(
|
||||
syncId: 1)
|
||||
.notifier)
|
||||
.addChangedPart(
|
||||
ActionType.updateItem,
|
||||
widget.manga!.id,
|
||||
widget.manga!.toJson(),
|
||||
false);
|
||||
});
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
|
|
@ -1791,6 +1820,15 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
isar.mangas.putSync(manga
|
||||
..customCoverImage = null
|
||||
..customCoverFromTracker = null);
|
||||
ref
|
||||
.read(
|
||||
synchingProvider(syncId: 1)
|
||||
.notifier)
|
||||
.addChangedPart(
|
||||
ActionType.updateItem,
|
||||
manga.id,
|
||||
manga.toJson(),
|
||||
false);
|
||||
});
|
||||
Navigator.pop(context);
|
||||
} else if (value == 1) {
|
||||
|
|
@ -1814,6 +1852,15 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
isar.mangas.putSync(manga
|
||||
..customCoverImage =
|
||||
customCoverImage);
|
||||
ref
|
||||
.read(synchingProvider(
|
||||
syncId: 1)
|
||||
.notifier)
|
||||
.addChangedPart(
|
||||
ActionType.updateItem,
|
||||
manga.id,
|
||||
manga.toJson(),
|
||||
false);
|
||||
});
|
||||
botToast(
|
||||
context.l10n.cover_updated,
|
||||
|
|
@ -1920,6 +1967,10 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
manga.description = description.text;
|
||||
manga.name = name.text;
|
||||
isar.mangas.putSync(manga);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.updateItem, manga.id,
|
||||
manga.toJson(), false);
|
||||
});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ import 'package:go_router/go_router.dart';
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/category.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/history.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/custom_floating_action_btn.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
|
|
@ -197,6 +199,10 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
|
|||
model.favorite = false;
|
||||
model.dateAdded = 0;
|
||||
isar.mangas.putSync(model);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.updateItem, model.id,
|
||||
model.toJson(), false);
|
||||
});
|
||||
},
|
||||
child: Column(
|
||||
|
|
@ -236,6 +242,14 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
|
|||
model.favorite = true;
|
||||
model.dateAdded = DateTime.now().millisecondsSinceEpoch;
|
||||
isar.mangas.putSync(model);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(
|
||||
ActionType.addItem, null, model.toJson(), false);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.updateItem, model.id,
|
||||
model.toJson(), false);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
@ -355,6 +369,16 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
|
|||
model.dateAdded =
|
||||
DateTime.now().millisecondsSinceEpoch;
|
||||
isar.mangas.putSync(model);
|
||||
ref
|
||||
.read(
|
||||
synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.addItem,
|
||||
model.id, model.toJson(), false);
|
||||
ref
|
||||
.read(
|
||||
synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.updateItem,
|
||||
model.id, model.toJson(), false);
|
||||
});
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/download.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/modules/manga/download/providers/download_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'state_providers.g.dart';
|
||||
|
||||
|
|
@ -311,6 +313,8 @@ class ChapterSetIsBookmarkState extends _$ChapterSetIsBookmarkState {
|
|||
chapter.isBookmarked = !chapter.isBookmarked!;
|
||||
isar.chapters.putSync(chapter..manga.value = manga);
|
||||
chapter.manga.saveSync();
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateChapter, chapter.id, chapter.toJson(), false);
|
||||
}
|
||||
});
|
||||
ref.read(isLongPressedStateProvider.notifier).update(false);
|
||||
|
|
@ -330,6 +334,8 @@ class ChapterSetIsReadState extends _$ChapterSetIsReadState {
|
|||
chapter.isRead = !chapter.isRead!;
|
||||
isar.chapters.putSync(chapter..manga.value = manga);
|
||||
chapter.manga.saveSync();
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateChapter, chapter.id, chapter.toJson(), false);
|
||||
}
|
||||
});
|
||||
ref.read(isLongPressedStateProvider.notifier).update(false);
|
||||
|
|
|
|||
|
|
@ -816,7 +816,7 @@ class _ChapterFilterResultStateProviderElement
|
|||
}
|
||||
|
||||
String _$chapterSetIsBookmarkStateHash() =>
|
||||
r'113131bb13e50566390ee3e34aa2f08820a8870c';
|
||||
r'9b4359e87f6083323cc49d20bedde0ce0f61d9b3';
|
||||
|
||||
abstract class _$ChapterSetIsBookmarkState
|
||||
extends BuildlessAutoDisposeNotifier<void> {
|
||||
|
|
@ -963,7 +963,7 @@ class _ChapterSetIsBookmarkStateProviderElement
|
|||
}
|
||||
|
||||
String _$chapterSetIsReadStateHash() =>
|
||||
r'c319f81ec30565ad81a28cb0a8ce7fddcb47cd77';
|
||||
r'9cfd45df3f359a43140c023a584b52f8c81cbace';
|
||||
|
||||
abstract class _$ChapterSetIsReadState
|
||||
extends BuildlessAutoDisposeNotifier<void> {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||
import 'package:mangayomi/eval/model/m_manga.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/update.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/services/get_detail.dart';
|
||||
import 'package:mangayomi/utils/utils.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
|
@ -56,6 +58,9 @@ Future<dynamic> updateMangaDetail(Ref ref,
|
|||
}
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.putSync(manga);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.updateItem, manga.id, manga.toJson(), false);
|
||||
manga.lastUpdate = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
List<Chapter> chapters = [];
|
||||
|
|
@ -81,6 +86,8 @@ Future<dynamic> updateMangaDetail(Ref ref,
|
|||
for (var chap in chapters.reversed.toList()) {
|
||||
isar.chapters.putSync(chap);
|
||||
chap.manga.saveSync();
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.addChapter, chap.id, chap.toJson(), false);
|
||||
if (manga.chapters.isNotEmpty) {
|
||||
final update = Update(
|
||||
mangaId: mangaId,
|
||||
|
|
@ -89,6 +96,8 @@ Future<dynamic> updateMangaDetail(Ref ref,
|
|||
..chapter.value = chap;
|
||||
isar.updates.putSync(update);
|
||||
update.chapter.saveSync();
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.addUpdate, update.id, update.toJson(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -103,6 +112,10 @@ Future<dynamic> updateMangaDetail(Ref ref,
|
|||
oldChap.scanlator = newChap.scanlator;
|
||||
isar.chapters.putSync(oldChap);
|
||||
oldChap.manga.saveSync();
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateItem, manga.id, manga.toJson(), false);
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateChapter, oldChap.id, oldChap.toJson(), false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'update_manga_detail_providers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$updateMangaDetailHash() => r'a86fe8fea46e411203182287c970cd80cc9a1a0c';
|
||||
String _$updateMangaDetailHash() => r'ebd820d3e9d1900c464aebfbf711f43f6619e586';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/download.dart';
|
||||
import 'package:mangayomi/models/history.dart';
|
||||
|
|
@ -11,6 +12,7 @@ import 'package:mangayomi/models/track.dart';
|
|||
import 'package:mangayomi/models/track_preference.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.dart';
|
||||
import 'package:mangayomi/modules/more/providers/incognito_mode_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/providers/track_providers.dart';
|
||||
import 'package:mangayomi/utils/chapter_recognition.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
|
@ -159,6 +161,8 @@ class ReaderController extends _$ReaderController {
|
|||
Manga? manga = chapter.manga.value;
|
||||
manga!.lastRead = DateTime.now().millisecondsSinceEpoch;
|
||||
isar.mangas.putSync(manga);
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateItem, manga.id, manga.toJson(), false);
|
||||
});
|
||||
History? history;
|
||||
|
||||
|
|
@ -184,6 +188,13 @@ class ReaderController extends _$ReaderController {
|
|||
isar.writeTxnSync(() {
|
||||
isar.historys.putSync(history!);
|
||||
history.chapter.saveSync();
|
||||
if (empty) {
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.addHistory, null, history.toJson(), false);
|
||||
} else {
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateHistory, history.id, history.toJson(), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -194,6 +205,8 @@ class ReaderController extends _$ReaderController {
|
|||
isar.writeTxnSync(() {
|
||||
chap.isBookmarked = !isBookmarked;
|
||||
isar.chapters.putSync(chap);
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateChapter, chapter.id, chapter.toJson(), false);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -331,6 +344,8 @@ class ReaderController extends _$ReaderController {
|
|||
chap.isRead = isRead;
|
||||
chap.lastPageRead = isRead ? '1' : (newIndex + 1).toString();
|
||||
isar.chapters.putSync(chap);
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateChapter, chapter.id, chapter.toJson(), false);
|
||||
});
|
||||
if (isRead) {
|
||||
chapter.updateTrackChapterRead(ref);
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ class _CurrentIndexProviderElement
|
|||
Chapter get chapter => (origin as CurrentIndexProvider).chapter;
|
||||
}
|
||||
|
||||
String _$readerControllerHash() => r'471617bf6fa730d837c8e0d9504bde17cfb635a8';
|
||||
String _$readerControllerHash() => r'58256638f87a8c24ee8081260685692b6e819fc3';
|
||||
|
||||
abstract class _$ReaderController extends BuildlessAutoDisposeNotifier<void> {
|
||||
late final Chapter chapter;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import 'package:go_router/go_router.dart';
|
|||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/changed.dart' as changed;
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/page.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
|
|
@ -22,6 +23,7 @@ import 'package:mangayomi/modules/manga/reader/double_columm_view_center.dart';
|
|||
import 'package:mangayomi/modules/manga/reader/providers/color_filter_provider.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/widgets/color_filter_widget.dart';
|
||||
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
|
|
@ -335,6 +337,16 @@ class _MangaChapterPageGalleryState
|
|||
isar.mangas.putSync(manga
|
||||
..customCoverImage =
|
||||
imageBytes);
|
||||
ref
|
||||
.read(synchingProvider(
|
||||
syncId: 1)
|
||||
.notifier)
|
||||
.addChangedPart(
|
||||
changed.ActionType
|
||||
.updateItem,
|
||||
manga.id,
|
||||
manga.toJson(),
|
||||
false);
|
||||
});
|
||||
if (mounted) {
|
||||
Navigator.pop(context, "ok");
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/utils/date.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||
|
|
@ -153,17 +155,23 @@ class _ChapterListTileState extends State<ChapterListTile> {
|
|||
)
|
||||
],
|
||||
),
|
||||
trailing: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
isBookmarked = !isBookmarked;
|
||||
});
|
||||
isar.writeTxnSync(() => {
|
||||
isar.chapters.putSync(chapter..isBookmarked = isBookmarked),
|
||||
});
|
||||
},
|
||||
icon: Icon(isBookmarked ? Icons.bookmark : Icons.bookmark_outline,
|
||||
color: context.primaryColor),
|
||||
trailing: Consumer(
|
||||
builder: (context, ref, child) => IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
isBookmarked = !isBookmarked;
|
||||
});
|
||||
isar.writeTxnSync(() => {
|
||||
isar.chapters.putSync(chapter..isBookmarked = isBookmarked),
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.updateChapter, chapter.id,
|
||||
chapter.toJson(), false),
|
||||
});
|
||||
},
|
||||
icon: Icon(isBookmarked ? Icons.bookmark : Icons.bookmark_outline,
|
||||
color: context.primaryColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/category.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/more/categories/providers/isar_providers.dart';
|
||||
import 'package:mangayomi/modules/more/categories/widgets/custom_textfield.dart';
|
||||
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
|
||||
|
|
@ -206,6 +208,19 @@ class _CategoriesTabState extends ConsumerState<CategoriesTab> {
|
|||
index]
|
||||
.id!);
|
||||
});
|
||||
await ref
|
||||
.read(synchingProvider(
|
||||
syncId:
|
||||
1)
|
||||
.notifier)
|
||||
.addChangedPartAsync(
|
||||
ActionType
|
||||
.removeCategory,
|
||||
_entries[
|
||||
index]
|
||||
.id,
|
||||
"{}",
|
||||
true);
|
||||
if (context
|
||||
.mounted) {
|
||||
Navigator.pop(
|
||||
|
|
@ -291,12 +306,23 @@ class _CategoriesTabState extends ConsumerState<CategoriesTab> {
|
|||
isExist
|
||||
? null
|
||||
: () async {
|
||||
final category = Category(
|
||||
forItemType: widget.itemType,
|
||||
name: controller.text,
|
||||
);
|
||||
await isar.writeTxn(() async {
|
||||
await isar.categorys.put(Category(
|
||||
forItemType: widget.itemType,
|
||||
name: controller.text,
|
||||
));
|
||||
await isar.categorys
|
||||
.put(category);
|
||||
});
|
||||
await ref
|
||||
.read(
|
||||
synchingProvider(syncId: 1)
|
||||
.notifier)
|
||||
.addChangedPartAsync(
|
||||
ActionType.addCategory,
|
||||
category.id,
|
||||
category.toJson(),
|
||||
true);
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
|
@ -375,18 +401,27 @@ class _CategoriesTabState extends ConsumerState<CategoriesTab> {
|
|||
width: 15,
|
||||
),
|
||||
TextButton(
|
||||
onPressed:
|
||||
controller.text.isEmpty || isExist || isSameName
|
||||
? null
|
||||
: () async {
|
||||
await isar.writeTxn(() async {
|
||||
category.name = controller.text;
|
||||
await isar.categorys.put(category);
|
||||
});
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
onPressed: controller.text.isEmpty ||
|
||||
isExist ||
|
||||
isSameName
|
||||
? null
|
||||
: () async {
|
||||
await isar.writeTxn(() async {
|
||||
category.name = controller.text;
|
||||
await isar.categorys.put(category);
|
||||
});
|
||||
await ref
|
||||
.read(
|
||||
synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPartAsync(
|
||||
ActionType.renameCategory,
|
||||
category.id,
|
||||
category.toJson(),
|
||||
true);
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
l10n.ok,
|
||||
style: TextStyle(
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import 'package:mangayomi/modules/more/settings/appearance/providers/blend_level
|
|||
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/modules/more/settings/browse/providers/browse_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
|
@ -54,7 +55,7 @@ void doRestore(Ref ref, {required String path, required BuildContext context}) {
|
|||
}
|
||||
|
||||
@riverpod
|
||||
void restoreBackup(Ref ref, Map<String, dynamic> backup) {
|
||||
void restoreBackup(Ref ref, Map<String, dynamic> backup, {bool full = true}) {
|
||||
final version = backup['version'];
|
||||
if (["1", "2"].any((e) => e == version)) {
|
||||
try {
|
||||
|
|
@ -106,13 +107,15 @@ void restoreBackup(Ref ref, Map<String, dynamic> backup) {
|
|||
}
|
||||
}
|
||||
|
||||
isar.downloads.clearSync();
|
||||
if (downloads != null) {
|
||||
for (var download in downloads) {
|
||||
final chapter = isar.chapters.getSync(download.id!);
|
||||
if (chapter != null) {
|
||||
isar.downloads.putSync(download..chapter.value = chapter);
|
||||
download.chapter.saveSync();
|
||||
if (full) {
|
||||
isar.downloads.clearSync();
|
||||
if (downloads != null) {
|
||||
for (var download in downloads) {
|
||||
final chapter = isar.chapters.getSync(download.id!);
|
||||
if (chapter != null) {
|
||||
isar.downloads.putSync(download..chapter.value = chapter);
|
||||
download.chapter.saveSync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -157,31 +160,42 @@ void restoreBackup(Ref ref, Map<String, dynamic> backup) {
|
|||
isar.tracks.putAllSync(track);
|
||||
}
|
||||
|
||||
isar.trackPreferences.clearSync();
|
||||
if (trackPreferences != null) {
|
||||
isar.trackPreferences.putAllSync(trackPreferences);
|
||||
if (full) {
|
||||
isar.trackPreferences.clearSync();
|
||||
if (trackPreferences != null) {
|
||||
isar.trackPreferences.putAllSync(trackPreferences);
|
||||
}
|
||||
}
|
||||
|
||||
isar.sources.clearSync();
|
||||
if (extensions != null) {
|
||||
isar.sources.putAllSync(extensions);
|
||||
if (full) {
|
||||
isar.sources.clearSync();
|
||||
if (extensions != null) {
|
||||
isar.sources.putAllSync(extensions);
|
||||
}
|
||||
}
|
||||
|
||||
isar.sourcePreferences.clearSync();
|
||||
if (sourcesPrefs != null) {
|
||||
isar.sourcePreferences.putAllSync(sourcesPrefs);
|
||||
if (full) {
|
||||
isar.sourcePreferences.clearSync();
|
||||
if (sourcesPrefs != null) {
|
||||
isar.sourcePreferences.putAllSync(sourcesPrefs);
|
||||
}
|
||||
isar.settings.clearSync();
|
||||
if (settings != null) {
|
||||
isar.settings.putAllSync(settings);
|
||||
}
|
||||
}
|
||||
isar.settings.clearSync();
|
||||
if (settings != null) {
|
||||
isar.settings.putAllSync(settings);
|
||||
if (full) {
|
||||
ref.invalidate(themeModeStateProvider);
|
||||
ref.invalidate(blendLevelStateProvider);
|
||||
ref.invalidate(flexSchemeColorStateProvider);
|
||||
ref.invalidate(pureBlackDarkModeStateProvider);
|
||||
ref.invalidate(l10nLocaleStateProvider);
|
||||
ref.invalidate(navigationOrderStateProvider);
|
||||
ref.invalidate(hideItemsStateProvider);
|
||||
ref.invalidate(extensionsRepoStateProvider(ItemType.manga));
|
||||
ref.invalidate(extensionsRepoStateProvider(ItemType.anime));
|
||||
ref.invalidate(extensionsRepoStateProvider(ItemType.novel));
|
||||
}
|
||||
ref.invalidate(themeModeStateProvider);
|
||||
ref.invalidate(blendLevelStateProvider);
|
||||
ref.invalidate(flexSchemeColorStateProvider);
|
||||
ref.invalidate(pureBlackDarkModeStateProvider);
|
||||
ref.invalidate(l10nLocaleStateProvider);
|
||||
ref.invalidate(navigationOrderStateProvider);
|
||||
ref.invalidate(hideItemsStateProvider);
|
||||
});
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
|
|
@ -203,7 +217,7 @@ ItemType _convertToItemType(Map<String, dynamic> backup) {
|
|||
ItemType _convertToItemTypeCategory(Map<String, dynamic> backup) {
|
||||
final forManga = backup['forManga'];
|
||||
return forManga == null
|
||||
? ItemType.values[backup['itemType'] ?? 0]
|
||||
? ItemType.values[backup['forItemType'] ?? 0]
|
||||
: forManga
|
||||
? ItemType.manga
|
||||
: ItemType.anime;
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ class _DoRestoreProviderElement extends AutoDisposeProviderElement<void>
|
|||
BuildContext get context => (origin as DoRestoreProvider).context;
|
||||
}
|
||||
|
||||
String _$restoreBackupHash() => r'24405b9be28204324e47d6c1db34495d55a491d2';
|
||||
String _$restoreBackupHash() => r'2645a4e3f29e1e5b65acff8d66a6f634a8773acf';
|
||||
|
||||
/// See also [restoreBackup].
|
||||
@ProviderFor(restoreBackup)
|
||||
|
|
@ -186,10 +186,12 @@ class RestoreBackupFamily extends Family<void> {
|
|||
|
||||
/// See also [restoreBackup].
|
||||
RestoreBackupProvider call(
|
||||
Map<String, dynamic> backup,
|
||||
) {
|
||||
Map<String, dynamic> backup, {
|
||||
bool full = true,
|
||||
}) {
|
||||
return RestoreBackupProvider(
|
||||
backup,
|
||||
full: full,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -199,6 +201,7 @@ class RestoreBackupFamily extends Family<void> {
|
|||
) {
|
||||
return call(
|
||||
provider.backup,
|
||||
full: provider.full,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -221,11 +224,13 @@ class RestoreBackupFamily extends Family<void> {
|
|||
class RestoreBackupProvider extends AutoDisposeProvider<void> {
|
||||
/// See also [restoreBackup].
|
||||
RestoreBackupProvider(
|
||||
Map<String, dynamic> backup,
|
||||
) : this._internal(
|
||||
Map<String, dynamic> backup, {
|
||||
bool full = true,
|
||||
}) : this._internal(
|
||||
(ref) => restoreBackup(
|
||||
ref as RestoreBackupRef,
|
||||
backup,
|
||||
full: full,
|
||||
),
|
||||
from: restoreBackupProvider,
|
||||
name: r'restoreBackupProvider',
|
||||
|
|
@ -237,6 +242,7 @@ class RestoreBackupProvider extends AutoDisposeProvider<void> {
|
|||
allTransitiveDependencies:
|
||||
RestoreBackupFamily._allTransitiveDependencies,
|
||||
backup: backup,
|
||||
full: full,
|
||||
);
|
||||
|
||||
RestoreBackupProvider._internal(
|
||||
|
|
@ -247,9 +253,11 @@ class RestoreBackupProvider extends AutoDisposeProvider<void> {
|
|||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.backup,
|
||||
required this.full,
|
||||
}) : super.internal();
|
||||
|
||||
final Map<String, dynamic> backup;
|
||||
final bool full;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
|
|
@ -265,6 +273,7 @@ class RestoreBackupProvider extends AutoDisposeProvider<void> {
|
|||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
backup: backup,
|
||||
full: full,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -276,13 +285,16 @@ class RestoreBackupProvider extends AutoDisposeProvider<void> {
|
|||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is RestoreBackupProvider && other.backup == backup;
|
||||
return other is RestoreBackupProvider &&
|
||||
other.backup == backup &&
|
||||
other.full == full;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, backup.hashCode);
|
||||
hash = _SystemHash.combine(hash, full.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
|
|
@ -293,6 +305,9 @@ class RestoreBackupProvider extends AutoDisposeProvider<void> {
|
|||
mixin RestoreBackupRef on AutoDisposeProviderRef<void> {
|
||||
/// The parameter `backup` of this provider.
|
||||
Map<String, dynamic> get backup;
|
||||
|
||||
/// The parameter `full` of this provider.
|
||||
bool get full;
|
||||
}
|
||||
|
||||
class _RestoreBackupProviderElement extends AutoDisposeProviderElement<void>
|
||||
|
|
@ -301,6 +316,8 @@ class _RestoreBackupProviderElement extends AutoDisposeProviderElement<void>
|
|||
|
||||
@override
|
||||
Map<String, dynamic> get backup => (origin as RestoreBackupProvider).backup;
|
||||
@override
|
||||
bool get full => (origin as RestoreBackupProvider).full;
|
||||
}
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'browse_state_provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$getRepoInfosHash() => r'd5d5eca9fd23accd515bf51b470edb99a5d58733';
|
||||
String _$getRepoInfosHash() => r'250bc0082ac2841114d6f1815303955b8798240e';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/sync_preference.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'sync_providers.g.dart';
|
||||
|
|
@ -6,8 +10,8 @@ part 'sync_providers.g.dart';
|
|||
@riverpod
|
||||
class Synching extends _$Synching {
|
||||
@override
|
||||
SyncPreference? build({required int? syncId}) {
|
||||
return isar.syncPreferences.getSync(syncId!);
|
||||
SyncPreference build({required int? syncId}) {
|
||||
return isar.syncPreferences.getSync(syncId!) ?? SyncPreference(syncId: 1);
|
||||
}
|
||||
|
||||
void login(SyncPreference syncPreference) {
|
||||
|
|
@ -18,28 +22,152 @@ class Synching extends _$Synching {
|
|||
|
||||
void logout() {
|
||||
isar.writeTxnSync(() {
|
||||
isar.syncPreferences.deleteSync(syncId!);
|
||||
isar.syncPreferences.putSync(state..authToken = null);
|
||||
});
|
||||
}
|
||||
|
||||
void setLastUpload(int timestamp) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.syncPreferences.putSync(
|
||||
isar.syncPreferences.getSync(syncId!)!..lastUpload = timestamp);
|
||||
isar.syncPreferences.putSync(state..lastUpload = timestamp);
|
||||
});
|
||||
}
|
||||
|
||||
void setLastDownload(int timestamp) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.syncPreferences.putSync(
|
||||
isar.syncPreferences.getSync(syncId!)!..lastDownload = timestamp);
|
||||
isar.syncPreferences.putSync(state..lastDownload = timestamp);
|
||||
});
|
||||
}
|
||||
|
||||
void setLastSync(int timestamp) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.syncPreferences.putSync(state..lastSync = timestamp);
|
||||
});
|
||||
}
|
||||
|
||||
void setServer(String? server) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.syncPreferences
|
||||
.putSync(isar.syncPreferences.getSync(syncId!)!..server = server);
|
||||
isar.syncPreferences.putSync(state..server = server);
|
||||
});
|
||||
}
|
||||
|
||||
void setSyncOn(bool value) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.syncPreferences.putSync(state..syncOn = value);
|
||||
});
|
||||
}
|
||||
|
||||
List<ChangedPart> getAllChangedParts() {
|
||||
return isar.changedParts.filter().idIsNotNull().findAllSync();
|
||||
}
|
||||
|
||||
List<ChangedPart> getChangedParts(List<ActionType> actionTypes) {
|
||||
var query = isar.changedParts
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.and()
|
||||
.actionTypeEqualTo(actionTypes.first);
|
||||
for (final at in actionTypes.skip(1)) {
|
||||
query = query.or().actionTypeEqualTo(at);
|
||||
}
|
||||
return query.findAllSync();
|
||||
}
|
||||
|
||||
void addChangedPart(
|
||||
ActionType action, int? isarId, Object data, bool writeTxn) {
|
||||
if (!state.syncOn) {
|
||||
return;
|
||||
}
|
||||
final changedPart = isar.changedParts
|
||||
.filter()
|
||||
.actionTypeEqualTo(action)
|
||||
.isarIdEqualTo(isarId)
|
||||
.findFirstSync();
|
||||
if (writeTxn) {
|
||||
isar.writeTxnSync(() {
|
||||
if (changedPart != null) {
|
||||
isar.changedParts.putSync(changedPart
|
||||
..data = jsonEncode(data)
|
||||
..clientDate = DateTime.now().millisecondsSinceEpoch);
|
||||
} else {
|
||||
isar.changedParts.putSync(ChangedPart(
|
||||
actionType: action,
|
||||
isarId: isarId,
|
||||
data: jsonEncode(data),
|
||||
clientDate: DateTime.now().millisecondsSinceEpoch));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (changedPart != null) {
|
||||
isar.changedParts.putSync(changedPart
|
||||
..data = jsonEncode(data)
|
||||
..clientDate = DateTime.now().millisecondsSinceEpoch);
|
||||
} else {
|
||||
isar.changedParts.putSync(ChangedPart(
|
||||
actionType: action,
|
||||
isarId: isarId,
|
||||
data: jsonEncode(data),
|
||||
clientDate: DateTime.now().millisecondsSinceEpoch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addChangedPartAsync(
|
||||
ActionType action, int? isarId, Object data, bool writeTxn) async {
|
||||
if (!state.syncOn) {
|
||||
return;
|
||||
}
|
||||
final changedPart = isar.changedParts
|
||||
.filter()
|
||||
.actionTypeEqualTo(action)
|
||||
.isarIdEqualTo(isarId)
|
||||
.findFirstSync();
|
||||
if (writeTxn) {
|
||||
await isar.writeTxn(() async {
|
||||
if (changedPart != null) {
|
||||
await isar.changedParts.put(changedPart
|
||||
..data = jsonEncode(data)
|
||||
..clientDate = DateTime.now().millisecondsSinceEpoch);
|
||||
} else {
|
||||
await isar.changedParts.put(ChangedPart(
|
||||
actionType: action,
|
||||
isarId: isarId,
|
||||
data: jsonEncode(data),
|
||||
clientDate: DateTime.now().millisecondsSinceEpoch));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (changedPart != null) {
|
||||
await isar.changedParts.put(changedPart
|
||||
..data = jsonEncode(data)
|
||||
..clientDate = DateTime.now().millisecondsSinceEpoch);
|
||||
} else {
|
||||
await isar.changedParts.put(ChangedPart(
|
||||
actionType: action,
|
||||
isarId: isarId,
|
||||
data: jsonEncode(data),
|
||||
clientDate: DateTime.now().millisecondsSinceEpoch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clearChangedParts(List<ActionType> actions) {
|
||||
var temp = isar.changedParts
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.and()
|
||||
.actionTypeEqualTo(actions.first);
|
||||
for (ActionType action in actions.skip(1)) {
|
||||
temp = temp.or().actionTypeEqualTo(action);
|
||||
}
|
||||
final changedParts = temp.findAllSync().map((cp) => cp.id as Id).toList();
|
||||
isar.writeTxnSync(() {
|
||||
isar.changedParts.deleteAllSync(changedParts);
|
||||
});
|
||||
}
|
||||
|
||||
void clearAllChangedParts() {
|
||||
isar.writeTxnSync(() {
|
||||
isar.changedParts.clearSync();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'sync_providers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$synchingHash() => r'3ab44d9e753f2d4b51fd10af6c98ffac78cbf201';
|
||||
String _$synchingHash() => r'45ac5bd29f880dfc7ac1fcdf12f49d751325279b';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
@ -29,11 +29,10 @@ class _SystemHash {
|
|||
}
|
||||
}
|
||||
|
||||
abstract class _$Synching
|
||||
extends BuildlessAutoDisposeNotifier<SyncPreference?> {
|
||||
abstract class _$Synching extends BuildlessAutoDisposeNotifier<SyncPreference> {
|
||||
late final int? syncId;
|
||||
|
||||
SyncPreference? build({
|
||||
SyncPreference build({
|
||||
required int? syncId,
|
||||
});
|
||||
}
|
||||
|
|
@ -43,7 +42,7 @@ abstract class _$Synching
|
|||
const synchingProvider = SynchingFamily();
|
||||
|
||||
/// See also [Synching].
|
||||
class SynchingFamily extends Family<SyncPreference?> {
|
||||
class SynchingFamily extends Family<SyncPreference> {
|
||||
/// See also [Synching].
|
||||
const SynchingFamily();
|
||||
|
||||
|
|
@ -82,7 +81,7 @@ class SynchingFamily extends Family<SyncPreference?> {
|
|||
|
||||
/// See also [Synching].
|
||||
class SynchingProvider
|
||||
extends AutoDisposeNotifierProviderImpl<Synching, SyncPreference?> {
|
||||
extends AutoDisposeNotifierProviderImpl<Synching, SyncPreference> {
|
||||
/// See also [Synching].
|
||||
SynchingProvider({
|
||||
required int? syncId,
|
||||
|
|
@ -112,7 +111,7 @@ class SynchingProvider
|
|||
final int? syncId;
|
||||
|
||||
@override
|
||||
SyncPreference? runNotifierBuild(
|
||||
SyncPreference runNotifierBuild(
|
||||
covariant Synching notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
|
|
@ -137,8 +136,7 @@ class SynchingProvider
|
|||
}
|
||||
|
||||
@override
|
||||
AutoDisposeNotifierProviderElement<Synching, SyncPreference?>
|
||||
createElement() {
|
||||
AutoDisposeNotifierProviderElement<Synching, SyncPreference> createElement() {
|
||||
return _SynchingProviderElement(this);
|
||||
}
|
||||
|
||||
|
|
@ -158,13 +156,13 @@ class SynchingProvider
|
|||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin SynchingRef on AutoDisposeNotifierProviderRef<SyncPreference?> {
|
||||
mixin SynchingRef on AutoDisposeNotifierProviderRef<SyncPreference> {
|
||||
/// The parameter `syncId` of this provider.
|
||||
int? get syncId;
|
||||
}
|
||||
|
||||
class _SynchingProviderElement
|
||||
extends AutoDisposeNotifierProviderElement<Synching, SyncPreference?>
|
||||
extends AutoDisposeNotifierProviderElement<Synching, SyncPreference>
|
||||
with SynchingRef {
|
||||
_SynchingProviderElement(super.provider);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/utils/date.dart';
|
||||
import 'package:mangayomi/models/sync_preference.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/widgets/sync_listile.dart';
|
||||
|
|
@ -14,6 +16,8 @@ class SyncScreen extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final syncProvider = ref.watch(synchingProvider(syncId: 1));
|
||||
final changedParts = ref.watch(synchingProvider(syncId: 1).notifier);
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
|
|
@ -33,6 +37,16 @@ class SyncScreen extends ConsumerWidget {
|
|||
syncPreference.authToken?.isNotEmpty ?? false;
|
||||
return Column(
|
||||
children: [
|
||||
SwitchListTile(
|
||||
value: syncProvider.syncOn,
|
||||
title: Text(context.l10n.sync_on),
|
||||
onChanged: !isLogged
|
||||
? null
|
||||
: (value) {
|
||||
ref
|
||||
.read(SynchingProvider(syncId: 1).notifier)
|
||||
.setSyncOn(value);
|
||||
}),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15, right: 15, bottom: 10, top: 5),
|
||||
|
|
@ -62,6 +76,7 @@ class SyncScreen extends ConsumerWidget {
|
|||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(l10n.syncing_subtitle,
|
||||
softWrap: true,
|
||||
style: TextStyle(
|
||||
fontSize: 11, color: context.secondaryColor))
|
||||
],
|
||||
|
|
@ -80,6 +95,12 @@ class SyncScreen extends ConsumerWidget {
|
|||
const SizedBox(width: 10),
|
||||
Column(children: [
|
||||
const SizedBox(width: 20),
|
||||
Text(
|
||||
"${l10n.last_sync}: ${dateFormat((syncPreference.lastSync ?? 0).toString(), ref: ref, context: context)} ${dateFormatHour((syncPreference.lastSync ?? 0).toString(), context)}",
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: context.secondaryColor)),
|
||||
const SizedBox(width: 20),
|
||||
Text(
|
||||
"${l10n.last_upload}: ${dateFormat((syncPreference.lastUpload ?? 0).toString(), ref: ref, context: context)} ${dateFormatHour((syncPreference.lastUpload ?? 0).toString(), context)}",
|
||||
style: TextStyle(
|
||||
|
|
@ -98,6 +119,27 @@ class SyncScreen extends ConsumerWidget {
|
|||
),
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(width: 20),
|
||||
Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: !isLogged
|
||||
? null
|
||||
: () {
|
||||
ref
|
||||
.read(syncServerProvider(syncId: 1)
|
||||
.notifier)
|
||||
.startSync(l10n);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.sync,
|
||||
color: !isLogged
|
||||
? context.secondaryColor
|
||||
: context.primaryColor,
|
||||
)),
|
||||
Text(l10n.sync_button_sync),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Column(
|
||||
children: [
|
||||
|
|
@ -187,6 +229,94 @@ class SyncScreen extends ConsumerWidget {
|
|||
],
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: !isLogged
|
||||
? null
|
||||
: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
l10n.sync_confirm_snapshot),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.end,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
Colors
|
||||
.transparent,
|
||||
shadowColor: Colors
|
||||
.transparent,
|
||||
surfaceTintColor:
|
||||
Colors
|
||||
.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
color: context
|
||||
.secondaryColor),
|
||||
borderRadius:
|
||||
BorderRadius
|
||||
.circular(
|
||||
20))),
|
||||
onPressed: () {
|
||||
Navigator.pop(
|
||||
context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style: TextStyle(
|
||||
color: context
|
||||
.secondaryColor),
|
||||
)),
|
||||
const SizedBox(width: 15),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton
|
||||
.styleFrom(
|
||||
backgroundColor: Colors
|
||||
.red
|
||||
.withValues(
|
||||
alpha:
|
||||
0.7)),
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(
|
||||
syncServerProvider(
|
||||
syncId:
|
||||
1)
|
||||
.notifier)
|
||||
.createSnapshot(
|
||||
l10n);
|
||||
Navigator.pop(
|
||||
context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.dialog_confirm,
|
||||
style: TextStyle(
|
||||
color: context
|
||||
.secondaryColor),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.save_as,
|
||||
color: !isLogged
|
||||
? context.secondaryColor
|
||||
: context.primaryColor,
|
||||
)),
|
||||
Text(l10n.sync_button_snapshot),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Column(
|
||||
children: [
|
||||
IconButton(
|
||||
|
|
@ -249,7 +379,9 @@ class SyncScreen extends ConsumerWidget {
|
|||
1)
|
||||
.notifier)
|
||||
.downloadFromServer(
|
||||
l10n);
|
||||
l10n,
|
||||
false,
|
||||
true);
|
||||
Navigator.pop(
|
||||
context);
|
||||
},
|
||||
|
|
@ -275,7 +407,76 @@ class SyncScreen extends ConsumerWidget {
|
|||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
buildChangedItemWidget(
|
||||
l10n.sync_pending_manga,
|
||||
changedParts.getChangedParts([
|
||||
ActionType.addItem,
|
||||
ActionType.removeItem,
|
||||
ActionType.updateItem
|
||||
])),
|
||||
const SizedBox(height: 10),
|
||||
buildChangedItemWidget(
|
||||
l10n.sync_pending_chapter,
|
||||
changedParts.getChangedParts([
|
||||
ActionType.addChapter,
|
||||
ActionType.removeChapter,
|
||||
ActionType.updateChapter
|
||||
])),
|
||||
const SizedBox(height: 10),
|
||||
buildChangedItemWidget(
|
||||
l10n.sync_pending_category,
|
||||
changedParts.getChangedParts([
|
||||
ActionType.addCategory,
|
||||
ActionType.removeCategory,
|
||||
ActionType.renameCategory
|
||||
])),
|
||||
const SizedBox(height: 10),
|
||||
buildChangedItemWidget(
|
||||
l10n.sync_pending_history,
|
||||
changedParts.getChangedParts([
|
||||
ActionType.addHistory,
|
||||
ActionType.clearHistory,
|
||||
ActionType.removeHistory
|
||||
])),
|
||||
const SizedBox(height: 10),
|
||||
buildChangedItemWidget(
|
||||
l10n.sync_pending_update,
|
||||
changedParts.getChangedParts(
|
||||
[ActionType.addUpdate, ActionType.clearUpdates])),
|
||||
const SizedBox(height: 10),
|
||||
buildChangedItemWidget(
|
||||
l10n.sync_pending_extension,
|
||||
changedParts.getChangedParts([
|
||||
ActionType.addExtension,
|
||||
ActionType.removeExtension,
|
||||
ActionType.updateExtension
|
||||
])),
|
||||
const SizedBox(height: 10),
|
||||
buildChangedItemWidget(
|
||||
l10n.sync_pending_track,
|
||||
changedParts.getChangedParts([
|
||||
ActionType.addTrack,
|
||||
ActionType.removeTrack,
|
||||
ActionType.updateTrack
|
||||
])),
|
||||
const SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15, right: 15, bottom: 10, top: 5),
|
||||
child: Row(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(syncServerProvider(syncId: 1).notifier)
|
||||
.getSnapshots(l10n);
|
||||
},
|
||||
child: Text("Browse / Download older backups")),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
|
|
@ -284,6 +485,32 @@ class SyncScreen extends ConsumerWidget {
|
|||
}
|
||||
}
|
||||
|
||||
Widget buildChangedItemWidget(String text, List<ChangedPart> changedParts) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 25, right: 25, bottom: 10, top: 5),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"$text: ${changedParts.length}",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
background: Paint()
|
||||
..color = changedParts.isEmpty
|
||||
? Color.fromARGB(125, 78, 182, 92)
|
||||
: Color.fromARGB(123, 245, 233, 132)
|
||||
..strokeWidth = 20
|
||||
..strokeJoin = StrokeJoin.round
|
||||
..strokeCap = StrokeCap.round
|
||||
..style = PaintingStyle.stroke,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showDialogLogin(BuildContext context, WidgetRef ref) {
|
||||
final serverController = TextEditingController();
|
||||
final emailController = TextEditingController();
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/track_preference.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'track_providers.g.dart';
|
||||
|
||||
|
|
@ -38,13 +40,28 @@ class Tracks extends _$Tracks {
|
|||
}
|
||||
}
|
||||
|
||||
isar.writeTxnSync(() => isar.tracks.putSync(track
|
||||
..syncId = syncId
|
||||
..itemType = itemType));
|
||||
isar.writeTxnSync(() {
|
||||
isar.tracks.putSync(track
|
||||
..syncId = syncId
|
||||
..itemType = itemType);
|
||||
if (tra.isEmpty) {
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.addTrack, null, track.toJson(), false);
|
||||
} else {
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateTrack, track.id, track.toJson(), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void deleteTrackManga(Track track) {
|
||||
isar.writeTxnSync(() => isar.tracks.deleteSync(track.id!));
|
||||
isar.writeTxnSync(() {
|
||||
isar.tracks.deleteSync(track.id!);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.removeTrack, track.id, "{}", false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'track_providers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$tracksHash() => r'a6e052c4102bbe640a397c37887dd618a4512c49';
|
||||
String _$tracksHash() => r'de3a19fc6542e0f610d154978fbd0272259142fc';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/download.dart';
|
||||
import 'package:mangayomi/models/history.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'novel_reader_controller_provider.g.dart';
|
||||
|
||||
|
|
@ -33,6 +35,8 @@ class NovelReaderController extends _$NovelReaderController {
|
|||
Manga? manga = chapter.manga.value;
|
||||
manga!.lastRead = DateTime.now().millisecondsSinceEpoch;
|
||||
isar.mangas.putSync(manga);
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateItem, manga.id, manga.toJson(), false);
|
||||
});
|
||||
History? history;
|
||||
|
||||
|
|
@ -58,6 +62,13 @@ class NovelReaderController extends _$NovelReaderController {
|
|||
isar.writeTxnSync(() {
|
||||
isar.historys.putSync(history!);
|
||||
history.chapter.saveSync();
|
||||
if (empty) {
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.addHistory, null, history.toJson(), false);
|
||||
} else {
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateHistory, history.id, history.toJson(), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -71,6 +82,8 @@ class NovelReaderController extends _$NovelReaderController {
|
|||
ch.lastPageRead =
|
||||
(maxOffset != 0 ? newOffset / maxOffset : 0).toString();
|
||||
isar.chapters.putSync(ch);
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateChapter, chapter.id, chapter.toJson(), false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -82,6 +95,8 @@ class NovelReaderController extends _$NovelReaderController {
|
|||
isar.writeTxnSync(() {
|
||||
chap.isBookmarked = !isBookmarked;
|
||||
isar.chapters.putSync(chap);
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateChapter, chapter.id, chapter.toJson(), false);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ part of 'novel_reader_controller_provider.dart';
|
|||
// **************************************************************************
|
||||
|
||||
String _$novelReaderControllerHash() =>
|
||||
r'2eec885b858de8195e31a2d0b70feb56c1dc4268';
|
||||
r'f05612ee0d25a5e5592f4e931b4078d992079f37';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ import 'package:go_router/go_router.dart';
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/eval/model/m_manga.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/manga_detail_main.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
|
||||
import 'package:mangayomi/router/router.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
|
|
@ -64,6 +66,7 @@ class MangaImageCardWidget extends ConsumerWidget {
|
|||
cacheMaxAge: const Duration(days: 7)),
|
||||
onTap: () {
|
||||
pushToMangaReaderDetail(
|
||||
ref: ref,
|
||||
context: context,
|
||||
getManga: getMangaDetail!,
|
||||
lang: source.lang!,
|
||||
|
|
@ -72,6 +75,7 @@ class MangaImageCardWidget extends ConsumerWidget {
|
|||
},
|
||||
onLongPress: () {
|
||||
pushToMangaReaderDetail(
|
||||
ref: ref,
|
||||
context: context,
|
||||
getManga: getMangaDetail!,
|
||||
lang: source.lang!,
|
||||
|
|
@ -81,6 +85,7 @@ class MangaImageCardWidget extends ConsumerWidget {
|
|||
},
|
||||
onSecondaryTap: () {
|
||||
pushToMangaReaderDetail(
|
||||
ref: ref,
|
||||
context: context,
|
||||
getManga: getMangaDetail!,
|
||||
lang: source.lang!,
|
||||
|
|
@ -162,6 +167,7 @@ class MangaImageCardListTileWidget extends ConsumerWidget {
|
|||
child: InkWell(
|
||||
onTap: () {
|
||||
pushToMangaReaderDetail(
|
||||
ref: ref,
|
||||
context: context,
|
||||
getManga: getMangaDetail!,
|
||||
lang: source.lang!,
|
||||
|
|
@ -170,6 +176,7 @@ class MangaImageCardListTileWidget extends ConsumerWidget {
|
|||
},
|
||||
onLongPress: () {
|
||||
pushToMangaReaderDetail(
|
||||
ref: ref,
|
||||
context: context,
|
||||
getManga: getMangaDetail!,
|
||||
lang: source.lang!,
|
||||
|
|
@ -179,6 +186,7 @@ class MangaImageCardListTileWidget extends ConsumerWidget {
|
|||
},
|
||||
onSecondaryTap: () {
|
||||
pushToMangaReaderDetail(
|
||||
ref: ref,
|
||||
context: context,
|
||||
getManga: getMangaDetail!,
|
||||
lang: source.lang!,
|
||||
|
|
@ -247,6 +255,7 @@ class MangaImageCardListTileWidget extends ConsumerWidget {
|
|||
|
||||
Future<void> pushToMangaReaderDetail(
|
||||
{MManga? getManga,
|
||||
required WidgetRef ref,
|
||||
required String lang,
|
||||
required BuildContext context,
|
||||
required String source,
|
||||
|
|
@ -280,6 +289,9 @@ Future<void> pushToMangaReaderDetail(
|
|||
if (empty) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.putSync(manga);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.addItem, null, manga.toJson(), false);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -346,6 +358,8 @@ Future<void> pushToMangaReaderDetail(
|
|||
final getManga = isar.mangas.filter().idEqualTo(mangaId).findFirstSync()!;
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.putSync(getManga..favorite = !getManga.favorite!);
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateItem, getManga.id, getManga.toJson(), false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:isar/isar.dart';
|
|||
import 'package:mangayomi/eval/model/source_preference.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/category.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/download.dart';
|
||||
import 'package:mangayomi/models/update.dart';
|
||||
|
|
@ -146,6 +147,7 @@ class StorageProvider {
|
|||
|
||||
final isar = Isar.openSync([
|
||||
MangaSchema,
|
||||
ChangedPartSchema,
|
||||
ChapterSchema,
|
||||
CategorySchema,
|
||||
UpdateSchema,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'aniskip.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$aniSkipHash() => r'887869b54e2e151633efd46da83bde845e14f421';
|
||||
String _$aniSkipHash() => r'2e5d19b025a2207ff64da7bf7908450ea9e5ff8c';
|
||||
|
||||
/// See also [AniSkip].
|
||||
@ProviderFor(AniSkip)
|
||||
|
|
|
|||
|
|
@ -1,21 +1,22 @@
|
|||
import 'package:crypto/crypto.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||
import 'package:mangayomi/eval/model/source_preference.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/update.dart';
|
||||
import 'package:mangayomi/models/sync_preference.dart';
|
||||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/manga.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/sync_preference.dart';
|
||||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/update.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/providers/restore.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/models/jwt.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:mangayomi/services/http/m_client.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
part 'sync_server.g.dart';
|
||||
|
|
@ -26,6 +27,9 @@ class SyncServer extends _$SyncServer {
|
|||
final String _loginUrl = '/login';
|
||||
final String _uploadUrl = '/upload/full';
|
||||
final String _downloadUrl = '/download';
|
||||
final String _snapshotUrl = '/snapshot';
|
||||
final String _checkUrl = '/check';
|
||||
final String _syncUrl = '/sync';
|
||||
|
||||
@override
|
||||
void build({required int syncId}) {}
|
||||
|
|
@ -59,6 +63,168 @@ class SyncServer extends _$SyncServer {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> startSync(AppLocalizations l10n) async {
|
||||
botToast(l10n.sync_checking, second: 2);
|
||||
try {
|
||||
final changedParts = ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.getAllChangedParts();
|
||||
|
||||
if (changedParts.isNotEmpty) {
|
||||
final accessToken = _getAccessToken();
|
||||
|
||||
var response = await http.post(
|
||||
Uri.parse('${_getServer()}$_syncUrl'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $accessToken'
|
||||
},
|
||||
body: jsonEncode({'changedParts': changedParts}),
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
botToast(l10n.sync_upload_failed, second: 5);
|
||||
return;
|
||||
}
|
||||
var jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
final localHash = _getDataHash(_getData());
|
||||
final remoteHash = jsonData["hash"];
|
||||
if (localHash != remoteHash) {
|
||||
await downloadFromServer(l10n, true, false);
|
||||
}
|
||||
} else {
|
||||
await forceCheck(l10n, true);
|
||||
}
|
||||
ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.setLastSync(DateTime.now().millisecondsSinceEpoch);
|
||||
ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.clearAllChangedParts();
|
||||
ref.invalidate(synchingProvider(syncId: syncId));
|
||||
botToast(l10n.sync_download_finished, second: 2);
|
||||
} catch (error) {
|
||||
botToast(error.toString(), second: 5);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> forceCheck(AppLocalizations l10n, bool silent) async {
|
||||
if (!silent) {
|
||||
botToast(l10n.sync_checking, second: 2);
|
||||
}
|
||||
try {
|
||||
final accessToken = _getAccessToken();
|
||||
final localHash = _getDataHash(_getData());
|
||||
var response = await http.get(
|
||||
Uri.parse('${_getServer()}$_checkUrl'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $accessToken'
|
||||
},
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
botToast(l10n.sync_download_failed, second: 2);
|
||||
return;
|
||||
}
|
||||
var jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
final remoteHash = jsonData["hash"];
|
||||
if (localHash != remoteHash) {
|
||||
await downloadFromServer(l10n, silent, false);
|
||||
} else if (!silent) {
|
||||
botToast("Sync up to date", second: 2);
|
||||
}
|
||||
} catch (error) {
|
||||
botToast(error.toString(), second: 5);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List> getSnapshots(AppLocalizations l10n) async {
|
||||
try {
|
||||
final accessToken = _getAccessToken();
|
||||
|
||||
var response = await http.get(
|
||||
Uri.parse('${_getServer()}$_snapshotUrl'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $accessToken'
|
||||
},
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
botToast(l10n.server_error, second: 5);
|
||||
return List.empty();
|
||||
}
|
||||
var snapshots = jsonDecode(response.body) as List;
|
||||
for (final snapshot in snapshots) {
|
||||
print(
|
||||
"${snapshot["id"]} - ${DateTime.parse(snapshot["dbCreatedAt"]).millisecondsSinceEpoch}");
|
||||
}
|
||||
return snapshots;
|
||||
} catch (error) {
|
||||
botToast(error.toString(), second: 5);
|
||||
}
|
||||
return List.empty();
|
||||
}
|
||||
|
||||
Future<void> downloadSnapshot(
|
||||
AppLocalizations l10n, String snapshotId) async {
|
||||
botToast(l10n.sync_downloading, second: 2);
|
||||
try {
|
||||
final accessToken = _getAccessToken();
|
||||
|
||||
var response = await http.get(
|
||||
Uri.parse('${_getServer()}$_snapshotUrl/$snapshotId'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $accessToken'
|
||||
},
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
botToast(l10n.sync_download_failed, second: 5);
|
||||
return;
|
||||
}
|
||||
var jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
_restore(
|
||||
jsonData["backupData"] is String
|
||||
? jsonDecode(jsonData["backupData"])
|
||||
: jsonData["backupData"],
|
||||
true);
|
||||
ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.setLastDownload(DateTime.now().millisecondsSinceEpoch);
|
||||
ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.clearAllChangedParts();
|
||||
ref.invalidate(synchingProvider(syncId: syncId));
|
||||
botToast(l10n.sync_download_finished, second: 2);
|
||||
} catch (error) {
|
||||
botToast(error.toString(), second: 5);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createSnapshot(AppLocalizations l10n) async {
|
||||
botToast(l10n.sync_snapshot_creating, second: 2);
|
||||
try {
|
||||
final accessToken = _getAccessToken();
|
||||
|
||||
var response = await http.post(
|
||||
Uri.parse('${_getServer()}$_snapshotUrl'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $accessToken'
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 400) {
|
||||
botToast(l10n.sync_snapshot_no_data, second: 5);
|
||||
return;
|
||||
} else if (response.statusCode != 200) {
|
||||
botToast(l10n.server_error, second: 5);
|
||||
return;
|
||||
}
|
||||
botToast(l10n.sync_snapshot_created, second: 2);
|
||||
} catch (error) {
|
||||
botToast(error.toString(), second: 5);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> uploadToServer(AppLocalizations l10n) async {
|
||||
botToast(l10n.sync_uploading, second: 2);
|
||||
try {
|
||||
|
|
@ -80,14 +246,21 @@ class SyncServer extends _$SyncServer {
|
|||
ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.setLastUpload(DateTime.now().millisecondsSinceEpoch);
|
||||
ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.clearAllChangedParts();
|
||||
ref.invalidate(synchingProvider(syncId: syncId));
|
||||
botToast(l10n.sync_upload_finished, second: 2);
|
||||
} catch (error) {
|
||||
botToast(error.toString(), second: 5);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> downloadFromServer(AppLocalizations l10n) async {
|
||||
botToast(l10n.sync_downloading, second: 2);
|
||||
Future<void> downloadFromServer(
|
||||
AppLocalizations l10n, bool silent, bool full) async {
|
||||
if (!silent) {
|
||||
botToast(l10n.sync_downloading, second: 2);
|
||||
}
|
||||
try {
|
||||
final accessToken = _getAccessToken();
|
||||
|
||||
|
|
@ -103,18 +276,39 @@ class SyncServer extends _$SyncServer {
|
|||
return;
|
||||
}
|
||||
var jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
_restore(jsonData["backupData"] is String
|
||||
? jsonDecode(jsonData["backupData"])
|
||||
: jsonData["backupData"]);
|
||||
_restore(
|
||||
jsonData["backupData"] is String
|
||||
? jsonDecode(jsonData["backupData"])
|
||||
: jsonData["backupData"],
|
||||
full);
|
||||
ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.setLastDownload(DateTime.now().millisecondsSinceEpoch);
|
||||
botToast(l10n.sync_download_finished, second: 2);
|
||||
ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.clearAllChangedParts();
|
||||
ref.invalidate(synchingProvider(syncId: syncId));
|
||||
if (!silent) {
|
||||
botToast(l10n.sync_download_finished, second: 2);
|
||||
}
|
||||
} catch (error) {
|
||||
botToast(error.toString(), second: 5);
|
||||
}
|
||||
}
|
||||
|
||||
String _getDataHash(Map<String, dynamic> data) {
|
||||
Map<String, dynamic> datas = {};
|
||||
datas["version"] = data["version"];
|
||||
datas["manga"] = data["manga"];
|
||||
datas["categories"] = data["categories"];
|
||||
datas["chapters"] = data["chapters"];
|
||||
datas["tracks"] = data["tracks"];
|
||||
datas["history"] = data["history"];
|
||||
datas["updates"] = data["updates"];
|
||||
var encodedJson = jsonEncode(datas);
|
||||
return sha256.convert(utf8.encode(encodedJson)).toString();
|
||||
}
|
||||
|
||||
Map<String, dynamic> _getData() {
|
||||
Map<String, dynamic> datas = {};
|
||||
datas.addAll({"version": "2"});
|
||||
|
|
@ -189,13 +383,13 @@ class SyncServer extends _$SyncServer {
|
|||
return datas;
|
||||
}
|
||||
|
||||
void _restore(Map<String, dynamic> backup) {
|
||||
ref.read(restoreBackupProvider(backup));
|
||||
void _restore(Map<String, dynamic> backup, bool full) {
|
||||
ref.read(restoreBackupProvider(backup, full: full));
|
||||
}
|
||||
|
||||
String _getAccessToken() {
|
||||
final syncPrefs = ref.watch(synchingProvider(syncId: syncId));
|
||||
if (syncPrefs == null || syncPrefs.authToken == null) {
|
||||
if (syncPrefs.authToken == null) {
|
||||
return "";
|
||||
}
|
||||
var paddedPayload = syncPrefs.authToken!.split(".")[1];
|
||||
|
|
@ -216,6 +410,6 @@ class SyncServer extends _$SyncServer {
|
|||
|
||||
String _getServer() {
|
||||
final syncPrefs = ref.watch(synchingProvider(syncId: syncId));
|
||||
return syncPrefs?.server ?? "";
|
||||
return syncPrefs.server ?? "";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'sync_server.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$syncServerHash() => r'd752de5e7238487e6176bb3ec69d2bd98c0509ad';
|
||||
String _$syncServerHash() => r'f9e68540eb702f3e3498fc642454897921a17f42';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'anilist.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$anilistHash() => r'70e8cd537270a9054a1ef72de117fc7ad5545218';
|
||||
String _$anilistHash() => r'ddd07acc8d28d2aa95c942566109e9393ca9e5ed';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
Loading…
Reference in a new issue