fix: mobile unawaited_futures lint (#21661)

* chore: add unawaited_futures lint as warning

# Conflicts:
#	mobile/analysis_options.yaml

* remove unused dcm lints

They will be added back later on a case by case basis

* fix warning

# Conflicts:
#	mobile/lib/presentation/pages/drift_remote_album.page.dart

* auto gen file

* review changes

* conflict resolution

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong 2025-10-27 20:02:52 +05:30 committed by GitHub
parent 664a8fa499
commit ac0d646401
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
88 changed files with 491 additions and 538 deletions

View File

@ -17,7 +17,7 @@ linter:
# section below to disable rules from the `package:flutter_lints/flutter.yaml` # 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 # included above or to enable additional rules. A list of all available lints
# and their documentation is published at # 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 # 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 # section below, it can also be suppressed for a single line of code
@ -28,6 +28,7 @@ linter:
rules: rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule # avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
unawaited_futures: true
use_build_context_synchronously: false use_build_context_synchronously: false
require_trailing_commas: true require_trailing_commas: true
unrelated_type_equality_checks: true unrelated_type_equality_checks: true
@ -46,6 +47,8 @@ analyzer:
# TODO: Re-enable after upgrading custom_lint # TODO: Re-enable after upgrading custom_lint
# plugins: # plugins:
# - custom_lint # - custom_lint
errors:
unawaited_futures: warning
custom_lint: custom_lint:
debug: true debug: true
@ -152,160 +155,6 @@ dart_code_metrics:
# - avoid-passing-async-when-sync-expected # - avoid-passing-async-when-sync-expected
# - avoid-throw-in-catch-block # - avoid-throw-in-catch-block
- avoid-unused-parameters - 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-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-declaring-const-constructor
# - prefer-single-widget-per-file
- prefer-switch-expression - prefer-switch-expression
# - prefer-prefixed-global-constants
# - prefer-correct-callback-field-name

View File

@ -114,10 +114,10 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
configureFileDownloaderNotifications(); configureFileDownloaderNotifications();
// Notify the host that the background worker service has been initialized and is ready to use // Notify the host that the background worker service has been initialized and is ready to use
_backgroundHostApi.onInitialized(); unawaited(_backgroundHostApi.onInitialized());
} catch (error, stack) { } catch (error, stack) {
_logger.severe("Failed to initialize background worker", error, stack); _logger.severe("Failed to initialize background worker", error, stack);
_backgroundHostApi.close(); unawaited(_backgroundHostApi.close());
} }
} }

View File

@ -249,7 +249,7 @@ class LocalSyncService {
if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) { if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) {
_log.fine("No asset changes detected in album ${deviceAlbum.name}. Updating metadata."); _log.fine("No asset changes detected in album ${deviceAlbum.name}. Updating metadata.");
_localAlbumRepository.upsert(updatedDeviceAlbum); await _localAlbumRepository.upsert(updatedDeviceAlbum);
return true; return true;
} }

View File

@ -68,7 +68,7 @@ class RemoteImageRequest extends ImageRequest {
final cacheManager = this.cacheManager; final cacheManager = this.cacheManager;
final streamController = StreamController<List<int>>(sync: true); final streamController = StreamController<List<int>>(sync: true);
final Stream<List<int>> stream; final Stream<List<int>> stream;
cacheManager?.putStreamedFile(url, streamController.stream); unawaited(cacheManager?.putStreamedFile(url, streamController.stream));
stream = response.map((chunk) { stream = response.map((chunk) {
if (_isCancelled) { if (_isCancelled) {
throw StateError('Cancelled request'); throw StateError('Cancelled request');
@ -81,11 +81,11 @@ class RemoteImageRequest extends ImageRequest {
try { try {
final Uint8List bytes = await _downloadBytes(stream, response.contentLength); final Uint8List bytes = await _downloadBytes(stream, response.contentLength);
streamController.close(); unawaited(streamController.close());
return await ImmutableBuffer.fromUint8List(bytes); return await ImmutableBuffer.fromUint8List(bytes);
} catch (e) { } catch (e) {
streamController.addError(e); streamController.addError(e);
streamController.close(); unawaited(streamController.close());
if (_isCancelled) { if (_isCancelled) {
return null; return null;
} }
@ -143,7 +143,7 @@ class RemoteImageRequest extends ImageRequest {
return await _decodeBuffer(buffer, decode, scale); return await _decodeBuffer(buffer, decode, scale);
} catch (e) { } catch (e) {
log.severe('Failed to decode cached image', e); log.severe('Failed to decode cached image', e);
_evictFile(url); unawaited(_evictFile(url));
return null; return null;
} }
} }

View File

@ -361,15 +361,13 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
return _db.managers.localAlbumEntity.count(); return _db.managers.localAlbumEntity.count();
} }
Future unlinkRemoteAlbum(String id) async { Future<void> unlinkRemoteAlbum(String id) async {
return _db.localAlbumEntity.update() final query = _db.localAlbumEntity.update()..where((row) => row.id.equals(id));
..where((row) => row.id.equals(id)) await query.write(const LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(null)));
..write(const LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(null)));
} }
Future linkRemoteAlbum(String localAlbumId, String remoteAlbumId) async { Future<void> linkRemoteAlbum(String localAlbumId, String remoteAlbumId) async {
return _db.localAlbumEntity.update() final query = _db.localAlbumEntity.update()..where((row) => row.id.equals(localAlbumId));
..where((row) => row.id.equals(localAlbumId)) await query.write(LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(remoteAlbumId)));
..write(LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(remoteAlbumId)));
} }
} }

View File

