From 2f1ec5b724d7a72d62ecf9693d1023aab178b5d4 Mon Sep 17 00:00:00 2001 From: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Date: Wed, 1 Oct 2025 13:20:03 +0530 Subject: [PATCH] fix: prune stale assets --- .../domain/services/sync_stream.service.dart | 4 +-- .../repositories/sync_stream.repository.dart | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index bec7e6afda..5ed11598dc 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -130,9 +130,9 @@ class SyncStreamService { // to acknowledge that the client has processed all the backfill events case SyncEntityType.syncAckV1: return; - // No-op. SyncCompleteV1 is used to signal the completion of the sync process + // SyncCompleteV1 is used to signal the completion of the sync process. Cleanup stale assets and signal completion case SyncEntityType.syncCompleteV1: - return; + return _syncStreamRepository.pruneAssets(); // Request to reset the client state. Clear everything related to remote entities case SyncEntityType.syncResetV1: return _syncStreamRepository.reset(); diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index f4720fb110..487811c6cd 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -591,6 +591,40 @@ class SyncStreamRepository extends DriftDatabaseRepository { rethrow; } } + + Future pruneAssets() async { + try { + await _db.transaction(() async { + final authQuery = _db.authUserEntity.selectOnly() + ..addColumns([_db.authUserEntity.id]) + ..limit(1); + final currentUserId = await authQuery.map((row) => row.read(_db.authUserEntity.id)).getSingleOrNull(); + if (currentUserId == null) { + _logger.warning('No authenticated user found during pruneAssets. Skipping asset pruning.'); + return; + } + + final partnerQuery = _db.partnerEntity.selectOnly() + ..addColumns([_db.partnerEntity.sharedById]) + ..where(_db.partnerEntity.sharedWithId.equals(currentUserId)); + final partnerIds = await partnerQuery.map((row) => row.read(_db.partnerEntity.sharedById)).get(); + + final validUsers = {currentUserId, ...partnerIds.nonNulls}; + + // Asset is not owned by the current user or any of their partners and is not part of any (shared) album + // Likely a stale asset that was previously shared but has been removed + await _db.remoteAssetEntity.deleteWhere((asset) { + return asset.ownerId.isNotIn(validUsers) & + asset.id.isNotInQuery( + _db.remoteAlbumAssetEntity.selectOnly()..addColumns([_db.remoteAlbumAssetEntity.assetId]), + ); + }); + }); + } catch (error, stack) { + _logger.severe('Error: pruneAssets', error, stack); + // We do not rethrow here as this is a client-only cleanup and should not affect the sync process + } + } } extension on AssetTypeEnum {