123 lines
3.6 KiB
Text
123 lines
3.6 KiB
Text
import 'package:flutter/foundation.dart';
|
|
import 'package:aves/model/source/collection_source.dart';
|
|
|
|
import 'remote_http_api.dart';
|
|
import 'remote_models.dart';
|
|
import 'remote_repository.dart';
|
|
import 'remote_state_store.dart';
|
|
import 'package:aves/remote/collection_source_remote_ext.dart';
|
|
|
|
class RemoteSyncEngine {
|
|
RemoteSyncEngine({
|
|
required this.api,
|
|
required this.repo,
|
|
required this.source,
|
|
required this.state,
|
|
});
|
|
|
|
final RemoteHttpApi api;
|
|
final RemoteRepository repo;
|
|
final CollectionSource source;
|
|
final RemoteStateStore state;
|
|
|
|
static const int retentionDays = 30;
|
|
|
|
bool _isTooOld(String iso) {
|
|
final t = DateTime.tryParse(iso);
|
|
if (t == null) return true;
|
|
return DateTime.now().difference(t).inDays > retentionDays;
|
|
}
|
|
|
|
Future<void> progressiveSync() async {
|
|
final last = await state.getLastSyncIso();
|
|
if (last == null || last.isEmpty || _isTooOld(last)) {
|
|
await fullSync();
|
|
return;
|
|
}
|
|
await deltaSyncFrom(last);
|
|
await state.setLastSyncIso(DateTime.now().toUtc().toIso8601String());
|
|
}
|
|
|
|
Future<void> progressiveSyncFrom(String sinceIso) async {
|
|
if (_isTooOld(sinceIso)) {
|
|
await fullSync();
|
|
return;
|
|
}
|
|
await deltaSyncFrom(sinceIso);
|
|
await state.setLastSyncIso(DateTime.now().toUtc().toIso8601String());
|
|
}
|
|
|
|
Future<void> fullSync() async {
|
|
debugPrint('[remote] fullSync start');
|
|
|
|
final list = await api.getAllPhotos();
|
|
final items = list.map(RemotePhotoItem.fromJson).toList();
|
|
|
|
await repo.deleteAllRemotes();
|
|
await repo.upsertAll(items, chunkSize: 200);
|
|
|
|
await source.appendRemoteEntriesFromDb();
|
|
|
|
await state.setLastSyncIso(DateTime.now().toUtc().toIso8601String());
|
|
debugPrint('[remote] fullSync done items=${items.length}');
|
|
}
|
|
|
|
/// Delta sync: upsert changed + delete hardDeleted.
|
|
/// [appendAfter]=false utile se vuoi batchare più operazioni e appendare una volta sola.
|
|
Future<void> deltaSyncFrom(String sinceIso, {bool appendAfter = true}) async {
|
|
debugPrint('[remote] deltaSyncFrom since=$sinceIso');
|
|
|
|
List<Map<String, dynamic>> changed = const [];
|
|
List<Map<String, dynamic>> deleted = const [];
|
|
|
|
// più resiliente: separo i due fetch
|
|
try {
|
|
changed = await api.getChanges(sinceIso);
|
|
} catch (e) {
|
|
debugPrint('[remote] getChanges failed: $e');
|
|
// lasciamo che il chiamante decida (controller può fare recovery)
|
|
rethrow;
|
|
}
|
|
|
|
try {
|
|
deleted = await api.getDeletedHard(sinceIso);
|
|
} catch (e) {
|
|
debugPrint('[remote] getDeletedHard failed: $e');
|
|
// anche qui: rethrow per recovery (coerente con retry WS)
|
|
rethrow;
|
|
}
|
|
|
|
var didChange = false;
|
|
|
|
if (changed.isNotEmpty) {
|
|
final items = changed.map(RemotePhotoItem.fromJson).toList();
|
|
await repo.upsertAll(items, chunkSize: 200);
|
|
debugPrint('[remote] delta upsert=${items.length}');
|
|
didChange = true;
|
|
}
|
|
|
|
if (deleted.isNotEmpty) {
|
|
final ids = deleted.map((e) => e['id']?.toString()).whereType<String>().toSet();
|
|
if (ids.isNotEmpty) {
|
|
await repo.deleteRemotesByRemoteIds(ids);
|
|
debugPrint('[remote] delta hardDeleted=${ids.length}');
|
|
didChange = true;
|
|
}
|
|
}
|
|
|
|
// ✅ append solo se necessario e richiesto
|
|
if (appendAfter && didChange) {
|
|
await source.appendRemoteEntriesFromDb();
|
|
}
|
|
}
|
|
|
|
Future<void> upsertOneFromJson(Map<String, dynamic> json) async {
|
|
final it = RemotePhotoItem.fromJson(json);
|
|
await repo.upsertAll([it], chunkSize: 1);
|
|
}
|
|
|
|
Future<void> deleteRemoteIds(Set<String> ids) async {
|
|
await repo.deleteRemotesByRemoteIds(ids);
|
|
}
|
|
}
|
|
|