@ -159,7 +159,7 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
// Draw the app from edge to edge // Draw the app from edge to edge
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); unawaited(SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge));
// Sets the navigation bar color // Sets the navigation bar color
SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent); SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent);

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -51,7 +53,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
final isSuccess = await ref.read(albumProvider.notifier).leaveAlbum(album); final isSuccess = await ref.read(albumProvider.notifier).leaveAlbum(album);
if (isSuccess) { if (isSuccess) {
context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); unawaited(context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])));
} else { } else {
showErrorMessage(); showErrorMessage();
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -29,8 +31,8 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
if (newAlbum != null) { if (newAlbum != null) {
ref.watch(albumTitleProvider.notifier).clearAlbumTitle(); ref.watch(albumTitleProvider.notifier).clearAlbumTitle();
context.maybePop(true); unawaited(context.maybePop(true));
context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); unawaited(context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])));
} }
ScaffoldMessenger( ScaffoldMessenger(
@ -109,8 +111,8 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
centerTitle: false, centerTitle: false,
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.close_rounded), icon: const Icon(Icons.close_rounded),
onPressed: () async { onPressed: () {
context.maybePop(); unawaited(context.maybePop());
}, },
), ),
actions: [ actions: [

View File

@ -155,7 +155,7 @@ class BackupControllerPage extends HookConsumerWidget {
// waited until returning from selection // waited until returning from selection
await ref.read(backupProvider.notifier).backupAlbumSelectionDone(); await ref.read(backupProvider.notifier).backupAlbumSelectionDone();
// waited until backup albums are stored in DB // 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(), child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(),
), ),

View File

@ -270,7 +270,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget {
if (currentUser == null) { if (currentUser == null) {
return; 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(), child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(),
), ),

View File

@ -170,8 +170,8 @@ class DriftUploadDetailPage extends ConsumerWidget {
); );
} }
Future<void> _showFileDetailDialog(BuildContext context, DriftUploadStatus item) async { Future<void> _showFileDetailDialog(BuildContext context, DriftUploadStatus item) {
showDialog( return showDialog(
context: context, context: context,
builder: (context) => FileDetailDialog(uploadStatus: item), builder: (context) => FileDetailDialog(uploadStatus: item),
); );

View File

@ -33,7 +33,7 @@ class ActivitiesPage extends HookConsumerWidget {
Future<void> onAddComment(String comment) async { Future<void> onAddComment(String comment) async {
await activityNotifier.addComment(comment); await activityNotifier.addComment(comment);
// Scroll to the end of the list to show the newly added activity // Scroll to the end of the list to show the newly added activity
listViewScrollController.animateTo( await listViewScrollController.animateTo(
listViewScrollController.position.maxScrollExtent + 200, listViewScrollController.position.maxScrollExtent + 200,
duration: const Duration(milliseconds: 600), duration: const Duration(milliseconds: 600),
curve: Curves.fastOutSlowIn, curve: Curves.fastOutSlowIn,

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -170,11 +172,11 @@ class CreateAlbumPage extends HookConsumerWidget {
.createAlbum(ref.read(albumTitleProvider), selectedAssets.value); .createAlbum(ref.read(albumTitleProvider), selectedAssets.value);
if (newAlbum != null) { if (newAlbum != null) {
ref.read(albumProvider.notifier).refreshRemoteAlbums(); await ref.read(albumProvider.notifier).refreshRemoteAlbums();
selectedAssets.value = {}; selectedAssets.value = {};
ref.read(albumTitleProvider.notifier).clearAlbumTitle(); ref.read(albumTitleProvider.notifier).clearAlbumTitle();
ref.read(albumViewerProvider.notifier).disableEditAlbum(); ref.read(albumViewerProvider.notifier).disableEditAlbum();
context.replaceRoute(AlbumViewerRoute(albumId: newAlbum.id)); unawaited(context.replaceRoute(AlbumViewerRoute(albumId: newAlbum.id)));
} }
} }

View File

@ -95,7 +95,7 @@ class GalleryViewerPage extends HookConsumerWidget {
} catch (e) { } catch (e) {
// swallow error silently // swallow error silently
log.severe('Error precaching next image: $e'); log.severe('Error precaching next image: $e');
context.maybePop(); await context.maybePop();
} }
} }

View File

@ -267,11 +267,13 @@ class NativeVideoViewerPage extends HookConsumerWidget {
nc.onPlaybackReady.addListener(onPlaybackReady); nc.onPlaybackReady.addListener(onPlaybackReady);
nc.onPlaybackEnded.addListener(onPlaybackEnded); nc.onPlaybackEnded.addListener(onPlaybackEnded);
nc.loadVideoSource(source).catchError((error) { unawaited(
log.severe('Error loading video source: $error'); nc.loadVideoSource(source).catchError((error) {
}); log.severe('Error loading video source: $error');
}),
);
final loopVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo); final loopVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo);
nc.setLoop(loopVideo); unawaited(nc.setLoop(loopVideo));
controller.value = nc; controller.value = nc;
Timer(const Duration(milliseconds: 200), checkIfBuffering); Timer(const Duration(milliseconds: 200), checkIfBuffering);
@ -342,12 +344,12 @@ class NativeVideoViewerPage extends HookConsumerWidget {
useOnAppLifecycleStateChange((_, state) async { useOnAppLifecycleStateChange((_, state) async {
if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) {
controller.value?.play(); await controller.value?.play();
} else if (state == AppLifecycleState.paused) { } else if (state == AppLifecycleState.paused) {
final videoPlaying = await controller.value?.isPlaying(); final videoPlaying = await controller.value?.isPlaying();
if (videoPlaying ?? true) { if (videoPlaying ?? true) {
shouldPlayOnForeground.value = true; shouldPlayOnForeground.value = true;
controller.value?.pause(); await controller.value?.pause();
} else { } else {
shouldPlayOnForeground.value = false; shouldPlayOnForeground.value = false;
} }

View File

@ -55,48 +55,50 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
final backgroundManager = ref.read(backgroundSyncProvider); final backgroundManager = ref.read(backgroundSyncProvider);
final backupProvider = ref.read(driftBackupProvider.notifier); final backupProvider = ref.read(driftBackupProvider.notifier);
ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then( unawaited(
(_) async { ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then(
try { (_) async {
wsProvider.connect(); try {
infoProvider.getServerInfo(); wsProvider.connect();
unawaited(infoProvider.getServerInfo());
if (Store.isBetaTimelineEnabled) { if (Store.isBetaTimelineEnabled) {
bool syncSuccess = false; bool syncSuccess = false;
await Future.wait([
backgroundManager.syncLocal(),
backgroundManager.syncRemote().then((success) => syncSuccess = success),
]);
if (syncSuccess) {
await Future.wait([ await Future.wait([
backgroundManager.hashAssets().then((_) { backgroundManager.syncLocal(),
_resumeBackup(backupProvider); backgroundManager.syncRemote().then((success) => syncSuccess = success),
}),
_resumeBackup(backupProvider),
]); ]);
} else {
await backgroundManager.hashAssets();
}
if (Store.get(StoreKey.syncAlbums, false)) { if (syncSuccess) {
await backgroundManager.syncLinkedAlbum(); 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(),
onError: (exception) => { context.replaceRoute(const LoginRoute()),
log.severe('Failed to update auth info with access token: $accessToken'), },
ref.read(authProvider.notifier).logout(), ),
context.replaceRoute(const LoginRoute()),
},
); );
} else { } else {
log.severe('Missing crucial offline login info - Logging out completely'); log.severe('Missing crucial offline login info - Logging out completely');
ref.read(authProvider.notifier).logout(); unawaited(ref.read(authProvider.notifier).logout());
context.replaceRoute(const LoginRoute()); unawaited(context.replaceRoute(const LoginRoute()));
return; return;
} }
@ -106,11 +108,11 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
final needBetaMigration = Store.get(StoreKey.needBetaMigration, false); final needBetaMigration = Store.get(StoreKey.needBetaMigration, false);
if (needBetaMigration) { if (needBetaMigration) {
await Store.put(StoreKey.needBetaMigration, false); await Store.put(StoreKey.needBetaMigration, false);
context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: true)]); unawaited(context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: true)]));
return; return;
} }
context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()); unawaited(context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()));
} }
if (Store.isBetaTimelineEnabled) { if (Store.isBetaTimelineEnabled) {
@ -120,7 +122,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
final hasPermission = await ref.read(galleryPermissionNotifier.notifier).hasPermission; final hasPermission = await ref.read(galleryPermissionNotifier.notifier).hasPermission;
if (hasPermission) { if (hasPermission) {
// Resume backup (if enable) then navigate // 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<SplashScreenPage> {
if (isEnableBackup) { if (isEnableBackup) {
final currentUser = Store.tryGet(StoreKey.currentUser); final currentUser = Store.tryGet(StoreKey.currentUser);
if (currentUser != null) { if (currentUser != null) {
notifier.handleBackupResume(currentUser.id); unawaited(notifier.handleBackupResume(currentUser.id));
} }
} }
} }

View File

@ -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: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: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/extensions/build_context_extensions.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart'; import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'edit.page.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. /// A widget for cropping an image.
/// This widget uses [HookWidget] to manage its lifecycle and state. It allows /// 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), icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24),
onPressed: () async { onPressed: () async {
final croppedImage = await cropController.croppedImage(); final croppedImage = await cropController.croppedImage();
context.pushRoute(EditImageRoute(asset: asset, image: croppedImage, isEdited: true)); unawaited(context.pushRoute(EditImageRoute(asset: asset, image: croppedImage, isEdited: true)));
}, },
), ),
], ],

View File

@ -1,12 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui; 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/material.dart';
import 'package:flutter_hooks/flutter_hooks.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:immich_mobile/constants/filters.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:auto_route/auto_route.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
/// A widget for filtering an image. /// A widget for filtering an image.
@ -74,7 +75,7 @@ class FilterImagePage extends HookWidget {
icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24),
onPressed: () async { onPressed: () async {
final filteredImage = await applyFilterAndConvert(colorFilter.value); 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)));
}, },
), ),
], ],

View File

@ -1,14 +1,16 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart' show useState; import 'package:flutter_hooks/flutter_hooks.dart' show useState;
import 'package:hooks_riverpod/hooks_riverpod.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/extensions/build_context_extensions.dart';
import 'package:immich_mobile/providers/local_auth.provider.dart'; import 'package:immich_mobile/providers/local_auth.provider.dart';
import 'package:immich_mobile/routing/router.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_registration_form.dart';
import 'package:immich_mobile/widgets/forms/pin_verification_form.dart'; import 'package:immich_mobile/widgets/forms/pin_verification_form.dart';
import 'package:immich_mobile/entities/store.entity.dart';
@RoutePage() @RoutePage()
class PinAuthPage extends HookConsumerWidget { class PinAuthPage extends HookConsumerWidget {
@ -35,9 +37,9 @@ class PinAuthPage extends HookConsumerWidget {
); );
if (isBetaTimeline) { if (isBetaTimeline) {
context.replaceRoute(const DriftLockedFolderRoute()); unawaited(context.replaceRoute(const DriftLockedFolderRoute()));
} else { } else {
context.replaceRoute(const LockedRoute()); unawaited(context.replaceRoute(const LockedRoute()));
} }
} }
} }

View File

