From df0ed1e8da5f570c3bbd39567255332b7217a128 Mon Sep 17 00:00:00 2001
From: Peter Ombodi
Date: Tue, 7 Oct 2025 18:24:57 +0300
Subject: [PATCH] remove trashed asset model remove trash_sync.service refactor
DriftTrashedLocalAssetRepository, LocalSyncService
---
.../models/asset/trashed_asset.model.dart | 117 ------------------
.../domain/services/local_sync.service.dart | 52 +++-----
.../domain/services/trash_sync.service.dart | 53 --------
.../entities/trashed_local_asset.entity.dart | 5 +-
.../trashed_local_asset.repository.dart | 116 ++++++++---------
5 files changed, 81 insertions(+), 262 deletions(-)
delete mode 100644 mobile/lib/domain/models/asset/trashed_asset.model.dart
delete mode 100644 mobile/lib/domain/services/trash_sync.service.dart
diff --git a/mobile/lib/domain/models/asset/trashed_asset.model.dart b/mobile/lib/domain/models/asset/trashed_asset.model.dart
deleted file mode 100644
index 44732b3b01..0000000000
--- a/mobile/lib/domain/models/asset/trashed_asset.model.dart
+++ /dev/null
@@ -1,117 +0,0 @@
-import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
-
-class TrashedAsset extends LocalAsset {
- final String albumId;
-
- const TrashedAsset({
- required this.albumId,
- required super.id,
- super.remoteId,
- required super.name,
- super.checksum,
- required super.type,
- required super.createdAt,
- required super.updatedAt,
- super.width,
- super.height,
- super.durationInSeconds,
- super.isFavorite = false,
- super.livePhotoVideoId,
- super.orientation = 0,
- });
-
- @override
- TrashedAsset copyWith({
- String? id,
- String? remoteId,
- String? name,
- String? checksum,
- AssetType? type,
- DateTime? createdAt,
- DateTime? updatedAt,
- int? width,
- int? height,
- int? durationInSeconds,
- bool? isFavorite,
- String? livePhotoVideoId,
- int? orientation,
- String? albumId,
- }) {
- return TrashedAsset(
- id: id ?? this.id,
- remoteId: remoteId ?? this.remoteId,
- name: name ?? this.name,
- checksum: checksum ?? this.checksum,
- type: type ?? this.type,
- createdAt: createdAt ?? this.createdAt,
- updatedAt: updatedAt ?? this.updatedAt,
- width: width ?? this.width,
- height: height ?? this.height,
- durationInSeconds: durationInSeconds ?? this.durationInSeconds,
- isFavorite: isFavorite ?? this.isFavorite,
- livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
- orientation: orientation ?? this.orientation,
- albumId: albumId ?? this.albumId,
- );
- }
-
- @override
- bool operator ==(Object other) =>
- identical(this, other) ||
- other is TrashedAsset &&
- runtimeType == other.runtimeType &&
- // LocalAsset identity
- id == other.id &&
- name == other.name &&
- remoteId == other.remoteId &&
- checksum == other.checksum &&
- type == other.type &&
- createdAt == other.createdAt &&
- updatedAt == other.updatedAt &&
- width == other.width &&
- height == other.height &&
- durationInSeconds == other.durationInSeconds &&
- isFavorite == other.isFavorite &&
- livePhotoVideoId == other.livePhotoVideoId &&
- orientation == other.orientation &&
- // TrashedAsset extras
- albumId == other.albumId;
-
- @override
- int get hashCode => Object.hash(
- id,
- name,
- remoteId,
- checksum,
- type,
- createdAt,
- updatedAt,
- width,
- height,
- durationInSeconds,
- isFavorite,
- livePhotoVideoId,
- orientation,
- albumId,
- );
-
- @override
- String toString() {
- return 'TrashedAsset('
- 'id: $id, '
- 'remoteId: $remoteId, '
- 'name: $name, '
- 'checksum: $checksum, '
- 'type: $type, '
- 'createdAt: $createdAt, '
- 'updatedAt: $updatedAt, '
- 'width: $width, '
- 'height: $height, '
- 'durationInSeconds: $durationInSeconds, '
- 'isFavorite: $isFavorite, '
- 'livePhotoVideoId: $livePhotoVideoId, '
- 'orientation: $orientation, '
- 'albumId: $albumId, '
- ')';
- }
-}
diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart
index 7fec2d4d4f..5ca5c43e9f 100644
--- a/mobile/lib/domain/services/local_sync.service.dart
+++ b/mobile/lib/domain/services/local_sync.service.dart
@@ -4,7 +4,6 @@ import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
-import 'package:immich_mobile/domain/models/asset/trashed_asset.model.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart';
@@ -299,15 +298,9 @@ class LocalSyncService {
if (trashUpdates.isEmpty) {
return Future.value();
}
- final trashedAssets = [];
- for (final update in trashUpdates) {
- final albums = delta.assetAlbums.cast>();
- for (final String id in albums[update.id]!.cast().nonNulls) {
- trashedAssets.add(update.toTrashedAsset(id));
- }
- }
- _log.info("updateLocalTrashChanges trashedAssets: ${trashedAssets.map((e) => e.id)}");
- await _trashedLocalAssetRepository.saveTrashedAssets(trashedAssets);
+ final trashedAssets = delta.toTrashedAssets();
+ _log.info("updateLocalTrashChanges trashedAssets: ${trashedAssets.map((e) => e.asset.id)}");
+ await _trashedLocalAssetRepository.applyDelta(trashedAssets);
await _applyRemoteRestoreToLocal();
}
@@ -321,7 +314,7 @@ class LocalSyncService {
_log.info("syncDeviceTrashSnapshot prepare, album: ${album.id}/${album.name}");
final trashedPlatformAssets = await _nativeSyncApi.getTrashedAssetsForAlbum(album.id);
final trashedAssets = trashedPlatformAssets.toTrashedAssets(album.id);
- await _trashedLocalAssetRepository.applyTrashSnapshot(trashedAssets, album.id);
+ await _trashedLocalAssetRepository.applyTrashSnapshot(trashedAssets);
}
await _applyRemoteRestoreToLocal();
}
@@ -352,33 +345,22 @@ extension on Iterable {
extension on Iterable {
List toLocalAssets() {
- return map(
- (e) => LocalAsset(
- id: e.id,
- name: e.name,
- checksum: null,
- type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.other,
- createdAt: tryFromSecondsSinceEpoch(e.createdAt, isUtc: true) ?? DateTime.timestamp(),
- updatedAt: tryFromSecondsSinceEpoch(e.updatedAt, isUtc: true) ?? DateTime.timestamp(),
- width: e.width,
- height: e.height,
- durationInSeconds: e.durationInSeconds,
- orientation: e.orientation,
- isFavorite: e.isFavorite,
- ),
- ).toList();
+ return map((e) => e.toLocalAsset()).toList();
+ }
+
+ Iterable toTrashedAssets(String albumId) {
+ return map((e) => (albumId: albumId, asset: e.toLocalAsset()));
}
}
extension on PlatformAsset {
- TrashedAsset toTrashedAsset(String albumId) => TrashedAsset(
+ LocalAsset toLocalAsset() => LocalAsset(
id: id,
name: name,
checksum: null,
type: AssetType.values.elementAtOrNull(type) ?? AssetType.other,
- createdAt: tryFromSecondsSinceEpoch(createdAt) ?? DateTime.now(),
- updatedAt: tryFromSecondsSinceEpoch(updatedAt) ?? DateTime.now(),
- albumId: albumId,
+ createdAt: tryFromSecondsSinceEpoch(createdAt, isUtc: true) ?? DateTime.timestamp(),
+ updatedAt: tryFromSecondsSinceEpoch(createdAt, isUtc: true) ?? DateTime.timestamp(),
width: width,
height: height,
durationInSeconds: durationInSeconds,
@@ -387,8 +369,12 @@ extension on PlatformAsset {
);
}
-extension PlatformAssetsExtension on Iterable {
- Iterable toTrashedAssets(String albumId) {
- return map((e) => e.toTrashedAsset(albumId));
+extension SyncDeltaExtension on SyncDelta {
+ Iterable toTrashedAssets() {
+ return updates.map((e) {
+ final albums = assetAlbums.cast>();
+ final albumId = albums[e.id]!.cast().first;
+ return (albumId: albumId, asset: e.toLocalAsset());
+ });
}
}
diff --git a/mobile/lib/domain/services/trash_sync.service.dart b/mobile/lib/domain/services/trash_sync.service.dart
deleted file mode 100644
index b2c54df1e2..0000000000
--- a/mobile/lib/domain/services/trash_sync.service.dart
+++ /dev/null
@@ -1,53 +0,0 @@
-import 'package:immich_mobile/extensions/platform_extensions.dart';
-import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
-import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
-import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.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';
-
-class TrashSyncService {
- final AppSettingsService _appSettingsService;
- final DriftLocalAssetRepository _localAssetRepository;
- final DriftTrashedLocalAssetRepository _trashedLocalAssetRepository;
- final LocalFilesManagerRepository _localFilesManager;
- final StorageRepository _storageRepository;
- final Logger _logger = Logger('TrashService');
-
- TrashSyncService({
- required AppSettingsService appSettingsService,
- required DriftLocalAssetRepository localAssetRepository,
- required DriftTrashedLocalAssetRepository trashedLocalAssetRepository,
- required LocalFilesManagerRepository localFilesManager,
- required StorageRepository storageRepository,
- }) : _appSettingsService = appSettingsService,
- _localAssetRepository = localAssetRepository,
- _trashedLocalAssetRepository = trashedLocalAssetRepository,
- _localFilesManager = localFilesManager,
- _storageRepository = storageRepository;
-
- bool get isTrashSyncMode =>
- CurrentPlatform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid);
-
- Future handleRemoteTrashed(Iterable checksums) async {
- if (checksums.isEmpty) {
- return Future.value();
- } else {
- final localAssetsToTrash = await _localAssetRepository.getAssetsFromBackupAlbums(checksums);
- if (localAssetsToTrash.isNotEmpty) {
- final mediaUrls = await Future.wait(
- localAssetsToTrash.values
- .expand((e) => e)
- .map((localAsset) => _storageRepository.getAssetEntityForAsset(localAsset).then((e) => e?.getMediaUrl())),
- );
- _logger.info("Moving to trash ${mediaUrls.join(", ")} assets");
- final result = await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList());
- if (result) {
- await _trashedLocalAssetRepository.trashLocalAsset(localAssetsToTrash);
- }
- } else {
- _logger.info("No assets found in backup-enabled albums for assets: $checksums");
- }
- }
- }
-}
diff --git a/mobile/lib/infrastructure/entities/trashed_local_asset.entity.dart b/mobile/lib/infrastructure/entities/trashed_local_asset.entity.dart
index 10a4cefe66..3b58e85bc8 100644
--- a/mobile/lib/infrastructure/entities/trashed_local_asset.entity.dart
+++ b/mobile/lib/infrastructure/entities/trashed_local_asset.entity.dart
@@ -1,5 +1,5 @@
import 'package:drift/drift.dart';
-import 'package:immich_mobile/domain/models/asset/trashed_asset.model.dart';
+import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
@@ -23,10 +23,9 @@ class TrashedLocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntity
}
extension TrashedLocalAssetEntityDataDomainExtension on TrashedLocalAssetEntityData {
- TrashedAsset toDto() => TrashedAsset(
+ LocalAsset toLocalAsset() => LocalAsset(
id: id,
name: name,
- albumId: albumId,
checksum: checksum,
type: type,
createdAt: createdAt,
diff --git a/mobile/lib/infrastructure/repositories/trashed_local_asset.repository.dart b/mobile/lib/infrastructure/repositories/trashed_local_asset.repository.dart
index d653c58a6b..dcc3a03d98 100644
--- a/mobile/lib/infrastructure/repositories/trashed_local_asset.repository.dart
+++ b/mobile/lib/infrastructure/repositories/trashed_local_asset.repository.dart
@@ -2,13 +2,14 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
-import 'package:immich_mobile/domain/models/asset/trashed_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
+typedef TrashedAsset = ({String albumId, LocalAsset asset});
+
class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository {
final Drift _db;
@@ -30,12 +31,12 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository {
});
}
- Future> getAssetsToHash(Iterable albumIds) {
+ Future> getAssetsToHash(Iterable albumIds) {
final query = _db.trashedLocalAssetEntity.select()..where((r) => r.albumId.isIn(albumIds) & r.checksum.isNull());
- return query.map((row) => row.toDto()).get();
+ return query.map((row) => row.toLocalAsset()).get();
}
- Future> getToRestore() async {
+ Future> getToRestore() async {
final selectedAlbumIds = (_db.selectOnly(_db.localAlbumEntity)
..addColumns([_db.localAlbumEntity.id])
..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected)));
@@ -52,55 +53,54 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository {
))
.get();
- return rows.map((result) => result.readTable(_db.trashedLocalAssetEntity).toDto());
+ return rows.map((result) => result.readTable(_db.trashedLocalAssetEntity).toLocalAsset());
}
/// Applies resulted snapshot of trashed assets:
/// - upserts incoming rows
/// - deletes rows that are not present in the snapshot
- Future applyTrashSnapshot(Iterable assets, String albumId) async {
- if (assets.isEmpty) {
+ Future applyTrashSnapshot(Iterable trashedAssets) async {
+ if (trashedAssets.isEmpty) {
await _db.delete(_db.trashedLocalAssetEntity).go();
return;
}
- Map localChecksumById = await _getCachedChecksums(assets);
+ final assetIds = trashedAssets.map((e) => e.asset.id).toSet();
+ Map localChecksumById = await _getCachedChecksums(assetIds);
+
return _db.transaction(() async {
await _db.batch((batch) {
- for (final asset in assets) {
- final effectiveChecksum = localChecksumById[asset.id] ?? asset.checksum;
+ for (final item in trashedAssets) {
+ final effectiveChecksum = localChecksumById[item.asset.id] ?? item.asset.checksum;
final companion = TrashedLocalAssetEntityCompanion.insert(
- id: asset.id,
- albumId: albumId,
+ id: item.asset.id,
+ albumId: item.albumId,
checksum: effectiveChecksum == null ? const Value.absent() : Value(effectiveChecksum),
- name: asset.name,
- type: asset.type,
- createdAt: Value(asset.createdAt),
- updatedAt: Value(asset.updatedAt),
- width: Value(asset.width),
- height: Value(asset.height),
- durationInSeconds: Value(asset.durationInSeconds),
- isFavorite: Value(asset.isFavorite),
- orientation: Value(asset.orientation),
+ name: item.asset.name,
+ type: item.asset.type,
+ createdAt: Value(item.asset.createdAt),
+ updatedAt: Value(item.asset.updatedAt),
+ width: Value(item.asset.width),
+ height: Value(item.asset.height),
+ durationInSeconds: Value(item.asset.durationInSeconds),
+ isFavorite: Value(item.asset.isFavorite),
+ orientation: Value(item.asset.orientation),
);
batch.insert<$TrashedLocalAssetEntityTable, TrashedLocalAssetEntityData>(
_db.trashedLocalAssetEntity,
companion,
- onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(asset.updatedAt)),
+ onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(item.asset.updatedAt)),
);
}
});
- final keepIds = assets.map((asset) => asset.id);
-
- if (keepIds.length <= 32000) {
- await (_db.delete(_db.trashedLocalAssetEntity)..where((row) => row.id.isNotIn(keepIds))).go();
+ if (assetIds.length <= 32000) {
+ await (_db.delete(_db.trashedLocalAssetEntity)..where((row) => row.id.isNotIn(assetIds))).go();
} else {
- final keepIdsSet = keepIds.toSet();
final existingIds = await (_db.selectOnly(
_db.trashedLocalAssetEntity,
)..addColumns([_db.trashedLocalAssetEntity.id])).map((r) => r.read(_db.trashedLocalAssetEntity.id)!).get();
- final idToDelete = existingIds.where((id) => !keepIdsSet.contains(id));
+ final idToDelete = existingIds.where((id) => !assetIds.contains(id));
await _db.batch((batch) {
for (final id in idToDelete) {
batch.deleteWhere(_db.trashedLocalAssetEntity, (row) => row.id.equals(id));
@@ -110,33 +110,33 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository {
});
}
- Future saveTrashedAssets(Iterable assets) async {
- if (assets.isEmpty) {
+ Future applyDelta(Iterable trashedAssets) async {
+ if (trashedAssets.isEmpty) {
return;
}
-
- Map localChecksumById = await _getCachedChecksums(assets);
+ final assetIds = trashedAssets.map((e) => e.asset.id).toSet();
+ Map localChecksumById = await _getCachedChecksums(assetIds);
await _db.batch((batch) {
- for (final asset in assets) {
- final effectiveChecksum = localChecksumById[asset.id] ?? asset.checksum;
+ for (final item in trashedAssets) {
+ final effectiveChecksum = localChecksumById[item.asset.id] ?? item.asset.checksum;
final companion = TrashedLocalAssetEntityCompanion.insert(
- id: asset.id,
- albumId: asset.albumId,
- name: asset.name,
- type: asset.type,
+ id: item.asset.id,
+ albumId: item.albumId,
+ name: item.asset.name,
+ type: item.asset.type,
checksum: effectiveChecksum == null ? const Value.absent() : Value(effectiveChecksum),
- createdAt: Value(asset.createdAt),
- width: Value(asset.width),
- height: Value(asset.height),
- durationInSeconds: Value(asset.durationInSeconds),
- isFavorite: Value(asset.isFavorite),
- orientation: Value(asset.orientation),
+ createdAt: Value(item.asset.createdAt),
+ width: Value(item.asset.width),
+ height: Value(item.asset.height),
+ durationInSeconds: Value(item.asset.durationInSeconds),
+ isFavorite: Value(item.asset.isFavorite),
+ orientation: Value(item.asset.orientation),
);
batch.insert<$TrashedLocalAssetEntityTable, TrashedLocalAssetEntityData>(
_db.trashedLocalAssetEntity,
companion,
- onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(asset.updatedAt)),
+ onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(item.asset.updatedAt)),
);
}
});
@@ -171,7 +171,7 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository {
TrashedLocalAssetEntityCompanion(
id: Value(asset.id),
name: Value(asset.name),
- albumId: Value(entry.key),
+ // albumId: Value(entry.key),
checksum: asset.checksum == null ? const Value.absent() : Value(asset.checksum),
type: Value(asset.type),
width: Value(asset.width),
@@ -237,17 +237,21 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository {
}
//attempt to reuse existing checksums
- Future