From 8ed81ac3e11399b17c7a6de52340eef0081fadc1 Mon Sep 17 00:00:00 2001 From: Peter Ombodi Date: Mon, 12 Jan 2026 18:16:36 +0200 Subject: [PATCH] feat(mobile): do not restore locally deleted assets during trash sync (Android) (#24218) * feat(trash_sync): do not restore assets deleted locally only small fixes * feat(trash_sync): revert tag name * feat(trash_sync): resolve merge conflicts * refactor(trash_sync): consolidate local asset deletion logic * feat(mobile): Add TrashOrigin enum Replace isRestorable to sourse change related logic in repo * feat(mobile): fix format * fix(mobile): fix restoration scope * fix(mobile): Add coverage for ActionService deleteLocal paths Update LocalSyncService tests Set default value for source column * fix(mobile): db - require trash origin and update drift schema --------- Co-authored-by: Peter Ombodi --- .../drift_schemas/main/drift_schema_v15.json | Bin 0 -> 41478 bytes .../entities/trashed_local_asset.entity.dart | 9 ++ .../trashed_local_asset.entity.drift.dart | Bin 36389 -> 39064 bytes .../repositories/db.repository.dart | 5 +- .../repositories/db.repository.steps.dart | Bin 174748 -> 187936 bytes .../trashed_local_asset.repository.dart | 61 ++++++++- .../local_files_manager.repository.dart | 8 +- mobile/lib/services/action.service.dart | 46 +++---- .../services/local_sync_service_test.dart | 13 +- mobile/test/drift/main/generated/schema.dart | Bin 1672 -> 1827 bytes .../test/drift/main/generated/schema_v15.dart | Bin 0 -> 257454 bytes mobile/test/services/action.service_test.dart | 118 ++++++++++++++++++ 12 files changed, 230 insertions(+), 30 deletions(-) create mode 100644 mobile/drift_schemas/main/drift_schema_v15.json create mode 100644 mobile/test/drift/main/generated/schema_v15.dart create mode 100644 mobile/test/services/action.service_test.dart diff --git a/mobile/drift_schemas/main/drift_schema_v15.json b/mobile/drift_schemas/main/drift_schema_v15.json new file mode 100644 index 0000000000000000000000000000000000000000..8c56e7fa4c1e3d8d0d37298fb15e7f2eb76cc9e5 GIT binary patch literal 41478 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 literal 0 HcmV?d00001 diff --git a/mobile/lib/infrastructure/entities/trashed_local_asset.entity.dart b/mobile/lib/infrastructure/entities/trashed_local_asset.entity.dart index 308130b9e..2eaff5d5f 100644 --- a/mobile/lib/infrastructure/entities/trashed_local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/trashed_local_asset.entity.dart @@ -4,6 +4,13 @@ import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; +enum TrashOrigin { + // do not change this order! + localSync, + remoteSync, + localUser, +} + @TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)') @TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_album ON trashed_local_asset_entity (album_id)') class TrashedLocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { @@ -19,6 +26,8 @@ class TrashedLocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntity IntColumn get orientation => integer().withDefault(const Constant(0))(); + IntColumn get source => intEnum()(); + @override Set get primaryKey => {id, albumId}; } diff --git a/mobile/lib/infrastructure/entities/trashed_local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/trashed_local_asset.entity.drift.dart index aab226c3a2140f2484abeccd98cb27bcb9b43bfd..eeec2b3019b88ee269a8763fd0c686c4711c497c 100644 GIT binary patch delta 1516 zcmah}-%C?b9A|gaZKiHIH-F^xtjujXhh}J5cQeu+@G++Od>IJ!vXYz+>n2ayMngd6eeoJ)_(JwI1AMjEpwnU`zmkJ! zykd*$VR5$=J{DN)s?p{8N!5*oRp~Mus4hmZVsqlFCAW?6!I_z-$|P`LY}f(Il^LL~ za>LBH0UvkGYD|b=Z6Gth-|NCz6~8IS_Uy8%X;E1D>G`EGr{uAC$@|R;KH8p`yinDj zOfEAqQ0GymZadfvktQSjp^Ly&XMn7FYnCbzs_N@9Wr&_Qu?WF@(`SRy8Q*Kp%GHw_ z1Q{+b^HEli zSYdyP;=l)Nq{n2ysaD75Qgel02eedK563g16rT_l561y@g?Uke7qJ3pY}4T{=c+We zB-Il6x^ zG|klJ$XU)#w?YeU>7LXY5t1vju+CcHQ^z4fWxRZXAGtoemGq|fohGqJD&719W1{qk z8__-pI!d90Nyjg_Q&g!UAh8^a{C<=S)*K4z{98fYjKmNgyb*yFSzbbZl>9ZC*kRP5 zD$*YbQm`H=Fv&n0P?>yyqk;{Vh3}g-f@!}MmI7&bRUD<SG1c8_~3? z0T(q>ULx?$&q%V=4m@+oPC<1{Pn7S delta 257 zcmV+c0sj7&u>z%>0o4}u7jfDb3LyBJRglav@IlN=-rv(+Gp z36twDDYMclxB;`OED-~<>o4d5ld3EYvNF{blQ&7xvlL1R60`AKC;_vtUiSgB#b8bW zvn^s41d|b!6SLfA5d^bxYk&i@ZEwZ_vrln+0<-ROTm+NxTo#isl|_?paRsyQc=`de z5PJ#%lT|novp0N%0s$$L0be4MKy(SS5P#$Wlf_^ilR$I{vqOO$0<(*Q6atgOg?6)B zhVKEB^NbO*dx&lVvlWZD0kiXrF#?nETo$umj}irwEmQ}S&}IyinRro?Ky(SSewF(H Hlc1OQP3~y0 diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index b42aa3155..9ea0ba52e 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -95,7 +95,7 @@ class Drift extends $Drift implements IDatabaseRepository { } @override - int get schemaVersion => 14; + int get schemaVersion => 15; @override MigrationStrategy get migration => MigrationStrategy( @@ -190,6 +190,9 @@ class Drift extends $Drift implements IDatabaseRepository { await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.latitude); await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.longitude); }, + from14To15: (m, v15) async { + await m.addColumn(v15.trashedLocalAssetEntity, v15.trashedLocalAssetEntity.source); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 21a3db527477523d3c877db6d831a22684688bc7..38e0cec638a1c50d42a918d7ddd9a868fc1cb056 100644 GIT binary patch delta 290 zcmbPpm21Hs?uHh|ElfhwC+p4>W->IL>^M`6(Qxv+7kVI017p#4|5PSBHAd6veceoo zjHZ(VSxvUvMlrQ9PLE<{<~#W1a51S%zg;{4L0wljgyb8VZs(oNw1$&CIk7lZ!O!W<@fpev_uSj3db3RW=v;$kKh lM$_#ZmoSB~;4z1FCDTMkB?Tm-t&rF{TnY-BI$WC8TmbO~U6KF* delta 44 zcmV+{0Mq}VybGMw3V?(Gv;q>Bwt?kx761HP6W4a+yajQ1}Q8GDLV?M C5fHNg diff --git a/mobile/lib/infrastructure/repositories/trashed_local_asset.repository.dart b/mobile/lib/infrastructure/repositories/trashed_local_asset.repository.dart index 498e4227b..7e93713c4 100644 --- a/mobile/lib/infrastructure/repositories/trashed_local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/trashed_local_asset.repository.dart @@ -48,7 +48,8 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository { _db.remoteAssetEntity.checksum.equalsExp(_db.trashedLocalAssetEntity.checksum), ), ])..where( - _db.trashedLocalAssetEntity.albumId.isInQuery(selectedAlbumIds) & + _db.trashedLocalAssetEntity.source.equalsValue(TrashOrigin.remoteSync) & + _db.trashedLocalAssetEntity.albumId.isInQuery(selectedAlbumIds) & _db.remoteAssetEntity.deletedAt.isNull(), )) .get(); @@ -84,6 +85,7 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository { durationInSeconds: Value(item.asset.durationInSeconds), isFavorite: Value(item.asset.isFavorite), orientation: Value(item.asset.orientation), + source: TrashOrigin.localSync, ); batch.insert<$TrashedLocalAssetEntityTable, TrashedLocalAssetEntityData>( @@ -124,7 +126,7 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository { Future trashLocalAsset(Map> assetsByAlbums) async { if (assetsByAlbums.isEmpty) { - return; + return Future.value(); } final companions = []; @@ -147,6 +149,7 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository { orientation: Value(asset.orientation), createdAt: Value(asset.createdAt), updatedAt: Value(asset.updatedAt), + source: const Value(TrashOrigin.remoteSync), ), ); } @@ -165,7 +168,7 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository { Future applyRestoredAssets(List idList) async { if (idList.isEmpty) { - return; + return Future.value(); } final trashedAssets = []; @@ -205,6 +208,58 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository { }); } + Future applyTrashedAssets(List idList) async { + if (idList.isEmpty) { + return Future.value(); + } + + final trashedAssets = <({LocalAssetEntityData asset, String albumId})>[]; + + for (final slice in idList.slices(kDriftMaxChunk)) { + final rows = await (_db.select(_db.localAlbumAssetEntity).join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ])..where(_db.localAlbumAssetEntity.assetId.isIn(slice))).get(); + + final assetsWithAlbum = rows.map( + (row) => + (albumId: row.readTable(_db.localAlbumAssetEntity).albumId, asset: row.readTable(_db.localAssetEntity)), + ); + + trashedAssets.addAll(assetsWithAlbum); + } + + if (trashedAssets.isEmpty) { + return; + } + + final companions = trashedAssets.map((e) { + return TrashedLocalAssetEntityCompanion.insert( + id: e.asset.id, + name: e.asset.name, + type: e.asset.type, + createdAt: Value(e.asset.createdAt), + updatedAt: Value(e.asset.updatedAt), + width: Value(e.asset.width), + height: Value(e.asset.height), + durationInSeconds: Value(e.asset.durationInSeconds), + checksum: Value(e.asset.checksum), + isFavorite: Value(e.asset.isFavorite), + orientation: Value(e.asset.orientation), + source: TrashOrigin.localUser, + albumId: e.albumId, + ); + }); + + await _db.transaction(() async { + for (final companion in companions) { + await _db.into(_db.trashedLocalAssetEntity).insertOnConflictUpdate(companion); + } + for (final slice in idList.slices(kDriftMaxChunk)) { + await (_db.delete(_db.localAssetEntity)..where((t) => t.id.isIn(slice))).go(); + } + }); + } + Future>> getToTrash() async { final result = >{}; diff --git a/mobile/lib/repositories/local_files_manager.repository.dart b/mobile/lib/repositories/local_files_manager.repository.dart index 765c9a6f0..6a6200b2e 100644 --- a/mobile/lib/repositories/local_files_manager.repository.dart +++ b/mobile/lib/repositories/local_files_manager.repository.dart @@ -10,7 +10,7 @@ final localFilesManagerRepositoryProvider = Provider( class LocalFilesManagerRepository { LocalFilesManagerRepository(this._service); - final Logger _logger = Logger('SyncStreamService'); + final Logger _logger = Logger('LocalFilesManagerRepo'); final LocalFilesManagerService _service; Future moveToTrash(List mediaUrls) async { @@ -38,8 +38,10 @@ class LocalFilesManagerRepository { for (final asset in assets) { _logger.info("Restoring from trash, localId: ${asset.id}, remoteId: ${asset.checksum}"); try { - await _service.restoreFromTrashById(asset.id, asset.type.index); - restoredIds.add(asset.id); + final result = await _service.restoreFromTrashById(asset.id, asset.type.index); + if (result) { + restoredIds.add(asset.id); + } } catch (e) { _logger.warning("Restoring failure: $e"); } diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index 4261613a1..4d6e9611d 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -5,9 +5,13 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/repositories/asset_api.repository.dart'; @@ -28,6 +32,7 @@ final actionServiceProvider = Provider( ref.watch(localAssetRepository), ref.watch(driftAlbumApiRepositoryProvider), ref.watch(remoteAlbumRepository), + ref.watch(trashedLocalAssetRepository), ref.watch(assetMediaRepositoryProvider), ref.watch(downloadRepositoryProvider), ), @@ -39,6 +44,7 @@ class ActionService { final DriftLocalAssetRepository _localAssetRepository; final DriftAlbumApiRepository _albumApiRepository; final DriftRemoteAlbumRepository _remoteAlbumRepository; + final DriftTrashedLocalAssetRepository _trashedLocalAssetRepository; final AssetMediaRepository _assetMediaRepository; final DownloadRepository _downloadRepository; @@ -48,6 +54,7 @@ class ActionService { this._localAssetRepository, this._albumApiRepository, this._remoteAlbumRepository, + this._trashedLocalAssetRepository, this._assetMediaRepository, this._downloadRepository, ); @@ -82,11 +89,7 @@ class ActionService { // Ask user if they want to delete local copies if (localIds.isNotEmpty) { - final deletedIds = await _assetMediaRepository.deleteAll(localIds); - - if (deletedIds.isNotEmpty) { - await _localAssetRepository.delete(deletedIds); - } + await _deleteLocalAssets(localIds); } } @@ -110,11 +113,7 @@ class ActionService { await _remoteAssetRepository.trash(remoteIds); if (localIds.isNotEmpty) { - final deletedIds = await _assetMediaRepository.deleteAll(localIds); - - if (deletedIds.isNotEmpty) { - await _localAssetRepository.delete(deletedIds); - } + await _deleteLocalAssets(localIds); } } @@ -123,22 +122,12 @@ class ActionService { await _remoteAssetRepository.delete(remoteIds); if (localIds.isNotEmpty) { - final deletedIds = await _assetMediaRepository.deleteAll(localIds); - - if (deletedIds.isNotEmpty) { - await _localAssetRepository.delete(deletedIds); - } + await _deleteLocalAssets(localIds); } } Future deleteLocal(List localIds) async { - final deletedIds = await _assetMediaRepository.deleteAll(localIds); - if (deletedIds.isNotEmpty) { - await _localAssetRepository.delete(deletedIds); - return deletedIds.length; - } - - return 0; + return await _deleteLocalAssets(localIds); } Future editLocation(List remoteIds, BuildContext context) async { @@ -242,4 +231,17 @@ class ActionService { Future> downloadAll(List assets) { return _downloadRepository.downloadAllAssets(assets); } + + Future _deleteLocalAssets(List localIds) async { + final deletedIds = await _assetMediaRepository.deleteAll(localIds); + if (deletedIds.isEmpty) { + return 0; + } + if (CurrentPlatform.isAndroid && Store.get(StoreKey.manageLocalMediaAndroid, false)) { + await _trashedLocalAssetRepository.applyTrashedAssets(deletedIds); + } else { + await _localAssetRepository.delete(deletedIds); + } + return deletedIds.length; + } } diff --git a/mobile/test/domain/services/local_sync_service_test.dart b/mobile/test/domain/services/local_sync_service_test.dart index 92ab01c7e..45088305e 100644 --- a/mobile/test/domain/services/local_sync_service_test.dart +++ b/mobile/test/domain/services/local_sync_service_test.dart @@ -153,7 +153,14 @@ void main() { 'album-a': [platformAsset], }); - verify(() => mockTrashedLocalAssetRepository.processTrashSnapshot(any())).called(1); + final trashedSnapshot = + verify(() => mockTrashedLocalAssetRepository.processTrashSnapshot(captureAny())).captured.single + as Iterable; + expect(trashedSnapshot.length, 1); + final trashedEntry = trashedSnapshot.single; + expect(trashedEntry.albumId, 'album-a'); + expect(trashedEntry.asset.id, platformAsset.id); + expect(trashedEntry.asset.name, platformAsset.name); verify(() => mockTrashedLocalAssetRepository.getToTrash()).called(1); verify(() => mockLocalFilesManager.restoreAssetsFromTrash(any())).called(1); @@ -174,6 +181,10 @@ void main() { await sut.processTrashedAssets({}); + final trashedSnapshot = + verify(() => mockTrashedLocalAssetRepository.processTrashSnapshot(captureAny())).captured.single + as Iterable; + expect(trashedSnapshot, isEmpty); verifyNever(() => mockLocalFilesManager.restoreAssetsFromTrash(any())); verifyNever(() => mockTrashedLocalAssetRepository.applyRestoredAssets(any())); }); diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index 5e196105747470bdfe81ad3b12e4b246d40439ca..9edeed5ddff6f4b74bc4400613dccea3ab7c12c9 100644 GIT binary patch delta 210 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 y{~nGjf&Czk;2!CZ assetMediaRepository.deleteAll(ids)).thenAnswer((_) async => ids); + when(() => trashedLocalAssetRepository.applyTrashedAssets(ids)).thenAnswer((_) async {}); + + final result = await sut.deleteLocal(ids); + + expect(result, ids.length); + verify(() => assetMediaRepository.deleteAll(ids)).called(1); + verify(() => trashedLocalAssetRepository.applyTrashedAssets(ids)).called(1); + verifyNever(() => localAssetRepository.delete(any())); + }); + + test('deletes locally when Android trash handling is disabled', () async { + await Store.put(StoreKey.manageLocalMediaAndroid, false); + const ids = ['c']; + + when(() => assetMediaRepository.deleteAll(ids)).thenAnswer((_) async => ids); + when(() => localAssetRepository.delete(ids)).thenAnswer((_) async {}); + + final result = await sut.deleteLocal(ids); + + expect(result, ids.length); + verify(() => assetMediaRepository.deleteAll(ids)).called(1); + verify(() => localAssetRepository.delete(ids)).called(1); + verifyNever(() => trashedLocalAssetRepository.applyTrashedAssets(any())); + }); + + test('short-circuits when nothing was deleted', () async { + await Store.put(StoreKey.manageLocalMediaAndroid, true); + const ids = ['x']; + + when(() => assetMediaRepository.deleteAll(ids)).thenAnswer((_) async => []); + + final result = await sut.deleteLocal(ids); + + expect(result, 0); + verify(() => assetMediaRepository.deleteAll(ids)).called(1); + verifyNever(() => trashedLocalAssetRepository.applyTrashedAssets(any())); + verifyNever(() => localAssetRepository.delete(any())); + }); + }); +}