@ -333,7 +333,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
changeExpiry: changeExpiry, changeExpiry: changeExpiry,
); );
ref.invalidate(sharedLinksStateProvider); ref.invalidate(sharedLinksStateProvider);
context.maybePop(); await context.maybePop();
} }
return Scaffold( return Scaffold(

View File

@ -82,10 +82,12 @@ class PhotosPage extends HookConsumerWidget {
final fullRefresh = refreshCount.value > 0; final fullRefresh = refreshCount.value > 0;
if (fullRefresh) { if (fullRefresh) {
Future.wait([ unawaited(
ref.read(assetProvider.notifier).getAllAsset(clear: true), Future.wait([
ref.read(albumProvider.notifier).refreshRemoteAlbums(), ref.read(assetProvider.notifier).getAllAsset(clear: true),
]); ref.read(albumProvider.notifier).refreshRemoteAlbums(),
]),
);
// refresh was forced: user requested another refresh within 2 seconds // refresh was forced: user requested another refresh within 2 seconds
refreshCount.value = 0; refreshCount.value = 0;

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
@ -83,7 +84,7 @@ class MapPage extends HookConsumerWidget {
isLoading.value = true; isLoading.value = true;
markers.value = await ref.read(mapMarkersProvider.future); markers.value = await ref.read(mapMarkersProvider.future);
assetsDebouncer.run(updateAssetsInBounds); assetsDebouncer.run(updateAssetsInBounds);
reloadLayers(); await reloadLayers();
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }
@ -128,7 +129,7 @@ class MapPage extends HookConsumerWidget {
); );
if (marker != null) { if (marker != null) {
updateAssetMarkerPosition(marker); await updateAssetMarkerPosition(marker);
} else { } else {
// If no asset was previously selected and no new asset is available, close the bottom sheet // If no asset was previously selected and no new asset is available, close the bottom sheet
if (selectedMarker.value == null) { if (selectedMarker.value == null) {
@ -165,7 +166,7 @@ class MapPage extends HookConsumerWidget {
if (asset.isVideo) { if (asset.isVideo) {
ref.read(showControlsProvider.notifier).show = false; 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 /// BOTTOM SHEET CALLBACKS
@ -209,7 +210,7 @@ class MapPage extends HookConsumerWidget {
} }
if (mapController.value != null && location != null) { if (mapController.value != null && location != null) {
mapController.value!.animateCamera( await mapController.value!.animateCamera(
CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), mapZoomToAssetLevel), CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), mapZoomToAssetLevel),
duration: const Duration(milliseconds: 800), duration: const Duration(milliseconds: 800),
); );

View File

@ -8,9 +8,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/maplibrecontroller_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:immich_mobile/widgets/map/map_theme_override.dart';
import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:maplibre_gl/maplibre_gl.dart';
import 'package:immich_mobile/utils/map_utils.dart';
@RoutePage() @RoutePage()
class MapLocationPickerPage extends HookConsumerWidget { class MapLocationPickerPage extends HookConsumerWidget {
@ -30,7 +30,7 @@ class MapLocationPickerPage extends HookConsumerWidget {
Future<void> onMapClick(Point<num> point, LatLng centre) async { Future<void> onMapClick(Point<num> point, LatLng centre) async {
selectedLatLng.value = centre; selectedLatLng.value = centre;
controller.value?.animateCamera(CameraUpdate.newLatLng(centre)); await controller.value?.animateCamera(CameraUpdate.newLatLng(centre));
if (marker.value != null) { if (marker.value != null) {
await controller.value?.updateSymbol(marker.value!, SymbolOptions(geometry: centre)); await controller.value?.updateSymbol(marker.value!, SymbolOptions(geometry: centre));
} }
@ -49,7 +49,7 @@ class MapLocationPickerPage extends HookConsumerWidget {
var currentLatLng = LatLng(currentLocation.latitude, currentLocation.longitude); var currentLatLng = LatLng(currentLocation.latitude, currentLocation.longitude);
selectedLatLng.value = currentLatLng; selectedLatLng.value = currentLatLng;
controller.value?.animateCamera(CameraUpdate.newLatLng(currentLatLng)); await controller.value?.animateCamera(CameraUpdate.newLatLng(currentLatLng));
} }
return MapThemeOverride( return MapThemeOverride(

View File

@ -266,7 +266,7 @@ class SearchPage extends HookConsumerWidget {
filter.value = filter.value.copyWith(date: SearchDateFilter()); filter.value = filter.value.copyWith(date: SearchDateFilter());
dateRangeCurrentFilterWidget.value = null; dateRangeCurrentFilterWidget.value = null;
search(); unawaited(search());
return; return;
} }
@ -295,7 +295,7 @@ class SearchPage extends HookConsumerWidget {
); );
} }
search(); unawaited(search());
} }
// MEDIA PICKER // MEDIA PICKER

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -47,7 +49,7 @@ class DriftAlbumOptionsPage extends HookConsumerWidget {
void leaveAlbum() async { void leaveAlbum() async {
try { try {
await ref.read(remoteAlbumProvider.notifier).leaveAlbum(album.id, userId: userId); await ref.read(remoteAlbumProvider.notifier).leaveAlbum(album.id, userId: userId);
context.navigateTo(const DriftAlbumsRoute()); unawaited(context.navigateTo(const DriftAlbumsRoute()));
} catch (_) { } catch (_) {
showErrorMessage(); showErrorMessage();
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -179,7 +181,7 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
if (album != null) { if (album != null) {
ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album);
context.replaceRoute(RemoteAlbumRoute(album: album)); unawaited(context.replaceRoute(RemoteAlbumRoute(album: album)));
} }
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -139,7 +141,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
toastType: ToastType.success, toastType: ToastType.success,
); );
context.pushRoute(const DriftAlbumsRoute()); unawaited(context.pushRoute(const DriftAlbumsRoute()));
} catch (e) { } catch (e) {
ImmichToast.show( ImmichToast.show(
context: context, context: context,
@ -161,12 +163,12 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
setState(() { setState(() {
_album = _album.copyWith(name: result.name, description: result.description ?? ''); _album = _album.copyWith(name: result.name, description: result.description ?? '');
}); });
HapticFeedback.mediumImpact(); unawaited(HapticFeedback.mediumImpact());
} }
} }
Future<void> showActivity(BuildContext context) async { Future<void> showActivity(BuildContext context) async {
context.pushRoute(const DriftActivitiesRoute()); unawaited(context.pushRoute(const DriftActivitiesRoute()));
} }
Future<void> showOptionSheet(BuildContext context) async { Future<void> showOptionSheet(BuildContext context) async {
@ -175,56 +177,58 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
final canAddPhotos = final canAddPhotos =
await ref.read(remoteAlbumServiceProvider).getUserRole(_album.id, user?.id ?? '') == AlbumUserRole.editor; await ref.read(remoteAlbumServiceProvider).getUserRole(_album.id, user?.id ?? '') == AlbumUserRole.editor;
showModalBottomSheet( unawaited(
context: context, showModalBottomSheet(
backgroundColor: context.colorScheme.surface, context: context,
isScrollControlled: false, backgroundColor: context.colorScheme.surface,
builder: (context) { isScrollControlled: false,
return DriftRemoteAlbumOption( builder: (context) {
onDeleteAlbum: isOwner return DriftRemoteAlbumOption(
? () async { onDeleteAlbum: isOwner
await deleteAlbum(context); ? () async {
if (context.mounted) { await deleteAlbum(context);
if (context.mounted) {
context.pop();
}
}
: null,
onAddUsers: isOwner
? () async {
await addUsers(context);
context.pop(); context.pop();
} }
} : null,
: null, onAddPhotos: isOwner || canAddPhotos
onAddUsers: isOwner ? () async {
? () async { await addAssets(context);
await addUsers(context); context.pop();
context.pop(); }
} : null,
: null, onToggleAlbumOrder: isOwner
onAddPhotos: isOwner || canAddPhotos ? () async {
? () async { await toggleAlbumOrder();
await addAssets(context); context.pop();
context.pop(); }
} : null,
: null, onEditAlbum: isOwner
onToggleAlbumOrder: isOwner ? () async {
? () async { context.pop();
await toggleAlbumOrder(); await showEditTitleAndDescription(context);
context.pop(); }
} : null,
: null, onCreateSharedLink: isOwner
onEditAlbum: isOwner ? () async {
? () async { context.pop();
context.pop(); unawaited(context.pushRoute(SharedLinkEditRoute(albumId: _album.id)));
await showEditTitleAndDescription(context); }
} : null,
: null, onShowOptions: () {
onCreateSharedLink: isOwner context.pop();
? () async { context.pushRoute(const DriftAlbumOptionsRoute());
context.pop(); },
context.pushRoute(SharedLinkEditRoute(albumId: _album.id)); );
} },
: null, ),
onShowOptions: () {
context.pop();
context.pushRoute(const DriftAlbumOptionsRoute());
},
);
},
); );
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:crop_image/crop_image.dart'; import 'package:crop_image/crop_image.dart';
import 'package:easy_localization/easy_localization.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), icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24),
onPressed: () async { onPressed: () async {
final croppedImage = await cropController.croppedImage(); final croppedImage = await cropController.croppedImage();
context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true)); unawaited(context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true)));
}, },
), ),
], ],

View File

@ -70,7 +70,7 @@ class DriftEditImagePage extends ConsumerWidget {
Logger("SaveEditedImage").warning("Failed to retrieve the saved image back from OS", e); 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); _exitEditing(context);
ImmichToast.show(durationInSecond: 3, context: context, msg: 'Image Saved!'); ImmichToast.show(durationInSecond: 3, context: context, msg: 'Image Saved!');

View File

@ -75,7 +75,7 @@ class DriftFilterImagePage extends HookWidget {
icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24),
onPressed: () async { onPressed: () async {
final filteredImage = await applyFilterAndConvert(colorFilter.value); 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)));
}, },
), ),
], ],

View File

@ -271,7 +271,7 @@ class DriftSearchPage extends HookConsumerWidget {
filter.value = filter.value.copyWith(date: SearchDateFilter()); filter.value = filter.value.copyWith(date: SearchDateFilter());
dateRangeCurrentFilterWidget.value = null; dateRangeCurrentFilterWidget.value = null;
search(); unawaited(search());
return; return;
} }
@ -301,7 +301,7 @@ class DriftSearchPage extends HookConsumerWidget {
); );
} }
search(); unawaited(search());
} }
// MEDIA PICKER // MEDIA PICKER

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/constants/enums.dart';
@ -15,7 +17,7 @@ class AdvancedInfoActionButton extends ConsumerWidget {
return; return;
} }
ref.read(actionProvider.notifier).troubleshoot(source, context); unawaited(ref.read(actionProvider.notifier).troubleshoot(source, context));
} }
@override @override

View File

