handle cloud id migration
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:immich_mobile/domain/utils/migrate_cloud_ids.dart';
|
||||
import 'package:immich_mobile/domain/utils/sync_linked_album.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/sync.provider.dart';
|
||||
import 'package:immich_mobile/utils/isolate.dart';
|
||||
@@ -21,8 +22,13 @@ class BackgroundSyncManager {
|
||||
final SyncCallback? onHashingComplete;
|
||||
final SyncErrorCallback? onHashingError;
|
||||
|
||||
final SyncCallback? onCloudIdSyncStart;
|
||||
final SyncCallback? onCloudIdSyncComplete;
|
||||
final SyncErrorCallback? onCloudIdSyncError;
|
||||
|
||||
Cancelable<void>? _syncTask;
|
||||
Cancelable<void>? _syncWebsocketTask;
|
||||
Cancelable<void>? _cloudIdSyncTask;
|
||||
Cancelable<void>? _deviceAlbumSyncTask;
|
||||
Cancelable<void>? _linkedAlbumSyncTask;
|
||||
Cancelable<void>? _hashTask;
|
||||
@@ -37,6 +43,9 @@ class BackgroundSyncManager {
|
||||
this.onHashingStart,
|
||||
this.onHashingComplete,
|
||||
this.onHashingError,
|
||||
this.onCloudIdSyncStart,
|
||||
this.onCloudIdSyncComplete,
|
||||
this.onCloudIdSyncError,
|
||||
});
|
||||
|
||||
Future<void> cancel() async {
|
||||
@@ -54,6 +63,11 @@ class BackgroundSyncManager {
|
||||
_syncWebsocketTask?.cancel();
|
||||
_syncWebsocketTask = null;
|
||||
|
||||
if (_cloudIdSyncTask != null) {
|
||||
futures.add(_cloudIdSyncTask!.future);
|
||||
}
|
||||
_cloudIdSyncTask?.cancel();
|
||||
|
||||
if (_linkedAlbumSyncTask != null) {
|
||||
futures.add(_linkedAlbumSyncTask!.future);
|
||||
}
|
||||
@@ -173,6 +187,24 @@ class BackgroundSyncManager {
|
||||
return _linkedAlbumSyncTask!.whenComplete(() {
|
||||
_linkedAlbumSyncTask = null;
|
||||
});
|
||||
|
||||
Future<void> syncCloudIds() {
|
||||
if (_cloudIdSyncTask != null) {
|
||||
return _cloudIdSyncTask!.future;
|
||||
}
|
||||
|
||||
onCloudIdSyncStart?.call();
|
||||
|
||||
_cloudIdSyncTask = runInIsolateGentle(computation: migrateCloudIds);
|
||||
return _cloudIdSyncTask!
|
||||
.whenComplete(() {
|
||||
onCloudIdSyncComplete?.call();
|
||||
_cloudIdSyncTask = null;
|
||||
})
|
||||
.catchError((error) {
|
||||
onCloudIdSyncError?.call(error.toString());
|
||||
_cloudIdSyncTask = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/asset_metadata.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/sync.provider.dart';
|
||||
// ignore: import_rule_openapi
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
Future<void> migrateCloudIds(ProviderContainer ref) async {
|
||||
final db = ref.read(driftProvider);
|
||||
// Populate cloud IDs for local assets that don't have one yet
|
||||
await _populateCloudIds(db);
|
||||
|
||||
// Wait for remote sync to complete, so we have up-to-date asset metadata entries
|
||||
await ref.read(syncStreamServiceProvider).sync();
|
||||
|
||||
// Fetch the mapping for backed up assets that have a cloud ID locally but do not have a cloud ID on the server
|
||||
final mappingsToUpdate = await _fetchCloudIdMappings(db);
|
||||
final assetApi = ref.read(apiServiceProvider).assetsApi;
|
||||
for (final mapping in mappingsToUpdate) {
|
||||
final mobileMeta = AssetMetadataUpsertItemDto(
|
||||
key: AssetMetadataKey.mobileApp,
|
||||
value: {"iCloudId": mapping.cloudId},
|
||||
);
|
||||
await assetApi.updateAssetMetadata(mapping.assetId, AssetMetadataUpsertDto(items: [mobileMeta]));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _populateCloudIds(Drift drift) async {
|
||||
final query = drift.localAssetEntity.selectOnly()
|
||||
..addColumns([drift.localAssetEntity.id])
|
||||
..where(drift.localAssetEntity.cloudId.isNull());
|
||||
final ids = await query.map((row) => row.read(drift.localAssetEntity.id)!).get();
|
||||
final cloudMapping = await NativeSyncApi().getCloudIdForAssetIds(ids);
|
||||
await DriftLocalAlbumRepository(drift).updateCloudMapping(cloudMapping);
|
||||
}
|
||||
|
||||
typedef _CloudIdMapping = ({String assetId, String cloudId});
|
||||
|
||||
Future<List<_CloudIdMapping>> _fetchCloudIdMappings(Drift drift) async {
|
||||
final query =
|
||||
drift.remoteAssetEntity.selectOnly().join([
|
||||
leftOuterJoin(
|
||||
drift.localAssetEntity,
|
||||
drift.localAssetEntity.checksum.equalsExp(drift.remoteAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
drift.remoteAssetMetadataEntity,
|
||||
drift.remoteAssetMetadataEntity.assetId.equalsExp(drift.remoteAssetEntity.id) &
|
||||
drift.remoteAssetMetadataEntity.key.equalsValue(RemoteAssetMetadataKey.mobileApp),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..addColumns([drift.remoteAssetEntity.id, drift.localAssetEntity.cloudId])
|
||||
..where(
|
||||
drift.localAssetEntity.id.isNotNull() &
|
||||
drift.localAssetEntity.cloudId.isNotNull() &
|
||||
drift.remoteAssetMetadataEntity.cloudId.isNull(),
|
||||
);
|
||||
return query
|
||||
.map(
|
||||
(row) => (assetId: row.read(drift.remoteAssetEntity.id)!, cloudId: row.read(drift.localAssetEntity.cloudId)!),
|
||||
)
|
||||
.get();
|
||||
}
|
||||
Reference in New Issue
Block a user