mirror of
https://github.com/immich-app/immich.git
synced 2025-12-20 09:15:35 +03:00
fixes fixes
This commit is contained in:
@@ -401,8 +401,10 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
|||||||
var mappings: [String: String?] = [:]
|
var mappings: [String: String?] = [:]
|
||||||
let result = PHPhotoLibrary.shared().cloudIdentifierMappings(forLocalIdentifiers: assetIds)
|
let result = PHPhotoLibrary.shared().cloudIdentifierMappings(forLocalIdentifiers: assetIds)
|
||||||
for (key, value) in result {
|
for (key, value) in result {
|
||||||
let id = try? value.get().stringValue
|
// Ignores invalid cloud ids of the format "GUID:ID:". Valid Ids are of the form "GUID:ID:HASH"
|
||||||
mappings[key] = id
|
if let cloudId = try? value.get().stringValue, !cloudId.hasSuffix(":") {
|
||||||
|
mappings[key] = cloudId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return mappings;
|
return mappings;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
|
|
||||||
part 'local_asset.model.dart';
|
part 'local_asset.model.dart';
|
||||||
part 'remote_asset.model.dart';
|
part 'remote_asset.model.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/platform_extensions.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/local_album.repository.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/storage.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart';
|
||||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||||
@@ -18,6 +19,7 @@ import 'package:logging/logging.dart';
|
|||||||
|
|
||||||
class LocalSyncService {
|
class LocalSyncService {
|
||||||
final DriftLocalAlbumRepository _localAlbumRepository;
|
final DriftLocalAlbumRepository _localAlbumRepository;
|
||||||
|
final DriftLocalAssetRepository _localAssetRepository;
|
||||||
final NativeSyncApi _nativeSyncApi;
|
final NativeSyncApi _nativeSyncApi;
|
||||||
final DriftTrashedLocalAssetRepository _trashedLocalAssetRepository;
|
final DriftTrashedLocalAssetRepository _trashedLocalAssetRepository;
|
||||||
final LocalFilesManagerRepository _localFilesManager;
|
final LocalFilesManagerRepository _localFilesManager;
|
||||||
@@ -26,11 +28,13 @@ class LocalSyncService {
|
|||||||
|
|
||||||
LocalSyncService({
|
LocalSyncService({
|
||||||
required DriftLocalAlbumRepository localAlbumRepository,
|
required DriftLocalAlbumRepository localAlbumRepository,
|
||||||
|
required DriftLocalAssetRepository localAssetRepository,
|
||||||
required DriftTrashedLocalAssetRepository trashedLocalAssetRepository,
|
required DriftTrashedLocalAssetRepository trashedLocalAssetRepository,
|
||||||
required LocalFilesManagerRepository localFilesManager,
|
required LocalFilesManagerRepository localFilesManager,
|
||||||
required StorageRepository storageRepository,
|
required StorageRepository storageRepository,
|
||||||
required NativeSyncApi nativeSyncApi,
|
required NativeSyncApi nativeSyncApi,
|
||||||
}) : _localAlbumRepository = localAlbumRepository,
|
}) : _localAlbumRepository = localAlbumRepository,
|
||||||
|
_localAssetRepository = localAssetRepository,
|
||||||
_trashedLocalAssetRepository = trashedLocalAssetRepository,
|
_trashedLocalAssetRepository = trashedLocalAssetRepository,
|
||||||
_localFilesManager = localFilesManager,
|
_localFilesManager = localFilesManager,
|
||||||
_storageRepository = storageRepository,
|
_storageRepository = storageRepository,
|
||||||
@@ -47,6 +51,12 @@ class LocalSyncService {
|
|||||||
_log.warning("syncTrashedAssets cannot proceed because MANAGE_MEDIA permission is missing");
|
_log.warning("syncTrashedAssets cannot proceed because MANAGE_MEDIA permission is missing");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CurrentPlatform.isIOS) {
|
||||||
|
final assets = await _localAssetRepository.getEmptyCloudIdAssets();
|
||||||
|
await _mapIosCloudIds(assets);
|
||||||
|
}
|
||||||
|
|
||||||
if (full || await _nativeSyncApi.shouldFullSync()) {
|
if (full || await _nativeSyncApi.shouldFullSync()) {
|
||||||
_log.fine("Full sync request from ${full ? "user" : "native"}");
|
_log.fine("Full sync request from ${full ? "user" : "native"}");
|
||||||
return await fullSync();
|
return await fullSync();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/asset_metadata.model.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/db.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||||
@@ -32,7 +33,7 @@ Future<void> syncCloudIds(ProviderContainer ref) async {
|
|||||||
for (final mapping in mappingsToUpdate) {
|
for (final mapping in mappingsToUpdate) {
|
||||||
final mobileMeta = AssetMetadataUpsertItemDto(
|
final mobileMeta = AssetMetadataUpsertItemDto(
|
||||||
key: AssetMetadataKey.mobileApp,
|
key: AssetMetadataKey.mobileApp,
|
||||||
value: RemoteAssetMobileAppMetadata(cloudId: mapping.cloudId),
|
value: RemoteAssetMobileAppMetadata(cloudId: mapping.cloudId, eTag: mapping.eTag),
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
await assetApi.updateAssetMetadata(mapping.assetId, AssetMetadataUpsertDto(items: [mobileMeta]));
|
await assetApi.updateAssetMetadata(mapping.assetId, AssetMetadataUpsertDto(items: [mobileMeta]));
|
||||||
@@ -51,7 +52,7 @@ Future<void> _populateCloudIds(Drift drift) async {
|
|||||||
await DriftLocalAlbumRepository(drift).updateCloudMapping(cloudMapping);
|
await DriftLocalAlbumRepository(drift).updateCloudMapping(cloudMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef _CloudIdMapping = ({String assetId, String cloudId});
|
typedef _CloudIdMapping = ({String assetId, String cloudId, String eTag});
|
||||||
|
|
||||||
Future<List<_CloudIdMapping>> _fetchCloudIdMappings(Drift drift, String userId) async {
|
Future<List<_CloudIdMapping>> _fetchCloudIdMappings(Drift drift, String userId) async {
|
||||||
final query =
|
final query =
|
||||||
@@ -67,16 +68,31 @@ Future<List<_CloudIdMapping>> _fetchCloudIdMappings(Drift drift, String userId)
|
|||||||
useColumns: false,
|
useColumns: false,
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
..addColumns([drift.remoteAssetEntity.id, drift.localAssetEntity.iCloudId])
|
..addColumns([
|
||||||
|
drift.remoteAssetEntity.id,
|
||||||
|
drift.localAssetEntity.iCloudId,
|
||||||
|
drift.localAssetEntity.createdAt,
|
||||||
|
drift.localAssetEntity.adjustmentTime,
|
||||||
|
drift.localAssetEntity.latitude,
|
||||||
|
drift.localAssetEntity.longitude,
|
||||||
|
])
|
||||||
..where(
|
..where(
|
||||||
drift.localAssetEntity.id.isNotNull() &
|
drift.localAssetEntity.id.isNotNull() &
|
||||||
drift.localAssetEntity.iCloudId.isNotNull() &
|
drift.localAssetEntity.iCloudId.isNotNull() &
|
||||||
drift.remoteAssetEntity.ownerId.equals(userId) &
|
drift.remoteAssetEntity.ownerId.equals(userId) &
|
||||||
drift.remoteAssetCloudIdEntity.cloudId.isNull(),
|
drift.remoteAssetCloudIdEntity.cloudId.isNull(),
|
||||||
);
|
);
|
||||||
return query
|
return query.map((row) {
|
||||||
.map(
|
final createdAt = row.read(drift.localAssetEntity.createdAt)!;
|
||||||
(row) => (assetId: row.read(drift.remoteAssetEntity.id)!, cloudId: row.read(drift.localAssetEntity.iCloudId)!),
|
final adjustmentTime = row.read(drift.localAssetEntity.adjustmentTime);
|
||||||
)
|
final latitude = row.read(drift.localAssetEntity.latitude);
|
||||||
.get();
|
final longitude = row.read(drift.localAssetEntity.longitude);
|
||||||
|
final eTag =
|
||||||
|
"${createdAt.millisecondsSinceEpoch ~/ 1000}$kUploadETagDelimiter${(adjustmentTime?.millisecondsSinceEpoch ?? 0) ~/ 1000}$kUploadETagDelimiter${latitude ?? 0}$kUploadETagDelimiter${longitude ?? 0}";
|
||||||
|
return (
|
||||||
|
assetId: row.read(drift.remoteAssetEntity.id)!,
|
||||||
|
cloudId: row.read(drift.localAssetEntity.iCloudId)!,
|
||||||
|
eTag: eTag,
|
||||||
|
);
|
||||||
|
}).get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,11 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<LocalAsset>> getEmptyCloudIdAssets() {
|
||||||
|
final query = _db.localAssetEntity.select()..where((row) => row.iCloudId.isNull());
|
||||||
|
return query.map((row) => row.toDto()).get();
|
||||||
|
}
|
||||||
|
|
||||||
Future<Map<String, String>> getHashMappingFromCloudId() async {
|
Future<Map<String, String>> getHashMappingFromCloudId() async {
|
||||||
final createdAt = coalesce([_db.localAssetEntity.createdAt.strftime('%s'), const Constant('0')]);
|
final createdAt = coalesce([_db.localAssetEntity.createdAt.strftime('%s'), const Constant('0')]);
|
||||||
final adjustmentTime = coalesce([_db.localAssetEntity.adjustmentTime.strftime('%s'), const Constant('0')]);
|
final adjustmentTime = coalesce([_db.localAssetEntity.adjustmentTime.strftime('%s'), const Constant('0')]);
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ class _AssetPropertiesSectionState extends ConsumerState<_AssetPropertiesSection
|
|||||||
final albums = await ref.read(assetServiceProvider).getSourceAlbums(asset.id);
|
final albums = await ref.read(assetServiceProvider).getSourceAlbums(asset.id);
|
||||||
properties.add(_PropertyItem(label: 'Album', value: albums.map((a) => a.name).join(', ')));
|
properties.add(_PropertyItem(label: 'Album', value: albums.map((a) => a.name).join(', ')));
|
||||||
if (CurrentPlatform.isIOS) {
|
if (CurrentPlatform.isIOS) {
|
||||||
|
properties.add(_PropertyItem(label: 'Cloud ID', value: asset.cloudId));
|
||||||
properties.add(_PropertyItem(label: 'Adjustment Time', value: asset.adjustmentTime?.toString()));
|
properties.add(_PropertyItem(label: 'Adjustment Time', value: asset.adjustmentTime?.toString()));
|
||||||
}
|
}
|
||||||
properties.add(
|
properties.add(
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ final syncStreamRepositoryProvider = Provider((ref) => SyncStreamRepository(ref.
|
|||||||
final localSyncServiceProvider = Provider(
|
final localSyncServiceProvider = Provider(
|
||||||
(ref) => LocalSyncService(
|
(ref) => LocalSyncService(
|
||||||
localAlbumRepository: ref.watch(localAlbumRepository),
|
localAlbumRepository: ref.watch(localAlbumRepository),
|
||||||
|
localAssetRepository: ref.watch(localAssetRepository),
|
||||||
trashedLocalAssetRepository: ref.watch(trashedLocalAssetRepository),
|
trashedLocalAssetRepository: ref.watch(trashedLocalAssetRepository),
|
||||||
localFilesManager: ref.watch(localFilesManagerRepositoryProvider),
|
localFilesManager: ref.watch(localFilesManagerRepositoryProvider),
|
||||||
storageRepository: ref.watch(storageRepositoryProvider),
|
storageRepository: ref.watch(storageRepositoryProvider),
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:immich_mobile/domain/services/store.service.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.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/storage.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart';
|
||||||
@@ -25,6 +26,7 @@ import '../../repository.mocks.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
late LocalSyncService sut;
|
late LocalSyncService sut;
|
||||||
late DriftLocalAlbumRepository mockLocalAlbumRepository;
|
late DriftLocalAlbumRepository mockLocalAlbumRepository;
|
||||||
|
late DriftLocalAssetRepository mockLocalAssetRepository;
|
||||||
late DriftTrashedLocalAssetRepository mockTrashedLocalAssetRepository;
|
late DriftTrashedLocalAssetRepository mockTrashedLocalAssetRepository;
|
||||||
late LocalFilesManagerRepository mockLocalFilesManager;
|
late LocalFilesManagerRepository mockLocalFilesManager;
|
||||||
late StorageRepository mockStorageRepository;
|
late StorageRepository mockStorageRepository;
|
||||||
@@ -47,6 +49,7 @@ void main() {
|
|||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
mockLocalAlbumRepository = MockLocalAlbumRepository();
|
mockLocalAlbumRepository = MockLocalAlbumRepository();
|
||||||
|
mockLocalAssetRepository = MockLocalAssetRepository();
|
||||||
mockTrashedLocalAssetRepository = MockTrashedLocalAssetRepository();
|
mockTrashedLocalAssetRepository = MockTrashedLocalAssetRepository();
|
||||||
mockLocalFilesManager = MockLocalFilesManagerRepository();
|
mockLocalFilesManager = MockLocalFilesManagerRepository();
|
||||||
mockStorageRepository = MockStorageRepository();
|
mockStorageRepository = MockStorageRepository();
|
||||||
@@ -66,6 +69,7 @@ void main() {
|
|||||||
|
|
||||||
sut = LocalSyncService(
|
sut = LocalSyncService(
|
||||||
localAlbumRepository: mockLocalAlbumRepository,
|
localAlbumRepository: mockLocalAlbumRepository,
|
||||||
|
localAssetRepository: mockLocalAssetRepository,
|
||||||
trashedLocalAssetRepository: mockTrashedLocalAssetRepository,
|
trashedLocalAssetRepository: mockTrashedLocalAssetRepository,
|
||||||
localFilesManager: mockLocalFilesManager,
|
localFilesManager: mockLocalFilesManager,
|
||||||
storageRepository: mockStorageRepository,
|
storageRepository: mockStorageRepository,
|
||||||
|
|||||||
Reference in New Issue
Block a user