@ -39,7 +39,7 @@ class ShareActionButton extends ConsumerWidget {
return; return;
} }
showDialog( await showDialog(
context: context, context: context,
builder: (BuildContext buildContext) { builder: (BuildContext buildContext) {
ref.read(actionProvider.notifier).shareAssets(source, context).then((ActionResult result) { ref.read(actionProvider.notifier).shareAssets(source, context).then((ActionResult result) {

View File

@ -121,7 +121,7 @@ class _AlbumSelectorState extends ConsumerState<AlbumSelector> {
// we need to re-filter the albums after sorting // we need to re-filter the albums after sorting
// so shownAlbums gets updated // so shownAlbums gets updated
filterAlbums(); unawaited(filterAlbums());
} }
Future<void> filterAlbums() async { Future<void> filterAlbums() async {
@ -711,7 +711,7 @@ class AddToAlbumHeader extends ConsumerWidget {
ref.read(currentRemoteAlbumProvider.notifier).setAlbum(newAlbum); ref.read(currentRemoteAlbumProvider.notifier).setAlbum(newAlbum);
ref.read(multiSelectProvider.notifier).reset(); ref.read(multiSelectProvider.notifier).reset();
context.pushRoute(RemoteAlbumRoute(album: newAlbum)); unawaited(context.pushRoute(RemoteAlbumRoute(album: newAlbum)));
} }
return SliverPadding( return SliverPadding(

View File

@ -635,9 +635,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
// Listen for control visibility changes and change system UI mode accordingly // Listen for control visibility changes and change system UI mode accordingly
ref.listen(assetViewerProvider.select((value) => value.showingControls), (_, showingControls) async { ref.listen(assetViewerProvider.select((value) => value.showingControls), (_, showingControls) async {
if (showingControls) { if (showingControls) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); unawaited(SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge));
} else { } else {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); unawaited(SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky));
} }
}); });

View File

@ -295,11 +295,13 @@ class NativeVideoViewer extends HookConsumerWidget {
nc.onPlaybackReady.addListener(onPlaybackReady); nc.onPlaybackReady.addListener(onPlaybackReady);
nc.onPlaybackEnded.addListener(onPlaybackEnded); nc.onPlaybackEnded.addListener(onPlaybackEnded);
nc.loadVideoSource(source).catchError((error) { unawaited(
log.severe('Error loading video source: $error'); nc.loadVideoSource(source).catchError((error) {
}); log.severe('Error loading video source: $error');
}),
);
final loopVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo); final loopVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo);
nc.setLoop(!asset.isMotionPhoto && loopVideo); unawaited(nc.setLoop(!asset.isMotionPhoto && loopVideo));
controller.value = nc; controller.value = nc;
Timer(const Duration(milliseconds: 200), checkIfBuffering); Timer(const Duration(milliseconds: 200), checkIfBuffering);
@ -373,12 +375,12 @@ class NativeVideoViewer extends HookConsumerWidget {
useOnAppLifecycleStateChange((_, state) async { useOnAppLifecycleStateChange((_, state) async {
if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) {
controller.value?.play(); await controller.value?.play();
} else if (state == AppLifecycleState.paused) { } else if (state == AppLifecycleState.paused) {
final videoPlaying = await controller.value?.isPlaying(); final videoPlaying = await controller.value?.isPlaying();
if (videoPlaying ?? true) { if (videoPlaying ?? true) {
shouldPlayOnForeground.value = true; shouldPlayOnForeground.value = true;
controller.value?.pause(); await controller.value?.pause();
} else { } else {
shouldPlayOnForeground.value = false; shouldPlayOnForeground.value = false;
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:async/async.dart'; import 'package:async/async.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
@ -51,14 +53,14 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
Stream<ImageInfo> loadRequest(ImageRequest request, ImageDecoderCallback decode) async* { Stream<ImageInfo> loadRequest(ImageRequest request, ImageDecoderCallback decode) async* {
if (isCancelled) { if (isCancelled) {
this.request = null; this.request = null;
evict(); unawaited(evict());
return; return;
} }
try { try {
final image = await request.load(decode); final image = await request.load(decode);
if (image == null || isCancelled) { if (image == null || isCancelled) {
evict(); unawaited(evict());
return; return;
} }
yield image; yield image;

View File

@ -85,7 +85,7 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
yield* initialImageStream(); yield* initialImageStream();
if (isCancelled) { if (isCancelled) {
evict(); unawaited(evict());
return; return;
} }
@ -103,7 +103,7 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
} }
if (isCancelled) { if (isCancelled) {
evict(); unawaited(evict());
return; return;
} }

View File

@ -87,7 +87,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
yield* initialImageStream(); yield* initialImageStream();
if (isCancelled) { if (isCancelled) {
evict(); unawaited(evict());
return; return;
} }
@ -100,7 +100,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
yield* loadRequest(request, decode); yield* loadRequest(request, decode);
if (isCancelled) { if (isCancelled) {
evict(); unawaited(evict());
return; return;
} }

View File

@ -115,12 +115,14 @@ class _DriftMapState extends ConsumerState<DriftMap> {
} }
final bounds = await controller.getVisibleRegion(); final bounds = await controller.getVisibleRegion();
_reloadMutex.run(() async { unawaited(
if (mounted && ref.read(mapStateProvider.notifier).setBounds(bounds)) { _reloadMutex.run(() async {
final markers = await ref.read(mapMarkerProvider(bounds).future); if (mounted && ref.read(mapStateProvider.notifier).setBounds(bounds)) {
await reloadMarkers(markers); final markers = await ref.read(mapMarkerProvider(bounds).future);
} await reloadMarkers(markers);
}); }
}),
);
} }
Future<void> reloadMarkers(Map<String, dynamic> markers) async { Future<void> reloadMarkers(Map<String, dynamic> markers) async {
@ -148,7 +150,7 @@ class _DriftMapState extends ConsumerState<DriftMap> {
final controller = mapController; final controller = mapController;
if (controller != null && location != null) { if (controller != null && location != null) {
controller.animateCamera( await controller.animateCamera(
CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), MapUtils.mapZoomToAssetLevel), CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), MapUtils.mapZoomToAssetLevel),
duration: const Duration(milliseconds: 800), duration: const Duration(milliseconds: 800),
); );

View File

@ -73,7 +73,7 @@ class MapUtils {
try { try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled && !silent) { if (!serviceEnabled && !silent) {
showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog(context)); unawaited(showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog(context)));
return (null, LocationPermission.deniedForever); return (null, LocationPermission.deniedForever);
} }

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:auto_route/auto_route.dart'; 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/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/asset_viewer/is_motion_video_playing.provider.dart';
import 'package:immich_mobile/providers/haptic_feedback.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/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/providers/timeline/multiselect.provider.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
@ -156,11 +157,13 @@ class _AssetTileWidget extends ConsumerWidget {
await ref.read(timelineServiceProvider).loadAssets(assetIndex, 1); await ref.read(timelineServiceProvider).loadAssets(assetIndex, 1);
ref.read(isPlayingMotionVideoProvider.notifier).playing = false; ref.read(isPlayingMotionVideoProvider.notifier).playing = false;
AssetViewer.setAsset(ref, asset); AssetViewer.setAsset(ref, asset);
ctx.pushRoute( unawaited(
AssetViewerRoute( ctx.pushRoute(
initialIndex: assetIndex, AssetViewerRoute(
timelineService: ref.read(timelineServiceProvider), initialIndex: assetIndex,
heroOffset: heroOffset, timelineService: ref.read(timelineServiceProvider),
heroOffset: heroOffset,
),
), ),
); );
} }

View File

@ -242,7 +242,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
} }
try { try {
LogService.I.flush(); await LogService.I.flush();
} catch (_) {} } catch (_) {}
} }
@ -255,7 +255,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
// Flush logs before closing database // Flush logs before closing database
try { try {
LogService.I.flush(); await LogService.I.flush();
} catch (_) {} } catch (_) {}
// Close Isar database safely // Close Isar database safely

View File

@ -98,7 +98,7 @@ class AssetNotifier extends StateNotifier<bool> {
Future<void> onNewAssetUploaded(Asset newAsset) async { Future<void> onNewAssetUploaded(Asset newAsset) async {
// eTag on device is not valid after partially modifying the assets // eTag on device is not valid after partially modifying the assets
Store.delete(StoreKey.assetETag); await Store.delete(StoreKey.assetETag);
await _syncService.syncNewAssetToDb(newAsset); await _syncService.syncNewAssetToDb(newAsset);
} }

View File

@ -1,14 +1,16 @@
import 'dart:async';
import 'package:background_downloader/background_downloader.dart'; import 'package:background_downloader/background_downloader.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/extensions/build_context_extensions.dart';
import 'package:immich_mobile/models/download/download_state.model.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/models/download/livephotos_medatada.model.dart';
import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/album.service.dart';
import 'package:immich_mobile/services/download.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/services/share.service.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart';
import 'package:immich_mobile/widgets/common/share_dialog.dart'; import 'package:immich_mobile/widgets/common/share_dialog.dart';
@ -159,24 +161,26 @@ class DownloadStateNotifier extends StateNotifier<DownloadState> {
} }
void shareAsset(Asset asset, BuildContext context) async { void shareAsset(Asset asset, BuildContext context) async {
showDialog( unawaited(
context: context, showDialog(
builder: (BuildContext buildContext) { context: context,
_shareService.shareAsset(asset, context).then((bool status) { builder: (BuildContext buildContext) {
if (!status) { _shareService.shareAsset(asset, context).then((bool status) {
ImmichToast.show( if (!status) {
context: context, ImmichToast.show(
msg: 'image_viewer_page_state_provider_share_error'.tr(), context: context,
toastType: ToastType.error, msg: 'image_viewer_page_state_provider_share_error'.tr(),
gravity: ToastGravity.BOTTOM, toastType: ToastType.error,
); gravity: ToastGravity.BOTTOM,
} );
buildContext.pop(); }
}); buildContext.pop();
return const ShareDialog(); });
}, return const ShareDialog();
barrierDismissible: false, },
useRootNavigator: false, barrierDismissible: false,
useRootNavigator: false,
),
); );
} }
} }

View File

@ -104,7 +104,7 @@ class ShareIntentUploadStateNotifier extends StateNotifier<List<ShareIntentAttac
Future<void> upload(File file) async { Future<void> upload(File file) async {
final task = await _buildUploadTask(hash(file.path).toString(), file); final task = await _buildUploadTask(hash(file.path).toString(), file);
_uploadService.enqueueTasks([task]); await _uploadService.enqueueTasks([task]);
} }
Future<UploadTask> _buildUploadTask(String id, File file, {Map<String, String>? fields}) async { Future<UploadTask> _buildUploadTask(String id, File file, {Map<String, String>? fields}) async {

View File

@ -380,7 +380,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
state = state.copyWith(backgroundBackup: isEnabled); state = state.copyWith(backgroundBackup: isEnabled);
if (isEnabled != Store.get(StoreKey.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) { if (state.backupProgress != BackUpProgressEnum.inBackground) {
@ -474,7 +474,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
); );
await notifyBackgroundServiceCanRun(); await notifyBackgroundServiceCanRun();
} else { } else {
openAppSettings(); await openAppSettings();
} }
} }
@ -533,10 +533,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
progressInFileSpeedUpdateTime: DateTime.now(), progressInFileSpeedUpdateTime: DateTime.now(),
progressInFileSpeedUpdateSentBytes: 0, progressInFileSpeedUpdateSentBytes: 0,
); );
_updatePersistentAlbumsSelection(); await _updatePersistentAlbumsSelection();
} }
updateDiskInfo(); await updateDiskInfo();
} }
void _onUploadProgress(int sent, int total) { void _onUploadProgress(int sent, int total) {

View File

@ -2,10 +2,10 @@ import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.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/entities/asset.entity.dart';
import 'package:immich_mobile/providers/asset.provider.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/confirm_dialog.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
@ -44,7 +44,7 @@ class BackupVerification extends _$BackupVerification {
} }
return; return;
} }
WakelockPlus.enable(); unawaited(WakelockPlus.enable());
const limit = 100; const limit = 100;
final toDelete = await ref.read(backupVerificationServiceProvider).findWronglyBackedUpAssets(limit: limit); final toDelete = await ref.read(backupVerificationServiceProvider).findWronglyBackedUpAssets(limit: limit);
@ -73,7 +73,7 @@ class BackupVerification extends _$BackupVerification {
} }
} }
} finally { } finally {
WakelockPlus.disable(); unawaited(WakelockPlus.disable());
state = false; state = false;
} }
} }

