1
0
mirror of https://github.com/immich-app/immich.git synced 2025-08-10 23:22:22 +02:00

feat(mobile): add album asset sync (#19522)

* feat(mobile): add album asset sync

* add SyncAlbumToAssetDeleteV1 to openapi-spec

* update delete queries to use where in statements

* clear remote album when clear remote data

* fix: bad merge

* fix: bad merge

* fix: _SyncAckV1 return type

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: wuzihao051119 <wuzihao051119@outlook.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
shenlong
2025-06-26 19:20:39 +05:30
committed by GitHub
parent 24a4cba953
commit ea3a14ed25
39 changed files with 3059 additions and 100 deletions

View File

@@ -1,13 +1,17 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart' as api show AssetVisibility;
import 'package:openapi/api.dart' hide AssetVisibility;
import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole;
import 'package:openapi/api.dart' hide AssetVisibility, AlbumUserRole;
class SyncStreamRepository extends DriftDatabaseRepository {
final Logger _logger = Logger('DriftSyncStreamRepository');
@@ -17,16 +21,10 @@ class SyncStreamRepository extends DriftDatabaseRepository {
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final user in data) {
batch.delete(
_db.userEntity,
UserEntityCompanion(id: Value(user.userId)),
);
}
});
await _db.userEntity
.deleteWhere((row) => row.id.isIn(data.map((e) => e.userId)));
} catch (error, stack) {
_logger.severe('Error while processing SyncUserDeleteV1', error, stack);
_logger.severe('Error: SyncUserDeleteV1', error, stack);
rethrow;
}
}
@@ -48,7 +46,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
}
});
} catch (error, stack) {
_logger.severe('Error while processing SyncUserV1', error, stack);
_logger.severe('Error: SyncUserV1', error, stack);
rethrow;
}
}
@@ -67,7 +65,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
}
});
} catch (e, s) {
_logger.severe('Error while processing SyncPartnerDeleteV1', e, s);
_logger.severe('Error: SyncPartnerDeleteV1', e, s);
rethrow;
}
}
@@ -90,67 +88,30 @@ class SyncStreamRepository extends DriftDatabaseRepository {
}
});
} catch (e, s) {
_logger.severe('Error while processing SyncPartnerV1', e, s);
_logger.severe('Error: SyncPartnerV1', e, s);
rethrow;
}
}
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
Future<void> deleteAssetsV1(
Iterable<SyncAssetDeleteV1> data, {
String debugLabel = 'user',
}) async {
try {
await _deleteAssetsV1(data);
await _db.remoteAssetEntity
.deleteWhere((row) => row.id.isIn(data.map((e) => e.assetId)));
} catch (e, s) {
_logger.severe('Error while processing deleteAssetsV1', e, s);
_logger.severe('Error: deleteAssetsV1 - $debugLabel', e, s);
rethrow;
}
}
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data) async {
Future<void> updateAssetsV1(
Iterable<SyncAssetV1> data, {
String debugLabel = 'user',
}) async {
try {
await _updateAssetsV1(data);
} catch (e, s) {
_logger.severe('Error while processing updateAssetsV1', e, s);
rethrow;
}
}
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
try {
await _deleteAssetsV1(data);
} catch (e, s) {
_logger.severe('Error while processing deletePartnerAssetsV1', e, s);
rethrow;
}
}
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data) async {
try {
await _updateAssetsV1(data);
} catch (e, s) {
_logger.severe('Error while processing updatePartnerAssetsV1', e, s);
rethrow;
}
}
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
try {
await _updateAssetExifV1(data);
} catch (e, s) {
_logger.severe('Error while processing updateAssetsExifV1', e, s);
rethrow;
}
}
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
try {
await _updateAssetExifV1(data);
} catch (e, s) {
_logger.severe('Error while processing updatePartnerAssetsExifV1', e, s);
rethrow;
}
}
Future<void> _updateAssetsV1(Iterable<SyncAssetV1> data) =>
_db.batch((batch) {
await _db.batch((batch) {
for (final asset in data) {
final companion = RemoteAssetEntityCompanion(
name: Value(asset.originalFileName),
@@ -175,19 +136,18 @@ class SyncStreamRepository extends DriftDatabaseRepository {
);
}
});
} catch (e, s) {
_logger.severe('Error: updateAssetsV1 - $debugLabel', e, s);
rethrow;
}
}
Future<void> _deleteAssetsV1(Iterable<SyncAssetDeleteV1> assets) =>
_db.batch((batch) {
for (final asset in assets) {
batch.delete(
_db.remoteAssetEntity,
RemoteAssetEntityCompanion(id: Value(asset.assetId)),
);
}
});
Future<void> _updateAssetExifV1(Iterable<SyncAssetExifV1> data) =>
_db.batch((batch) {
Future<void> updateAssetsExifV1(
Iterable<SyncAssetExifV1> data, {
String debugLabel = 'user',
}) async {
try {
await _db.batch((batch) {
for (final exif in data) {
final companion = RemoteExifEntityCompanion(
city: Value(exif.city),
@@ -219,6 +179,141 @@ class SyncStreamRepository extends DriftDatabaseRepository {
);
}
});
} catch (e, s) {
_logger.severe('Error: updateAssetsExifV1 - $debugLabel', e, s);
rethrow;
}
}
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async {
try {
await _db.remoteAlbumEntity
.deleteWhere((row) => row.id.isIn(data.map((e) => e.albumId)));
} catch (e, s) {
_logger.severe('Error: deleteAlbumsV1', e, s);
rethrow;
}
}
Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data) async {
try {
await _db.batch((batch) {
for (final album in data) {
final companion = RemoteAlbumEntityCompanion(
name: Value(album.name),
description: Value(album.description),
isActivityEnabled: Value(album.isActivityEnabled),
order: Value(album.order.toAlbumAssetOrder()),
thumbnailAssetId: Value(album.thumbnailAssetId),
ownerId: Value(album.ownerId),
createdAt: Value(album.createdAt),
updatedAt: Value(album.updatedAt),
);
batch.insert(
_db.remoteAlbumEntity,
companion.copyWith(id: Value(album.id)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (e, s) {
_logger.severe('Error: updateAlbumsV1', e, s);
rethrow;
}
}
Future<void> deleteAlbumUsersV1(Iterable<SyncAlbumUserDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final album in data) {
batch.delete(
_db.remoteAlbumUserEntity,
RemoteAlbumUserEntityCompanion(
albumId: Value(album.albumId),
userId: Value(album.userId),
),
);
}
});
} catch (e, s) {
_logger.severe('Error: deleteAlbumUsersV1', e, s);
rethrow;
}
}
Future<void> updateAlbumUsersV1(
Iterable<SyncAlbumUserV1> data, {
String debugLabel = 'user',
}) async {
try {
await _db.batch((batch) {
for (final album in data) {
final companion = RemoteAlbumUserEntityCompanion(
role: Value(album.role.toAlbumUserRole()),
);
batch.insert(
_db.remoteAlbumUserEntity,
companion.copyWith(
albumId: Value(album.albumId),
userId: Value(album.userId),
),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (e, s) {
_logger.severe('Error: updateAlbumUsersV1 - $debugLabel', e, s);
rethrow;
}
}
Future<void> deleteAlbumToAssetsV1(
Iterable<SyncAlbumToAssetDeleteV1> data,
) async {
try {
await _db.batch((batch) {
for (final album in data) {
batch.delete(
_db.remoteAlbumAssetEntity,
RemoteAlbumAssetEntityCompanion(
albumId: Value(album.albumId),
assetId: Value(album.assetId),
),
);
}
});
} catch (e, s) {
_logger.severe('Error: deleteAlbumToAssetsV1', e, s);
rethrow;
}
}
Future<void> updateAlbumToAssetsV1(
Iterable<SyncAlbumToAssetV1> data, {
String debugLabel = 'user',
}) async {
try {
await _db.batch((batch) {
for (final album in data) {
final companion = RemoteAlbumAssetEntityCompanion(
albumId: Value(album.albumId),
assetId: Value(album.assetId),
);
batch.insert(
_db.remoteAlbumAssetEntity,
companion,
onConflict: DoNothing(),
);
}
});
} catch (e, s) {
_logger.severe('Error: updateAlbumToAssetsV1 - $debugLabel', e, s);
rethrow;
}
}
}
extension on AssetTypeEnum {
@@ -231,6 +326,22 @@ extension on AssetTypeEnum {
};
}
extension on AssetOrder {
AlbumAssetOrder toAlbumAssetOrder() => switch (this) {
AssetOrder.asc => AlbumAssetOrder.asc,
AssetOrder.desc => AlbumAssetOrder.desc,
_ => throw Exception('Unknown AssetOrder value: $this'),
};
}
extension on api.AlbumUserRole {
AlbumUserRole toAlbumUserRole() => switch (this) {
api.AlbumUserRole.editor => AlbumUserRole.editor,
api.AlbumUserRole.viewer => AlbumUserRole.viewer,
_ => throw Exception('Unknown AlbumUserRole value: $this'),
};
}
extension on api.AssetVisibility {
AssetVisibility toAssetVisibility() => switch (this) {
api.AssetVisibility.timeline => AssetVisibility.timeline,