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

156 lines
4.5 KiB
Dart

// lib/remote/collection_source_remote_ext.dart
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/events.dart';
import 'package:aves/services/common/services.dart';
import 'package:flutter/foundation.dart';
import 'remote_origin.dart';
import 'package:aves/remote/remote_sync_bus.dart';
extension CollectionSourceRemoteExt on CollectionSource {
// ============================================================
// CACHE INTERNA (PRO)
// ============================================================
static Map<String, AvesEntry> _cacheMem = {};
static Map<String, AvesEntry> _cacheDb = {};
static bool _cacheValid = false;
void _rebuildCacheFromMemory() {
_cacheMem = {
for (final e in allEntries)
if (e.origin == RemoteOrigin.value &&
!e.trashed &&
e.remoteId != null &&
e.remoteId!.isNotEmpty)
e.remoteId!: e,
};
_cacheValid = true;
}
// ============================================================
// SYNC REMOTI (DIFF PRO)
// ============================================================
Future<void> appendRemoteEntriesFromDb() async {
// 1) carica dal DB tutte le entry remote
final Set<AvesEntry> remotiDb =
await localMediaDb.loadEntries(origin: RemoteOrigin.value);
// costruisci cache DB
_cacheDb = {
for (final e in remotiDb)
if (e.remoteId != null && e.remoteId!.isNotEmpty) e.remoteId!: e,
};
// costruisci cache memoria se non valida
if (!_cacheValid) {
_rebuildCacheFromMemory();
}
// 2) diff
final idsDb = _cacheDb.keys.toSet();
final idsMem = _cacheMem.keys.toSet();
final idsToAdd = idsDb.difference(idsMem);
final idsToRemove = idsMem.difference(idsDb);
// UPDATE: remoti presenti in entrambi ma con metadata cambiati
final idsToUpdate = <String>{};
for (final id in idsDb.intersection(idsMem)) {
final db = _cacheDb[id]!;
final mem = _cacheMem[id]!;
if (db.dateModifiedMillis != mem.dateModifiedMillis ||
db.sizeBytes != mem.sizeBytes ||
db.remoteRotation != mem.remoteRotation ||
db.width != mem.width ||
db.height != mem.height) {
idsToUpdate.add(id);
}
}
debugPrint(
'[remote-diff-pro] add=${idsToAdd.length} del=${idsToRemove.length} upd=${idsToUpdate.length}');
// ============================================================
// APPLY DEL
// ============================================================
if (idsToRemove.isNotEmpty) {
final urisToRemove = idsToRemove
.map((id) => _cacheMem[id]?.uri)
.whereType<String>()
.toSet();
removeEntries(
urisToRemove,
includeTrash: false,
);
// aggiorna cache
for (final id in idsToRemove) {
_cacheMem.remove(id);
}
}
// ============================================================
// APPLY ADD
// ============================================================
if (idsToAdd.isNotEmpty) {
final toAdd = idsToAdd.map((id) => _cacheDb[id]!).toSet();
addEntries(toAdd);
// aggiorna cache
for (final e in toAdd) {
_cacheMem[e.remoteId!] = e;
}
}
// ============================================================
// APPLY UPDATE
// ============================================================
if (idsToUpdate.isNotEmpty) {
final toUpdate = idsToUpdate.map((id) => _cacheDb[id]!).toSet();
await refreshEntries(
toUpdate,
EntryDataType.values.toSet(),
);
// aggiorna cache
for (final e in toUpdate) {
_cacheMem[e.remoteId!] = e;
}
}
// ============================================================
// NOTIFICA LENS
// ============================================================
if (idsToAdd.isNotEmpty) {
eventBus.fire(
EntryAddedEvent(idsToAdd.map((id) => _cacheDb[id]!).toSet()),
);
}
if (idsToRemove.isNotEmpty || idsToUpdate.isNotEmpty) {
eventBus.fire(EntryRefreshedEvent(const {}));
}
debugPrint('[remote-diff-pro] done: mem=${_cacheMem.length}');
}
}
/// Visibilità dei remoti (usata da CollectionLens)
extension RemoteVisibilityExt on CollectionSource {
bool get remoteVisible {
final state = RemoteSyncBus.instance.stateNotifier.value;
return state == RemoteSyncState.syncing ||
state == RemoteSyncState.upToDate;
}
}