View File

@ -7,7 +7,7 @@ part of 'backup_verification.provider.dart';
// ************************************************************************** // **************************************************************************
String _$backupVerificationHash() => String _$backupVerificationHash() =>
r'b204e43ab575d5fa5b2ee663297f32bcee9074f5'; r'b4b34909ed1af3f28877ea457d53a4a18b6417f8';
/// See also [BackupVerification]. /// See also [BackupVerification].
@ProviderFor(BackupVerification) @ProviderFor(BackupVerification)

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:cancellation_token_http/http.dart'; 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/backup_album.service.dart';
import 'package:immich_mobile/services/local_notification.service.dart'; import 'package:immich_mobile/services/local_notification.service.dart';
import 'package:immich_mobile/utils/backup_progress.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:immich_mobile/widgets/common/immich_toast.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:photo_manager/photo_manager.dart' show PMProgressHandler; import 'package:photo_manager/photo_manager.dart' show PMProgressHandler;
import 'package:immich_mobile/utils/debug_print.dart';
final manualUploadProvider = StateNotifierProvider<ManualUploadNotifier, ManualUploadState>((ref) { final manualUploadProvider = StateNotifierProvider<ManualUploadNotifier, ManualUploadState>((ref) {
return ManualUploadNotifier( return ManualUploadNotifier(
@ -294,7 +295,7 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
); );
} }
} else { } else {
openAppSettings(); unawaited(openAppSettings());
dPrint(() => "[_startUpload] Do not have permission to the gallery"); dPrint(() => "[_startUpload] Do not have permission to the gallery");
} }
} catch (e) { } catch (e) {

View File

@ -3,7 +3,6 @@ import 'dart:io';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart';
@ -77,7 +76,7 @@ class ImmichLocalImageProvider extends ImageProvider<ImmichLocalImageProvider> {
} catch (error, stack) { } catch (error, stack) {
log.severe('Error loading local image ${asset.fileName}', error, stack); log.severe('Error loading local image ${asset.fileName}', error, stack);
} finally { } finally {
chunkEvents.close(); unawaited(chunkEvents.close());
} }
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:background_downloader/background_downloader.dart'; import 'package:background_downloader/background_downloader.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -70,7 +72,7 @@ class ActionNotifier extends Notifier<void> {
void _downloadLivePhotoCallback(TaskStatusUpdate update) async { void _downloadLivePhotoCallback(TaskStatusUpdate update) async {
if (update.status == TaskStatus.complete) { if (update.status == TaskStatus.complete) {
final livePhotosId = LivePhotosMetadata.fromJson(update.task.metaData).id; 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<void> {
if (assets.length > 1) { if (assets.length > 1) {
return ActionResult(count: assets.length, success: false, error: 'Cannot troubleshoot multiple assets'); 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); return ActionResult(count: assets.length, success: true);
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/models/shared_link/shared_link.model.dart'; import 'package:immich_mobile/models/shared_link/shared_link.model.dart';
import 'package:immich_mobile/services/shared_link.service.dart'; import 'package:immich_mobile/services/shared_link.service.dart';
@ -16,7 +18,7 @@ class SharedLinksNotifier extends StateNotifier<AsyncValue<List<SharedLink>>> {
Future<void> deleteLink(String id) async { Future<void> deleteLink(String id) async {
await _sharedLinkService.deleteSharedLink(id); await _sharedLinkService.deleteSharedLink(id);
state = const AsyncLoading(); state = const AsyncLoading();
fetchLinks(); unawaited(fetchLinks());
} }
} }

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart'; 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 // we dont want to await the share result since the
// "preparing" dialog will not disappear until // "preparing" dialog will not disappear until
final size = context.sizeData; final size = context.sizeData;
Share.shareXFiles( unawaited(
downloadedXFiles, Share.shareXFiles(
sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), downloadedXFiles,
).then((result) async { sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)),
for (var file in tempFiles) { ).then((result) async {
try { for (var file in tempFiles) {
await file.delete(); try {
} catch (e) { await file.delete();
_log.warning("Failed to delete temporary file: ${file.path}", e); } catch (e) {
_log.warning("Failed to delete temporary file: ${file.path}", e);
}
} }
} }),
}); );
return downloadedXFiles.length; return downloadedXFiles.length;
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -12,7 +14,7 @@ class AppNavigationObserver extends AutoRouterObserver {
@override @override
Future<void> didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) async { Future<void> didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) async {
Future(() => ref.read(inLockedViewProvider.notifier).state = false); unawaited(Future(() => ref.read(inLockedViewProvider.notifier).state = false));
} }
@override @override

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
@ -26,18 +27,18 @@ class AuthGuard extends AutoRouteGuard {
if (res == null || res.authStatus != true) { if (res == null || res.authStatus != true) {
// If the access token is invalid, take user back to login // If the access token is invalid, take user back to login
_log.fine('User token is invalid. Redirecting to login'); _log.fine('User token is invalid. Redirecting to login');
router.replaceAll([const LoginRoute()]); unawaited(router.replaceAll([const LoginRoute()]));
} }
} on StoreKeyNotFoundException catch (_) { } on StoreKeyNotFoundException catch (_) {
// If there is no access token, take us to the login page // If there is no access token, take us to the login page
_log.warning('No access token in the store.'); _log.warning('No access token in the store.');
router.replaceAll([const LoginRoute()]); unawaited(router.replaceAll([const LoginRoute()]));
return; return;
} on ApiException catch (e) { } on ApiException catch (e) {
// On an unauthorized request, take us to the login page // On an unauthorized request, take us to the login page
if (e.code == HttpStatus.unauthorized) { if (e.code == HttpStatus.unauthorized) {
_log.warning("Unauthorized access token."); _log.warning("Unauthorized access token.");
router.replaceAll([const LoginRoute()]); unawaited(router.replaceAll([const LoginRoute()]));
return; return;
} }
} catch (e) { } catch (e) {

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
@ -13,7 +15,7 @@ class BackupPermissionGuard extends AutoRouteGuard {
if (p) { if (p) {
resolver.next(true); resolver.next(true);
} else { } else {
router.push(const PermissionOnboardingRoute()); unawaited(router.push(const PermissionOnboardingRoute()));
} }
} }
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
@ -13,12 +15,14 @@ class GalleryGuard extends AutoRouteGuard {
// Replace instead of pushing duplicate // Replace instead of pushing duplicate
final args = resolver.route.args as GalleryViewerRouteArgs; final args = resolver.route.args as GalleryViewerRouteArgs;
router.replace( unawaited(
GalleryViewerRoute( router.replace(
renderList: args.renderList, GalleryViewerRoute(
initialIndex: args.initialIndex, renderList: args.renderList,
heroOffset: args.heroOffset, initialIndex: args.initialIndex,
showStack: args.showStack, heroOffset: args.heroOffset,
showStack: args.showStack,
),
), ),
); );
// Prevent further navigation since we replaced the route // Prevent further navigation since we replaced the route

View File

@ -1,8 +1,9 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/services/local_auth.service.dart'; import 'package:immich_mobile/services/local_auth.service.dart';
import 'package:immich_mobile/services/secure_storage.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 /// Check if a pincode has been created but this user. Show the form to create if not exist
if (!authStatus.pinCode) { if (!authStatus.pinCode) {
router.push(PinAuthRoute(createPinCode: true)); unawaited(router.push(PinAuthRoute(createPinCode: true)));
} }
if (authStatus.isElevated) { if (authStatus.isElevated) {
@ -42,7 +43,7 @@ class LockedGuard extends AutoRouteGuard {
/// the user has enabled the biometric authentication /// the user has enabled the biometric authentication
final securePinCode = await _secureStorageService.read(kSecuredPinCode); final securePinCode = await _secureStorageService.read(kSecuredPinCode);
if (securePinCode == null) { if (securePinCode == null) {
router.push(PinAuthRoute()); unawaited(router.push(PinAuthRoute()));
return; return;
} }
@ -74,7 +75,7 @@ class LockedGuard extends AutoRouteGuard {
} on ApiException { } on ApiException {
// PIN code has changed, need to re-enter to access // PIN code has changed, need to re-enter to access
await _secureStorageService.delete(kSecuredPinCode); await _secureStorageService.delete(kSecuredPinCode);
router.push(PinAuthRoute()); unawaited(router.push(PinAuthRoute()));
} catch (error) { } catch (error) {
_log.severe("Failed to access locked page", error); _log.severe("Failed to access locked page", error);
resolver.next(false); resolver.next(false);

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -50,7 +52,7 @@ class ActionService {
); );
Future<void> shareLink(List<String> remoteIds, BuildContext context) async { Future<void> shareLink(List<String> remoteIds, BuildContext context) async {
context.pushRoute(SharedLinkEditRoute(assetsList: remoteIds)); unawaited(context.pushRoute(SharedLinkEditRoute(assetsList: remoteIds)));
} }
Future<void> favorite(List<String> remoteIds) async { Future<void> favorite(List<String> remoteIds) async {

View File

@ -83,7 +83,7 @@ class AlbumService {
if (selectedIds.isEmpty) { if (selectedIds.isEmpty) {
final numLocal = await _albumRepository.count(local: true); final numLocal = await _albumRepository.count(local: true);
if (numLocal > 0) { if (numLocal > 0) {
_syncService.removeAllLocalAlbumsAndAssets(); await _syncService.removeAllLocalAlbumsAndAssets();
} }
return false; return false;
} }

View File

@ -6,11 +6,11 @@ import 'package:device_info_plus/device_info_plus.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.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/url_helper.dart';
import 'package:immich_mobile/utils/user_agent.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:openapi/api.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 { class ApiService implements Authentication {
late ApiClient _apiClient; late ApiClient _apiClient;
@ -86,7 +86,7 @@ class ApiService implements Authentication {
setEndpoint(endpoint); setEndpoint(endpoint);
// Save in local database for next startup // Save in local database for next startup
Store.put(StoreKey.serverEndpoint, endpoint); await Store.put(StoreKey.serverEndpoint, endpoint);
return endpoint; return endpoint;
} }

View File

@ -58,7 +58,7 @@ class AuthService {
Future<String> validateServerUrl(String url) async { Future<String> validateServerUrl(String url) async {
final validUrl = await _apiService.resolveAndSetEndpoint(url); final validUrl = await _apiService.resolveAndSetEndpoint(url);
await _apiService.setDeviceInfoHeader(); await _apiService.setDeviceInfoHeader();
Store.put(StoreKey.serverUrl, validUrl); await Store.put(StoreKey.serverUrl, validUrl);
return validUrl; return validUrl;
} }

View File

@ -291,7 +291,7 @@ class BackgroundService {
case "backgroundProcessing": case "backgroundProcessing":
case "onAssetsChanged": case "onAssetsChanged":
try { try {
_clearErrorNotifications(); unawaited(_clearErrorNotifications());
// iOS should time out after some threshold so it doesn't wait // iOS should time out after some threshold so it doesn't wait
// indefinitely and can run later // indefinitely and can run later
@ -342,7 +342,7 @@ class BackgroundService {
); );
HttpSSLOptions.apply(); HttpSSLOptions.apply();
ref.read(apiServiceProvider).setAccessToken(Store.get(StoreKey.accessToken)); await ref.read(apiServiceProvider).setAccessToken(Store.get(StoreKey.accessToken));
await ref.read(authServiceProvider).setOpenApiServiceEndpoint(); await ref.read(authServiceProvider).setOpenApiServiceEndpoint();
dPrint(() => "[BG UPLOAD] Using endpoint: ${ref.read(apiServiceProvider).apiClient.basePath}"); 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).deleteAll(toDelete);
await ref.read(backupAlbumRepositoryProvider).updateAll(toUpsert); await ref.read(backupAlbumRepositoryProvider).updateAll(toUpsert);
} else if (Store.tryGet(StoreKey.backupFailedSince) == null) { } else if (Store.tryGet(StoreKey.backupFailedSince) == null) {
Store.put(StoreKey.backupFailedSince, DateTime.now()); await Store.put(StoreKey.backupFailedSince, DateTime.now());
return false; return false;
} }
// Android should check for new assets added while performing backup // Android should check for new assets added while performing backup
@ -412,9 +412,11 @@ class BackgroundService {
try { try {
toUpload = await backupService.removeAlreadyUploadedAssets(toUpload); toUpload = await backupService.removeAlreadyUploadedAssets(toUpload);
} catch (e) { } catch (e) {
_showErrorNotification( unawaited(
title: "backup_background_service_error_title".tr(), _showErrorNotification(
content: "backup_background_service_connection_failed_message".tr(), title: "backup_background_service_error_title".tr(),
content: "backup_background_service_connection_failed_message".tr(),
),
); );
return false; return false;
} }
@ -428,13 +430,15 @@ class BackgroundService {
} }
_assetsToUploadCount = toUpload.length; _assetsToUploadCount = toUpload.length;
_uploadedAssetsCount = 0; _uploadedAssetsCount = 0;
_updateNotification( unawaited(
title: "backup_background_service_in_progress_notification".tr(), _updateNotification(
content: notifyTotalProgress ? formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount) : null, title: "backup_background_service_in_progress_notification".tr(),
progress: 0, content: notifyTotalProgress ? formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount) : null,
max: notifyTotalProgress ? _assetsToUploadCount : 0, progress: 0,
indeterminate: !notifyTotalProgress, max: notifyTotalProgress ? _assetsToUploadCount : 0,
onlyIfFG: !notifyTotalProgress, indeterminate: !notifyTotalProgress,
onlyIfFG: !notifyTotalProgress,
),
); );
_cancellationToken = CancellationToken(); _cancellationToken = CancellationToken();
@ -452,9 +456,11 @@ class BackgroundService {
); );
if (!ok && !_cancellationToken!.isCancelled) { if (!ok && !_cancellationToken!.isCancelled) {
_showErrorNotification( unawaited(
title: "backup_background_service_error_title".tr(), _showErrorNotification(
content: "backup_background_service_backup_failed_message".tr(), title: "backup_background_service_error_title".tr(),
content: "backup_background_service_backup_failed_message".tr(),
),
); );
} }

View File

@ -120,7 +120,7 @@ class BackupVerificationService {
await tuple.fileMediaRepository.enableBackgroundAccess(); await tuple.fileMediaRepository.enableBackgroundAccess();
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
apiService.setEndpoint(tuple.endpoint); apiService.setEndpoint(tuple.endpoint);
apiService.setAccessToken(tuple.auth); await apiService.setAccessToken(tuple.auth);
for (int i = 0; i < tuple.deleteCandidates.length; i++) { for (int i = 0; i < tuple.deleteCandidates.length; i++) {
if (await _compareAssets(tuple.deleteCandidates[i], tuple.originals[i], apiService)) { if (await _compareAssets(tuple.deleteCandidates[i], tuple.originals[i], apiService)) {
result.add(tuple.deleteCandidates[i]); result.add(tuple.deleteCandidates[i]);

View File

@ -1,9 +1,9 @@
import 'package:immich_mobile/mixins/error_logger.mixin.dart'; import 'package:immich_mobile/mixins/error_logger.mixin.dart';
import 'package:immich_mobile/models/map/map_marker.model.dart'; import 'package:immich_mobile/models/map/map_marker.model.dart';
import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/utils/user_agent.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:maplibre_gl/maplibre_gl.dart';
import 'package:immich_mobile/utils/user_agent.dart';
class MapService with ErrorLoggerMixin { class MapService with ErrorLoggerMixin {
final ApiService _apiService; final ApiService _apiService;
@ -16,7 +16,7 @@ class MapService with ErrorLoggerMixin {
Future<void> _setMapUserAgentHeader() async { Future<void> _setMapUserAgentHeader() async {
final userAgent = await getUserAgentString(); final userAgent = await getUserAgentString();
setHttpHeaders({'User-Agent': userAgent}); await setHttpHeaders({'User-Agent': userAgent});
} }
Future<Iterable<MapMarker>> getMapMarkers({ Future<Iterable<MapMarker>> getMapMarkers({

View File

@ -1,13 +1,15 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/entities/asset.entity.dart';
import 'package:immich_mobile/extensions/response_extensions.dart';
import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/api.provider.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'api.service.dart'; import 'api.service.dart';
final shareServiceProvider = Provider((ref) => ShareService(ref.watch(apiServiceProvider))); final shareServiceProvider = Provider((ref) => ShareService(ref.watch(apiServiceProvider)));
@ -58,9 +60,11 @@ class ShareService {
} }
final size = MediaQuery.of(context).size; final size = MediaQuery.of(context).size;
Share.shareXFiles( unawaited(
downloadedXFiles, Share.shareXFiles(
sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), downloadedXFiles,
sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)),
),
); );
return true; return true;
} catch (error) { } catch (error) {

View File

@ -705,7 +705,7 @@ class SyncService {
if (assets.isEmpty) return; if (assets.isEmpty) return;
if (Platform.isAndroid && _appSettingsService.getSetting<bool>(AppSettingsEnum.manageLocalMediaAndroid)) { if (Platform.isAndroid && _appSettingsService.getSetting<bool>(AppSettingsEnum.manageLocalMediaAndroid)) {
_toggleTrashStatusForAssets(assets); await _toggleTrashStatusForAssets(assets);
} }
try { try {

View File

@ -214,7 +214,7 @@ class UploadService {
void _handleTaskStatusUpdate(TaskStatusUpdate update) async { void _handleTaskStatusUpdate(TaskStatusUpdate update) async {
switch (update.status) { switch (update.status) {
case TaskStatus.complete: case TaskStatus.complete:
_handleLivePhoto(update); unawaited(_handleLivePhoto(update));
if (CurrentPlatform.isIOS) { if (CurrentPlatform.isIOS) {
try { try {
@ -259,7 +259,7 @@ class UploadService {
return; return;
} }
enqueueTasks([uploadTask]); await enqueueTasks([uploadTask]);
} catch (error, stackTrace) { } catch (error, stackTrace) {
dPrint(() => "Error handling live photo upload task: $error $stackTrace"); dPrint(() => "Error handling live photo upload task: $error $stackTrace");
} }

View File

@ -1,8 +1,10 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.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/models/map/map_marker.model.dart';
import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart';
import 'package:geolocator/geolocator.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:maplibre_gl/maplibre_gl.dart';
@ -68,7 +70,7 @@ class MapUtils {
try { try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled && !silent) { if (!serviceEnabled && !silent) {
showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog()); unawaited(showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog()));
return (null, LocationPermission.deniedForever); return (null, LocationPermission.deniedForever);
} }

View File

@ -101,7 +101,7 @@ Future<void> handleEditDateTime(WidgetRef ref, BuildContext context, List<Asset>
return; return;
} }
ref.read(assetServiceProvider).changeDateTime(selection.toList(), dateTime); await ref.read(assetServiceProvider).changeDateTime(selection.toList(), dateTime);
} }
Future<void> handleEditLocation(WidgetRef ref, BuildContext context, List<Asset> selection) async { Future<void> handleEditLocation(WidgetRef ref, BuildContext context, List<Asset> selection) async {
@ -120,7 +120,7 @@ Future<void> handleEditLocation(WidgetRef ref, BuildContext context, List<Asset>
return; return;
} }
ref.read(assetServiceProvider).changeLocation(selection.toList(), location); await ref.read(assetServiceProvider).changeLocation(selection.toList(), location);
} }
Future<void> handleSetAssetsVisibility( Future<void> handleSetAssetsVisibility(

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -57,7 +59,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge
deleteAlbum() async { deleteAlbum() async {
final bool success = await ref.watch(albumProvider.notifier).deleteAlbum(album); 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) { if (!success) {
ImmichToast.show( ImmichToast.show(
@ -105,7 +107,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge
bool isSuccess = await ref.watch(albumProvider.notifier).leaveAlbum(album); bool isSuccess = await ref.watch(albumProvider.notifier).leaveAlbum(album);
if (isSuccess) { if (isSuccess) {
context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); unawaited(context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])));
} else { } else {
context.pop(); context.pop();
ImmichToast.show( ImmichToast.show(

View File

@ -314,10 +314,10 @@ class MultiselectGrid extends HookConsumerWidget {
final result = await ref.read(albumServiceProvider).createAlbumWithGeneratedName(assets); final result = await ref.read(albumServiceProvider).createAlbumWithGeneratedName(assets);
if (result != null) { if (result != null) {
ref.watch(albumProvider.notifier).refreshRemoteAlbums(); unawaited(ref.watch(albumProvider.notifier).refreshRemoteAlbums());
selectionEnabledHook.value = false; selectionEnabledHook.value = false;
context.pushRoute(AlbumViewerRoute(albumId: result.id)); unawaited(context.pushRoute(AlbumViewerRoute(albumId: result.id)));
} }
} finally { } finally {
processing.value = false; processing.value = false;
@ -346,7 +346,7 @@ class MultiselectGrid extends HookConsumerWidget {
); );
if (remoteAssets.isNotEmpty) { if (remoteAssets.isNotEmpty) {
handleEditDateTime(ref, context, remoteAssets.toList()); unawaited(handleEditDateTime(ref, context, remoteAssets.toList()));
} }
} finally { } finally {
selectionEnabledHook.value = false; selectionEnabledHook.value = false;
@ -361,7 +361,7 @@ class MultiselectGrid extends HookConsumerWidget {
); );
if (remoteAssets.isNotEmpty) { if (remoteAssets.isNotEmpty) {
handleEditLocation(ref, context, remoteAssets.toList()); unawaited(handleEditLocation(ref, context, remoteAssets.toList()));
} }
} finally { } finally {
selectionEnabledHook.value = false; selectionEnabledHook.value = false;

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:auto_route/auto_route.dart'; 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 // to not throw the error when the next preCache index is called
if (totalAssets.value == 1 || assetIndex.value == totalAssets.value - 1) { if (totalAssets.value == 1 || assetIndex.value == totalAssets.value - 1) {
// Handle only one asset // Handle only one asset
context.maybePop(); await context.maybePop();
} }
totalAssets.value -= 1; totalAssets.value -= 1;
@ -111,18 +112,20 @@ class BottomGalleryBar extends ConsumerWidget {
} }
// Asset is permanently removed // Asset is permanently removed
showDialog( unawaited(
context: context, showDialog(
builder: (BuildContext _) { context: context,
return DeleteDialog( builder: (BuildContext _) {
onDelete: () async { return DeleteDialog(
final isDeleted = await onDelete(true); onDelete: () async {
if (isDeleted) { final isDeleted = await onDelete(true);
removeAssetFromStack(); if (isDeleted) {
} removeAssetFromStack();
}, }
); },
}, );
},
),
); );
} }
@ -150,7 +153,7 @@ class BottomGalleryBar extends ConsumerWidget {
onTap: () async { onTap: () async {
await unStack(); await unStack();
ctx.pop(); ctx.pop();
context.maybePop(); await context.maybePop();
}, },
title: const Text("viewer_unstack", style: TextStyle(fontWeight: FontWeight.bold)).tr(), title: const Text("viewer_unstack", style: TextStyle(fontWeight: FontWeight.bold)).tr(),
), ),
@ -178,9 +181,11 @@ class BottomGalleryBar extends ConsumerWidget {
void handleEdit() async { void handleEdit() async {
final image = Image(image: ImmichImage.imageProvider(asset: asset)); final image = Image(image: ImmichImage.imageProvider(asset: asset));
context.navigator.push( unawaited(
MaterialPageRoute( context.navigator.push(
builder: (context) => EditImagePage(asset: asset, image: image, isEdited: false), MaterialPageRoute(
builder: (context) => EditImagePage(asset: asset, image: image, isEdited: false),
),
), ),
); );
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -93,7 +95,7 @@ class CastDialog extends ConsumerWidget {
} }
if (!isCurrentDevice(deviceName)) { if (!isCurrentDevice(deviceName)) {
ref.read(castProvider.notifier).connect(type, deviceObj); unawaited(ref.read(castProvider.notifier).connect(type, deviceObj));
} }
}, },
); );

View File

@ -1,11 +1,12 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/domain/models/exif.model.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:immich_mobile/widgets/map/map_thumbnail.dart';
import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:maplibre_gl/maplibre_gl.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:immich_mobile/utils/debug_print.dart';
class ExifMap extends StatelessWidget { class ExifMap extends StatelessWidget {
final ExifInfo exifInfo; final ExifInfo exifInfo;
@ -68,7 +69,7 @@ class ExifMap extends StatelessWidget {
} }
dPrint(() => 'Opening Map Uri: $uri'); dPrint(() => 'Opening Map Uri: $uri');
launchUrl(uri); unawaited(launchUrl(uri));
}, },
onCreated: onMapCreated, onCreated: onMapCreated,
); );

View File

@ -1,19 +1,21 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store; 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: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/extensions/build_context_extensions.dart';
import 'package:immich_mobile/models/backup/backup_state.model.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart';
import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/asset.provider.dart';
import 'package:immich_mobile/providers/auth.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/backup.provider.dart';
import 'package:immich_mobile/providers/backup/manual_upload.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/locale_provider.dart';
import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/providers/websocket.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/routing/router.dart';
import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:immich_mobile/utils/bytes_units.dart';
import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_profile_info.dart'; import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_profile_info.dart';
@ -97,25 +99,27 @@ class ImmichAppBarDialog extends HookConsumerWidget {
return; return;
} }
showDialog( unawaited(
context: context, showDialog(
builder: (BuildContext ctx) { context: context,
return ConfirmDialog( builder: (BuildContext ctx) {
title: "app_bar_signout_dialog_title", return ConfirmDialog(
content: "app_bar_signout_dialog_content", title: "app_bar_signout_dialog_title",
ok: "yes", content: "app_bar_signout_dialog_content",
onOk: () async { ok: "yes",
isLoggingOut.value = true; onOk: () async {
await ref.read(authProvider.notifier).logout().whenComplete(() => isLoggingOut.value = false); isLoggingOut.value = true;
await ref.read(authProvider.notifier).logout().whenComplete(() => isLoggingOut.value = false);
ref.read(manualUploadProvider.notifier).cancelBackup(); ref.read(manualUploadProvider.notifier).cancelBackup();
ref.read(backupProvider.notifier).cancelBackup(); ref.read(backupProvider.notifier).cancelBackup();
ref.read(assetProvider.notifier).clearAllAssets(); unawaited(ref.read(assetProvider.notifier).clearAllAssets());
ref.read(websocketProvider.notifier).disconnect(); ref.read(websocketProvider.notifier).disconnect();
context.replaceRoute(const LoginRoute()); unawaited(context.replaceRoute(const LoginRoute()));
}, },
); );
}, },
),
); );
}, },
trailing: isLoggingOut.value trailing: isLoggingOut.value

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/providers/auth.provider.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/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/upload_profile_image.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/immich_loading_indicator.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(currentUserProvider.notifier).refresh();
} }
ref.read(backupProvider.notifier).updateDiskInfo(); unawaited(ref.read(backupProvider.notifier).updateDiskInfo());
} }
} }
} }

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
@ -188,17 +189,17 @@ class LoginForm extends HookConsumerWidget {
final result = await ref.read(authProvider.notifier).login(emailController.text, passwordController.text); final result = await ref.read(authProvider.notifier).login(emailController.text, passwordController.text);
if (result.shouldChangePassword && !result.isAdmin) { if (result.shouldChangePassword && !result.isAdmin) {
context.pushRoute(const ChangePasswordRoute()); unawaited(context.pushRoute(const ChangePasswordRoute()));
} else { } else {
final isBeta = Store.isBetaTimelineEnabled; final isBeta = Store.isBetaTimelineEnabled;
if (isBeta) { if (isBeta) {
await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission(); await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission();
handleSyncFlow(); unawaited(handleSyncFlow());
ref.read(websocketProvider.notifier).connect(); ref.read(websocketProvider.notifier).connect();
context.replaceRoute(const TabShellRoute()); unawaited(context.replaceRoute(const TabShellRoute()));
return; return;
} }
context.replaceRoute(const TabControllerRoute()); unawaited(context.replaceRoute(const TabControllerRoute()));
} }
} catch (error) { } catch (error) {
ImmichToast.show( ImmichToast.show(
@ -288,15 +289,15 @@ class LoginForm extends HookConsumerWidget {
final permission = ref.watch(galleryPermissionNotifier); final permission = ref.watch(galleryPermissionNotifier);
final isBeta = Store.isBetaTimelineEnabled; final isBeta = Store.isBetaTimelineEnabled;
if (!isBeta && (permission.isGranted || permission.isLimited)) { if (!isBeta && (permission.isGranted || permission.isLimited)) {
ref.watch(backupProvider.notifier).resumeBackup(); unawaited(ref.watch(backupProvider.notifier).resumeBackup());
} }
if (isBeta) { if (isBeta) {
await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission(); await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission();
handleSyncFlow(); unawaited(handleSyncFlow());
context.replaceRoute(const TabShellRoute()); unawaited(context.replaceRoute(const TabShellRoute()));
return; return;
} }
context.replaceRoute(const TabControllerRoute()); unawaited(context.replaceRoute(const TabControllerRoute()));
} }
} catch (error, stack) { } catch (error, stack) {
log.severe('Error logging in with OAuth: $error', stack); log.severe('Error logging in with OAuth: $error', stack);

View File

@ -1,11 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/extensions/build_context_extensions.dart';
import 'package:immich_mobile/models/map/map_event.model.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/utils/draggable_scroll_controller.dart';
import 'package:immich_mobile/widgets/map/map_asset_grid.dart';
class MapBottomSheet extends HookConsumerWidget { class MapBottomSheet extends HookConsumerWidget {
final Stream<MapEvent> mapEventStream; final Stream<MapEvent> mapEventStream;
@ -34,7 +34,11 @@ class MapBottomSheet extends HookConsumerWidget {
void handleMapEvents(MapEvent event) async { void handleMapEvents(MapEvent event) async {
if (event is MapCloseBottomSheet) { 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,
);
} }
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -42,7 +44,7 @@ class BetaTimelineListTile extends ConsumerWidget {
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () async {
Navigator.of(context).pop(); Navigator.of(context).pop();
context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: value)]); unawaited(context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: value)]));
}, },
child: Text("ok".t(context: context)), child: Text("ok".t(context: context)),
), ),

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
@ -102,13 +104,13 @@ class LocalNetworkPreference extends HookConsumerWidget {
), ),
); );
} else { } else {
saveWifiName(wifiName); unawaited(saveWifiName(wifiName));
} }
final serverEndpoint = ref.read(authProvider.notifier).getServerEndpoint(); final serverEndpoint = ref.read(authProvider.notifier).getServerEndpoint();
if (serverEndpoint != null) { if (serverEndpoint != null) {
saveLocalEndpoint(serverEndpoint); unawaited(saveLocalEndpoint(serverEndpoint));
} }
} }

