From 31bfadb5856958ea7b4be9fa7d20235bf9c06c50 Mon Sep 17 00:00:00 2001 From: Peter Ombodi Date: Wed, 6 Aug 2025 11:54:18 +0300 Subject: [PATCH] refactor code (use separate TrashService) --- mobile/lib/domain/services/asset.service.dart | 56 +--------------- .../domain/services/sync_stream.service.dart | 10 +-- mobile/lib/domain/services/trash.service.dart | 65 +++++++++++++++++++ .../infrastructure/asset.provider.dart | 12 ---- .../infrastructure/sync.provider.dart | 3 +- .../infrastructure/trash.provider.dart | 20 ++++++ mobile/test/domain/service.mock.dart | 4 +- .../services/sync_stream_service_test.dart | 12 ++-- 8 files changed, 102 insertions(+), 80 deletions(-) create mode 100644 mobile/lib/domain/services/trash.service.dart create mode 100644 mobile/lib/providers/infrastructure/trash.provider.dart diff --git a/mobile/lib/domain/services/asset.service.dart b/mobile/lib/domain/services/asset.service.dart index 8124e5abfa..c8cc61314e 100644 --- a/mobile/lib/domain/services/asset.service.dart +++ b/mobile/lib/domain/services/asset.service.dart @@ -2,36 +2,20 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; -import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; import 'package:immich_mobile/infrastructure/utils/exif.converter.dart'; -import 'package:immich_mobile/repositories/local_files_manager.repository.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:logging/logging.dart'; import 'package:platform/platform.dart'; class AssetService { - final AppSettingsService _appSettingsService; final RemoteAssetRepository _remoteAssetRepository; final DriftLocalAssetRepository _localAssetRepository; - final LocalFilesManagerRepository _localFilesManager; - final StorageRepository _storageRepository; final Platform _platform; - final Logger _logger; const AssetService({ - required AppSettingsService appSettingsService, required RemoteAssetRepository remoteAssetRepository, required DriftLocalAssetRepository localAssetRepository, - required LocalFilesManagerRepository localFilesManager, - required StorageRepository storageRepository, - required Logger logger, - }) : _appSettingsService = appSettingsService, - _remoteAssetRepository = remoteAssetRepository, + }) : _remoteAssetRepository = remoteAssetRepository, _localAssetRepository = localAssetRepository, - _localFilesManager = localFilesManager, - _storageRepository = storageRepository, - _platform = const LocalPlatform(), - _logger = logger; + _platform = const LocalPlatform(); Stream watchAsset(BaseAsset asset) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).id; @@ -100,40 +84,4 @@ class AssetService { Future getLocalHashedCount() { return _localAssetRepository.getHashedCount(); } - - Future handleRemoteTrashChanges(Iterable<({String checksum, DateTime? deletedAt})> syncItems) async { - if (_platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { - final trashedItems = syncItems.where((item) => item.deletedAt != null); - - if (trashedItems.isNotEmpty) { - final trashedAssetsChecksums = trashedItems.map((syncItem) => syncItem.checksum); - final localAssetsToTrash = await _localAssetRepository.getByChecksums(trashedAssetsChecksums); - if (localAssetsToTrash.isNotEmpty) { - final mediaUrls = await Future.wait( - localAssetsToTrash.map( - (localAsset) => _storageRepository.getAssetEntityForAsset(localAsset).then((e) => e?.getMediaUrl()), - ), - ); - _logger.fine("Moving to trash ${mediaUrls.join(", ")} assets"); - await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList()); - } - } - final modifiedItems = syncItems.where((e) => e.deletedAt == null); - if (modifiedItems.isNotEmpty) { - final modifiedChecksums = modifiedItems.map((syncItem) => syncItem.checksum); - final remoteAssetsToRestore = await _remoteAssetRepository.getByChecksums( - modifiedChecksums, - isTrashed: true, - ); - if (remoteAssetsToRestore.isNotEmpty) { - _logger.fine("Restoring from trash ${remoteAssetsToRestore.map((e) => e.name).join(", ")} assets"); - for (final remoteAsset in remoteAssetsToRestore) { - await _localFilesManager.restoreFromTrash(remoteAsset.name, remoteAsset.type.index); - } - } - } - } else { - return Future.value(); - } - } } diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index aa60975729..7b5d65483e 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:immich_mobile/domain/models/sync_event.model.dart'; -import 'package:immich_mobile/domain/services/asset.service.dart'; +import 'package:immich_mobile/domain/services/trash.service.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; @@ -13,17 +13,17 @@ class SyncStreamService { final SyncApiRepository _syncApiRepository; final SyncStreamRepository _syncStreamRepository; - final AssetService _assetService; + final TrashService _trashService; final bool Function()? _cancelChecker; SyncStreamService({ required SyncApiRepository syncApiRepository, required SyncStreamRepository syncStreamRepository, - required AssetService assetService, + required TrashService trashService, bool Function()? cancelChecker, }) : _syncApiRepository = syncApiRepository, _syncStreamRepository = syncStreamRepository, - _assetService = assetService, + _trashService = trashService, _cancelChecker = cancelChecker; bool get isCancelled => _cancelChecker?.call() ?? false; @@ -119,7 +119,7 @@ class SyncStreamService { return _syncStreamRepository.deletePartnerV1(data.cast()); case SyncEntityType.assetV1: final remoteSyncAssets = data.cast(); - await _assetService.handleRemoteTrashChanges( + await _trashService.handleRemoteChanges( remoteSyncAssets.map((e) => (checksum: e.checksum, deletedAt: e.deletedAt)), ); return _syncStreamRepository.updateAssetsV1(remoteSyncAssets); diff --git a/mobile/lib/domain/services/trash.service.dart b/mobile/lib/domain/services/trash.service.dart new file mode 100644 index 0000000000..5bce4713f7 --- /dev/null +++ b/mobile/lib/domain/services/trash.service.dart @@ -0,0 +1,65 @@ +import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/repositories/local_files_manager.repository.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:logging/logging.dart'; +import 'package:platform/platform.dart'; + +class TrashService { + final AppSettingsService _appSettingsService; + final RemoteAssetRepository _remoteAssetRepository; + final DriftLocalAssetRepository _localAssetRepository; + final LocalFilesManagerRepository _localFilesManager; + final StorageRepository _storageRepository; + final Platform _platform; + final Logger _logger; + + const TrashService({ + required AppSettingsService appSettingsService, + required RemoteAssetRepository remoteAssetRepository, + required DriftLocalAssetRepository localAssetRepository, + required LocalFilesManagerRepository localFilesManager, + required StorageRepository storageRepository, + required Logger logger, + }) : _appSettingsService = appSettingsService, + _remoteAssetRepository = remoteAssetRepository, + _localAssetRepository = localAssetRepository, + _localFilesManager = localFilesManager, + _storageRepository = storageRepository, + _platform = const LocalPlatform(), + _logger = logger; + + Future handleRemoteChanges(Iterable<({String checksum, DateTime? deletedAt})> syncItems) async { + if (_platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { + final trashedItems = syncItems.where((item) => item.deletedAt != null); + + if (trashedItems.isNotEmpty) { + final trashedAssetsChecksums = trashedItems.map((syncItem) => syncItem.checksum); + final localAssetsToTrash = await _localAssetRepository.getByChecksums(trashedAssetsChecksums); + if (localAssetsToTrash.isNotEmpty) { + final mediaUrls = await Future.wait( + localAssetsToTrash.map( + (localAsset) => _storageRepository.getAssetEntityForAsset(localAsset).then((e) => e?.getMediaUrl()), + ), + ); + _logger.info("Moving to trash ${mediaUrls.join(", ")} assets"); + await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList()); + } + } + final modifiedItems = syncItems.where((e) => e.deletedAt == null); + if (modifiedItems.isNotEmpty) { + final modifiedChecksums = modifiedItems.map((syncItem) => syncItem.checksum); + final remoteAssetsToRestore = await _remoteAssetRepository.getByChecksums(modifiedChecksums, isTrashed: true); + if (remoteAssetsToRestore.isNotEmpty) { + _logger.info("Restoring from trash ${remoteAssetsToRestore.map((e) => e.name).join(", ")} assets"); + for (final remoteAsset in remoteAssetsToRestore) { + await _localFilesManager.restoreFromTrash(remoteAsset.name, remoteAsset.type.index); + } + } + } + } else { + return Future.value(); + } + } +} diff --git a/mobile/lib/providers/infrastructure/asset.provider.dart b/mobile/lib/providers/infrastructure/asset.provider.dart index 340823d791..102e6aa60c 100644 --- a/mobile/lib/providers/infrastructure/asset.provider.dart +++ b/mobile/lib/providers/infrastructure/asset.provider.dart @@ -2,11 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/services/asset.service.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; -import 'package:immich_mobile/repositories/local_files_manager.repository.dart'; -import 'package:logging/logging.dart'; final localAssetRepository = Provider( (ref) => DriftLocalAssetRepository(ref.watch(driftProvider)), @@ -18,22 +14,14 @@ final remoteAssetRepositoryProvider = Provider( final assetServiceProvider = Provider( (ref) => AssetService( - appSettingsService: ref.watch(appSettingsServiceProvider), remoteAssetRepository: ref.watch(remoteAssetRepositoryProvider), localAssetRepository: ref.watch(localAssetRepository), - localFilesManager: ref.watch(localFilesManagerRepositoryProvider), - storageRepository: ref.watch(storageRepositoryProvider), - logger: Logger('AssetService'), ), ); final placesProvider = FutureProvider>( (ref) => AssetService( - appSettingsService: ref.watch(appSettingsServiceProvider), remoteAssetRepository: ref.watch(remoteAssetRepositoryProvider), localAssetRepository: ref.watch(localAssetRepository), - localFilesManager: ref.watch(localFilesManagerRepositoryProvider), - storageRepository: ref.watch(storageRepositoryProvider), - logger: Logger('AssetService'), ).getPlaces(), ); diff --git a/mobile/lib/providers/infrastructure/sync.provider.dart b/mobile/lib/providers/infrastructure/sync.provider.dart index af0a7e17d5..c5a01e5980 100644 --- a/mobile/lib/providers/infrastructure/sync.provider.dart +++ b/mobile/lib/providers/infrastructure/sync.provider.dart @@ -11,12 +11,13 @@ import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/trash.provider.dart'; final syncStreamServiceProvider = Provider( (ref) => SyncStreamService( syncApiRepository: ref.watch(syncApiRepositoryProvider), syncStreamRepository: ref.watch(syncStreamRepositoryProvider), - assetService: ref.watch(assetServiceProvider), + trashService: ref.watch(trashServiceProvider), cancelChecker: ref.watch(cancellationProvider), ), ); diff --git a/mobile/lib/providers/infrastructure/trash.provider.dart b/mobile/lib/providers/infrastructure/trash.provider.dart new file mode 100644 index 0000000000..06a4188da8 --- /dev/null +++ b/mobile/lib/providers/infrastructure/trash.provider.dart @@ -0,0 +1,20 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/services/trash.service.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; +import 'package:immich_mobile/repositories/local_files_manager.repository.dart'; +import 'package:logging/logging.dart'; + +import 'asset.provider.dart'; + +final trashServiceProvider = Provider( + (ref) => TrashService( + appSettingsService: ref.watch(appSettingsServiceProvider), + remoteAssetRepository: ref.watch(remoteAssetRepositoryProvider), + localAssetRepository: ref.watch(localAssetRepository), + localFilesManager: ref.watch(localFilesManagerRepositoryProvider), + storageRepository: ref.watch(storageRepositoryProvider), + logger: Logger('TrashService'), + ), +); + diff --git a/mobile/test/domain/service.mock.dart b/mobile/test/domain/service.mock.dart index 7aaa31c68a..b134ec190c 100644 --- a/mobile/test/domain/service.mock.dart +++ b/mobile/test/domain/service.mock.dart @@ -1,5 +1,5 @@ -import 'package:immich_mobile/domain/services/asset.service.dart'; import 'package:immich_mobile/domain/services/store.service.dart'; +import 'package:immich_mobile/domain/services/trash.service.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/domain/utils/background_sync.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; @@ -19,4 +19,4 @@ class MockAppSettingsService extends Mock implements AppSettingsService {} class MockUploadService extends Mock implements UploadService {} -class MockAssetService extends Mock implements AssetService {} +class MockTrashService extends Mock implements TrashService {} diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart index cfb661878f..b58ffe36fd 100644 --- a/mobile/test/domain/services/sync_stream_service_test.dart +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/domain/models/sync_event.model.dart'; -import 'package:immich_mobile/domain/services/asset.service.dart'; import 'package:immich_mobile/domain/services/sync_stream.service.dart'; +import 'package:immich_mobile/domain/services/trash.service.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; import 'package:mocktail/mocktail.dart'; @@ -32,7 +32,7 @@ void main() { late SyncStreamService sut; late SyncStreamRepository mockSyncStreamRepo; late SyncApiRepository mockSyncApiRepo; - late AssetService mockAssetService; + late TrashService mockTrashService; late Function(List, Function()) handleEventsCallback; late _MockAbortCallbackWrapper mockAbortCallbackWrapper; @@ -42,7 +42,7 @@ void main() { mockSyncStreamRepo = MockSyncStreamRepository(); mockSyncApiRepo = MockSyncApiRepository(); mockAbortCallbackWrapper = _MockAbortCallbackWrapper(); - mockAssetService = MockAssetService(); + mockTrashService = MockTrashService(); when(() => mockAbortCallbackWrapper()).thenReturn(false); when(() => mockSyncApiRepo.streamChanges(any())).thenAnswer((invocation) async { @@ -87,7 +87,7 @@ void main() { sut = SyncStreamService( syncApiRepository: mockSyncApiRepo, syncStreamRepository: mockSyncStreamRepo, - assetService: mockAssetService, + trashService: mockTrashService, ); }); @@ -153,7 +153,7 @@ void main() { sut = SyncStreamService( syncApiRepository: mockSyncApiRepo, syncStreamRepository: mockSyncStreamRepo, - assetService: mockAssetService, + trashService: mockTrashService, cancelChecker: cancellationChecker.call, ); await sut.sync(); @@ -189,7 +189,7 @@ void main() { sut = SyncStreamService( syncApiRepository: mockSyncApiRepo, syncStreamRepository: mockSyncStreamRepo, - assetService: mockAssetService, + trashService: mockTrashService, cancelChecker: cancellationChecker.call, );