fix: process upload only after successful remote sync (#22360)

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong
2025-09-29 20:40:00 +05:30
committed by GitHub
parent fea5e6783c
commit ee3c07d049
14 changed files with 156 additions and 73 deletions
+34 -2
View File
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@@ -8,6 +10,7 @@ import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/intl_keys.g.dart';
import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
@@ -16,8 +19,7 @@ import 'package:immich_mobile/providers/sync_status.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/widgets/backup/backup_info_card.dart';
import 'dart:async';
import 'package:logging/logging.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
@RoutePage()
@@ -63,7 +65,10 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
.where((album) => album.backupSelection == BackupSelection.selected)
.toList();
final error = ref.watch(driftBackupProvider.select((p) => p.error));
final backupNotifier = ref.read(driftBackupProvider.notifier);
final backupSyncManager = ref.read(backgroundSyncProvider);
Future<void> startBackup() async {
final currentUser = Store.tryGet(StoreKey.currentUser);
@@ -71,7 +76,14 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
return;
}
final syncSuccess = await backupSyncManager.syncRemote();
await backupNotifier.getBackupStatus(currentUser.id);
if (!syncSuccess) {
Logger("DriftBackupPage").warning("Remote sync did not complete successfully, skipping backup");
await backupNotifier.updateError(BackupError.syncFailed);
return;
}
await backupNotifier.startBackup(currentUser.id);
}
@@ -114,6 +126,26 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
const _RemainderCard(),
const Divider(),
BackupToggleButton(onStart: () async => await startBackup(), onStop: () async => await stopBackup()),
switch (error) {
BackupError.none => const SizedBox.shrink(),
BackupError.syncFailed => Padding(
padding: const EdgeInsets.only(top: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Icon(Icons.warning_rounded, color: context.colorScheme.error, fill: 1),
const SizedBox(width: 8),
Text(
IntlKeys.backup_error_sync_failed.t(),
style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.error),
textAlign: TextAlign.center,
),
],
),
),
},
TextButton.icon(
icon: const Icon(Icons.info_outline_rounded),
onPressed: () => context.pushRoute(const DriftUploadDetailRoute()),
@@ -18,6 +18,7 @@ import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart';
import 'package:immich_mobile/widgets/common/search_field.dart';
import 'package:logging/logging.dart';
@RoutePage()
class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget {
@@ -112,7 +113,18 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
// Waits for hashing to be cancelled before starting a new one
unawaited(nativeSync.cancelHashing().whenComplete(() => backgroundSync.hashAssets()));
if (isBackupEnabled) {
unawaited(backupNotifier.cancel().whenComplete(() => backupNotifier.startBackup(user.id)));
unawaited(
backupNotifier.cancel().whenComplete(
() => backgroundSync.syncRemote().then((success) {
if (success) {
return backupNotifier.startBackup(user.id);
} else {
Logger('DriftBackupAlbumSelectionPage').warning('Background sync failed, not starting backup');
backupNotifier.updateError(BackupError.syncFailed);
}
}),
),
);
}
}
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -5,10 +7,12 @@ import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart';
import 'package:logging/logging.dart';
@RoutePage()
class DriftBackupOptionsPage extends ConsumerWidget {
@@ -54,9 +58,19 @@ class DriftBackupOptionsPage extends ConsumerWidget {
);
final backupNotifier = ref.read(driftBackupProvider.notifier);
backupNotifier.cancel().then((_) {
backupNotifier.startBackup(currentUser.id);
});
final backgroundSync = ref.read(backgroundSyncProvider);
unawaited(
backupNotifier.cancel().whenComplete(
() => backgroundSync.syncRemote().then((success) {
if (success) {
return backupNotifier.startBackup(currentUser.id);
} else {
Logger('DriftBackupOptionsPage').warning('Background sync failed, not starting backup');
backupNotifier.updateError(BackupError.syncFailed);
}
}),
),
);
}
},
child: Scaffold(
@@ -62,14 +62,24 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
infoProvider.getServerInfo();
if (Store.isBetaTimelineEnabled) {
await Future.wait([backgroundManager.syncLocal(), backgroundManager.syncRemote()]);
bool syncSuccess = false;
await Future.wait([
backgroundManager.hashAssets().then((_) {
_resumeBackup(backupProvider);
}),
_resumeBackup(backupProvider),
backgroundManager.syncLocal(),
backgroundManager.syncRemote().then((success) => syncSuccess = success),
]);
if (syncSuccess) {
await Future.wait([
backgroundManager.hashAssets().then((_) {
_resumeBackup(backupProvider);
}),
_resumeBackup(backupProvider),
]);
} else {
backupProvider.updateError(BackupError.syncFailed);
await backgroundManager.hashAssets();
}
if (Store.get(StoreKey.syncAlbums, false)) {
await backgroundManager.syncLinkedAlbum();
}