View File

@ -53,7 +53,7 @@ void main() {
}); });
tearDown(() async { tearDown(() async {
sut.dispose(); unawaited(sut.dispose());
await controller.close(); await controller.close();
}); });
@ -129,7 +129,7 @@ void main() {
final stream = sut.watch(StoreKey.accessToken); final stream = sut.watch(StoreKey.accessToken);
final events = <String?>[_kAccessToken, _kAccessToken.toUpperCase(), null, _kAccessToken.toLowerCase()]; final events = <String?>[_kAccessToken, _kAccessToken.toUpperCase(), null, _kAccessToken.toLowerCase()];
expectLater(stream, emitsInOrder(events)); unawaited(expectLater(stream, emitsInOrder(events)));
for (final event in events) { for (final event in events) {
valueController.add(event); valueController.add(event);

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart';
@ -99,7 +101,7 @@ void main() {
final count = await db.storeValues.count(); final count = await db.storeValues.count();
expect(count, isNot(isZero)); expect(count, isNot(isZero));
await sut.deleteAll(); 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 { test('watch()', () async {
final stream = sut.watch(StoreKey.version); final stream = sut.watch(StoreKey.version);
expectLater(stream, emitsInOrder([_kTestVersion, _kTestVersion + 10])); unawaited(expectLater(stream, emitsInOrder([_kTestVersion, _kTestVersion + 10])));
await pumpEventQueue(); await pumpEventQueue();
await sut.upsert(StoreKey.version, _kTestVersion + 10); await sut.upsert(StoreKey.version, _kTestVersion + 10);
}); });
test('watchAll()', () async { test('watchAll()', () async {
final stream = sut.watchAll(); final stream = sut.watchAll();
expectLater( unawaited(
stream, expectLater(
emitsInOrder([ stream,
[ emitsInOrder([
const StoreDto<Object>(StoreKey.version, _kTestVersion), [
StoreDto<Object>(StoreKey.backupFailedSince, _kTestBackupFailed), const StoreDto<Object>(StoreKey.version, _kTestVersion),
const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken), StoreDto<Object>(StoreKey.backupFailedSince, _kTestBackupFailed),
const StoreDto<Object>(StoreKey.colorfulInterface, _kTestColorfulInterface), const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken),
], const StoreDto<Object>(StoreKey.colorfulInterface, _kTestColorfulInterface),
[ ],
const StoreDto<Object>(StoreKey.version, _kTestVersion + 10), [
StoreDto<Object>(StoreKey.backupFailedSince, _kTestBackupFailed), const StoreDto<Object>(StoreKey.version, _kTestVersion + 10),
const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken), StoreDto<Object>(StoreKey.backupFailedSince, _kTestBackupFailed),
const StoreDto<Object>(StoreKey.colorfulInterface, _kTestColorfulInterface), const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken),
], const StoreDto<Object>(StoreKey.colorfulInterface, _kTestColorfulInterface),
]), ],
]),
),
); );
await sut.upsert(StoreKey.version, _kTestVersion + 10); await sut.upsert(StoreKey.version, _kTestVersion + 10);
}); });

