add server version check & auto sync cloud ids on compatible servers

pull/20418/head
shenlong-tanwen 2025-12-04 22:36:24 +07:00
parent 0a84783841
commit d47e9a5db4
6 changed files with 35 additions and 8 deletions

@ -84,6 +84,7 @@ Future<List<_CloudIdMapping>> _fetchCloudIdMappings(Drift drift, String userId)
useColumns: false,
),
])..where(
// Only select assets that have a local cloud ID but either no remote cloud ID or a mismatched eTag
drift.localAssetEntity.id.isNotNull() &
drift.localAssetEntity.iCloudId.isNotNull() &
drift.remoteAssetEntity.ownerId.equals(userId) &

@ -10,4 +10,8 @@ class ServerVersion extends SemVer {
}
ServerVersion.fromDto(ServerVersionResponseDto dto) : super(major: dto.major, minor: dto.minor, patch: dto.patch_);
bool isAtLeast({int major = 0, int minor = 0, int patch = 0}) {
return this >= SemVer(major: major, minor: minor, patch: patch);
}
}

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
@ -60,7 +61,8 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
(_) async {
try {
wsProvider.connect();
unawaited(infoProvider.getServerInfo());
await infoProvider.getServerInfo();
final serverInfo = ref.read(serverInfoProvider);
if (Store.isBetaTimelineEnabled) {
bool syncSuccess = false;
@ -75,6 +77,9 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
_resumeBackup(backupProvider);
}),
_resumeBackup(backupProvider),
// Sync cloud IDs if server version is compatible
if (CurrentPlatform.isIOS && serverInfo.serverVersion.isAtLeast(major: 2, minor: 4))
backgroundManager.syncCloudIds(),
]);
} else {
await backgroundManager.hashAssets();

@ -147,6 +147,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
final backgroundManager = _ref.read(backgroundSyncProvider);
final isAlbumLinkedSyncEnable = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
final serverInfo = _ref.read(serverInfoProvider);
try {
bool syncSuccess = false;
@ -160,6 +161,9 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
_resumeBackup();
}),
_resumeBackup(),
// Sync cloud IDs if server version is compatible
if (CurrentPlatform.isIOS && serverInfo.serverVersion.isAtLeast(major: 2, minor: 4))
backgroundManager.syncCloudIds(),
]);
} else {
await _safeRun(backgroundManager.hashAssets(), "hashAssets");

@ -15,10 +15,12 @@ import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/infrastructure/repositories/backup.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/models/server_info/server_info.model.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/repositories/asset_media.repository.dart';
import 'package:immich_mobile/repositories/upload.repository.dart';
import 'package:immich_mobile/services/api.service.dart';
@ -35,6 +37,7 @@ final uploadServiceProvider = Provider((ref) {
ref.watch(localAssetRepository),
ref.watch(appSettingsServiceProvider),
ref.watch(assetMediaRepositoryProvider),
ref.watch(serverInfoProvider),
);
ref.onDispose(service.dispose);
@ -49,6 +52,7 @@ class UploadService {
this._localAssetRepository,
this._appSettingsService,
this._assetMediaRepository,
this._serverInfo,
) {
_uploadRepository.onUploadStatus = _onUploadCallback;
_uploadRepository.onTaskProgress = _onTaskProgressCallback;
@ -60,6 +64,7 @@ class UploadService {
final DriftLocalAssetRepository _localAssetRepository;
final AppSettingsService _appSettingsService;
final AssetMediaRepository _assetMediaRepository;
final ServerInfo _serverInfo;
final Logger _logger = Logger('UploadService');
final StreamController<TaskStatusUpdate> _taskStatusController = StreamController<TaskStatusUpdate>.broadcast();
@ -431,13 +436,18 @@ class UploadService {
'fileModifiedAt': modifiedAt.toUtc().toIso8601String(),
'isFavorite': isFavorite?.toString() ?? 'false',
'duration': '0',
'metadata': jsonEncode([
RemoteAssetMetadataItem(
key: RemoteAssetMetadataKey.mobileApp,
value: RemoteAssetMobileAppMetadata(cloudId: cloudId, eTag: eTag),
),
]),
if (fields != null) ...fields,
// Include cloudId and eTag in metadata if available and server version supports it
if (CurrentPlatform.isIOS &&
cloudId != null &&
eTag != null &&
_serverInfo.serverVersion.isAtLeast(major: 2, minor: 4))
'metadata': jsonEncode([
RemoteAssetMetadataItem(
key: RemoteAssetMetadataKey.mobileApp,
value: RemoteAssetMobileAppMetadata(cloudId: cloudId, eTag: eTag),
),
]),
};
return UploadTask(

@ -13,6 +13,7 @@ import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
import 'package:immich_mobile/providers/infrastructure/trash_sync.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/providers/sync_status.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart';
@ -25,6 +26,8 @@ class SyncStatusAndActions extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final serverInfo = ref.read(serverInfoProvider);
Future<void> exportDatabase() async {
try {
// WAL Checkpoint to ensure all changes are written to the database
@ -151,7 +154,7 @@ class SyncStatusAndActions extends HookConsumerWidget {
ref.read(backgroundSyncProvider).hashAssets();
},
),
if (CurrentPlatform.isIOS)
if (CurrentPlatform.isIOS && serverInfo.serverVersion.isAtLeast(major: 2, minor: 4))
ListTile(
title: Text(
"sync_cloud_ids".t(context: context),