mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-03-11 17:25:32 +00:00
finished snapshots and added auto sync
This commit is contained in:
parent
815ccbc19a
commit
7b00d0f7e4
13 changed files with 492 additions and 61 deletions
|
|
@ -225,8 +225,26 @@
|
|||
"sync_pending_track": "Ausstehende Änderungen für Trackings",
|
||||
"sync_snapshot_creating": "Erstelle Snapshot...",
|
||||
"sync_snapshot_created": "Snapshot wurde erstellt!",
|
||||
"sync_snapshot_deleting": "Lösche Snapshot...",
|
||||
"sync_snapshot_deleted": "Snapshot wurde gelöscht!",
|
||||
"sync_snapshot_no_data": "Keine Daten zum Sichern! Lade erstmal alles hoch!",
|
||||
"server_error": "Server error!",
|
||||
"sync_browse_snapshots": "Durchsuche ältere Backups",
|
||||
"sync_snapshots": "Snapshots",
|
||||
"sync_load_snapshot": "Snapshot laden",
|
||||
"sync_delete_snapshot": "Snapshot löschen",
|
||||
"sync_auto": "Auto Sync",
|
||||
"sync_auto_warning": "Auto Sync ist derzeit ein experimentelles Feature!",
|
||||
"sync_auto_off": "Aus",
|
||||
"sync_auto_30_seconds": "Alle 30 Sekunden",
|
||||
"sync_auto_1_minute": "Jede Minute",
|
||||
"sync_auto_5_minutes": "Alle 5 Minuten",
|
||||
"sync_auto_10_minutes": "Alle 10 Minuten",
|
||||
"sync_auto_30_minutes": "Alle 30 Minuten",
|
||||
"sync_auto_1_hour": "Jede Stunde",
|
||||
"sync_auto_3_hours": "Alle 3 Stunden",
|
||||
"sync_auto_6_hours": "Alle 6 Stunden",
|
||||
"sync_auto_12_hours": "Alle 12 Stunden",
|
||||
"server_error": "Server Fehler!",
|
||||
"dialog_confirm": "Fortfahren",
|
||||
"description": "Beschreibung",
|
||||
"full_screen_player": "Vollbildmodus aktivieren",
|
||||
|
|
|
|||
|
|
@ -225,7 +225,25 @@
|
|||
"sync_pending_track": "Track changes pending",
|
||||
"sync_snapshot_creating": "Creating snapshot...",
|
||||
"sync_snapshot_created": "Snapshot created!",
|
||||
"sync_snapshot_deleting": "Deleting snapshot...",
|
||||
"sync_snapshot_deleted": "Snapshot deleted!",
|
||||
"sync_snapshot_no_data": "No data to create a snapshot! Do a full upload first!",
|
||||
"sync_browse_snapshots": "Browse older backups",
|
||||
"sync_snapshots": "Snapshots",
|
||||
"sync_load_snapshot": "Load snapshot",
|
||||
"sync_delete_snapshot": "Delete snapshot",
|
||||
"sync_auto": "Auto Sync",
|
||||
"sync_auto_warning": "Auto Sync is currently an experimental feature!",
|
||||
"sync_auto_off": "Off",
|
||||
"sync_auto_30_seconds": "Every 30 seconds",
|
||||
"sync_auto_1_minute": "Every 1 minute",
|
||||
"sync_auto_5_minutes": "Every 5 minutes",
|
||||
"sync_auto_10_minutes": "Every 10 minutes",
|
||||
"sync_auto_30_minutes": "Every 30 minutes",
|
||||
"sync_auto_1_hour": "Every 1 hour",
|
||||
"sync_auto_3_hours": "Every 3 hours",
|
||||
"sync_auto_6_hours": "Every 6 hours",
|
||||
"sync_auto_12_hours": "Every 12 hours",
|
||||
"server_error": "Server error!",
|
||||
"dialog_confirm": "Confirm",
|
||||
"description": "Description",
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ class SyncPreference {
|
|||
|
||||
bool syncOn = false;
|
||||
|
||||
int autoSyncFrequency = 0;
|
||||
|
||||
SyncPreference({
|
||||
this.syncId,
|
||||
this.email,
|
||||
|
|
@ -29,6 +31,7 @@ class SyncPreference {
|
|||
this.lastDownload,
|
||||
this.server,
|
||||
this.syncOn = false,
|
||||
this.autoSyncFrequency = 0,
|
||||
});
|
||||
|
||||
SyncPreference.fromJson(Map<String, dynamic> json) {
|
||||
|
|
@ -40,6 +43,7 @@ class SyncPreference {
|
|||
lastDownload = json['lastDownload'];
|
||||
server = json['server'];
|
||||
syncOn = json['syncOn'] ?? false;
|
||||
syncOn = json['autoSyncFrequency'] ?? 0;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
|
@ -49,6 +53,7 @@ class SyncPreference {
|
|||
'lastSync': lastSync,
|
||||
'lastUpload': lastUpload,
|
||||
'lastDownload': lastDownload,
|
||||
'syncOn': syncOn
|
||||
'syncOn': syncOn,
|
||||
'autoSyncFrequency': autoSyncFrequency,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,33 +22,38 @@ const SyncPreferenceSchema = CollectionSchema(
|
|||
name: r'authToken',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'email': PropertySchema(
|
||||
r'autoSyncFrequency': PropertySchema(
|
||||
id: 1,
|
||||
name: r'autoSyncFrequency',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'email': PropertySchema(
|
||||
id: 2,
|
||||
name: r'email',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'lastDownload': PropertySchema(
|
||||
id: 2,
|
||||
id: 3,
|
||||
name: r'lastDownload',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'lastSync': PropertySchema(
|
||||
id: 3,
|
||||
id: 4,
|
||||
name: r'lastSync',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'lastUpload': PropertySchema(
|
||||
id: 4,
|
||||
id: 5,
|
||||
name: r'lastUpload',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'server': PropertySchema(
|
||||
id: 5,
|
||||
id: 6,
|
||||
name: r'server',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'syncOn': PropertySchema(
|
||||
id: 6,
|
||||
id: 7,
|
||||
name: r'syncOn',
|
||||
type: IsarType.bool,
|
||||
)
|
||||
|
|
@ -101,12 +106,13 @@ void _syncPreferenceSerialize(
|
|||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeString(offsets[0], object.authToken);
|
||||
writer.writeString(offsets[1], object.email);
|
||||
writer.writeLong(offsets[2], object.lastDownload);
|
||||
writer.writeLong(offsets[3], object.lastSync);
|
||||
writer.writeLong(offsets[4], object.lastUpload);
|
||||
writer.writeString(offsets[5], object.server);
|
||||
writer.writeBool(offsets[6], object.syncOn);
|
||||
writer.writeLong(offsets[1], object.autoSyncFrequency);
|
||||
writer.writeString(offsets[2], object.email);
|
||||
writer.writeLong(offsets[3], object.lastDownload);
|
||||
writer.writeLong(offsets[4], object.lastSync);
|
||||
writer.writeLong(offsets[5], object.lastUpload);
|
||||
writer.writeString(offsets[6], object.server);
|
||||
writer.writeBool(offsets[7], object.syncOn);
|
||||
}
|
||||
|
||||
SyncPreference _syncPreferenceDeserialize(
|
||||
|
|
@ -117,13 +123,14 @@ SyncPreference _syncPreferenceDeserialize(
|
|||
) {
|
||||
final object = SyncPreference(
|
||||
authToken: reader.readStringOrNull(offsets[0]),
|
||||
email: reader.readStringOrNull(offsets[1]),
|
||||
lastDownload: reader.readLongOrNull(offsets[2]),
|
||||
lastSync: reader.readLongOrNull(offsets[3]),
|
||||
lastUpload: reader.readLongOrNull(offsets[4]),
|
||||
server: reader.readStringOrNull(offsets[5]),
|
||||
autoSyncFrequency: reader.readLongOrNull(offsets[1]) ?? 0,
|
||||
email: reader.readStringOrNull(offsets[2]),
|
||||
lastDownload: reader.readLongOrNull(offsets[3]),
|
||||
lastSync: reader.readLongOrNull(offsets[4]),
|
||||
lastUpload: reader.readLongOrNull(offsets[5]),
|
||||
server: reader.readStringOrNull(offsets[6]),
|
||||
syncId: id,
|
||||
syncOn: reader.readBoolOrNull(offsets[6]) ?? false,
|
||||
syncOn: reader.readBoolOrNull(offsets[7]) ?? false,
|
||||
);
|
||||
return object;
|
||||
}
|
||||
|
|
@ -138,16 +145,18 @@ P _syncPreferenceDeserializeProp<P>(
|
|||
case 0:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 1:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
return (reader.readLongOrNull(offset) ?? 0) as P;
|
||||
case 2:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 3:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 4:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 5:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 6:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 7:
|
||||
return (reader.readBoolOrNull(offset) ?? false) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
|
|
@ -402,6 +411,62 @@ extension SyncPreferenceQueryFilter
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterFilterCondition>
|
||||
autoSyncFrequencyEqualTo(int value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'autoSyncFrequency',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterFilterCondition>
|
||||
autoSyncFrequencyGreaterThan(
|
||||
int value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'autoSyncFrequency',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterFilterCondition>
|
||||
autoSyncFrequencyLessThan(
|
||||
int value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'autoSyncFrequency',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterFilterCondition>
|
||||
autoSyncFrequencyBetween(
|
||||
int lower,
|
||||
int upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'autoSyncFrequency',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterFilterCondition>
|
||||
emailIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
|
|
@ -1038,6 +1103,20 @@ extension SyncPreferenceQuerySortBy
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterSortBy>
|
||||
sortByAutoSyncFrequency() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'autoSyncFrequency', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterSortBy>
|
||||
sortByAutoSyncFrequencyDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'autoSyncFrequency', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterSortBy> sortByEmail() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'email', Sort.asc);
|
||||
|
|
@ -1133,6 +1212,20 @@ extension SyncPreferenceQuerySortThenBy
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterSortBy>
|
||||
thenByAutoSyncFrequency() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'autoSyncFrequency', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterSortBy>
|
||||
thenByAutoSyncFrequencyDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'autoSyncFrequency', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QAfterSortBy> thenByEmail() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'email', Sort.asc);
|
||||
|
|
@ -1235,6 +1328,13 @@ extension SyncPreferenceQueryWhereDistinct
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QDistinct>
|
||||
distinctByAutoSyncFrequency() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'autoSyncFrequency');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, SyncPreference, QDistinct> distinctByEmail(
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
|
|
@ -1290,6 +1390,13 @@ extension SyncPreferenceQueryProperty
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, int, QQueryOperations>
|
||||
autoSyncFrequencyProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'autoSyncFrequency');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<SyncPreference, String?, QQueryOperations> emailProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'email');
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/update.dart';
|
||||
import 'package:mangayomi/models/source.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/loading_icon.dart';
|
||||
import 'package:mangayomi/services/fetch_anime_sources.dart';
|
||||
import 'package:mangayomi/services/fetch_manga_sources.dart';
|
||||
|
|
@ -21,6 +23,7 @@ import 'package:mangayomi/providers/l10n_providers.dart';
|
|||
import 'package:mangayomi/router/router.dart';
|
||||
import 'package:mangayomi/services/fetch_novel_sources.dart';
|
||||
import 'package:mangayomi/services/fetch_sources_list.dart';
|
||||
import 'package:mangayomi/services/sync_server.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:mangayomi/modules/library/providers/library_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/providers/incognito_mode_state_provider.dart';
|
||||
|
|
@ -52,6 +55,8 @@ class _MainScreenState extends ConsumerState<MainScreen> {
|
|||
}
|
||||
|
||||
late final navigationOrder = ref.watch(navigationOrderStateProvider);
|
||||
late final autoSyncFrequency =
|
||||
ref.watch(synchingProvider(syncId: 1)).autoSyncFrequency;
|
||||
late String? location =
|
||||
ref.watch(routerCurrentLocationStateProvider(context));
|
||||
late String defaultLocation = navigationOrder.first;
|
||||
|
|
@ -63,6 +68,19 @@ class _MainScreenState extends ConsumerState<MainScreen> {
|
|||
Timer.periodic(Duration(minutes: 5), (timer) {
|
||||
ref.read(checkAndBackupProvider);
|
||||
});
|
||||
if (autoSyncFrequency != 0) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
Timer.periodic(Duration(seconds: autoSyncFrequency), (timer) {
|
||||
try {
|
||||
ref.read(syncServerProvider(syncId: 1).notifier).startSync(l10n);
|
||||
} catch (e) {
|
||||
botToast(
|
||||
"Failed to sync! Maybe the sync server is down. Restart the app to resume auto sync.");
|
||||
timer.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ref.watch(checkForUpdateProvider(context: context));
|
||||
ref.watch(fetchMangaSourcesListProvider(id: null, reFresh: false));
|
||||
ref.watch(fetchAnimeSourcesListProvider(id: null, reFresh: false));
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ void restoreBackup(Ref ref, Map<String, dynamic> backup, {bool full = true}) {
|
|||
}
|
||||
}
|
||||
if (full) {
|
||||
ref.read(synchingProvider(syncId: 1).notifier).clearAllChangedParts();
|
||||
ref.read(synchingProvider(syncId: 1).notifier).clearAllChangedParts(false);
|
||||
ref.invalidate(themeModeStateProvider);
|
||||
ref.invalidate(blendLevelStateProvider);
|
||||
ref.invalidate(flexSchemeColorStateProvider);
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ class _DoRestoreProviderElement extends AutoDisposeProviderElement<void>
|
|||
BuildContext get context => (origin as DoRestoreProvider).context;
|
||||
}
|
||||
|
||||
String _$restoreBackupHash() => r'4285c4f788c892ddcedb48951284e181cc821541';
|
||||
String _$restoreBackupHash() => r'0b6bdb8eff801da7efa7b3776f80e50bee4d4ad1';
|
||||
|
||||
/// See also [restoreBackup].
|
||||
@ProviderFor(restoreBackup)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class Synching extends _$Synching {
|
|||
isar.writeTxnSync(() {
|
||||
isar.syncPreferences.putSync(syncPreference);
|
||||
});
|
||||
ref.invalidate(synchingProvider(syncId: syncId));
|
||||
ref.invalidateSelf();
|
||||
ref.invalidate(syncServerProvider(syncId: syncId!));
|
||||
}
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ class Synching extends _$Synching {
|
|||
isar.writeTxnSync(() {
|
||||
isar.syncPreferences.putSync(state..authToken = null);
|
||||
});
|
||||
ref.invalidate(synchingProvider(syncId: syncId));
|
||||
ref.invalidateSelf();
|
||||
ref.invalidate(syncServerProvider(syncId: syncId!));
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +61,13 @@ class Synching extends _$Synching {
|
|||
});
|
||||
}
|
||||
|
||||
void setAutoSyncFrequency(int value) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.syncPreferences.putSync(state..autoSyncFrequency = value);
|
||||
});
|
||||
ref.invalidateSelf();
|
||||
}
|
||||
|
||||
List<ChangedPart> getAllChangedParts() {
|
||||
return isar.changedParts.filter().idIsNotNull().findAllSync();
|
||||
}
|
||||
|
|
@ -170,9 +177,13 @@ class Synching extends _$Synching {
|
|||
});
|
||||
}
|
||||
|
||||
void clearAllChangedParts() {
|
||||
isar.writeTxnSync(() {
|
||||
void clearAllChangedParts(bool txn) {
|
||||
if (txn) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.changedParts.clearSync();
|
||||
});
|
||||
} else {
|
||||
isar.changedParts.clearSync();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'sync_providers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$synchingHash() => r'abef3be5c474e8bf6f9d2b5af7587caf061d1fbd';
|
||||
String _$synchingHash() => r'8a4f7f408bf0ac26f4a21368620051ecba3adf53';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -10,17 +10,29 @@ import 'package:mangayomi/modules/more/settings/sync/widgets/sync_listile.dart';
|
|||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/services/sync_server.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:mangayomi/utils/language.dart';
|
||||
|
||||
class SyncScreen extends ConsumerWidget {
|
||||
const SyncScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final autoSyncOptions = [0, 30, 60, 300, 600, 1800, 3600];
|
||||
final syncProvider = ref.watch(synchingProvider(syncId: 1));
|
||||
final changedParts = ref.watch(synchingProvider(syncId: 1).notifier);
|
||||
final autoSyncFrequency =
|
||||
ref.watch(synchingProvider(syncId: 1)).autoSyncFrequency;
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
final autoSyncOptions = {
|
||||
l10n.sync_auto_off: 0,
|
||||
l10n.sync_auto_30_seconds: 30,
|
||||
l10n.sync_auto_1_minute: 60,
|
||||
l10n.sync_auto_5_minutes: 300,
|
||||
l10n.sync_auto_10_minutes: 600,
|
||||
l10n.sync_auto_30_minutes: 1800,
|
||||
l10n.sync_auto_1_hour: 3600,
|
||||
l10n.sync_auto_3_hours: 10800,
|
||||
l10n.sync_auto_6_hours: 21600,
|
||||
l10n.sync_auto_12_hours: 43200,
|
||||
};
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(l10nLocalizations(context)!.syncing),
|
||||
|
|
@ -56,7 +68,7 @@ class SyncScreen extends ConsumerWidget {
|
|||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
l10n.app_language,
|
||||
l10n.sync_auto,
|
||||
),
|
||||
content: SizedBox(
|
||||
width: context.width(0.8),
|
||||
|
|
@ -64,20 +76,23 @@ class SyncScreen extends ConsumerWidget {
|
|||
shrinkWrap: true,
|
||||
itemCount: autoSyncOptions.length,
|
||||
itemBuilder: (context, index) {
|
||||
final option = autoSyncOptions[index];
|
||||
final optionName =
|
||||
autoSyncOptions.keys.elementAt(index);
|
||||
final optionValue = autoSyncOptions.values
|
||||
.elementAt(index);
|
||||
return RadioListTile(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
value: option,
|
||||
groupValue: 0,
|
||||
value: optionValue,
|
||||
groupValue: autoSyncFrequency,
|
||||
onChanged: (value) {
|
||||
/*ref
|
||||
.read(l10nLocaleStateProvider
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1)
|
||||
.notifier)
|
||||
.setLocale(locale);*/
|
||||
.setAutoSyncFrequency(value!);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
title: Text(completeLanguageName("")),
|
||||
title: Text(optionName),
|
||||
);
|
||||
},
|
||||
)),
|
||||
|
|
@ -100,13 +115,34 @@ class SyncScreen extends ConsumerWidget {
|
|||
);
|
||||
});
|
||||
},
|
||||
title: Text(l10n.app_language),
|
||||
title: Text(l10n.sync_auto),
|
||||
subtitle: Text(
|
||||
completeLanguageName(""),
|
||||
autoSyncOptions.entries
|
||||
.where((o) => o.value == autoSyncFrequency)
|
||||
.first
|
||||
.key,
|
||||
style: TextStyle(
|
||||
fontSize: 11, color: context.secondaryColor),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.warning_amber_outlined,
|
||||
color: context.secondaryColor,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(l10n.sync_auto_warning,
|
||||
softWrap: true,
|
||||
style: TextStyle(
|
||||
fontSize: 11, color: context.secondaryColor))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15, right: 15, bottom: 10, top: 5),
|
||||
|
|
@ -531,13 +567,197 @@ class SyncScreen extends ConsumerWidget {
|
|||
ElevatedButton(
|
||||
onPressed: !isLogged
|
||||
? null
|
||||
: () {
|
||||
ref
|
||||
: () async {
|
||||
final snapshots = await ref
|
||||
.read(syncServerProvider(syncId: 1)
|
||||
.notifier)
|
||||
.getSnapshots(l10n);
|
||||
if (context.mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
l10n.sync_snapshots,
|
||||
),
|
||||
content: SizedBox(
|
||||
width: context.width(0.8),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: snapshots.length,
|
||||
itemBuilder:
|
||||
(context, index) {
|
||||
return Padding(
|
||||
padding:
|
||||
const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 5),
|
||||
child: Card(
|
||||
child: Column(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
Colors
|
||||
.transparent,
|
||||
elevation:
|
||||
0,
|
||||
shadowColor:
|
||||
Colors
|
||||
.transparent,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(0),
|
||||
bottomRight: Radius.circular(0),
|
||||
topRight: Radius.circular(10),
|
||||
topLeft: Radius.circular(10)))),
|
||||
onPressed: () {},
|
||||
child: Row(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment
|
||||
.end,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons
|
||||
.save),
|
||||
const SizedBox(
|
||||
width:
|
||||
10,
|
||||
),
|
||||
Expanded(
|
||||
child:
|
||||
Text("${dateFormat((snapshots[index].createdAt!).toString(), ref: ref, context: context)} ${dateFormatHour((snapshots[index].createdAt!).toString(), context)}"))
|
||||
],
|
||||
)),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.end,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed:
|
||||
() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
l10n.sync_load_snapshot,
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(l10n.cancel)),
|
||||
const SizedBox(
|
||||
width: 15,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await ref.read(SyncServerProvider(syncId: 1).notifier).downloadSnapshot(l10n, snapshots[index].uuid!);
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
l10n.ok,
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
icon:
|
||||
const Icon(Icons.cloud_download_outlined)),
|
||||
IconButton(
|
||||
onPressed:
|
||||
() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
l10n.sync_delete_snapshot,
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(l10n.cancel)),
|
||||
const SizedBox(
|
||||
width: 15,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await ref.read(syncServerProvider(syncId: 1).notifier).deleteSnapshot(l10n, snapshots[index].uuid!);
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
l10n.ok,
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
icon:
|
||||
const Icon(Icons.delete_outlined))
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(
|
||||
context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style: TextStyle(
|
||||
color: context
|
||||
.primaryColor),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Text("Browse / Download older backups")),
|
||||
child: Text(l10n.sync_browse_snapshots)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -173,9 +173,11 @@ void checkIfSourceIsObsolete(
|
|||
sourceList.where((e) => e.id != null).map((e) => e.id).toList();
|
||||
if (ids.isNotEmpty) {
|
||||
isar.writeTxnSync(() {
|
||||
if (source.isObsolete != !ids.contains(source.id)) {
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateExtension, source.id, source.toJson(), false);
|
||||
}
|
||||
isar.sources.putSync(source..isObsolete = !ids.contains(source.id));
|
||||
ref.read(synchingProvider(syncId: 1).notifier).addChangedPart(
|
||||
ActionType.updateExtension, source.id, source.toJson(), false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ class SyncServer extends _$SyncServer {
|
|||
|
||||
final syncNotifier = ref.read(synchingProvider(syncId: syncId).notifier);
|
||||
syncNotifier.setLastSync(DateTime.now().millisecondsSinceEpoch);
|
||||
syncNotifier.clearAllChangedParts();
|
||||
syncNotifier.clearAllChangedParts(true);
|
||||
|
||||
ref.invalidate(synchingProvider(syncId: syncId));
|
||||
botToast(l10n.sync_download_finished, second: 2);
|
||||
|
|
@ -136,7 +136,7 @@ class SyncServer extends _$SyncServer {
|
|||
}
|
||||
}
|
||||
|
||||
Future<List> getSnapshots(AppLocalizations l10n) async {
|
||||
Future<List<Snapshot>> getSnapshots(AppLocalizations l10n) async {
|
||||
try {
|
||||
final accessToken = _getAccessToken();
|
||||
|
||||
|
|
@ -151,10 +151,10 @@ class SyncServer extends _$SyncServer {
|
|||
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}");
|
||||
var temp = jsonDecode(response.body) as List;
|
||||
final snapshots = List<Snapshot>.empty(growable: true);
|
||||
for (final snapshot in temp) {
|
||||
snapshots.add(Snapshot.fromJson(snapshot));
|
||||
}
|
||||
return snapshots;
|
||||
} catch (error) {
|
||||
|
|
@ -221,6 +221,28 @@ class SyncServer extends _$SyncServer {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> deleteSnapshot(AppLocalizations l10n, String snapshotId) async {
|
||||
botToast(l10n.sync_snapshot_deleting, second: 2);
|
||||
try {
|
||||
final accessToken = _getAccessToken();
|
||||
|
||||
var response = await http.delete(
|
||||
Uri.parse('${_getServer()}$_snapshotUrl/$snapshotId'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $accessToken'
|
||||
},
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
botToast(l10n.server_error, second: 5);
|
||||
return;
|
||||
}
|
||||
botToast(l10n.sync_snapshot_deleted, second: 2);
|
||||
} catch (error) {
|
||||
botToast(error.toString(), second: 5);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> uploadToServer(AppLocalizations l10n) async {
|
||||
botToast(l10n.sync_uploading, second: 2);
|
||||
try {
|
||||
|
|
@ -239,12 +261,11 @@ class SyncServer extends _$SyncServer {
|
|||
botToast(l10n.sync_upload_failed, second: 5);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.setLastUpload(DateTime.now().millisecondsSinceEpoch);
|
||||
ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.clearAllChangedParts();
|
||||
|
||||
final syncNotifier = ref.read(synchingProvider(syncId: syncId).notifier);
|
||||
syncNotifier.setLastUpload(DateTime.now().millisecondsSinceEpoch);
|
||||
syncNotifier.clearAllChangedParts(true);
|
||||
|
||||
ref.invalidate(synchingProvider(syncId: syncId));
|
||||
botToast(l10n.sync_upload_finished, second: 2);
|
||||
} catch (error) {
|
||||
|
|
@ -407,3 +428,14 @@ class SyncServer extends _$SyncServer {
|
|||
return syncPrefs.server ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
class Snapshot {
|
||||
String? uuid;
|
||||
int? createdAt;
|
||||
Snapshot({required this.uuid, required this.createdAt});
|
||||
|
||||
Snapshot.fromJson(Map<String, dynamic> json) {
|
||||
uuid = json['id'];
|
||||
createdAt = DateTime.parse(json["dbCreatedAt"]).millisecondsSinceEpoch;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'sync_server.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$syncServerHash() => r'b1d19a01737badaa79b42a7a39f2f7782463ee63';
|
||||
String _$syncServerHash() => r'd13b5d6eaded02a9256e64550e98983b17ab70f4';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
Loading…
Reference in a new issue