diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index c04e1dafdc..275a38a970 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -17,7 +17,7 @@ linter: # section below to disable rules from the `package:flutter_lints/flutter.yaml` # included above or to enable additional rules. A list of all available lints # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. + # https://dart.dev/tools/linter-rules # # Instead of disabling a lint rule for the entire project in the # section below, it can also be suppressed for a single line of code @@ -28,6 +28,7 @@ linter: rules: # avoid_print: false # Uncomment to disable the `avoid_print` rule # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + unawaited_futures: true use_build_context_synchronously: false require_trailing_commas: true unrelated_type_equality_checks: true @@ -46,6 +47,8 @@ analyzer: # TODO: Re-enable after upgrading custom_lint # plugins: # - custom_lint + errors: + unawaited_futures: warning custom_lint: debug: true @@ -152,160 +155,6 @@ dart_code_metrics: # - avoid-passing-async-when-sync-expected # - avoid-throw-in-catch-block - avoid-unused-parameters - # - avoid-unnecessary-type-assertions - # - avoid-unnecessary-type-casts - # - avoid-unrelated-type-assertions - # - avoid-unrelated-type-casts - # - no-empty-block - # - no-equal-then-else - # - prefer-correct-test-file-name - prefer-const-border-radius - # - prefer-match-file-name - # - prefer-return-await - # - avoid-self-assignment - # - avoid-self-compare - # - avoid-shadowing - # - prefer-iterable-of - # - no-equal-switch-case - # - no-equal-conditions - # - avoid-equal-expressions - # - avoid-missed-calls - # - avoid-unnecessary-negations - # - avoid-unused-generics - # - function-always-returns-null - # - avoid-throw-objects-without-tostring - # - avoid-unsafe-collection-methods - # - prefer-wildcard-pattern - # - no-equal-switch-expression-cases - # - avoid-future-tostring - # - avoid-unassigned-late-fields - # - avoid-nested-futures - # - avoid-generics-shadowing - # - prefer-parentheses-with-if-null - # - no-equal-nested-conditions - # - avoid-shadowed-extension-methods - # - avoid-unnecessary-conditionals - # - avoid-double-slash-imports - # - avoid-map-keys-contains - # - prefer-correct-json-casts - # - avoid-duplicate-mixins - # - avoid-nullable-interpolation - # - avoid-unused-instances - # - prefer-correct-for-loop-increment - # - prefer-public-exception-classes - # - avoid-uncaught-future-errors - # - always-remove-listener - # - avoid-unnecessary-setstate - # - check-for-equals-in-render-object-setters - # - consistent-update-render-object - # - use-setstate-synchronously - # - avoid-incomplete-copy-with - # - proper-super-calls - # - dispose-fields - # - avoid-empty-setstate - # - avoid-state-constructors - # - avoid-recursive-widget-calls - # - avoid-missing-image-alt - # - avoid-passing-self-as-argument - # - avoid-unnecessary-if - # - avoid-unconditional-break - # - avoid-referencing-discarded-variables - # - avoid-unnecessary-local-late - # - avoid-wildcard-cases-with-enums - # - match-getter-setter-field-names - # - avoid-accessing-collections-by-constant-index - # - prefer-unique-test-names - # - avoid-duplicate-cascades - # - prefer-specific-cases-first - # - avoid-duplicate-switch-case-conditions - # - prefer-explicit-function-type - # - avoid-misused-test-matchers - # - avoid-duplicate-test-assertions - # - prefer-switch-with-enums - # - prefer-any-or-every - # - avoid-duplicate-map-keys - # - avoid-nullable-tostring - # - avoid-undisposed-instances - # - avoid-duplicate-initializers - # - avoid-unassigned-stream-subscriptions - # - avoid-empty-test-groups - # - avoid-not-encodable-in-to-json - # - avoid-contradictory-expressions - # - avoid-excessive-expressions - # - prefer-private-extension-type-field - # - avoid-renaming-representation-getters - # - avoid-empty-spread - # - avoid-unnecessary-gesture-detector - # - avoid-missing-completer-stack-trace - # - avoid-casting-to-extension-type - # - prefer-overriding-parent-equality - # - avoid-missing-controller - # - avoid-unknown-pragma - # - avoid-conditions-with-boolean-literals - # - avoid-multi-assignment - # - avoid-collection-equality-checks - # - avoid-only-rethrow - # - avoid-incorrect-image-opacity - # - avoid-misused-set-literals - # - dispose-class-fields - # - avoid-suspicious-super-overrides - # - avoid-assignments-as-conditions - # - avoid-unused-assignment - # - avoid-unnecessary-overrides - # - avoid-implicitly-nullable-extension-types - # Enable with the next release - # - avoid-late-final-reassignment - # - avoid-duplicate-constant-values - # - function-always-returns-same-value - # - avoid-flexible-outside-flex - # - avoid-unnecessary-patterns - # - use-closest-build-context - # - avoid-commented-out-code - # - avoid-recursive-tostring - # - avoid-enum-values-by-index - # - avoid-constant-assert-conditions - # - avoid-inconsistent-digit-separators - # - pass-existing-future-to-future-builder - # - pass-existing-stream-to-stream-builder - - # Code simplification - # - avoid-redundant-async - # - avoid-redundant-else - # - avoid-unnecessary-nullable-return-type - # - avoid-redundant-pragma-inline - # - avoid-nested-records - # - avoid-redundant-positional-field-name - # - avoid-explicit-pattern-field-name - # - prefer-simpler-patterns-null-check - # - avoid-unnecessary-return - # - avoid-duplicate-patterns - # - avoid-keywords-in-wildcard-pattern - # - avoid-unnecessary-futures - # - avoid-unnecessary-reassignment - # - avoid-unnecessary-call - # - avoid-unnecessary-stateful-widgets - # - prefer-dedicated-media-query-methods - # - avoid-unnecessary-overrides-in-state - # - move-variable-closer-to-its-usage - # - avoid-nullable-parameters-with-default-values - # - prefer-null-aware-spread - # - avoid-inferrable-type-arguments - # - avoid-unnecessary-super - # - avoid-unnecessary-collections - # - avoid-unnecessary-extends - # - avoid-unnecessary-enum-arguments - # - prefer-contains - # Enable with the next release - # - prefer-simpler-boolean-expressions - # - prefer-spacing - # - avoid-unnecessary-continue - # - avoid-unnecessary-compare-to - - # Style - # - prefer-trailing-comma - # - unnecessary-trailing-comma - prefer-declaring-const-constructor - # - prefer-single-widget-per-file - prefer-switch-expression - # - prefer-prefixed-global-constants - # - prefer-correct-callback-field-name diff --git a/mobile/lib/domain/services/background_worker.service.dart b/mobile/lib/domain/services/background_worker.service.dart index e6ac3eaebd..5c228ba67c 100644 --- a/mobile/lib/domain/services/background_worker.service.dart +++ b/mobile/lib/domain/services/background_worker.service.dart @@ -114,10 +114,10 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { configureFileDownloaderNotifications(); // Notify the host that the background worker service has been initialized and is ready to use - _backgroundHostApi.onInitialized(); + unawaited(_backgroundHostApi.onInitialized()); } catch (error, stack) { _logger.severe("Failed to initialize background worker", error, stack); - _backgroundHostApi.close(); + unawaited(_backgroundHostApi.close()); } } diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index ca356c80d8..94a8a19e73 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -249,7 +249,7 @@ class LocalSyncService { if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) { _log.fine("No asset changes detected in album ${deviceAlbum.name}. Updating metadata."); - _localAlbumRepository.upsert(updatedDeviceAlbum); + await _localAlbumRepository.upsert(updatedDeviceAlbum); return true; } diff --git a/mobile/lib/infrastructure/loaders/remote_image_request.dart b/mobile/lib/infrastructure/loaders/remote_image_request.dart index 78f6b9479b..03dcd6454a 100644 --- a/mobile/lib/infrastructure/loaders/remote_image_request.dart +++ b/mobile/lib/infrastructure/loaders/remote_image_request.dart @@ -68,7 +68,7 @@ class RemoteImageRequest extends ImageRequest { final cacheManager = this.cacheManager; final streamController = StreamController>(sync: true); final Stream> stream; - cacheManager?.putStreamedFile(url, streamController.stream); + unawaited(cacheManager?.putStreamedFile(url, streamController.stream)); stream = response.map((chunk) { if (_isCancelled) { throw StateError('Cancelled request'); @@ -81,11 +81,11 @@ class RemoteImageRequest extends ImageRequest { try { final Uint8List bytes = await _downloadBytes(stream, response.contentLength); - streamController.close(); + unawaited(streamController.close()); return await ImmutableBuffer.fromUint8List(bytes); } catch (e) { streamController.addError(e); - streamController.close(); + unawaited(streamController.close()); if (_isCancelled) { return null; } @@ -143,7 +143,7 @@ class RemoteImageRequest extends ImageRequest { return await _decodeBuffer(buffer, decode, scale); } catch (e) { log.severe('Failed to decode cached image', e); - _evictFile(url); + unawaited(_evictFile(url)); return null; } } diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index e4bff24879..63259bc62b 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -361,15 +361,13 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { return _db.managers.localAlbumEntity.count(); } - Future unlinkRemoteAlbum(String id) async { - return _db.localAlbumEntity.update() - ..where((row) => row.id.equals(id)) - ..write(const LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(null))); + Future unlinkRemoteAlbum(String id) async { + final query = _db.localAlbumEntity.update()..where((row) => row.id.equals(id)); + await query.write(const LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(null))); } - Future linkRemoteAlbum(String localAlbumId, String remoteAlbumId) async { - return _db.localAlbumEntity.update() - ..where((row) => row.id.equals(localAlbumId)) - ..write(LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(remoteAlbumId))); + Future linkRemoteAlbum(String localAlbumId, String remoteAlbumId) async { + final query = _db.localAlbumEntity.update()..where((row) => row.id.equals(localAlbumId)); + await query.write(LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(remoteAlbumId))); } } diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index b1d87b36ab..c3804d97f6 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -159,7 +159,7 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve WidgetsBinding.instance.addObserver(this); // Draw the app from edge to edge - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + unawaited(SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge)); // Sets the navigation bar color SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent); diff --git a/mobile/lib/pages/album/album_options.page.dart b/mobile/lib/pages/album/album_options.page.dart index 20d4dbd325..b0f682ffed 100644 --- a/mobile/lib/pages/album/album_options.page.dart +++ b/mobile/lib/pages/album/album_options.page.dart @@ -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'; @@ -51,7 +53,7 @@ class AlbumOptionsPage extends HookConsumerWidget { final isSuccess = await ref.read(albumProvider.notifier).leaveAlbum(album); if (isSuccess) { - context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); + unawaited(context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()]))); } else { showErrorMessage(); } diff --git a/mobile/lib/pages/album/album_shared_user_selection.page.dart b/mobile/lib/pages/album/album_shared_user_selection.page.dart index 562f02a2ab..ec084b1859 100644 --- a/mobile/lib/pages/album/album_shared_user_selection.page.dart +++ b/mobile/lib/pages/album/album_shared_user_selection.page.dart @@ -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'; @@ -29,8 +31,8 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { if (newAlbum != null) { ref.watch(albumTitleProvider.notifier).clearAlbumTitle(); - context.maybePop(true); - context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); + unawaited(context.maybePop(true)); + unawaited(context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()]))); } ScaffoldMessenger( @@ -109,8 +111,8 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { centerTitle: false, leading: IconButton( icon: const Icon(Icons.close_rounded), - onPressed: () async { - context.maybePop(); + onPressed: () { + unawaited(context.maybePop()); }, ), actions: [ diff --git a/mobile/lib/pages/backup/backup_controller.page.dart b/mobile/lib/pages/backup/backup_controller.page.dart index 4f55d00ea0..1e008be1bb 100644 --- a/mobile/lib/pages/backup/backup_controller.page.dart +++ b/mobile/lib/pages/backup/backup_controller.page.dart @@ -155,7 +155,7 @@ class BackupControllerPage extends HookConsumerWidget { // waited until returning from selection await ref.read(backupProvider.notifier).backupAlbumSelectionDone(); // waited until backup albums are stored in DB - ref.read(albumProvider.notifier).refreshDeviceAlbums(); + await ref.read(albumProvider.notifier).refreshDeviceAlbums(); }, child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 2e7c3e946c..47052ea436 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -270,7 +270,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { if (currentUser == null) { return; } - ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + unawaited(ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id)); }, child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), diff --git a/mobile/lib/pages/backup/drift_upload_detail.page.dart b/mobile/lib/pages/backup/drift_upload_detail.page.dart index 80956b708f..1b8aa57eaa 100644 --- a/mobile/lib/pages/backup/drift_upload_detail.page.dart +++ b/mobile/lib/pages/backup/drift_upload_detail.page.dart @@ -170,8 +170,8 @@ class DriftUploadDetailPage extends ConsumerWidget { ); } - Future _showFileDetailDialog(BuildContext context, DriftUploadStatus item) async { - showDialog( + Future _showFileDetailDialog(BuildContext context, DriftUploadStatus item) { + return showDialog( context: context, builder: (context) => FileDetailDialog(uploadStatus: item), ); diff --git a/mobile/lib/pages/common/activities.page.dart b/mobile/lib/pages/common/activities.page.dart index 1a1955af40..9d1123dbca 100644 --- a/mobile/lib/pages/common/activities.page.dart +++ b/mobile/lib/pages/common/activities.page.dart @@ -33,7 +33,7 @@ class ActivitiesPage extends HookConsumerWidget { Future onAddComment(String comment) async { await activityNotifier.addComment(comment); // Scroll to the end of the list to show the newly added activity - listViewScrollController.animateTo( + await listViewScrollController.animateTo( listViewScrollController.position.maxScrollExtent + 200, duration: const Duration(milliseconds: 600), curve: Curves.fastOutSlowIn, diff --git a/mobile/lib/pages/common/create_album.page.dart b/mobile/lib/pages/common/create_album.page.dart index 5a0d4154f8..0a28dfeb5a 100644 --- a/mobile/lib/pages/common/create_album.page.dart +++ b/mobile/lib/pages/common/create_album.page.dart @@ -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'; @@ -170,11 +172,11 @@ class CreateAlbumPage extends HookConsumerWidget { .createAlbum(ref.read(albumTitleProvider), selectedAssets.value); if (newAlbum != null) { - ref.read(albumProvider.notifier).refreshRemoteAlbums(); + await ref.read(albumProvider.notifier).refreshRemoteAlbums(); selectedAssets.value = {}; ref.read(albumTitleProvider.notifier).clearAlbumTitle(); ref.read(albumViewerProvider.notifier).disableEditAlbum(); - context.replaceRoute(AlbumViewerRoute(albumId: newAlbum.id)); + unawaited(context.replaceRoute(AlbumViewerRoute(albumId: newAlbum.id))); } } diff --git a/mobile/lib/pages/common/gallery_viewer.page.dart b/mobile/lib/pages/common/gallery_viewer.page.dart index 3c279dfcd2..9a7e78ddb8 100644 --- a/mobile/lib/pages/common/gallery_viewer.page.dart +++ b/mobile/lib/pages/common/gallery_viewer.page.dart @@ -95,7 +95,7 @@ class GalleryViewerPage extends HookConsumerWidget { } catch (e) { // swallow error silently log.severe('Error precaching next image: $e'); - context.maybePop(); + await context.maybePop(); } } diff --git a/mobile/lib/pages/common/native_video_viewer.page.dart b/mobile/lib/pages/common/native_video_viewer.page.dart index 5f4eaeaaad..9cd9f6bd5e 100644 --- a/mobile/lib/pages/common/native_video_viewer.page.dart +++ b/mobile/lib/pages/common/native_video_viewer.page.dart @@ -267,11 +267,13 @@ class NativeVideoViewerPage extends HookConsumerWidget { nc.onPlaybackReady.addListener(onPlaybackReady); nc.onPlaybackEnded.addListener(onPlaybackEnded); - nc.loadVideoSource(source).catchError((error) { - log.severe('Error loading video source: $error'); - }); + unawaited( + nc.loadVideoSource(source).catchError((error) { + log.severe('Error loading video source: $error'); + }), + ); final loopVideo = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.loopVideo); - nc.setLoop(loopVideo); + unawaited(nc.setLoop(loopVideo)); controller.value = nc; Timer(const Duration(milliseconds: 200), checkIfBuffering); @@ -342,12 +344,12 @@ class NativeVideoViewerPage extends HookConsumerWidget { useOnAppLifecycleStateChange((_, state) async { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { - controller.value?.play(); + await controller.value?.play(); } else if (state == AppLifecycleState.paused) { final videoPlaying = await controller.value?.isPlaying(); if (videoPlaying ?? true) { shouldPlayOnForeground.value = true; - controller.value?.pause(); + await controller.value?.pause(); } else { shouldPlayOnForeground.value = false; } diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index 29b3dcd3be..c1d621f474 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -55,48 +55,50 @@ class SplashScreenPageState extends ConsumerState { final backgroundManager = ref.read(backgroundSyncProvider); final backupProvider = ref.read(driftBackupProvider.notifier); - ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then( - (_) async { - try { - wsProvider.connect(); - infoProvider.getServerInfo(); + unawaited( + ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then( + (_) async { + try { + wsProvider.connect(); + unawaited(infoProvider.getServerInfo()); - if (Store.isBetaTimelineEnabled) { - bool syncSuccess = false; - await Future.wait([ - backgroundManager.syncLocal(), - backgroundManager.syncRemote().then((success) => syncSuccess = success), - ]); - - if (syncSuccess) { + if (Store.isBetaTimelineEnabled) { + bool syncSuccess = false; await Future.wait([ - backgroundManager.hashAssets().then((_) { - _resumeBackup(backupProvider); - }), - _resumeBackup(backupProvider), + backgroundManager.syncLocal(), + backgroundManager.syncRemote().then((success) => syncSuccess = success), ]); - } else { - await backgroundManager.hashAssets(); - } - if (Store.get(StoreKey.syncAlbums, false)) { - await backgroundManager.syncLinkedAlbum(); + if (syncSuccess) { + await Future.wait([ + backgroundManager.hashAssets().then((_) { + _resumeBackup(backupProvider); + }), + _resumeBackup(backupProvider), + ]); + } else { + await backgroundManager.hashAssets(); + } + + if (Store.get(StoreKey.syncAlbums, false)) { + await backgroundManager.syncLinkedAlbum(); + } } + } catch (e) { + log.severe('Failed establishing connection to the server: $e'); } - } catch (e) { - log.severe('Failed establishing connection to the server: $e'); - } - }, - onError: (exception) => { - log.severe('Failed to update auth info with access token: $accessToken'), - ref.read(authProvider.notifier).logout(), - context.replaceRoute(const LoginRoute()), - }, + }, + onError: (exception) => { + log.severe('Failed to update auth info with access token: $accessToken'), + ref.read(authProvider.notifier).logout(), + context.replaceRoute(const LoginRoute()), + }, + ), ); } else { log.severe('Missing crucial offline login info - Logging out completely'); - ref.read(authProvider.notifier).logout(); - context.replaceRoute(const LoginRoute()); + unawaited(ref.read(authProvider.notifier).logout()); + unawaited(context.replaceRoute(const LoginRoute())); return; } @@ -106,11 +108,11 @@ class SplashScreenPageState extends ConsumerState { final needBetaMigration = Store.get(StoreKey.needBetaMigration, false); if (needBetaMigration) { await Store.put(StoreKey.needBetaMigration, false); - context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: true)]); + unawaited(context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: true)])); return; } - context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()); + unawaited(context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute())); } if (Store.isBetaTimelineEnabled) { @@ -120,7 +122,7 @@ class SplashScreenPageState extends ConsumerState { final hasPermission = await ref.read(galleryPermissionNotifier.notifier).hasPermission; if (hasPermission) { // Resume backup (if enable) then navigate - ref.watch(backupProvider.notifier).resumeBackup(); + await ref.watch(backupProvider.notifier).resumeBackup(); } } @@ -130,7 +132,7 @@ class SplashScreenPageState extends ConsumerState { if (isEnableBackup) { final currentUser = Store.tryGet(StoreKey.currentUser); if (currentUser != null) { - notifier.handleBackupResume(currentUser.id); + unawaited(notifier.handleBackupResume(currentUser.id)); } } } diff --git a/mobile/lib/pages/editing/crop.page.dart b/mobile/lib/pages/editing/crop.page.dart index 35fd615800..8cd13fed64 100644 --- a/mobile/lib/pages/editing/crop.page.dart +++ b/mobile/lib/pages/editing/crop.page.dart @@ -1,13 +1,16 @@ -import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:auto_route/auto_route.dart'; import 'package:crop_image/crop_image.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; + import 'edit.page.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:auto_route/auto_route.dart'; /// A widget for cropping an image. /// This widget uses [HookWidget] to manage its lifecycle and state. It allows @@ -35,7 +38,7 @@ class CropImagePage extends HookWidget { icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { final croppedImage = await cropController.croppedImage(); - context.pushRoute(EditImageRoute(asset: asset, image: croppedImage, isEdited: true)); + unawaited(context.pushRoute(EditImageRoute(asset: asset, image: croppedImage, isEdited: true))); }, ), ], diff --git a/mobile/lib/pages/editing/filter.page.dart b/mobile/lib/pages/editing/filter.page.dart index 6d41b4c5b8..f8b144bb96 100644 --- a/mobile/lib/pages/editing/filter.page.dart +++ b/mobile/lib/pages/editing/filter.page.dart @@ -1,12 +1,13 @@ import 'dart:async'; import 'dart:ui' as ui; + +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/constants/filters.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:auto_route/auto_route.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; /// A widget for filtering an image. @@ -74,7 +75,7 @@ class FilterImagePage extends HookWidget { icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { final filteredImage = await applyFilterAndConvert(colorFilter.value); - context.pushRoute(EditImageRoute(asset: asset, image: filteredImage, isEdited: true)); + unawaited(context.pushRoute(EditImageRoute(asset: asset, image: filteredImage, isEdited: true))); }, ), ], diff --git a/mobile/lib/pages/library/locked/pin_auth.page.dart b/mobile/lib/pages/library/locked/pin_auth.page.dart index 36befa0016..a39c91871b 100644 --- a/mobile/lib/pages/library/locked/pin_auth.page.dart +++ b/mobile/lib/pages/library/locked/pin_auth.page.dart @@ -1,14 +1,16 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart' show useState; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/local_auth.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/forms/pin_registration_form.dart'; import 'package:immich_mobile/widgets/forms/pin_verification_form.dart'; -import 'package:immich_mobile/entities/store.entity.dart'; @RoutePage() class PinAuthPage extends HookConsumerWidget { @@ -35,9 +37,9 @@ class PinAuthPage extends HookConsumerWidget { ); if (isBetaTimeline) { - context.replaceRoute(const DriftLockedFolderRoute()); + unawaited(context.replaceRoute(const DriftLockedFolderRoute())); } else { - context.replaceRoute(const LockedRoute()); + unawaited(context.replaceRoute(const LockedRoute())); } } } diff --git a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart index 8b66bb231f..1d7eaef080 100644 --- a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart +++ b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart @@ -333,7 +333,7 @@ class SharedLinkEditPage extends HookConsumerWidget { changeExpiry: changeExpiry, ); ref.invalidate(sharedLinksStateProvider); - context.maybePop(); + await context.maybePop(); } return Scaffold( diff --git a/mobile/lib/pages/photos/photos.page.dart b/mobile/lib/pages/photos/photos.page.dart index 957c32b224..7f57247ec4 100644 --- a/mobile/lib/pages/photos/photos.page.dart +++ b/mobile/lib/pages/photos/photos.page.dart @@ -82,10 +82,12 @@ class PhotosPage extends HookConsumerWidget { final fullRefresh = refreshCount.value > 0; if (fullRefresh) { - Future.wait([ - ref.read(assetProvider.notifier).getAllAsset(clear: true), - ref.read(albumProvider.notifier).refreshRemoteAlbums(), - ]); + unawaited( + Future.wait([ + ref.read(assetProvider.notifier).getAllAsset(clear: true), + ref.read(albumProvider.notifier).refreshRemoteAlbums(), + ]), + ); // refresh was forced: user requested another refresh within 2 seconds refreshCount.value = 0; diff --git a/mobile/lib/pages/search/map/map.page.dart b/mobile/lib/pages/search/map/map.page.dart index 34522f0f04..a93b826f03 100644 --- a/mobile/lib/pages/search/map/map.page.dart +++ b/mobile/lib/pages/search/map/map.page.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:math'; import 'package:auto_route/auto_route.dart'; @@ -83,7 +84,7 @@ class MapPage extends HookConsumerWidget { isLoading.value = true; markers.value = await ref.read(mapMarkersProvider.future); assetsDebouncer.run(updateAssetsInBounds); - reloadLayers(); + await reloadLayers(); } finally { isLoading.value = false; } @@ -128,7 +129,7 @@ class MapPage extends HookConsumerWidget { ); if (marker != null) { - updateAssetMarkerPosition(marker); + await updateAssetMarkerPosition(marker); } else { // If no asset was previously selected and no new asset is available, close the bottom sheet if (selectedMarker.value == null) { @@ -165,7 +166,7 @@ class MapPage extends HookConsumerWidget { if (asset.isVideo) { ref.read(showControlsProvider.notifier).show = false; } - context.pushRoute(GalleryViewerRoute(initialIndex: 0, heroOffset: 0, renderList: renderList)); + unawaited(context.pushRoute(GalleryViewerRoute(initialIndex: 0, heroOffset: 0, renderList: renderList))); } /// BOTTOM SHEET CALLBACKS @@ -209,7 +210,7 @@ class MapPage extends HookConsumerWidget { } if (mapController.value != null && location != null) { - mapController.value!.animateCamera( + await mapController.value!.animateCamera( CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), mapZoomToAssetLevel), duration: const Duration(milliseconds: 800), ); diff --git a/mobile/lib/pages/search/map/map_location_picker.page.dart b/mobile/lib/pages/search/map/map_location_picker.page.dart index 0fe8b769f5..94e6627c98 100644 --- a/mobile/lib/pages/search/map/map_location_picker.page.dart +++ b/mobile/lib/pages/search/map/map_location_picker.page.dart @@ -8,9 +8,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/maplibrecontroller_extensions.dart'; +import 'package:immich_mobile/utils/map_utils.dart'; import 'package:immich_mobile/widgets/map/map_theme_override.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; -import 'package:immich_mobile/utils/map_utils.dart'; @RoutePage() class MapLocationPickerPage extends HookConsumerWidget { @@ -30,7 +30,7 @@ class MapLocationPickerPage extends HookConsumerWidget { Future onMapClick(Point point, LatLng centre) async { selectedLatLng.value = centre; - controller.value?.animateCamera(CameraUpdate.newLatLng(centre)); + await controller.value?.animateCamera(CameraUpdate.newLatLng(centre)); if (marker.value != null) { await controller.value?.updateSymbol(marker.value!, SymbolOptions(geometry: centre)); } @@ -49,7 +49,7 @@ class MapLocationPickerPage extends HookConsumerWidget { var currentLatLng = LatLng(currentLocation.latitude, currentLocation.longitude); selectedLatLng.value = currentLatLng; - controller.value?.animateCamera(CameraUpdate.newLatLng(currentLatLng)); + await controller.value?.animateCamera(CameraUpdate.newLatLng(currentLatLng)); } return MapThemeOverride( diff --git a/mobile/lib/pages/search/search.page.dart b/mobile/lib/pages/search/search.page.dart index 7623829b51..902110f6a8 100644 --- a/mobile/lib/pages/search/search.page.dart +++ b/mobile/lib/pages/search/search.page.dart @@ -266,7 +266,7 @@ class SearchPage extends HookConsumerWidget { filter.value = filter.value.copyWith(date: SearchDateFilter()); dateRangeCurrentFilterWidget.value = null; - search(); + unawaited(search()); return; } @@ -295,7 +295,7 @@ class SearchPage extends HookConsumerWidget { ); } - search(); + unawaited(search()); } // MEDIA PICKER diff --git a/mobile/lib/presentation/pages/drift_album_options.page.dart b/mobile/lib/presentation/pages/drift_album_options.page.dart index 7f49a1ff79..2116e5c5cc 100644 --- a/mobile/lib/presentation/pages/drift_album_options.page.dart +++ b/mobile/lib/presentation/pages/drift_album_options.page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; @@ -47,7 +49,7 @@ class DriftAlbumOptionsPage extends HookConsumerWidget { void leaveAlbum() async { try { await ref.read(remoteAlbumProvider.notifier).leaveAlbum(album.id, userId: userId); - context.navigateTo(const DriftAlbumsRoute()); + unawaited(context.navigateTo(const DriftAlbumsRoute())); } catch (_) { showErrorMessage(); } diff --git a/mobile/lib/presentation/pages/drift_create_album.page.dart b/mobile/lib/presentation/pages/drift_create_album.page.dart index c70c4a0bd7..2e263ba1db 100644 --- a/mobile/lib/presentation/pages/drift_create_album.page.dart +++ b/mobile/lib/presentation/pages/drift_create_album.page.dart @@ -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'; @@ -179,7 +181,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { if (album != null) { ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); - context.replaceRoute(RemoteAlbumRoute(album: album)); + unawaited(context.replaceRoute(RemoteAlbumRoute(album: album))); } } diff --git a/mobile/lib/presentation/pages/drift_remote_album.page.dart b/mobile/lib/presentation/pages/drift_remote_album.page.dart index 23d82dcb92..2d70978ea5 100644 --- a/mobile/lib/presentation/pages/drift_remote_album.page.dart +++ b/mobile/lib/presentation/pages/drift_remote_album.page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -139,7 +141,7 @@ class _RemoteAlbumPageState extends ConsumerState { toastType: ToastType.success, ); - context.pushRoute(const DriftAlbumsRoute()); + unawaited(context.pushRoute(const DriftAlbumsRoute())); } catch (e) { ImmichToast.show( context: context, @@ -161,12 +163,12 @@ class _RemoteAlbumPageState extends ConsumerState { setState(() { _album = _album.copyWith(name: result.name, description: result.description ?? ''); }); - HapticFeedback.mediumImpact(); + unawaited(HapticFeedback.mediumImpact()); } } Future showActivity(BuildContext context) async { - context.pushRoute(const DriftActivitiesRoute()); + unawaited(context.pushRoute(const DriftActivitiesRoute())); } Future showOptionSheet(BuildContext context) async { @@ -175,56 +177,58 @@ class _RemoteAlbumPageState extends ConsumerState { final canAddPhotos = await ref.read(remoteAlbumServiceProvider).getUserRole(_album.id, user?.id ?? '') == AlbumUserRole.editor; - showModalBottomSheet( - context: context, - backgroundColor: context.colorScheme.surface, - isScrollControlled: false, - builder: (context) { - return DriftRemoteAlbumOption( - onDeleteAlbum: isOwner - ? () async { - await deleteAlbum(context); - if (context.mounted) { + unawaited( + showModalBottomSheet( + context: context, + backgroundColor: context.colorScheme.surface, + isScrollControlled: false, + builder: (context) { + return DriftRemoteAlbumOption( + onDeleteAlbum: isOwner + ? () async { + await deleteAlbum(context); + if (context.mounted) { + context.pop(); + } + } + : null, + onAddUsers: isOwner + ? () async { + await addUsers(context); context.pop(); } - } - : null, - onAddUsers: isOwner - ? () async { - await addUsers(context); - context.pop(); - } - : null, - onAddPhotos: isOwner || canAddPhotos - ? () async { - await addAssets(context); - context.pop(); - } - : null, - onToggleAlbumOrder: isOwner - ? () async { - await toggleAlbumOrder(); - context.pop(); - } - : null, - onEditAlbum: isOwner - ? () async { - context.pop(); - await showEditTitleAndDescription(context); - } - : null, - onCreateSharedLink: isOwner - ? () async { - context.pop(); - context.pushRoute(SharedLinkEditRoute(albumId: _album.id)); - } - : null, - onShowOptions: () { - context.pop(); - context.pushRoute(const DriftAlbumOptionsRoute()); - }, - ); - }, + : null, + onAddPhotos: isOwner || canAddPhotos + ? () async { + await addAssets(context); + context.pop(); + } + : null, + onToggleAlbumOrder: isOwner + ? () async { + await toggleAlbumOrder(); + context.pop(); + } + : null, + onEditAlbum: isOwner + ? () async { + context.pop(); + await showEditTitleAndDescription(context); + } + : null, + onCreateSharedLink: isOwner + ? () async { + context.pop(); + unawaited(context.pushRoute(SharedLinkEditRoute(albumId: _album.id))); + } + : null, + onShowOptions: () { + context.pop(); + context.pushRoute(const DriftAlbumOptionsRoute()); + }, + ); + }, + ), ); } diff --git a/mobile/lib/presentation/pages/editing/drift_crop.page.dart b/mobile/lib/presentation/pages/editing/drift_crop.page.dart index 5b14292aa2..d8219e3b3c 100644 --- a/mobile/lib/presentation/pages/editing/drift_crop.page.dart +++ b/mobile/lib/presentation/pages/editing/drift_crop.page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:crop_image/crop_image.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -34,7 +36,7 @@ class DriftCropImagePage extends HookWidget { icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { final croppedImage = await cropController.croppedImage(); - context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true)); + unawaited(context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true))); }, ), ], diff --git a/mobile/lib/presentation/pages/editing/drift_edit.page.dart b/mobile/lib/presentation/pages/editing/drift_edit.page.dart index e24a1967f2..f9903b6b94 100644 --- a/mobile/lib/presentation/pages/editing/drift_edit.page.dart +++ b/mobile/lib/presentation/pages/editing/drift_edit.page.dart @@ -70,7 +70,7 @@ class DriftEditImagePage extends ConsumerWidget { Logger("SaveEditedImage").warning("Failed to retrieve the saved image back from OS", e); } - ref.read(backgroundSyncProvider).syncLocal(full: true); + unawaited(ref.read(backgroundSyncProvider).syncLocal(full: true)); _exitEditing(context); ImmichToast.show(durationInSecond: 3, context: context, msg: 'Image Saved!'); diff --git a/mobile/lib/presentation/pages/editing/drift_filter.page.dart b/mobile/lib/presentation/pages/editing/drift_filter.page.dart index 75c3f81de2..8198a41bbe 100644 --- a/mobile/lib/presentation/pages/editing/drift_filter.page.dart +++ b/mobile/lib/presentation/pages/editing/drift_filter.page.dart @@ -75,7 +75,7 @@ class DriftFilterImagePage extends HookWidget { icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { final filteredImage = await applyFilterAndConvert(colorFilter.value); - context.pushRoute(DriftEditImageRoute(asset: asset, image: filteredImage, isEdited: true)); + unawaited(context.pushRoute(DriftEditImageRoute(asset: asset, image: filteredImage, isEdited: true))); }, ), ], diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index 069beef33e..965e31678e 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -271,7 +271,7 @@ class DriftSearchPage extends HookConsumerWidget { filter.value = filter.value.copyWith(date: SearchDateFilter()); dateRangeCurrentFilterWidget.value = null; - search(); + unawaited(search()); return; } @@ -301,7 +301,7 @@ class DriftSearchPage extends HookConsumerWidget { ); } - search(); + unawaited(search()); } // MEDIA PICKER diff --git a/mobile/lib/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart index 170f827fdb..cb2581bc6d 100644 --- a/mobile/lib/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; @@ -15,7 +17,7 @@ class AdvancedInfoActionButton extends ConsumerWidget { return; } - ref.read(actionProvider.notifier).troubleshoot(source, context); + unawaited(ref.read(actionProvider.notifier).troubleshoot(source, context)); } @override diff --git a/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart index 740ac528b0..6bcf099487 100644 --- a/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart @@ -39,7 +39,7 @@ class ShareActionButton extends ConsumerWidget { return; } - showDialog( + await showDialog( context: context, builder: (BuildContext buildContext) { ref.read(actionProvider.notifier).shareAssets(source, context).then((ActionResult result) { diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index bffe3d3941..cbac6c8b93 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -121,7 +121,7 @@ class _AlbumSelectorState extends ConsumerState { // we need to re-filter the albums after sorting // so shownAlbums gets updated - filterAlbums(); + unawaited(filterAlbums()); } Future filterAlbums() async { @@ -711,7 +711,7 @@ class AddToAlbumHeader extends ConsumerWidget { ref.read(currentRemoteAlbumProvider.notifier).setAlbum(newAlbum); ref.read(multiSelectProvider.notifier).reset(); - context.pushRoute(RemoteAlbumRoute(album: newAlbum)); + unawaited(context.pushRoute(RemoteAlbumRoute(album: newAlbum))); } return SliverPadding( diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index 12f8771982..2e3009d934 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -635,9 +635,9 @@ class _AssetViewerState extends ConsumerState { // Listen for control visibility changes and change system UI mode accordingly ref.listen(assetViewerProvider.select((value) => value.showingControls), (_, showingControls) async { if (showingControls) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + unawaited(SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge)); } else { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); + unawaited(SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky)); } }); diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart index 916201aa70..08b5b25343 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -295,11 +295,13 @@ class NativeVideoViewer extends HookConsumerWidget { nc.onPlaybackReady.addListener(onPlaybackReady); nc.onPlaybackEnded.addListener(onPlaybackEnded); - nc.loadVideoSource(source).catchError((error) { - log.severe('Error loading video source: $error'); - }); + unawaited( + nc.loadVideoSource(source).catchError((error) { + log.severe('Error loading video source: $error'); + }), + ); final loopVideo = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.loopVideo); - nc.setLoop(!asset.isMotionPhoto && loopVideo); + unawaited(nc.setLoop(!asset.isMotionPhoto && loopVideo)); controller.value = nc; Timer(const Duration(milliseconds: 200), checkIfBuffering); @@ -373,12 +375,12 @@ class NativeVideoViewer extends HookConsumerWidget { useOnAppLifecycleStateChange((_, state) async { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { - controller.value?.play(); + await controller.value?.play(); } else if (state == AppLifecycleState.paused) { final videoPlaying = await controller.value?.isPlaying(); if (videoPlaying ?? true) { shouldPlayOnForeground.value = true; - controller.value?.pause(); + await controller.value?.pause(); } else { shouldPlayOnForeground.value = false; } diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index 810340aeb8..e77803c206 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:async/async.dart'; import 'package:flutter/widgets.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; @@ -51,14 +53,14 @@ mixin CancellableImageProviderMixin on CancellableImageProvide Stream loadRequest(ImageRequest request, ImageDecoderCallback decode) async* { if (isCancelled) { this.request = null; - evict(); + unawaited(evict()); return; } try { final image = await request.load(decode); if (image == null || isCancelled) { - evict(); + unawaited(evict()); return; } yield image; diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index f90961ea5a..c5dca57f9c 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -85,7 +85,7 @@ class LocalFullImageProvider extends CancellableImageProvider { } final bounds = await controller.getVisibleRegion(); - _reloadMutex.run(() async { - if (mounted && ref.read(mapStateProvider.notifier).setBounds(bounds)) { - final markers = await ref.read(mapMarkerProvider(bounds).future); - await reloadMarkers(markers); - } - }); + unawaited( + _reloadMutex.run(() async { + if (mounted && ref.read(mapStateProvider.notifier).setBounds(bounds)) { + final markers = await ref.read(mapMarkerProvider(bounds).future); + await reloadMarkers(markers); + } + }), + ); } Future reloadMarkers(Map markers) async { @@ -148,7 +150,7 @@ class _DriftMapState extends ConsumerState { final controller = mapController; if (controller != null && location != null) { - controller.animateCamera( + await controller.animateCamera( CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), MapUtils.mapZoomToAssetLevel), duration: const Duration(milliseconds: 800), ); diff --git a/mobile/lib/presentation/widgets/map/map_utils.dart b/mobile/lib/presentation/widgets/map/map_utils.dart index 1c18fc48d6..80df5995b6 100644 --- a/mobile/lib/presentation/widgets/map/map_utils.dart +++ b/mobile/lib/presentation/widgets/map/map_utils.dart @@ -73,7 +73,7 @@ class MapUtils { try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled && !silent) { - showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog(context)); + unawaited(showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog(context))); return (null, LocationPermission.deniedForever); } diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index 8121bb76d3..a0a98c34d7 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:math' as math; import 'package:auto_route/auto_route.dart'; @@ -15,8 +16,8 @@ import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart' import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart'; import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -156,11 +157,13 @@ class _AssetTileWidget extends ConsumerWidget { await ref.read(timelineServiceProvider).loadAssets(assetIndex, 1); ref.read(isPlayingMotionVideoProvider.notifier).playing = false; AssetViewer.setAsset(ref, asset); - ctx.pushRoute( - AssetViewerRoute( - initialIndex: assetIndex, - timelineService: ref.read(timelineServiceProvider), - heroOffset: heroOffset, + unawaited( + ctx.pushRoute( + AssetViewerRoute( + initialIndex: assetIndex, + timelineService: ref.read(timelineServiceProvider), + heroOffset: heroOffset, + ), ), ); } diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 3b51874ab5..6d0c0acb0d 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -242,7 +242,7 @@ class AppLifeCycleNotifier extends StateNotifier { } try { - LogService.I.flush(); + await LogService.I.flush(); } catch (_) {} } @@ -255,7 +255,7 @@ class AppLifeCycleNotifier extends StateNotifier { // Flush logs before closing database try { - LogService.I.flush(); + await LogService.I.flush(); } catch (_) {} // Close Isar database safely diff --git a/mobile/lib/providers/asset.provider.dart b/mobile/lib/providers/asset.provider.dart index 75635950ff..d5a4e42b74 100644 --- a/mobile/lib/providers/asset.provider.dart +++ b/mobile/lib/providers/asset.provider.dart @@ -98,7 +98,7 @@ class AssetNotifier extends StateNotifier { Future onNewAssetUploaded(Asset newAsset) async { // eTag on device is not valid after partially modifying the assets - Store.delete(StoreKey.assetETag); + await Store.delete(StoreKey.assetETag); await _syncService.syncNewAssetToDb(newAsset); } diff --git a/mobile/lib/providers/asset_viewer/download.provider.dart b/mobile/lib/providers/asset_viewer/download.provider.dart index 36b935abe7..a461d5766a 100644 --- a/mobile/lib/providers/asset_viewer/download.provider.dart +++ b/mobile/lib/providers/asset_viewer/download.provider.dart @@ -1,14 +1,16 @@ +import 'dart:async'; + import 'package:background_downloader/background_downloader.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/download/download_state.model.dart'; import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/download.service.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/services/share.service.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/share_dialog.dart'; @@ -159,24 +161,26 @@ class DownloadStateNotifier extends StateNotifier { } void shareAsset(Asset asset, BuildContext context) async { - showDialog( - context: context, - builder: (BuildContext buildContext) { - _shareService.shareAsset(asset, context).then((bool status) { - if (!status) { - ImmichToast.show( - context: context, - msg: 'image_viewer_page_state_provider_share_error'.tr(), - toastType: ToastType.error, - gravity: ToastGravity.BOTTOM, - ); - } - buildContext.pop(); - }); - return const ShareDialog(); - }, - barrierDismissible: false, - useRootNavigator: false, + unawaited( + showDialog( + context: context, + builder: (BuildContext buildContext) { + _shareService.shareAsset(asset, context).then((bool status) { + if (!status) { + ImmichToast.show( + context: context, + msg: 'image_viewer_page_state_provider_share_error'.tr(), + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + } + buildContext.pop(); + }); + return const ShareDialog(); + }, + barrierDismissible: false, + useRootNavigator: false, + ), ); } } diff --git a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart index 7b2ab5b27a..0f9c32b410 100644 --- a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart +++ b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart @@ -104,7 +104,7 @@ class ShareIntentUploadStateNotifier extends StateNotifier upload(File file) async { final task = await _buildUploadTask(hash(file.path).toString(), file); - _uploadService.enqueueTasks([task]); + await _uploadService.enqueueTasks([task]); } Future _buildUploadTask(String id, File file, {Map? fields}) async { diff --git a/mobile/lib/providers/backup/backup.provider.dart b/mobile/lib/providers/backup/backup.provider.dart index 03666466ff..9eb01b6109 100644 --- a/mobile/lib/providers/backup/backup.provider.dart +++ b/mobile/lib/providers/backup/backup.provider.dart @@ -380,7 +380,7 @@ class BackupNotifier extends StateNotifier { state = state.copyWith(backgroundBackup: isEnabled); if (isEnabled != Store.get(StoreKey.backgroundBackup, !isEnabled)) { - Store.put(StoreKey.backgroundBackup, isEnabled); + await Store.put(StoreKey.backgroundBackup, isEnabled); } if (state.backupProgress != BackUpProgressEnum.inBackground) { @@ -474,7 +474,7 @@ class BackupNotifier extends StateNotifier { ); await notifyBackgroundServiceCanRun(); } else { - openAppSettings(); + await openAppSettings(); } } @@ -533,10 +533,10 @@ class BackupNotifier extends StateNotifier { progressInFileSpeedUpdateTime: DateTime.now(), progressInFileSpeedUpdateSentBytes: 0, ); - _updatePersistentAlbumsSelection(); + await _updatePersistentAlbumsSelection(); } - updateDiskInfo(); + await updateDiskInfo(); } void _onUploadProgress(int sent, int total) { diff --git a/mobile/lib/providers/backup/backup_verification.provider.dart b/mobile/lib/providers/backup/backup_verification.provider.dart index da4253576b..50270e87ca 100644 --- a/mobile/lib/providers/backup/backup_verification.provider.dart +++ b/mobile/lib/providers/backup/backup_verification.provider.dart @@ -2,10 +2,10 @@ import 'dart:async'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; -import 'package:immich_mobile/providers/backup/backup.provider.dart'; -import 'package:immich_mobile/services/backup_verification.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; +import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/services/backup_verification.service.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -44,7 +44,7 @@ class BackupVerification extends _$BackupVerification { } return; } - WakelockPlus.enable(); + unawaited(WakelockPlus.enable()); const limit = 100; final toDelete = await ref.read(backupVerificationServiceProvider).findWronglyBackedUpAssets(limit: limit); @@ -73,7 +73,7 @@ class BackupVerification extends _$BackupVerification { } } } finally { - WakelockPlus.disable(); + unawaited(WakelockPlus.disable()); state = false; } } diff --git a/mobile/lib/providers/backup/backup_verification.provider.g.dart b/mobile/lib/providers/backup/backup_verification.provider.g.dart index 727e06a12c..13f6819fa7 100644 --- a/mobile/lib/providers/backup/backup_verification.provider.g.dart +++ b/mobile/lib/providers/backup/backup_verification.provider.g.dart @@ -7,7 +7,7 @@ part of 'backup_verification.provider.dart'; // ************************************************************************** String _$backupVerificationHash() => - r'b204e43ab575d5fa5b2ee663297f32bcee9074f5'; + r'b4b34909ed1af3f28877ea457d53a4a18b6417f8'; /// See also [BackupVerification]. @ProviderFor(BackupVerification) diff --git a/mobile/lib/providers/backup/manual_upload.provider.dart b/mobile/lib/providers/backup/manual_upload.provider.dart index bfc079bfa3..6ad8730356 100644 --- a/mobile/lib/providers/backup/manual_upload.provider.dart +++ b/mobile/lib/providers/backup/manual_upload.provider.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:cancellation_token_http/http.dart'; @@ -26,11 +27,11 @@ import 'package:immich_mobile/services/backup.service.dart'; import 'package:immich_mobile/services/backup_album.service.dart'; import 'package:immich_mobile/services/local_notification.service.dart'; import 'package:immich_mobile/utils/backup_progress.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_manager/photo_manager.dart' show PMProgressHandler; -import 'package:immich_mobile/utils/debug_print.dart'; final manualUploadProvider = StateNotifierProvider((ref) { return ManualUploadNotifier( @@ -294,7 +295,7 @@ class ManualUploadNotifier extends StateNotifier { ); } } else { - openAppSettings(); + unawaited(openAppSettings()); dPrint(() => "[_startUpload] Do not have permission to the gallery"); } } catch (e) { diff --git a/mobile/lib/providers/image/immich_local_image_provider.dart b/mobile/lib/providers/image/immich_local_image_provider.dart index 8c46c52906..b9e09eb357 100644 --- a/mobile/lib/providers/image/immich_local_image_provider.dart +++ b/mobile/lib/providers/image/immich_local_image_provider.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'dart:ui' as ui; import 'package:cached_network_image/cached_network_image.dart'; - import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; @@ -77,7 +76,7 @@ class ImmichLocalImageProvider extends ImageProvider { } catch (error, stack) { log.severe('Error loading local image ${asset.fileName}', error, stack); } finally { - chunkEvents.close(); + unawaited(chunkEvents.close()); } } diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 7542934426..9467f63483 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; @@ -70,7 +72,7 @@ class ActionNotifier extends Notifier { void _downloadLivePhotoCallback(TaskStatusUpdate update) async { if (update.status == TaskStatus.complete) { final livePhotosId = LivePhotosMetadata.fromJson(update.task.metaData).id; - _downloadService.saveLivePhotos(update.task, livePhotosId); + unawaited(_downloadService.saveLivePhotos(update.task, livePhotosId)); } } @@ -131,7 +133,7 @@ class ActionNotifier extends Notifier { if (assets.length > 1) { return ActionResult(count: assets.length, success: false, error: 'Cannot troubleshoot multiple assets'); } - context.pushRoute(AssetTroubleshootRoute(asset: assets.first)); + unawaited(context.pushRoute(AssetTroubleshootRoute(asset: assets.first))); return ActionResult(count: assets.length, success: true); } diff --git a/mobile/lib/providers/shared_link.provider.dart b/mobile/lib/providers/shared_link.provider.dart index f574554bcb..fb44aea203 100644 --- a/mobile/lib/providers/shared_link.provider.dart +++ b/mobile/lib/providers/shared_link.provider.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/shared_link/shared_link.model.dart'; import 'package:immich_mobile/services/shared_link.service.dart'; @@ -16,7 +18,7 @@ class SharedLinksNotifier extends StateNotifier>> { Future deleteLink(String id) async { await _sharedLinkService.deleteSharedLink(id); state = const AsyncLoading(); - fetchLinks(); + unawaited(fetchLinks()); } } diff --git a/mobile/lib/repositories/asset_media.repository.dart b/mobile/lib/repositories/asset_media.repository.dart index f71c919373..e377ff22d6 100644 --- a/mobile/lib/repositories/asset_media.repository.dart +++ b/mobile/lib/repositories/asset_media.repository.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; @@ -138,18 +139,20 @@ class AssetMediaRepository { // we dont want to await the share result since the // "preparing" dialog will not disappear until final size = context.sizeData; - Share.shareXFiles( - downloadedXFiles, - sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), - ).then((result) async { - for (var file in tempFiles) { - try { - await file.delete(); - } catch (e) { - _log.warning("Failed to delete temporary file: ${file.path}", e); + unawaited( + Share.shareXFiles( + downloadedXFiles, + sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), + ).then((result) async { + for (var file in tempFiles) { + try { + await file.delete(); + } catch (e) { + _log.warning("Failed to delete temporary file: ${file.path}", e); + } } - } - }); + }), + ); return downloadedXFiles.length; } diff --git a/mobile/lib/routing/app_navigation_observer.dart b/mobile/lib/routing/app_navigation_observer.dart index 26ec017b9a..b05a28172d 100644 --- a/mobile/lib/routing/app_navigation_observer.dart +++ b/mobile/lib/routing/app_navigation_observer.dart @@ -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'; @@ -12,7 +14,7 @@ class AppNavigationObserver extends AutoRouterObserver { @override Future didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) async { - Future(() => ref.read(inLockedViewProvider.notifier).state = false); + unawaited(Future(() => ref.read(inLockedViewProvider.notifier).state = false)); } @override diff --git a/mobile/lib/routing/auth_guard.dart b/mobile/lib/routing/auth_guard.dart index 33eb8e81ad..b0cd9ea9ea 100644 --- a/mobile/lib/routing/auth_guard.dart +++ b/mobile/lib/routing/auth_guard.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:auto_route/auto_route.dart'; @@ -26,18 +27,18 @@ class AuthGuard extends AutoRouteGuard { if (res == null || res.authStatus != true) { // If the access token is invalid, take user back to login _log.fine('User token is invalid. Redirecting to login'); - router.replaceAll([const LoginRoute()]); + unawaited(router.replaceAll([const LoginRoute()])); } } on StoreKeyNotFoundException catch (_) { // If there is no access token, take us to the login page _log.warning('No access token in the store.'); - router.replaceAll([const LoginRoute()]); + unawaited(router.replaceAll([const LoginRoute()])); return; } on ApiException catch (e) { // On an unauthorized request, take us to the login page if (e.code == HttpStatus.unauthorized) { _log.warning("Unauthorized access token."); - router.replaceAll([const LoginRoute()]); + unawaited(router.replaceAll([const LoginRoute()])); return; } } catch (e) { diff --git a/mobile/lib/routing/backup_permission_guard.dart b/mobile/lib/routing/backup_permission_guard.dart index 245a4b27af..f52516f2e5 100644 --- a/mobile/lib/routing/backup_permission_guard.dart +++ b/mobile/lib/routing/backup_permission_guard.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -13,7 +15,7 @@ class BackupPermissionGuard extends AutoRouteGuard { if (p) { resolver.next(true); } else { - router.push(const PermissionOnboardingRoute()); + unawaited(router.push(const PermissionOnboardingRoute())); } } } diff --git a/mobile/lib/routing/gallery_guard.dart b/mobile/lib/routing/gallery_guard.dart index eace8257b6..6a4b1bddab 100644 --- a/mobile/lib/routing/gallery_guard.dart +++ b/mobile/lib/routing/gallery_guard.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -13,12 +15,14 @@ class GalleryGuard extends AutoRouteGuard { // Replace instead of pushing duplicate final args = resolver.route.args as GalleryViewerRouteArgs; - router.replace( - GalleryViewerRoute( - renderList: args.renderList, - initialIndex: args.initialIndex, - heroOffset: args.heroOffset, - showStack: args.showStack, + unawaited( + router.replace( + GalleryViewerRoute( + renderList: args.renderList, + initialIndex: args.initialIndex, + heroOffset: args.heroOffset, + showStack: args.showStack, + ), ), ); // Prevent further navigation since we replaced the route diff --git a/mobile/lib/routing/locked_guard.dart b/mobile/lib/routing/locked_guard.dart index 851407ff16..ddb6a7e694 100644 --- a/mobile/lib/routing/locked_guard.dart +++ b/mobile/lib/routing/locked_guard.dart @@ -1,8 +1,9 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:flutter/services.dart'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/routing/router.dart'; - import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/local_auth.service.dart'; import 'package:immich_mobile/services/secure_storage.service.dart'; @@ -30,7 +31,7 @@ class LockedGuard extends AutoRouteGuard { /// Check if a pincode has been created but this user. Show the form to create if not exist if (!authStatus.pinCode) { - router.push(PinAuthRoute(createPinCode: true)); + unawaited(router.push(PinAuthRoute(createPinCode: true))); } if (authStatus.isElevated) { @@ -42,7 +43,7 @@ class LockedGuard extends AutoRouteGuard { /// the user has enabled the biometric authentication final securePinCode = await _secureStorageService.read(kSecuredPinCode); if (securePinCode == null) { - router.push(PinAuthRoute()); + unawaited(router.push(PinAuthRoute())); return; } @@ -74,7 +75,7 @@ class LockedGuard extends AutoRouteGuard { } on ApiException { // PIN code has changed, need to re-enter to access await _secureStorageService.delete(kSecuredPinCode); - router.push(PinAuthRoute()); + unawaited(router.push(PinAuthRoute())); } catch (error) { _log.severe("Failed to access locked page", error); resolver.next(false); diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index 9c3768080b..59b627ecc3 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -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'; @@ -50,7 +52,7 @@ class ActionService { ); Future shareLink(List remoteIds, BuildContext context) async { - context.pushRoute(SharedLinkEditRoute(assetsList: remoteIds)); + unawaited(context.pushRoute(SharedLinkEditRoute(assetsList: remoteIds))); } Future favorite(List remoteIds) async { diff --git a/mobile/lib/services/album.service.dart b/mobile/lib/services/album.service.dart index a9eee0528e..8d77b569e6 100644 --- a/mobile/lib/services/album.service.dart +++ b/mobile/lib/services/album.service.dart @@ -83,7 +83,7 @@ class AlbumService { if (selectedIds.isEmpty) { final numLocal = await _albumRepository.count(local: true); if (numLocal > 0) { - _syncService.removeAllLocalAlbumsAndAssets(); + await _syncService.removeAllLocalAlbumsAndAssets(); } return false; } diff --git a/mobile/lib/services/api.service.dart b/mobile/lib/services/api.service.dart index 4033ffb184..698ac3a159 100644 --- a/mobile/lib/services/api.service.dart +++ b/mobile/lib/services/api.service.dart @@ -6,11 +6,11 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:http/http.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/utils/url_helper.dart'; +import 'package:immich_mobile/utils/user_agent.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; -import 'package:immich_mobile/utils/user_agent.dart'; -import 'package:immich_mobile/utils/debug_print.dart'; class ApiService implements Authentication { late ApiClient _apiClient; @@ -86,7 +86,7 @@ class ApiService implements Authentication { setEndpoint(endpoint); // Save in local database for next startup - Store.put(StoreKey.serverEndpoint, endpoint); + await Store.put(StoreKey.serverEndpoint, endpoint); return endpoint; } diff --git a/mobile/lib/services/auth.service.dart b/mobile/lib/services/auth.service.dart index 91c23cac1c..3173f49957 100644 --- a/mobile/lib/services/auth.service.dart +++ b/mobile/lib/services/auth.service.dart @@ -58,7 +58,7 @@ class AuthService { Future validateServerUrl(String url) async { final validUrl = await _apiService.resolveAndSetEndpoint(url); await _apiService.setDeviceInfoHeader(); - Store.put(StoreKey.serverUrl, validUrl); + await Store.put(StoreKey.serverUrl, validUrl); return validUrl; } diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index 33a8e810f1..b69aa53014 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -291,7 +291,7 @@ class BackgroundService { case "backgroundProcessing": case "onAssetsChanged": try { - _clearErrorNotifications(); + unawaited(_clearErrorNotifications()); // iOS should time out after some threshold so it doesn't wait // indefinitely and can run later @@ -342,7 +342,7 @@ class BackgroundService { ); HttpSSLOptions.apply(); - ref.read(apiServiceProvider).setAccessToken(Store.get(StoreKey.accessToken)); + await ref.read(apiServiceProvider).setAccessToken(Store.get(StoreKey.accessToken)); await ref.read(authServiceProvider).setOpenApiServiceEndpoint(); dPrint(() => "[BG UPLOAD] Using endpoint: ${ref.read(apiServiceProvider).apiClient.basePath}"); @@ -385,7 +385,7 @@ class BackgroundService { await ref.read(backupAlbumRepositoryProvider).deleteAll(toDelete); await ref.read(backupAlbumRepositoryProvider).updateAll(toUpsert); } else if (Store.tryGet(StoreKey.backupFailedSince) == null) { - Store.put(StoreKey.backupFailedSince, DateTime.now()); + await Store.put(StoreKey.backupFailedSince, DateTime.now()); return false; } // Android should check for new assets added while performing backup @@ -412,9 +412,11 @@ class BackgroundService { try { toUpload = await backupService.removeAlreadyUploadedAssets(toUpload); } catch (e) { - _showErrorNotification( - title: "backup_background_service_error_title".tr(), - content: "backup_background_service_connection_failed_message".tr(), + unawaited( + _showErrorNotification( + title: "backup_background_service_error_title".tr(), + content: "backup_background_service_connection_failed_message".tr(), + ), ); return false; } @@ -428,13 +430,15 @@ class BackgroundService { } _assetsToUploadCount = toUpload.length; _uploadedAssetsCount = 0; - _updateNotification( - title: "backup_background_service_in_progress_notification".tr(), - content: notifyTotalProgress ? formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount) : null, - progress: 0, - max: notifyTotalProgress ? _assetsToUploadCount : 0, - indeterminate: !notifyTotalProgress, - onlyIfFG: !notifyTotalProgress, + unawaited( + _updateNotification( + title: "backup_background_service_in_progress_notification".tr(), + content: notifyTotalProgress ? formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount) : null, + progress: 0, + max: notifyTotalProgress ? _assetsToUploadCount : 0, + indeterminate: !notifyTotalProgress, + onlyIfFG: !notifyTotalProgress, + ), ); _cancellationToken = CancellationToken(); @@ -452,9 +456,11 @@ class BackgroundService { ); if (!ok && !_cancellationToken!.isCancelled) { - _showErrorNotification( - title: "backup_background_service_error_title".tr(), - content: "backup_background_service_backup_failed_message".tr(), + unawaited( + _showErrorNotification( + title: "backup_background_service_error_title".tr(), + content: "backup_background_service_backup_failed_message".tr(), + ), ); } diff --git a/mobile/lib/services/backup_verification.service.dart b/mobile/lib/services/backup_verification.service.dart index 94c4721cca..1e8d426df8 100644 --- a/mobile/lib/services/backup_verification.service.dart +++ b/mobile/lib/services/backup_verification.service.dart @@ -120,7 +120,7 @@ class BackupVerificationService { await tuple.fileMediaRepository.enableBackgroundAccess(); final ApiService apiService = ApiService(); apiService.setEndpoint(tuple.endpoint); - apiService.setAccessToken(tuple.auth); + await apiService.setAccessToken(tuple.auth); for (int i = 0; i < tuple.deleteCandidates.length; i++) { if (await _compareAssets(tuple.deleteCandidates[i], tuple.originals[i], apiService)) { result.add(tuple.deleteCandidates[i]); diff --git a/mobile/lib/services/map.service.dart b/mobile/lib/services/map.service.dart index 2d236f77ef..5b50e8a890 100644 --- a/mobile/lib/services/map.service.dart +++ b/mobile/lib/services/map.service.dart @@ -1,9 +1,9 @@ import 'package:immich_mobile/mixins/error_logger.mixin.dart'; import 'package:immich_mobile/models/map/map_marker.model.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/user_agent.dart'; import 'package:logging/logging.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; -import 'package:immich_mobile/utils/user_agent.dart'; class MapService with ErrorLoggerMixin { final ApiService _apiService; @@ -16,7 +16,7 @@ class MapService with ErrorLoggerMixin { Future _setMapUserAgentHeader() async { final userAgent = await getUserAgentString(); - setHttpHeaders({'User-Agent': userAgent}); + await setHttpHeaders({'User-Agent': userAgent}); } Future> getMapMarkers({ diff --git a/mobile/lib/services/share.service.dart b/mobile/lib/services/share.service.dart index 7ba385d71c..06a4a192d4 100644 --- a/mobile/lib/services/share.service.dart +++ b/mobile/lib/services/share.service.dart @@ -1,13 +1,15 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/response_extensions.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/extensions/response_extensions.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; + import 'api.service.dart'; final shareServiceProvider = Provider((ref) => ShareService(ref.watch(apiServiceProvider))); @@ -58,9 +60,11 @@ class ShareService { } final size = MediaQuery.of(context).size; - Share.shareXFiles( - downloadedXFiles, - sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), + unawaited( + Share.shareXFiles( + downloadedXFiles, + sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), + ), ); return true; } catch (error) { diff --git a/mobile/lib/services/sync.service.dart b/mobile/lib/services/sync.service.dart index 1a5cb2a116..f5b55f36eb 100644 --- a/mobile/lib/services/sync.service.dart +++ b/mobile/lib/services/sync.service.dart @@ -705,7 +705,7 @@ class SyncService { if (assets.isEmpty) return; if (Platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { - _toggleTrashStatusForAssets(assets); + await _toggleTrashStatusForAssets(assets); } try { diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index d46268a9d7..1ce0cf0322 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -214,7 +214,7 @@ class UploadService { void _handleTaskStatusUpdate(TaskStatusUpdate update) async { switch (update.status) { case TaskStatus.complete: - _handleLivePhoto(update); + unawaited(_handleLivePhoto(update)); if (CurrentPlatform.isIOS) { try { @@ -259,7 +259,7 @@ class UploadService { return; } - enqueueTasks([uploadTask]); + await enqueueTasks([uploadTask]); } catch (error, stackTrace) { dPrint(() => "Error handling live photo upload task: $error $stackTrace"); } diff --git a/mobile/lib/utils/map_utils.dart b/mobile/lib/utils/map_utils.dart index 80e20b7c6c..6213b214a9 100644 --- a/mobile/lib/utils/map_utils.dart +++ b/mobile/lib/utils/map_utils.dart @@ -1,8 +1,10 @@ +import 'dart:async'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; import 'package:immich_mobile/models/map/map_marker.model.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; -import 'package:geolocator/geolocator.dart'; import 'package:logging/logging.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; @@ -68,7 +70,7 @@ class MapUtils { try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled && !silent) { - showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog()); + unawaited(showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog())); return (null, LocationPermission.deniedForever); } diff --git a/mobile/lib/utils/selection_handlers.dart b/mobile/lib/utils/selection_handlers.dart index d128ef8fac..f0d333e262 100644 --- a/mobile/lib/utils/selection_handlers.dart +++ b/mobile/lib/utils/selection_handlers.dart @@ -101,7 +101,7 @@ Future handleEditDateTime(WidgetRef ref, BuildContext context, List return; } - ref.read(assetServiceProvider).changeDateTime(selection.toList(), dateTime); + await ref.read(assetServiceProvider).changeDateTime(selection.toList(), dateTime); } Future handleEditLocation(WidgetRef ref, BuildContext context, List selection) async { @@ -120,7 +120,7 @@ Future handleEditLocation(WidgetRef ref, BuildContext context, List return; } - ref.read(assetServiceProvider).changeLocation(selection.toList(), location); + await ref.read(assetServiceProvider).changeLocation(selection.toList(), location); } Future handleSetAssetsVisibility( diff --git a/mobile/lib/widgets/album/album_viewer_appbar.dart b/mobile/lib/widgets/album/album_viewer_appbar.dart index 420218d7e5..4fd4b31013 100644 --- a/mobile/lib/widgets/album/album_viewer_appbar.dart +++ b/mobile/lib/widgets/album/album_viewer_appbar.dart @@ -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'; @@ -57,7 +59,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge deleteAlbum() async { final bool success = await ref.watch(albumProvider.notifier).deleteAlbum(album); - context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); + unawaited(context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()]))); if (!success) { ImmichToast.show( @@ -105,7 +107,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge bool isSuccess = await ref.watch(albumProvider.notifier).leaveAlbum(album); if (isSuccess) { - context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); + unawaited(context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()]))); } else { context.pop(); ImmichToast.show( diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid.dart b/mobile/lib/widgets/asset_grid/multiselect_grid.dart index da2957c027..c0d8a6bea2 100644 --- a/mobile/lib/widgets/asset_grid/multiselect_grid.dart +++ b/mobile/lib/widgets/asset_grid/multiselect_grid.dart @@ -314,10 +314,10 @@ class MultiselectGrid extends HookConsumerWidget { final result = await ref.read(albumServiceProvider).createAlbumWithGeneratedName(assets); if (result != null) { - ref.watch(albumProvider.notifier).refreshRemoteAlbums(); + unawaited(ref.watch(albumProvider.notifier).refreshRemoteAlbums()); selectionEnabledHook.value = false; - context.pushRoute(AlbumViewerRoute(albumId: result.id)); + unawaited(context.pushRoute(AlbumViewerRoute(albumId: result.id))); } } finally { processing.value = false; @@ -346,7 +346,7 @@ class MultiselectGrid extends HookConsumerWidget { ); if (remoteAssets.isNotEmpty) { - handleEditDateTime(ref, context, remoteAssets.toList()); + unawaited(handleEditDateTime(ref, context, remoteAssets.toList())); } } finally { selectionEnabledHook.value = false; @@ -361,7 +361,7 @@ class MultiselectGrid extends HookConsumerWidget { ); if (remoteAssets.isNotEmpty) { - handleEditLocation(ref, context, remoteAssets.toList()); + unawaited(handleEditLocation(ref, context, remoteAssets.toList())); } } finally { selectionEnabledHook.value = false; diff --git a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart index 00f7bc494d..5707e3678f 100644 --- a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart +++ b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:auto_route/auto_route.dart'; @@ -81,7 +82,7 @@ class BottomGalleryBar extends ConsumerWidget { // to not throw the error when the next preCache index is called if (totalAssets.value == 1 || assetIndex.value == totalAssets.value - 1) { // Handle only one asset - context.maybePop(); + await context.maybePop(); } totalAssets.value -= 1; @@ -111,18 +112,20 @@ class BottomGalleryBar extends ConsumerWidget { } // Asset is permanently removed - showDialog( - context: context, - builder: (BuildContext _) { - return DeleteDialog( - onDelete: () async { - final isDeleted = await onDelete(true); - if (isDeleted) { - removeAssetFromStack(); - } - }, - ); - }, + unawaited( + showDialog( + context: context, + builder: (BuildContext _) { + return DeleteDialog( + onDelete: () async { + final isDeleted = await onDelete(true); + if (isDeleted) { + removeAssetFromStack(); + } + }, + ); + }, + ), ); } @@ -150,7 +153,7 @@ class BottomGalleryBar extends ConsumerWidget { onTap: () async { await unStack(); ctx.pop(); - context.maybePop(); + await context.maybePop(); }, title: const Text("viewer_unstack", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), @@ -178,9 +181,11 @@ class BottomGalleryBar extends ConsumerWidget { void handleEdit() async { final image = Image(image: ImmichImage.imageProvider(asset: asset)); - context.navigator.push( - MaterialPageRoute( - builder: (context) => EditImagePage(asset: asset, image: image, isEdited: false), + unawaited( + context.navigator.push( + MaterialPageRoute( + builder: (context) => EditImagePage(asset: asset, image: image, isEdited: false), + ), ), ); } diff --git a/mobile/lib/widgets/asset_viewer/cast_dialog.dart b/mobile/lib/widgets/asset_viewer/cast_dialog.dart index f7c80cca3d..d406f29a22 100644 --- a/mobile/lib/widgets/asset_viewer/cast_dialog.dart +++ b/mobile/lib/widgets/asset_viewer/cast_dialog.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -93,7 +95,7 @@ class CastDialog extends ConsumerWidget { } if (!isCurrentDevice(deviceName)) { - ref.read(castProvider.notifier).connect(type, deviceObj); + unawaited(ref.read(castProvider.notifier).connect(type, deviceObj)); } }, ); diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart index 0edafa88c5..893e534084 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart @@ -1,11 +1,12 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/widgets/map/map_thumbnail.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:url_launcher/url_launcher.dart'; -import 'package:immich_mobile/utils/debug_print.dart'; class ExifMap extends StatelessWidget { final ExifInfo exifInfo; @@ -68,7 +69,7 @@ class ExifMap extends StatelessWidget { } dPrint(() => 'Opening Map Uri: $uri'); - launchUrl(uri); + unawaited(launchUrl(uri)); }, onCreated: onMapCreated, ); diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart index e504cf0675..7233cc902e 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart @@ -1,19 +1,21 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart' hide Store; -import 'package:immich_mobile/entities/store.entity.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_profile_info.dart'; @@ -97,25 +99,27 @@ class ImmichAppBarDialog extends HookConsumerWidget { return; } - showDialog( - context: context, - builder: (BuildContext ctx) { - return ConfirmDialog( - title: "app_bar_signout_dialog_title", - content: "app_bar_signout_dialog_content", - ok: "yes", - onOk: () async { - isLoggingOut.value = true; - await ref.read(authProvider.notifier).logout().whenComplete(() => isLoggingOut.value = false); + unawaited( + showDialog( + context: context, + builder: (BuildContext ctx) { + return ConfirmDialog( + title: "app_bar_signout_dialog_title", + content: "app_bar_signout_dialog_content", + ok: "yes", + onOk: () async { + isLoggingOut.value = true; + await ref.read(authProvider.notifier).logout().whenComplete(() => isLoggingOut.value = false); - ref.read(manualUploadProvider.notifier).cancelBackup(); - ref.read(backupProvider.notifier).cancelBackup(); - ref.read(assetProvider.notifier).clearAllAssets(); - ref.read(websocketProvider.notifier).disconnect(); - context.replaceRoute(const LoginRoute()); - }, - ); - }, + ref.read(manualUploadProvider.notifier).cancelBackup(); + ref.read(backupProvider.notifier).cancelBackup(); + unawaited(ref.read(assetProvider.notifier).clearAllAssets()); + ref.read(websocketProvider.notifier).disconnect(); + unawaited(context.replaceRoute(const LoginRoute())); + }, + ); + }, + ), ); }, trailing: isLoggingOut.value diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart index 00366ca580..bc1d608b10 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -6,8 +8,8 @@ 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/providers/auth.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/upload_profile_image.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; @@ -54,7 +56,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget { ref.read(currentUserProvider.notifier).refresh(); } - ref.read(backupProvider.notifier).updateDiskInfo(); + unawaited(ref.read(backupProvider.notifier).updateDiskInfo()); } } } diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart index f100b58649..bb987d5bc0 100644 --- a/mobile/lib/widgets/forms/login/login_form.dart +++ b/mobile/lib/widgets/forms/login/login_form.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; @@ -188,17 +189,17 @@ class LoginForm extends HookConsumerWidget { final result = await ref.read(authProvider.notifier).login(emailController.text, passwordController.text); if (result.shouldChangePassword && !result.isAdmin) { - context.pushRoute(const ChangePasswordRoute()); + unawaited(context.pushRoute(const ChangePasswordRoute())); } else { final isBeta = Store.isBetaTimelineEnabled; if (isBeta) { await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission(); - handleSyncFlow(); + unawaited(handleSyncFlow()); ref.read(websocketProvider.notifier).connect(); - context.replaceRoute(const TabShellRoute()); + unawaited(context.replaceRoute(const TabShellRoute())); return; } - context.replaceRoute(const TabControllerRoute()); + unawaited(context.replaceRoute(const TabControllerRoute())); } } catch (error) { ImmichToast.show( @@ -288,15 +289,15 @@ class LoginForm extends HookConsumerWidget { final permission = ref.watch(galleryPermissionNotifier); final isBeta = Store.isBetaTimelineEnabled; if (!isBeta && (permission.isGranted || permission.isLimited)) { - ref.watch(backupProvider.notifier).resumeBackup(); + unawaited(ref.watch(backupProvider.notifier).resumeBackup()); } if (isBeta) { await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission(); - handleSyncFlow(); - context.replaceRoute(const TabShellRoute()); + unawaited(handleSyncFlow()); + unawaited(context.replaceRoute(const TabShellRoute())); return; } - context.replaceRoute(const TabControllerRoute()); + unawaited(context.replaceRoute(const TabControllerRoute())); } } catch (error, stack) { log.severe('Error logging in with OAuth: $error', stack); diff --git a/mobile/lib/widgets/map/map_bottom_sheet.dart b/mobile/lib/widgets/map/map_bottom_sheet.dart index baf85e8075..fba9e9a041 100644 --- a/mobile/lib/widgets/map/map_bottom_sheet.dart +++ b/mobile/lib/widgets/map/map_bottom_sheet.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/map/map_event.model.dart'; -import 'package:immich_mobile/widgets/map/map_asset_grid.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/utils/draggable_scroll_controller.dart'; +import 'package:immich_mobile/widgets/map/map_asset_grid.dart'; class MapBottomSheet extends HookConsumerWidget { final Stream mapEventStream; @@ -34,7 +34,11 @@ class MapBottomSheet extends HookConsumerWidget { void handleMapEvents(MapEvent event) async { if (event is MapCloseBottomSheet) { - sheetController.animateTo(0.1, duration: const Duration(milliseconds: 200), curve: Curves.linearToEaseOut); + await sheetController.animateTo( + 0.1, + duration: const Duration(milliseconds: 200), + curve: Curves.linearToEaseOut, + ); } } diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index 1fefb3dcfa..480665e614 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -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'; @@ -42,7 +44,7 @@ class BetaTimelineListTile extends ConsumerWidget { ElevatedButton( onPressed: () async { Navigator.of(context).pop(); - context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: value)]); + unawaited(context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: value)])); }, child: Text("ok".t(context: context)), ), diff --git a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart index 9fbc43a429..21e26c8f1f 100644 --- a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -102,13 +104,13 @@ class LocalNetworkPreference extends HookConsumerWidget { ), ); } else { - saveWifiName(wifiName); + unawaited(saveWifiName(wifiName)); } final serverEndpoint = ref.read(authProvider.notifier).getServerEndpoint(); if (serverEndpoint != null) { - saveLocalEndpoint(serverEndpoint); + unawaited(saveLocalEndpoint(serverEndpoint)); } } diff --git a/mobile/test/domain/services/store_service_test.dart b/mobile/test/domain/services/store_service_test.dart index d03e493843..996170b518 100644 --- a/mobile/test/domain/services/store_service_test.dart +++ b/mobile/test/domain/services/store_service_test.dart @@ -53,7 +53,7 @@ void main() { }); tearDown(() async { - sut.dispose(); + unawaited(sut.dispose()); await controller.close(); }); @@ -129,7 +129,7 @@ void main() { final stream = sut.watch(StoreKey.accessToken); final events = [_kAccessToken, _kAccessToken.toUpperCase(), null, _kAccessToken.toLowerCase()]; - expectLater(stream, emitsInOrder(events)); + unawaited(expectLater(stream, emitsInOrder(events))); for (final event in events) { valueController.add(event); diff --git a/mobile/test/infrastructure/repositories/store_repository_test.dart b/mobile/test/infrastructure/repositories/store_repository_test.dart index f6424beabc..18d41e32e0 100644 --- a/mobile/test/infrastructure/repositories/store_repository_test.dart +++ b/mobile/test/infrastructure/repositories/store_repository_test.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; @@ -99,7 +101,7 @@ void main() { final count = await db.storeValues.count(); expect(count, isNot(isZero)); await sut.deleteAll(); - expectLater(await db.storeValues.count(), isZero); + unawaited(expectLater(await db.storeValues.count(), isZero)); }); }); @@ -124,29 +126,31 @@ void main() { test('watch()', () async { final stream = sut.watch(StoreKey.version); - expectLater(stream, emitsInOrder([_kTestVersion, _kTestVersion + 10])); + unawaited(expectLater(stream, emitsInOrder([_kTestVersion, _kTestVersion + 10]))); await pumpEventQueue(); await sut.upsert(StoreKey.version, _kTestVersion + 10); }); test('watchAll()', () async { final stream = sut.watchAll(); - expectLater( - stream, - emitsInOrder([ - [ - const StoreDto(StoreKey.version, _kTestVersion), - StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed), - const StoreDto(StoreKey.accessToken, _kTestAccessToken), - const StoreDto(StoreKey.colorfulInterface, _kTestColorfulInterface), - ], - [ - const StoreDto(StoreKey.version, _kTestVersion + 10), - StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed), - const StoreDto(StoreKey.accessToken, _kTestAccessToken), - const StoreDto(StoreKey.colorfulInterface, _kTestColorfulInterface), - ], - ]), + unawaited( + expectLater( + stream, + emitsInOrder([ + [ + const StoreDto(StoreKey.version, _kTestVersion), + StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed), + const StoreDto(StoreKey.accessToken, _kTestAccessToken), + const StoreDto(StoreKey.colorfulInterface, _kTestColorfulInterface), + ], + [ + const StoreDto(StoreKey.version, _kTestVersion + 10), + StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed), + const StoreDto(StoreKey.accessToken, _kTestAccessToken), + const StoreDto(StoreKey.colorfulInterface, _kTestColorfulInterface), + ], + ]), + ), ); await sut.upsert(StoreKey.version, _kTestVersion + 10); }); diff --git a/mobile/test/modules/activity/activities_page_test.dart b/mobile/test/modules/activity/activities_page_test.dart index 05eac98111..39350530ea 100644 --- a/mobile/test/modules/activity/activities_page_test.dart +++ b/mobile/test/modules/activity/activities_page_test.dart @@ -64,9 +64,9 @@ void main() { TestUtils.init(); db = await TestUtils.initIsar(); await StoreService.init(storeRepository: IsarStoreRepository(db)); - Store.put(StoreKey.currentUser, UserStub.admin); - Store.put(StoreKey.serverEndpoint, ''); - Store.put(StoreKey.accessToken, ''); + await Store.put(StoreKey.currentUser, UserStub.admin); + await Store.put(StoreKey.serverEndpoint, ''); + await Store.put(StoreKey.accessToken, ''); }); setUp(() async { diff --git a/mobile/test/modules/activity/activity_text_field_test.dart b/mobile/test/modules/activity/activity_text_field_test.dart index 1163330c54..8f28b7f28e 100644 --- a/mobile/test/modules/activity/activity_text_field_test.dart +++ b/mobile/test/modules/activity/activity_text_field_test.dart @@ -35,8 +35,8 @@ void main() { TestUtils.init(); db = await TestUtils.initIsar(); await StoreService.init(storeRepository: IsarStoreRepository(db)); - Store.put(StoreKey.currentUser, UserStub.admin); - Store.put(StoreKey.serverEndpoint, ''); + await Store.put(StoreKey.currentUser, UserStub.admin); + await Store.put(StoreKey.serverEndpoint, ''); }); setUp(() { diff --git a/mobile/test/modules/activity/activity_tile_test.dart b/mobile/test/modules/activity/activity_tile_test.dart index eb4bb25848..718dfcce21 100644 --- a/mobile/test/modules/activity/activity_tile_test.dart +++ b/mobile/test/modules/activity/activity_tile_test.dart @@ -31,9 +31,9 @@ void main() { db = await TestUtils.initIsar(); // For UserCircleAvatar await StoreService.init(storeRepository: IsarStoreRepository(db)); - Store.put(StoreKey.currentUser, UserStub.admin); - Store.put(StoreKey.serverEndpoint, ''); - Store.put(StoreKey.accessToken, ''); + await Store.put(StoreKey.currentUser, UserStub.admin); + await Store.put(StoreKey.serverEndpoint, ''); + await Store.put(StoreKey.accessToken, ''); }); setUp(() { diff --git a/mobile/test/modules/utils/async_mutex_test.dart b/mobile/test/modules/utils/async_mutex_test.dart index d50567721b..08cafeb307 100644 --- a/mobile/test/modules/utils/async_mutex_test.dart +++ b/mobile/test/modules/utils/async_mutex_test.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/utils/async_mutex.dart'; @@ -7,11 +9,11 @@ void main() { AsyncMutex lock = AsyncMutex(); List events = []; expect(0, lock.enqueued); - lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(1))); + unawaited(lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(1)))); expect(1, lock.enqueued); - lock.run(() => Future.delayed(const Duration(milliseconds: 3), () => events.add(2))); + unawaited(lock.run(() => Future.delayed(const Duration(milliseconds: 3), () => events.add(2)))); expect(2, lock.enqueued); - lock.run(() => Future.delayed(const Duration(milliseconds: 1), () => events.add(3))); + unawaited(lock.run(() => Future.delayed(const Duration(milliseconds: 1), () => events.add(3)))); expect(3, lock.enqueued); await lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(4))); expect(0, lock.enqueued);