mirror of
https://github.com/immich-app/immich.git
synced 2025-06-03 13:44:16 -04:00
move empty checks inside repo
This commit is contained in:
parent
a7032bb3d9
commit
65b55e64f6
@ -2,24 +2,17 @@ import 'package:immich_mobile/domain/models/asset/asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
||||
|
||||
abstract interface class IAlbumMediaRepository {
|
||||
Future<List<LocalAlbum>> getAll({
|
||||
bool withModifiedTime = false,
|
||||
bool withAssetCount = false,
|
||||
bool withAssetTitle = false,
|
||||
});
|
||||
Future<List<LocalAlbum>> getAll();
|
||||
|
||||
Future<List<LocalAsset>> getAssetsForAlbum(
|
||||
String albumId, {
|
||||
bool withModifiedTime = false,
|
||||
bool withAssetTitle = true,
|
||||
DateTimeFilter? updateTimeCond,
|
||||
});
|
||||
|
||||
Future<LocalAlbum> refresh(
|
||||
String albumId, {
|
||||
bool withModifiedTime = false,
|
||||
bool withAssetCount = false,
|
||||
bool withAssetTitle = false,
|
||||
bool withModifiedTime = true,
|
||||
bool withAssetCount = true,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -11,13 +11,13 @@ import 'package:immich_mobile/utils/diff.dart';
|
||||
import 'package:immich_mobile/utils/nullable_value.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class SyncService {
|
||||
class DeviceSyncService {
|
||||
final IAlbumMediaRepository _albumMediaRepository;
|
||||
final ILocalAlbumRepository _localAlbumRepository;
|
||||
final ILocalAssetRepository _localAssetRepository;
|
||||
final Logger _log = Logger("SyncService");
|
||||
|
||||
SyncService({
|
||||
DeviceSyncService({
|
||||
required IAlbumMediaRepository albumMediaRepository,
|
||||
required ILocalAlbumRepository localAlbumRepository,
|
||||
required ILocalAssetRepository localAssetRepository,
|
||||
@ -25,46 +25,41 @@ class SyncService {
|
||||
_localAlbumRepository = localAlbumRepository,
|
||||
_localAssetRepository = localAssetRepository;
|
||||
|
||||
Future<bool> syncLocalAlbums() async {
|
||||
Future<void> syncAlbums() async {
|
||||
try {
|
||||
final Stopwatch stopwatch = Stopwatch()..start();
|
||||
// The deviceAlbums will not have the updatedAt field
|
||||
// and the assetCount will be 0. They are refreshed later
|
||||
// after the comparison
|
||||
// after the comparison. The orderby in the filter sorts the assets
|
||||
// and not the albums.
|
||||
final deviceAlbums =
|
||||
(await _albumMediaRepository.getAll()).sortedBy((a) => a.id);
|
||||
final dbAlbums =
|
||||
await _localAlbumRepository.getAll(sortBy: SortLocalAlbumsBy.id);
|
||||
|
||||
final hasChange = await diffSortedLists(
|
||||
await diffSortedLists(
|
||||
dbAlbums,
|
||||
deviceAlbums,
|
||||
compare: (a, b) => a.id.compareTo(b.id),
|
||||
both: syncLocalAlbum,
|
||||
onlyFirst: removeLocalAlbum,
|
||||
onlySecond: addLocalAlbum,
|
||||
both: updateAlbum,
|
||||
onlyFirst: removeAlbum,
|
||||
onlySecond: addAlbum,
|
||||
);
|
||||
|
||||
stopwatch.stop();
|
||||
_log.info("Full device sync took - ${stopwatch.elapsedMilliseconds}ms");
|
||||
return hasChange;
|
||||
} catch (e, s) {
|
||||
_log.severe("Error performing full device sync", e, s);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> addLocalAlbum(LocalAlbum newAlbum) async {
|
||||
Future<void> addAlbum(LocalAlbum newAlbum) async {
|
||||
try {
|
||||
_log.info("Adding device album ${newAlbum.name}");
|
||||
final deviceAlbum = await _albumMediaRepository.refresh(
|
||||
newAlbum.id,
|
||||
withModifiedTime: true,
|
||||
withAssetCount: true,
|
||||
);
|
||||
final deviceAlbum = await _albumMediaRepository.refresh(newAlbum.id);
|
||||
|
||||
final assets = deviceAlbum.assetCount > 0
|
||||
? (await _albumMediaRepository.getAssetsForAlbum(deviceAlbum.id))
|
||||
? await _albumMediaRepository.getAssetsForAlbum(deviceAlbum.id)
|
||||
: <LocalAsset>[];
|
||||
|
||||
final album = deviceAlbum.copyWith(
|
||||
@ -73,13 +68,13 @@ class SyncService {
|
||||
);
|
||||
|
||||
await _localAlbumRepository.insert(album, assets);
|
||||
_log.info("Successfully added device album ${album.name}");
|
||||
_log.fine("Successfully added device album ${album.name}");
|
||||
} catch (e, s) {
|
||||
_log.warning("Error while adding device album", e, s);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeLocalAlbum(LocalAlbum a) async {
|
||||
Future<void> removeAlbum(LocalAlbum a) async {
|
||||
_log.info("Removing device album ${a.name}");
|
||||
try {
|
||||
// Asset deletion is handled in the repository
|
||||
@ -90,39 +85,26 @@ class SyncService {
|
||||
}
|
||||
|
||||
// The deviceAlbum is ignored since we are going to refresh it anyways
|
||||
FutureOr<bool> syncLocalAlbum(LocalAlbum dbAlbum, LocalAlbum _) async {
|
||||
FutureOr<bool> updateAlbum(LocalAlbum dbAlbum, LocalAlbum _) async {
|
||||
try {
|
||||
_log.info("Syncing device album ${dbAlbum.name}");
|
||||
|
||||
final deviceAlbum = await _albumMediaRepository.refresh(
|
||||
dbAlbum.id,
|
||||
withModifiedTime: true,
|
||||
withAssetCount: true,
|
||||
);
|
||||
final deviceAlbum = await _albumMediaRepository.refresh(dbAlbum.id);
|
||||
|
||||
// Early return if album hasn't changed
|
||||
if (deviceAlbum.updatedAt.isAtSameMomentAs(dbAlbum.updatedAt) &&
|
||||
deviceAlbum.assetCount == dbAlbum.assetCount) {
|
||||
_log.info(
|
||||
_log.fine(
|
||||
"Device album ${dbAlbum.name} has not changed. Skipping sync.",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip empty albums that don't need syncing
|
||||
if (deviceAlbum.assetCount == 0 && dbAlbum.assetCount == 0) {
|
||||
await _localAlbumRepository.update(
|
||||
deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection),
|
||||
);
|
||||
_log.info("Album ${dbAlbum.name} is empty. Only metadata updated.");
|
||||
return true;
|
||||
}
|
||||
_log.fine("Device album ${dbAlbum.name} has changed. Syncing...");
|
||||
|
||||
_log.info("Device album ${dbAlbum.name} has changed. Syncing...");
|
||||
|
||||
// Faster path - only assets added
|
||||
if (await tryFastSync(dbAlbum, deviceAlbum)) {
|
||||
_log.info("Fast synced device album ${dbAlbum.name}");
|
||||
// Faster path - only new assets added
|
||||
if (await checkAddition(dbAlbum, deviceAlbum)) {
|
||||
_log.fine("Fast synced device album ${dbAlbum.name}");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -137,19 +119,15 @@ class SyncService {
|
||||
@visibleForTesting
|
||||
// The [deviceAlbum] is expected to be refreshed before calling this method
|
||||
// with modified time and asset count
|
||||
Future<bool> tryFastSync(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async {
|
||||
Future<bool> checkAddition(
|
||||
LocalAlbum dbAlbum,
|
||||
LocalAlbum deviceAlbum,
|
||||
) async {
|
||||
try {
|
||||
_log.info("Fast syncing device album ${dbAlbum.name}");
|
||||
if (!deviceAlbum.updatedAt.isAfter(dbAlbum.updatedAt)) {
|
||||
_log.info(
|
||||
"Local album ${deviceAlbum.name} has modifications. Proceeding to full sync",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
_log.fine("Fast syncing device album ${dbAlbum.name}");
|
||||
// Assets has been modified
|
||||
if (deviceAlbum.assetCount <= dbAlbum.assetCount) {
|
||||
_log.info("Local album has modifications. Proceeding to full sync");
|
||||
_log.fine("Local album has modifications. Proceeding to full sync");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -164,15 +142,15 @@ class SyncService {
|
||||
|
||||
// Early return if no new assets were found
|
||||
if (newAssets.isEmpty) {
|
||||
_log.info(
|
||||
"No new assets found despite album changes. Proceeding to full sync for ${dbAlbum.name}",
|
||||
_log.fine(
|
||||
"No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check whether there is only addition or if there has been deletions
|
||||
if (deviceAlbum.assetCount != dbAlbum.assetCount + newAssets.length) {
|
||||
_log.info("Local album has modifications. Proceeding to full sync");
|
||||
_log.fine("Local album has modifications. Proceeding to full sync");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -190,7 +168,7 @@ class SyncService {
|
||||
}
|
||||
}
|
||||
|
||||
await _handleUpdate(
|
||||
await _updateAlbum(
|
||||
deviceAlbum.copyWith(
|
||||
thumbnailId: NullableValue.valueOrEmpty(thumbnailId),
|
||||
backupSelection: dbAlbum.backupSelection,
|
||||
@ -221,7 +199,7 @@ class SyncService {
|
||||
_log.fine(
|
||||
"Device album ${deviceAlbum.name} is empty. Removing assets from DB.",
|
||||
);
|
||||
await _handleUpdate(
|
||||
await _updateAlbum(
|
||||
deviceAlbum.copyWith(
|
||||
// Clear thumbnail for empty album
|
||||
thumbnailId: const NullableValue.empty(),
|
||||
@ -246,37 +224,38 @@ class SyncService {
|
||||
_log.fine(
|
||||
"Device album ${deviceAlbum.name} is empty. Adding assets to DB.",
|
||||
);
|
||||
await _handleUpdate(updatedDeviceAlbum, assetsToUpsert: assetsInDevice);
|
||||
await _updateAlbum(updatedDeviceAlbum, assetsToUpsert: assetsInDevice);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sort assets by localId for the diffSortedLists function
|
||||
assetsInDb.sort((a, b) => a.localId.compareTo(b.localId));
|
||||
assert(assetsInDb.isSortedBy((a) => a.localId));
|
||||
assetsInDevice.sort((a, b) => a.localId.compareTo(b.localId));
|
||||
|
||||
final assetsToAddOrUpdate = <LocalAsset>[];
|
||||
final assetIdsToDelete = <String>[];
|
||||
final assetsToUpsert = <LocalAsset>[];
|
||||
final assetsToDelete = <String>[];
|
||||
|
||||
diffSortedListsSync(
|
||||
assetsInDb,
|
||||
assetsInDevice,
|
||||
compare: (a, b) => a.localId.compareTo(b.localId),
|
||||
both: (dbAsset, deviceAsset) {
|
||||
// Custom comparison to check if the asset has been modified without
|
||||
// comparing the checksum
|
||||
if (!_assetsEqual(dbAsset, deviceAsset)) {
|
||||
assetsToAddOrUpdate.add(deviceAsset);
|
||||
assetsToUpsert.add(deviceAsset);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onlyFirst: (dbAsset) => assetIdsToDelete.add(dbAsset.localId),
|
||||
onlySecond: (deviceAsset) => assetsToAddOrUpdate.add(deviceAsset),
|
||||
onlyFirst: (dbAsset) => assetsToDelete.add(dbAsset.localId),
|
||||
onlySecond: (deviceAsset) => assetsToUpsert.add(deviceAsset),
|
||||
);
|
||||
|
||||
_log.info(
|
||||
"Syncing ${deviceAlbum.name}. ${assetsToAddOrUpdate.length} assets to add/update and ${assetIdsToDelete.length} assets to delete",
|
||||
_log.fine(
|
||||
"Syncing ${deviceAlbum.name}. ${assetsToUpsert.length} assets to add/update and ${assetsToDelete.length} assets to delete",
|
||||
);
|
||||
|
||||
if (assetsToAddOrUpdate.isEmpty && assetIdsToDelete.isEmpty) {
|
||||
if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) {
|
||||
_log.fine(
|
||||
"No asset changes detected in album ${deviceAlbum.name}. Updating metadata.",
|
||||
);
|
||||
@ -284,40 +263,30 @@ class SyncService {
|
||||
return true;
|
||||
}
|
||||
|
||||
await _handleUpdate(
|
||||
await _updateAlbum(
|
||||
updatedDeviceAlbum,
|
||||
assetsToUpsert: assetsToAddOrUpdate,
|
||||
assetIdsToDelete: assetIdsToDelete,
|
||||
assetsToUpsert: assetsToUpsert,
|
||||
assetIdsToDelete: assetsToDelete,
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (e, s) {
|
||||
_log.warning("Error on full syncing local album: ${dbAlbum.name}", e, s);
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> _handleUpdate(
|
||||
Future<void> _updateAlbum(
|
||||
LocalAlbum album, {
|
||||
Iterable<LocalAsset>? assetsToUpsert,
|
||||
Iterable<String>? assetIdsToDelete,
|
||||
Iterable<LocalAsset> assetsToUpsert = const [],
|
||||
Iterable<String> assetIdsToDelete = const [],
|
||||
}) =>
|
||||
_localAlbumRepository.transaction(() async {
|
||||
if (assetsToUpsert != null && assetsToUpsert.isNotEmpty) {
|
||||
await _localAlbumRepository.addAssets(album.id, assetsToUpsert);
|
||||
}
|
||||
|
||||
await _localAlbumRepository.addAssets(album.id, assetsToUpsert);
|
||||
await _localAlbumRepository.update(album);
|
||||
|
||||
if (assetIdsToDelete != null && assetIdsToDelete.isNotEmpty) {
|
||||
await _localAlbumRepository.removeAssets(
|
||||
album.id,
|
||||
assetIdsToDelete,
|
||||
);
|
||||
}
|
||||
await _localAlbumRepository.removeAssets(album.id, assetIdsToDelete);
|
||||
});
|
||||
|
||||
// Helper method to check if assets are equal without relying on the checksum
|
||||
bool _assetsEqual(LocalAsset a, LocalAsset b) {
|
||||
return a.updatedAt.isAtSameMomentAs(b.updatedAt) &&
|
||||
a.createdAt.isAtSameMomentAs(b.createdAt) &&
|
@ -29,7 +29,7 @@ class BackgroundSyncManager {
|
||||
}
|
||||
|
||||
_deviceAlbumSyncTask = runInIsolateGentle(
|
||||
computation: (ref) => ref.read(syncServiceProvider).syncLocalAlbums(),
|
||||
computation: (ref) => ref.read(deviceSyncServiceProvider).syncAlbums(),
|
||||
);
|
||||
return _deviceAlbumSyncTask!.whenComplete(() {
|
||||
_deviceAlbumSyncTask = null;
|
||||
|
@ -36,41 +36,24 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
|
||||
);
|
||||
|
||||
@override
|
||||
Future<List<LocalAlbum>> getAll({
|
||||
withModifiedTime = false,
|
||||
withAssetCount = false,
|
||||
withAssetTitle = false,
|
||||
}) async {
|
||||
final filter = withModifiedTime || withAssetTitle
|
||||
? _getAlbumFilter(
|
||||
withAssetTitle: withAssetTitle,
|
||||
withModifiedTime: withModifiedTime,
|
||||
)
|
||||
|
||||
// Use an AdvancedCustomFilter to get all albums faster
|
||||
: AdvancedCustomFilter(
|
||||
orderBy: [OrderByItem.asc(CustomColumns.base.id)],
|
||||
);
|
||||
|
||||
final entities = await PhotoManager.getAssetPathList(
|
||||
hasAll: true,
|
||||
filterOption: filter,
|
||||
Future<List<LocalAlbum>> getAll() {
|
||||
final filter = AdvancedCustomFilter(
|
||||
orderBy: [OrderByItem.asc(CustomColumns.base.id)],
|
||||
);
|
||||
return entities.toDtoList(withAssetCount: withAssetCount);
|
||||
|
||||
return PhotoManager.getAssetPathList(hasAll: true, filterOption: filter)
|
||||
.then((e) => e.toDtoList());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<asset.LocalAsset>> getAssetsForAlbum(
|
||||
String albumId, {
|
||||
withModifiedTime = false,
|
||||
withAssetTitle = true,
|
||||
DateTimeFilter? updateTimeCond,
|
||||
}) async {
|
||||
final assetPathEntity = await AssetPathEntity.obtainPathFromProperties(
|
||||
id: albumId,
|
||||
optionGroup: _getAlbumFilter(
|
||||
withAssetTitle: withAssetTitle,
|
||||
withModifiedTime: withModifiedTime,
|
||||
withAssetTitle: true,
|
||||
updateTimeCond: updateTimeCond,
|
||||
),
|
||||
);
|
||||
@ -91,38 +74,31 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
|
||||
@override
|
||||
Future<LocalAlbum> refresh(
|
||||
String albumId, {
|
||||
withModifiedTime = false,
|
||||
withAssetCount = false,
|
||||
withAssetTitle = false,
|
||||
}) async =>
|
||||
(await AssetPathEntity.obtainPathFromProperties(
|
||||
bool withModifiedTime = true,
|
||||
bool withAssetCount = true,
|
||||
}) =>
|
||||
AssetPathEntity.obtainPathFromProperties(
|
||||
id: albumId,
|
||||
optionGroup: _getAlbumFilter(
|
||||
withAssetTitle: withAssetTitle,
|
||||
withModifiedTime: withModifiedTime,
|
||||
),
|
||||
))
|
||||
.toDto(withAssetCount: withAssetCount);
|
||||
optionGroup: _getAlbumFilter(withModifiedTime: withModifiedTime),
|
||||
).then((a) => a.toDto(withAssetCount: withAssetCount));
|
||||
}
|
||||
|
||||
extension on AssetEntity {
|
||||
Future<asset.LocalAsset> toDto() async {
|
||||
return asset.LocalAsset(
|
||||
localId: id,
|
||||
name: title ?? await titleAsync,
|
||||
type: switch (type) {
|
||||
AssetType.other => asset.AssetType.other,
|
||||
AssetType.image => asset.AssetType.image,
|
||||
AssetType.video => asset.AssetType.video,
|
||||
AssetType.audio => asset.AssetType.audio,
|
||||
},
|
||||
createdAt: createDateTime,
|
||||
updatedAt: modifiedDateTime,
|
||||
width: width,
|
||||
height: height,
|
||||
durationInSeconds: duration,
|
||||
);
|
||||
}
|
||||
Future<asset.LocalAsset> toDto() async => asset.LocalAsset(
|
||||
localId: id,
|
||||
name: title ?? await titleAsync,
|
||||
type: switch (type) {
|
||||
AssetType.other => asset.AssetType.other,
|
||||
AssetType.image => asset.AssetType.image,
|
||||
AssetType.video => asset.AssetType.video,
|
||||
AssetType.audio => asset.AssetType.audio,
|
||||
},
|
||||
createdAt: createDateTime,
|
||||
updatedAt: modifiedDateTime,
|
||||
width: width,
|
||||
height: height,
|
||||
durationInSeconds: duration,
|
||||
);
|
||||
}
|
||||
|
||||
extension on List<AssetEntity> {
|
||||
|
@ -52,9 +52,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
final assetsToDelete = _platform.isIOS
|
||||
? await _getUniqueAssetsInAlbum(albumId)
|
||||
: await _getAssetsIdsInAlbum(albumId);
|
||||
if (assetsToDelete.isNotEmpty) {
|
||||
await _deleteAssets(assetsToDelete);
|
||||
}
|
||||
await _deleteAssets(assetsToDelete);
|
||||
|
||||
// All the other assets that are still associated will be unlinked automatically on-cascade
|
||||
await _db.managers.localAlbumEntity
|
||||
@ -65,35 +63,36 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
@override
|
||||
Future<void> insert(LocalAlbum localAlbum, Iterable<LocalAsset> assets) =>
|
||||
transaction(() async {
|
||||
if (localAlbum.assetCount > 0) {
|
||||
await _upsertAssets(assets);
|
||||
}
|
||||
await _upsertAssets(assets);
|
||||
// Needs to be after asset upsert to link the thumbnail
|
||||
await _upsertAlbum(localAlbum);
|
||||
|
||||
if (localAlbum.assetCount > 0) {
|
||||
await _linkAssetsToAlbum(localAlbum.id, assets);
|
||||
}
|
||||
await _linkAssetsToAlbum(localAlbum.id, assets);
|
||||
});
|
||||
|
||||
@override
|
||||
Future<void> addAssets(String albumId, Iterable<LocalAsset> assets) =>
|
||||
transaction(() async {
|
||||
await _upsertAssets(assets);
|
||||
await _linkAssetsToAlbum(albumId, assets);
|
||||
});
|
||||
Future<void> addAssets(String albumId, Iterable<LocalAsset> assets) {
|
||||
if (assets.isEmpty) {
|
||||
return Future.value();
|
||||
}
|
||||
return transaction(() async {
|
||||
await _upsertAssets(assets);
|
||||
await _linkAssetsToAlbum(albumId, assets);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeAssets(String albumId, Iterable<String> assetIds) async {
|
||||
if (assetIds.isEmpty) {
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
if (_platform.isAndroid) {
|
||||
await _deleteAssets(assetIds);
|
||||
return;
|
||||
return _deleteAssets(assetIds);
|
||||
}
|
||||
|
||||
final uniqueAssets = await _getUniqueAssetsInAlbum(albumId);
|
||||
if (uniqueAssets.isEmpty) {
|
||||
await _unlinkAssetsFromAlbum(albumId, assetIds);
|
||||
return;
|
||||
return _unlinkAssetsFromAlbum(albumId, assetIds);
|
||||
}
|
||||
// Delete unique assets and unlink others
|
||||
final uniqueSet = uniqueAssets.toSet();
|
||||
@ -106,8 +105,10 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
assetsToUnLink.add(assetId);
|
||||
}
|
||||
}
|
||||
await _unlinkAssetsFromAlbum(albumId, assetsToUnLink);
|
||||
await _deleteAssets(assetsToDelete);
|
||||
return transaction(() async {
|
||||
await _unlinkAssetsFromAlbum(albumId, assetsToUnLink);
|
||||
await _deleteAssets(assetsToDelete);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@ -123,7 +124,9 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
.equalsExp(_db.localAssetEntity.localId),
|
||||
),
|
||||
],
|
||||
)..where(_db.localAlbumAssetEntity.albumId.equals(albumId));
|
||||
)
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||
..orderBy([OrderingTerm.desc(_db.localAssetEntity.localId)]);
|
||||
return query
|
||||
.map((row) => row.readTable(_db.localAssetEntity).toDto())
|
||||
.get();
|
||||
@ -147,30 +150,40 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
Future<void> _linkAssetsToAlbum(
|
||||
String albumId,
|
||||
Iterable<LocalAsset> assets,
|
||||
) =>
|
||||
_db.batch(
|
||||
(batch) => batch.insertAll(
|
||||
_db.localAlbumAssetEntity,
|
||||
assets.map(
|
||||
(a) => LocalAlbumAssetEntityCompanion.insert(
|
||||
assetId: a.localId,
|
||||
albumId: albumId,
|
||||
),
|
||||
) {
|
||||
if (assets.isEmpty) {
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
return _db.batch(
|
||||
(batch) => batch.insertAll(
|
||||
_db.localAlbumAssetEntity,
|
||||
assets.map(
|
||||
(a) => LocalAlbumAssetEntityCompanion.insert(
|
||||
assetId: a.localId,
|
||||
albumId: albumId,
|
||||
),
|
||||
mode: InsertMode.insertOrIgnore,
|
||||
),
|
||||
);
|
||||
mode: InsertMode.insertOrIgnore,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _unlinkAssetsFromAlbum(
|
||||
String albumId,
|
||||
Iterable<String> assetIds,
|
||||
) =>
|
||||
_db.batch(
|
||||
(batch) => batch.deleteWhere(
|
||||
_db.localAlbumAssetEntity,
|
||||
(f) => f.assetId.isIn(assetIds) & f.albumId.equals(albumId),
|
||||
),
|
||||
);
|
||||
) {
|
||||
if (assetIds.isEmpty) {
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
return _db.batch(
|
||||
(batch) => batch.deleteWhere(
|
||||
_db.localAlbumAssetEntity,
|
||||
(f) => f.assetId.isIn(assetIds) & f.albumId.equals(albumId),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<String>> _getAssetsIdsInAlbum(String albumId) {
|
||||
final query = _db.localAlbumAssetEntity.select()
|
||||
@ -193,30 +206,41 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
||||
return query.map((row) => row.read(assetId)!).get();
|
||||
}
|
||||
|
||||
Future<void> _upsertAssets(Iterable<LocalAsset> localAssets) =>
|
||||
_db.batch((batch) async {
|
||||
batch.insertAllOnConflictUpdate(
|
||||
_db.localAssetEntity,
|
||||
localAssets.map(
|
||||
(a) => LocalAssetEntityCompanion.insert(
|
||||
name: a.name,
|
||||
type: a.type,
|
||||
createdAt: Value(a.createdAt),
|
||||
updatedAt: Value(a.updatedAt),
|
||||
width: Value.absentIfNull(a.width),
|
||||
height: Value.absentIfNull(a.height),
|
||||
durationInSeconds: Value.absentIfNull(a.durationInSeconds),
|
||||
localId: a.localId,
|
||||
checksum: Value.absentIfNull(a.checksum),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
Future<void> _upsertAssets(Iterable<LocalAsset> localAssets) {
|
||||
if (localAssets.isEmpty) {
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
Future<void> _deleteAssets(Iterable<String> ids) => _db.batch(
|
||||
(batch) => batch.deleteWhere(
|
||||
_db.localAssetEntity,
|
||||
(f) => f.localId.isIn(ids),
|
||||
return _db.batch((batch) async {
|
||||
batch.insertAllOnConflictUpdate(
|
||||
_db.localAssetEntity,
|
||||
localAssets.map(
|
||||
(a) => LocalAssetEntityCompanion.insert(
|
||||
name: a.name,
|
||||
type: a.type,
|
||||
createdAt: Value(a.createdAt),
|
||||
updatedAt: Value(a.updatedAt),
|
||||
width: Value.absentIfNull(a.width),
|
||||
height: Value.absentIfNull(a.height),
|
||||
durationInSeconds: Value.absentIfNull(a.durationInSeconds),
|
||||
localId: a.localId,
|
||||
checksum: Value.absentIfNull(a.checksum),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _deleteAssets(Iterable<String> ids) {
|
||||
if (ids.isEmpty) {
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
return _db.batch(
|
||||
(batch) => batch.deleteWhere(
|
||||
_db.localAssetEntity,
|
||||
(f) => f.localId.isIn(ids),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/services/sync.service.dart';
|
||||
import 'package:immich_mobile/domain/services/device_sync.service.dart';
|
||||
import 'package:immich_mobile/domain/services/sync_stream.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
|
||||
@ -9,8 +9,8 @@ import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
|
||||
final syncServiceProvider = Provider(
|
||||
(ref) => SyncService(
|
||||
final deviceSyncServiceProvider = Provider(
|
||||
(ref) => DeviceSyncService(
|
||||
albumMediaRepository: ref.watch(albumMediaRepositoryProvider),
|
||||
localAlbumRepository: ref.watch(localAlbumRepository),
|
||||
localAssetRepository: ref.watch(localAssetProvider),
|
||||
|
File diff suppressed because it is too large
Load Diff
10
mobile/test/fixtures/local_album.stub.dart
vendored
10
mobile/test/fixtures/local_album.stub.dart
vendored
@ -22,4 +22,14 @@ abstract final class LocalAlbumStub {
|
||||
backupSelection: BackupSelection.selected,
|
||||
isAll: true,
|
||||
);
|
||||
|
||||
static LocalAlbum get album3 => LocalAlbum(
|
||||
id: "album3",
|
||||
name: "Album 3",
|
||||
updatedAt: DateTime(2020),
|
||||
assetCount: 20,
|
||||
thumbnailId: "123",
|
||||
backupSelection: BackupSelection.excluded,
|
||||
isAll: false,
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user