diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index 6b9ee9963f..8d7d87e35e 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -9,6 +9,19 @@ import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; import 'package:worker_manager/worker_manager.dart'; +const _kSyncTypeOrder = [ + SyncEntityType.userDeleteV1, + SyncEntityType.userV1, + SyncEntityType.partnerDeleteV1, + SyncEntityType.partnerV1, + SyncEntityType.assetDeleteV1, + SyncEntityType.assetV1, + SyncEntityType.assetExifV1, + SyncEntityType.partnerAssetDeleteV1, + SyncEntityType.partnerAssetV1, + SyncEntityType.partnerAssetExifV1, +]; + class SyncStreamService { final Logger _logger = Logger('SyncStreamService'); @@ -102,7 +115,12 @@ class SyncStreamService { final eventsMap = events.groupListsBy((event) => event.type); final Map acks = {}; - for (final entry in eventsMap.entries) { + for (final type in _kSyncTypeOrder) { + final data = eventsMap[type]; + if (data == null) { + continue; + } + if (_cancelChecker?.call() ?? false) { _logger.info("Sync cancelled, stopping stream"); mutex?.complete(); @@ -117,9 +135,6 @@ class SyncStreamService { return; } - final type = entry.key; - final data = entry.value; - if (data.isEmpty) { _logger.warning("Received empty sync events for $type"); continue; diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index dfa392b1fe..0bd456f0bb 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -11,12 +11,17 @@ class BackgroundSyncManager { BackgroundSyncManager(); - void cancel() { + Future cancel() { + final futures = []; + if (_userSyncTask != null) { + futures.add(_userSyncTask!.future); + } _userSyncTask?.cancel(); _userSyncTask = null; + return Future.wait(futures); } - Future syncUsers() async { + Future syncUsers() { if (_userSyncTask != null) { return _userSyncTask!.future; } @@ -27,5 +32,6 @@ class BackgroundSyncManager { _userSyncTask!.whenComplete(() { _userSyncTask = null; }); + return _userSyncTask!.future; } } diff --git a/mobile/lib/services/auth.service.dart b/mobile/lib/services/auth.service.dart index 4ffa9444c5..ec053c078b 100644 --- a/mobile/lib/services/auth.service.dart +++ b/mobile/lib/services/auth.service.dart @@ -120,9 +120,10 @@ class AuthService { /// - Asset ETag /// /// All deletions are executed in parallel using [Future.wait]. - Future clearLocalData() { - _backgroundSyncManager.cancel(); - return Future.wait([ + Future clearLocalData() async { + // Cancel any ongoing background sync operations before clearing data + await _backgroundSyncManager.cancel(); + await Future.wait([ _authRepository.clearLocalData(), Store.delete(StoreKey.currentUser), Store.delete(StoreKey.accessToken), diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart index 01ba4ae68b..707b14fc94 100644 --- a/mobile/test/domain/services/sync_stream_service_test.dart +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -275,7 +275,7 @@ void main() { bool handlerStarted = false; // Make handler wait so we can cancel it mid-flight - when(() => mockSyncStreamRepo.updateUsersV1(any())) + when(() => mockSyncStreamRepo.deleteUsersV1(any())) .thenAnswer((_) async { handlerStarted = true; await processingCompleter diff --git a/mobile/test/services/auth.service_test.dart b/mobile/test/services/auth.service_test.dart index bbb54d98d5..4ada98a6c9 100644 --- a/mobile/test/services/auth.service_test.dart +++ b/mobile/test/services/auth.service_test.dart @@ -120,7 +120,7 @@ void main() { group('logout', () { test('Should logout user', () async { when(() => authApiRepository.logout()).thenAnswer((_) async => {}); - when(() => backgroundSyncManager.cancel()).thenAnswer((_) => {}); + when(() => backgroundSyncManager.cancel()).thenAnswer((_) async => {}); when(() => authRepository.clearLocalData()) .thenAnswer((_) => Future.value(null)); @@ -134,7 +134,7 @@ void main() { test('Should clear local data even on server error', () async { when(() => authApiRepository.logout()) .thenThrow(Exception('Server error')); - when(() => backgroundSyncManager.cancel()).thenAnswer((_) => {}); + when(() => backgroundSyncManager.cancel()).thenAnswer((_) async => {}); when(() => authRepository.clearLocalData()) .thenAnswer((_) => Future.value(null));