View File

@ -64,9 +64,9 @@ void main() {
TestUtils.init(); TestUtils.init();
db = await TestUtils.initIsar(); db = await TestUtils.initIsar();
await StoreService.init(storeRepository: IsarStoreRepository(db)); await StoreService.init(storeRepository: IsarStoreRepository(db));
Store.put(StoreKey.currentUser, UserStub.admin); await Store.put(StoreKey.currentUser, UserStub.admin);
Store.put(StoreKey.serverEndpoint, ''); await Store.put(StoreKey.serverEndpoint, '');
Store.put(StoreKey.accessToken, ''); await Store.put(StoreKey.accessToken, '');
}); });
setUp(() async { setUp(() async {

View File

@ -35,8 +35,8 @@ void main() {
TestUtils.init(); TestUtils.init();
db = await TestUtils.initIsar(); db = await TestUtils.initIsar();
await StoreService.init(storeRepository: IsarStoreRepository(db)); await StoreService.init(storeRepository: IsarStoreRepository(db));
Store.put(StoreKey.currentUser, UserStub.admin); await Store.put(StoreKey.currentUser, UserStub.admin);
Store.put(StoreKey.serverEndpoint, ''); await Store.put(StoreKey.serverEndpoint, '');
}); });
setUp(() { setUp(() {

View File

@ -31,9 +31,9 @@ void main() {
db = await TestUtils.initIsar(); db = await TestUtils.initIsar();
// For UserCircleAvatar // For UserCircleAvatar
await StoreService.init(storeRepository: IsarStoreRepository(db)); await StoreService.init(storeRepository: IsarStoreRepository(db));
Store.put(StoreKey.currentUser, UserStub.admin); await Store.put(StoreKey.currentUser, UserStub.admin);
Store.put(StoreKey.serverEndpoint, ''); await Store.put(StoreKey.serverEndpoint, '');
Store.put(StoreKey.accessToken, ''); await Store.put(StoreKey.accessToken, '');
}); });
setUp(() { setUp(() {

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/utils/async_mutex.dart'; import 'package:immich_mobile/utils/async_mutex.dart';
@ -7,11 +9,11 @@ void main() {
AsyncMutex lock = AsyncMutex(); AsyncMutex lock = AsyncMutex();
List<int> events = []; List<int> events = [];
expect(0, lock.enqueued); 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); 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); 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); expect(3, lock.enqueued);
await lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(4))); await lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(4)));
expect(0, lock.enqueued); expect(0, lock.enqueued);