aves_mio22/lib/remote.ok/remote_sync_engine.dart.ok
2026-04-18 20:05:02 +02:00

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);
}
}