// 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 _cacheMem = {}; static Map _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 appendRemoteEntriesFromDb() async { // 1) carica dal DB tutte le entry remote final Set 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 = {}; 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() .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; } }