mirror of
https://github.com/immich-app/immich
synced 2026-08-15 13:03:57 +00:00
throw on unsupported
This commit is contained in:
parent
ffdd360bd7
commit
de28149d7d
8 changed files with 52 additions and 39 deletions
|
|
@ -144,7 +144,7 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
|||
|
||||
private func getMediaChanges() throws -> SyncDelta {
|
||||
guard #available(iOS 16, *) else {
|
||||
throw PigeonError(code: "UNSUPPORTED_OS", message: "This feature requires iOS 16 or later.", details: nil)
|
||||
throw PigeonError(code: kUnSupportedOSError, message: "This feature requires iOS 16 or later.", details: nil)
|
||||
}
|
||||
|
||||
guard PHPhotoLibrary.authorizationStatus(for: .readWrite) == .authorized else {
|
||||
|
|
@ -437,7 +437,7 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
|||
}
|
||||
|
||||
func getTrashedAssets() throws -> [String: [PlatformAsset]] {
|
||||
throw PigeonError(code: "UNSUPPORTED_OS", message: "This feature not supported on iOS.", details: nil)
|
||||
throw PigeonError(code: kUnSupportedOSError, message: "This feature not supported on iOS.", details: nil)
|
||||
}
|
||||
|
||||
func restoreFromTrashById(mediaId: String, type: Int64, completion: @escaping (Result<Bool, Error>) -> Void) {
|
||||
|
|
@ -477,9 +477,7 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
|||
|
||||
func getCloudIdForAssetIds(assetIds: [String]) throws -> [CloudIdResult] {
|
||||
guard #available(iOS 16, *) else {
|
||||
return assetIds.map {
|
||||
CloudIdResult(assetId: $0, error: "Cloud identifiers require iOS 16", errorKind: .unsupported)
|
||||
}
|
||||
throw PigeonError(code: kUnSupportedOSError, message: "This feature requires iOS 16 or later.", details: nil)
|
||||
}
|
||||
|
||||
var mappings: [CloudIdResult] = []
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ class LocalSyncService {
|
|||
await updateAlbum(dbAlbum, album);
|
||||
}
|
||||
|
||||
await _mapIosCloudIds(newAssets);
|
||||
await _mapCloudIds(newAssets);
|
||||
}
|
||||
await _nativeSyncApi.checkpointSync();
|
||||
} on PlatformException catch (e, s) {
|
||||
|
|
@ -167,7 +167,7 @@ class LocalSyncService {
|
|||
: <LocalAsset>[];
|
||||
|
||||
await _localAlbumRepository.upsert(album, toUpsert: assets);
|
||||
await _mapIosCloudIds(assets);
|
||||
await _mapCloudIds(assets);
|
||||
_log.fine("Successfully added device album ${album.name}");
|
||||
} catch (e, s) {
|
||||
_log.warning("Error while adding device album", e, s);
|
||||
|
|
@ -249,7 +249,7 @@ class LocalSyncService {
|
|||
toUpsert: newAssets,
|
||||
);
|
||||
|
||||
await _mapIosCloudIds(newAssets);
|
||||
await _mapCloudIds(newAssets);
|
||||
return true;
|
||||
} catch (e, s) {
|
||||
_log.warning("Error on fast syncing local album: ${dbAlbum.name}", e, s);
|
||||
|
|
@ -281,7 +281,7 @@ class LocalSyncService {
|
|||
if (dbAlbum.assetCount == 0) {
|
||||
_log.fine("Device album ${deviceAlbum.name} is empty. Adding assets to DB.");
|
||||
await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsInDevice);
|
||||
await _mapIosCloudIds(assetsInDevice);
|
||||
await _mapCloudIds(assetsInDevice);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -319,7 +319,7 @@ class LocalSyncService {
|
|||
}
|
||||
|
||||
await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsToUpsert, toDelete: assetsToDelete);
|
||||
await _mapIosCloudIds(assetsToUpsert);
|
||||
await _mapCloudIds(assetsToUpsert);
|
||||
|
||||
return true;
|
||||
} catch (e, s) {
|
||||
|
|
@ -328,12 +328,17 @@ class LocalSyncService {
|
|||
return true;
|
||||
}
|
||||
|
||||
Future<void> _mapIosCloudIds(List<LocalAsset> assets) async {
|
||||
Future<void> _mapCloudIds(List<LocalAsset> assets) async {
|
||||
if (!CurrentPlatform.isIOS || assets.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
await resolveCloudIds(_nativeSyncApi, _localAlbumRepository, assets.map((a) => a.id).toList());
|
||||
await resolveCloudIds(
|
||||
_nativeSyncApi,
|
||||
_localAlbumRepository,
|
||||
assets.map((a) => a.id).toList(),
|
||||
cancellation: _cancellation,
|
||||
);
|
||||
}
|
||||
|
||||
bool _assetsEqual(LocalAsset a, LocalAsset b) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
@visibleForTesting
|
||||
const kCloudIdChunkSize = 5000;
|
||||
|
||||
Future<void> resolveCloudIds(
|
||||
|
|
@ -14,31 +18,36 @@ Future<void> resolveCloudIds(
|
|||
}) async {
|
||||
final logger = Logger('resolveCloudIds');
|
||||
|
||||
for (int offset = 0; offset < assetIds.length; offset += kCloudIdChunkSize) {
|
||||
for (final batch in assetIds.slices(kCloudIdChunkSize)) {
|
||||
if (cancellation?.isCompleted ?? false) {
|
||||
logger.warning('Cloud ID resolution cancelled after $offset of ${assetIds.length} assets');
|
||||
logger.warning('Cloud ID resolution cancelled');
|
||||
return;
|
||||
}
|
||||
|
||||
final end = offset + kCloudIdChunkSize;
|
||||
final chunk = assetIds.sublist(offset, end > assetIds.length ? assetIds.length : end);
|
||||
|
||||
final cloudMapping = <String, String>{};
|
||||
for (final result in await nativeSyncApi.getCloudIdForAssetIds(chunk)) {
|
||||
if (result.cloudId != null) {
|
||||
cloudMapping[result.assetId] = result.cloudId!;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result.errorKind == CloudIdErrorKind.unsupported) {
|
||||
logger.warning('Cloud IDs unavailable: ${result.error ?? "unsupported"}');
|
||||
final List<CloudIdResult> results;
|
||||
try {
|
||||
results = await nativeSyncApi.getCloudIdForAssetIds(batch);
|
||||
} on PlatformException catch (error, stack) {
|
||||
if (error.code == kUnSupportedOSError) {
|
||||
logger.warning('Cloud IDs are unavailable on this device. Skipping resolution.', error, stack);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.fine(
|
||||
'Cannot fetch cloudId for asset with id: ${result.assetId}. '
|
||||
'Reason: ${result.errorKind?.name ?? "unknown"}. Error: ${result.error ?? "unknown"}',
|
||||
);
|
||||
logger.warning('Cannot fetch cloudIds for ${batch.length} assets. Skipping batch.', error, stack);
|
||||
continue;
|
||||
}
|
||||
|
||||
final cloudMapping = <String, String>{};
|
||||
for (final CloudIdResult(:assetId, :cloudId, :error, :errorKind) in results) {
|
||||
if (cloudId == null) {
|
||||
logger.fine(
|
||||
'Cannot fetch cloudId for asset with id: $assetId. '
|
||||
'Reason: ${errorKind?.name ?? "unknown"}. Error: ${error ?? "unknown"}',
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
cloudMapping[assetId] = cloudId;
|
||||
}
|
||||
|
||||
await albumRepository.updateCloudMapping(cloudMapping);
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||
unawaited(_resumeBackup());
|
||||
}),
|
||||
_resumeBackup(),
|
||||
_safeRun(backgroundManager.syncCloudIds(), "syncCloudIds"),
|
||||
_safeRun(backgroundManager.syncCloudIds, "syncCloudIds"),
|
||||
]);
|
||||
} else {
|
||||
await _safeRun(backgroundManager.hashAssets, "hashAssets");
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import 'package:pigeon/pigeon.dart';
|
|||
dartPackageName: 'immich_mobile',
|
||||
),
|
||||
)
|
||||
const String kUnSupportedOSError = 'UNSUPPORTED_OS';
|
||||
|
||||
enum PlatformAssetPlaybackStyle { unknown, image, video, imageAnimated, livePhoto, videoLooping }
|
||||
|
||||
class PlatformAsset {
|
||||
|
|
|
|||
|
|
@ -1362,10 +1362,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: pigeon
|
||||
sha256: "04cfefc8add8b47ddf9ccac8b92bb4edeb67c87f185c623ba0db118ac99334ad"
|
||||
sha256: f90254ef7b22db026fd8d5fd44718fd60697d50968a144a8bc251e5e95d79e10
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "26.3.4"
|
||||
version: "27.3.0"
|
||||
pinput:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ dev_dependencies:
|
|||
sdk: flutter
|
||||
mocktail: ^1.0.5
|
||||
# Type safe platform code
|
||||
pigeon: ^26.3.4
|
||||
pigeon: ^27.3.0
|
||||
|
||||
# cast 2.1.0 declares a loose bonsoir range but its code targets the 5.x API.
|
||||
# Pin bonsoir to 5.x until cast releases a version compatible with bonsoir 6.x.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/utils/cloud_id_resolver.dart';
|
||||
import 'package:immich_mobile/domain/utils/migrate_cloud_ids.dart';
|
||||
|
|
@ -84,11 +85,9 @@ void main() {
|
|||
|
||||
test('stops after the first chunk when cloud IDs are unsupported', () async {
|
||||
final ids = List.generate(kCloudIdChunkSize + 1, (i) => 'asset-$i');
|
||||
when(() => mockNativeSyncApi.getCloudIdForAssetIds(any())).thenAnswer(
|
||||
(invocation) async => (invocation.positionalArguments.first as List<String>)
|
||||
.map((id) => CloudIdResult(assetId: id, error: 'needs iOS 16', errorKind: .unsupported))
|
||||
.toList(),
|
||||
);
|
||||
when(
|
||||
() => mockNativeSyncApi.getCloudIdForAssetIds(any()),
|
||||
).thenThrow(PlatformException(code: kUnSupportedOSError));
|
||||
|
||||
await resolveCloudIds(mockNativeSyncApi, albumRepository, ids);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue