diff --git a/mobile/lib/domain/services/background_worker.service.dart b/mobile/lib/domain/services/background_worker.service.dart index 3c33f43c46..3359d76798 100644 --- a/mobile/lib/domain/services/background_worker.service.dart +++ b/mobile/lib/domain/services/background_worker.service.dart @@ -249,13 +249,14 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { final networkCapabilities = await _ref?.read(connectivityApiProvider).getCapabilities() ?? []; return _ref ?.read(uploadServiceProvider) - .startForegroundUpload( + .startUploadWithHttp( currentUser.id, networkCapabilities.isUnmetered, _cancellationToken, - (_, __, ___, ____) {}, // onProgress - not needed for background - (_, __) {}, // onSuccess - not needed for background - (_) {}, // onError - not needed for background + onProgress: (_, __, ___, ____) {}, + onSuccess: (_, __) {}, + onError: (_) {}, + onICloudProgress: (_, __) {}, ); }, (error, stack) { diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 058bcb559b..b98857040a 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -93,7 +93,7 @@ class _DriftBackupPageState extends ConsumerState { Logger("DriftBackupPage").warning("Remote sync did not complete successfully, skipping backup"); return; } - await backupNotifier.startBackup(currentUser.id); + await backupNotifier.startForegroundBackup(currentUser.id); } Future stopBackup() async { diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index e09c9f2577..08e67f903b 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -116,7 +116,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState backgroundSync.syncRemote().then((success) { if (success) { - return backupNotifier.startBackup(user.id); + return backupNotifier.startForegroundBackup(user.id); } else { Logger('DriftBackupAlbumSelectionPage').warning('Background sync failed, not starting backup'); } diff --git a/mobile/lib/pages/backup/drift_backup_options.page.dart b/mobile/lib/pages/backup/drift_backup_options.page.dart index 78e8b339b4..21d7a67547 100644 --- a/mobile/lib/pages/backup/drift_backup_options.page.dart +++ b/mobile/lib/pages/backup/drift_backup_options.page.dart @@ -63,7 +63,7 @@ class DriftBackupOptionsPage extends ConsumerWidget { backupNotifier.stopBackup().whenComplete( () => backgroundSync.syncRemote().then((success) { if (success) { - return backupNotifier.startBackup(currentUser.id); + return backupNotifier.startForegroundBackup(currentUser.id); } else { Logger('DriftBackupOptionsPage').warning('Background sync failed, not starting backup'); } diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index cbe3282621..a820385170 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -132,7 +132,7 @@ class SplashScreenPageState extends ConsumerState { if (isEnableBackup) { final currentUser = Store.tryGet(StoreKey.currentUser); if (currentUser != null) { - unawaited(notifier.startBackup(currentUser.id)); + unawaited(notifier.startForegroundBackup(currentUser.id)); } } } diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index eb6f0f1d5f..6dabd9a744 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -179,7 +179,10 @@ class AppLifeCycleNotifier extends StateNotifier { if (isEnableBackup) { final currentUser = Store.tryGet(StoreKey.currentUser); if (currentUser != null) { - await _safeRun(_ref.read(driftBackupProvider.notifier).startBackup(currentUser.id), "handleBackupResume"); + await _safeRun( + _ref.read(driftBackupProvider.notifier).startForegroundBackup(currentUser.id), + "handleBackupResume", + ); } } } diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index 7f6a459a87..853b89bd84 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -393,7 +393,7 @@ class DriftBackupNotifier extends StateNotifier { state = state.copyWith(isSyncing: isSyncing); } - Future startBackup(String userId) async { + Future startForegroundBackup(String userId) async { state = state.copyWith(error: BackupError.none); final cancelToken = CancellationToken(); @@ -403,13 +403,13 @@ class DriftBackupNotifier extends StateNotifier { final hasWifi = networkCapabilities.isUnmetered; _logger.info('Network capabilities: $networkCapabilities, hasWifi/isUnmetered: $hasWifi'); - return _uploadService.startForegroundUpload( + return _uploadService.startUploadWithHttp( userId, hasWifi, cancelToken, - _handleForegroundBackupProgress, - _handleForegroundBackupSuccess, - _handleForegroundBackupError, + onProgress: _handleForegroundBackupProgress, + onSuccess: _handleForegroundBackupSuccess, + onError: _handleForegroundBackupError, onICloudProgress: _handleICloudProgress, ); } @@ -497,7 +497,7 @@ class DriftBackupNotifier extends StateNotifier { if (tasks.isEmpty) { _logger.info("Start backup with URLSession"); - return _uploadService.startBackupWithURLSession(userId); + return _uploadService.startUploadWithURLSession(userId); } _logger.info("Tasks to resume: ${tasks.length}"); diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index f7b786b070..8a659b8fd3 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -94,56 +94,7 @@ class UploadRepository { ); } - Future uploadSingleAsset({ - required File file, - required String originalFileName, - required Map headers, - required Map fields, - required Client httpClient, - required CancellationToken cancelToken, - required void Function(int bytes, int totalBytes) onProgress, - }) async { - return _uploadFile( - file: file, - originalFileName: originalFileName, - headers: headers, - fields: fields, - httpClient: httpClient, - cancelToken: cancelToken, - onProgress: onProgress, - logContext: 'assetUpload', - ); - } - - /// Upload live photo video part and return the video asset ID - Future uploadLivePhotoVideo({ - required File livePhotoFile, - required String originalFileName, - required Map headers, - required Map fields, - required Client httpClient, - required CancellationToken cancelToken, - required void Function(int bytes, int totalBytes) onProgress, - }) async { - final result = await _uploadFile( - file: livePhotoFile, - originalFileName: originalFileName, - headers: headers, - fields: fields, - httpClient: httpClient, - cancelToken: cancelToken, - onProgress: onProgress, - logContext: 'livePhotoVideoUpload', - ); - - if (result.isSuccess && result.remoteAssetId != null) { - return result.remoteAssetId; - } - - return null; - } - - Future _uploadFile({ + Future uploadFile({ required File file, required String originalFileName, required Map headers, diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index 687f17ae69..84df206ce1 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -121,7 +121,7 @@ class UploadService { /// Find backup candidates /// Build the upload tasks /// Enqueue the tasks - Future startBackupWithURLSession(String userId) async { + Future startUploadWithURLSession(String userId) async { await _storageRepository.clearCache(); shouldAbortQueuingTasks = false; @@ -146,14 +146,14 @@ class UploadService { } } - Future startForegroundUpload( + Future startUploadWithHttp( String userId, bool hasWifi, - CancellationToken cancelToken, - void Function(String localAssetId, String filename, int bytes, int totalBytes) onProgress, - void Function(String localAssetId, String remoteAssetId) onSuccess, - void Function(String errorMessage) onError, { - void Function(String localAssetId, double progress)? onICloudProgress, + CancellationToken cancelToken, { + required void Function(String localAssetId, String filename, int bytes, int totalBytes) onProgress, + required void Function(String localAssetId, String remoteAssetId) onSuccess, + required void Function(String errorMessage) onError, + required void Function(String localAssetId, double progress) onICloudProgress, }) async { const concurrentUploads = 3; final httpClients = List.generate(concurrentUploads, (_) => Client()); @@ -186,7 +186,6 @@ class UploadService { final requireWifi = _shouldRequireWiFi(asset); if (requireWifi && !hasWifi) { - _logger.warning('Skipping upload for ${asset.id} because it requires WiFi'); continue; } @@ -194,9 +193,9 @@ class UploadService { asset, httpClient, cancelToken, - onProgress, - onSuccess, - onError, + onProgress: onProgress, + onSuccess: onSuccess, + onError: onError, onICloudProgress: onICloudProgress, ); } @@ -220,11 +219,11 @@ class UploadService { Future _uploadSingleAsset( LocalAsset asset, Client httpClient, - CancellationToken cancelToken, - void Function(String id, String filename, int bytes, int totalBytes) onProgress, - void Function(String localAssetId, String remoteAssetId) onSuccess, - void Function(String errorMessage) onError, { - void Function(String localAssetId, double progress)? onICloudProgress, + CancellationToken cancelToken, { + required void Function(String id, String filename, int bytes, int totalBytes) onProgress, + required void Function(String localAssetId, String remoteAssetId) onSuccess, + required void Function(String errorMessage) onError, + required void Function(String localAssetId, double progress) onICloudProgress, }) async { File? file; File? livePhotoFile; @@ -244,12 +243,10 @@ class UploadService { PMProgressHandler? progressHandler; StreamSubscription? progressSubscription; - if (onICloudProgress != null) { - progressHandler = PMProgressHandler(); - progressSubscription = progressHandler.stream.listen((event) { - onICloudProgress(asset.localId!, event.progress); - }); - } + progressHandler = PMProgressHandler(); + progressSubscription = progressHandler.stream.listen((event) { + onICloudProgress(asset.localId!, event.progress); + }); try { file = await _storageRepository.loadFileFromCloud(asset.id, progressHandler: progressHandler); @@ -260,7 +257,7 @@ class UploadService { ); } } finally { - await progressSubscription?.cancel(); + await progressSubscription.cancel(); } } else { // Get files locally @@ -300,23 +297,28 @@ class UploadService { String? livePhotoVideoId; if (entity.isLivePhoto && livePhotoFile != null) { final livePhotoTitle = p.setExtension(originalFileName, p.extension(livePhotoFile.path)); - livePhotoVideoId = await _uploadRepository.uploadLivePhotoVideo( - livePhotoFile: livePhotoFile, + + final livePhotoResult = await _uploadRepository.uploadFile( + file: livePhotoFile, originalFileName: livePhotoTitle, headers: headers, fields: fields, httpClient: httpClient, cancelToken: cancelToken, onProgress: (bytes, totalBytes) => onProgress(asset.localId!, livePhotoTitle, bytes, totalBytes), + logContext: 'livePhotoVideo[${asset.localId}]', ); + + if (livePhotoResult.isSuccess && livePhotoResult.remoteAssetId != null) { + livePhotoVideoId = livePhotoResult.remoteAssetId; + } } - // Add livePhotoVideoId to fields if available if (livePhotoVideoId != null) { fields['livePhotoVideoId'] = livePhotoVideoId; } - final result = await _uploadRepository.uploadSingleAsset( + final result = await _uploadRepository.uploadFile( file: file, originalFileName: originalFileName, headers: headers, @@ -324,6 +326,7 @@ class UploadService { httpClient: httpClient, cancelToken: cancelToken, onProgress: (bytes, totalBytes) => onProgress(asset.localId!, originalFileName, bytes, totalBytes), + logContext: 'asset[${asset.localId}]', ); if (result.isSuccess && result.remoteAssetId != null) {