Compare commits

..

8 Commits

Author SHA1 Message Date
Mert
4ab7f4297c Merge branch 'main' into fix/mobile-unawaited-futures 2025-09-19 09:49:23 -04:00
shenlong-tanwen
e8a9a2209d conflict resolution 2025-09-17 17:06:45 +05:30
shenlong-tanwen
379d9ab1e4 merge main 2025-09-17 16:08:34 +05:30
shenlong-tanwen
f3e427f268 review changes 2025-09-09 15:22:17 +05:30
shenlong-tanwen
28380b1b46 auto gen file 2025-09-08 21:07:41 +05:30
shenlong-tanwen
abb5df8bab fix warning 2025-09-08 10:03:43 +05:30
shenlong-tanwen
8face037da remove unused dcm lints
They will be added back later on a case by case basis
2025-09-08 09:35:48 +05:30
shenlong-tanwen
4a449104aa chore: add unawaited_futures lint as warning 2025-09-08 09:35:25 +05:30
137 changed files with 2029 additions and 2223 deletions

1
.gitignore vendored
View File

@@ -18,7 +18,6 @@ mobile/libisar.dylib
mobile/openapi/test
mobile/openapi/doc
mobile/openapi/.openapi-generator/FILES
mobile/ios/build
open-api/typescript-sdk/build
mobile/android/fastlane/report.xml

View File

@@ -25,9 +25,9 @@ It is not recommended to directly backup the `DB_DATA_LOCATION` folder. Doing so
### Automatic Database Dumps
:::info
:::warning
The automatic database dumps can be used to restore the database in the event of damage to the Postgres database files.
If the server fails to generate the database dump file, a notification will be shown in the in-app notification on the web
There is no monitoring for these dumps and you will not be notified if they are unsuccessful.
:::
:::caution

View File

@@ -169,6 +169,8 @@ Redis (Sentinel) URL example JSON before encoding:
| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning |
| `MACHINE_LEARNING_DEVICE_IDS`<sup>\*4</sup> | Device IDs to use in multi-GPU environments | `0` | machine learning |
| `MACHINE_LEARNING_MAX_BATCH_SIZE__FACIAL_RECOGNITION` | Set the maximum number of faces that will be processed at once by the facial recognition model | None (`1` if using OpenVINO) | machine learning |
| `MACHINE_LEARNING_PING_TIMEOUT` | How long (ms) to wait for a PING response when checking if an ML server is available | `2000` | server |
| `MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME` | How long to ignore ML servers that are offline before trying again | `30000` | server |
| `MACHINE_LEARNING_RKNN` | Enable RKNN hardware acceleration if supported | `True` | machine learning |
| `MACHINE_LEARNING_RKNN_THREADS` | How many threads of RKNN runtime should be spinned up while inferencing. | `1` | machine learning |

View File

@@ -123,13 +123,6 @@
"logging_enable_description": "Enable logging",
"logging_level_description": "When enabled, what log level to use.",
"logging_settings": "Logging",
"machine_learning_availability_checks": "Availability checks",
"machine_learning_availability_checks_description": "Automatically detect and prefer available machine learning servers",
"machine_learning_availability_checks_enabled": "Enable availability checks",
"machine_learning_availability_checks_interval": "Check interval",
"machine_learning_availability_checks_interval_description": "Interval in milliseconds between availability checks",
"machine_learning_availability_checks_timeout": "Request timeout",
"machine_learning_availability_checks_timeout_description": "Timeout in milliseconds for availability checks",
"machine_learning_clip_model": "CLIP model",
"machine_learning_clip_model_description": "The name of a CLIP model listed <link>here</link>. Note that you must re-run the 'Smart Search' job for all images upon changing a model.",
"machine_learning_duplicate_detection": "Duplicate Detection",
@@ -920,7 +913,6 @@
"cant_get_number_of_comments": "Can't get number of comments",
"cant_search_people": "Can't search people",
"cant_search_places": "Can't search places",
"clipboard_unsupported_mime_type": "The system clipboard does not support copying this type of content: {mimeType}",
"error_adding_assets_to_album": "Error adding assets to album",
"error_adding_users_to_album": "Error adding users to album",
"error_deleting_shared_user": "Error deleting shared user",
@@ -1924,7 +1916,6 @@
"stacktrace": "Stacktrace",
"start": "Start",
"start_date": "Start date",
"start_date_before_end_date": "Start date must be before end date",
"state": "State",
"status": "Status",
"stop_casting": "Stop casting",

View File

@@ -1,7 +1,7 @@
[tools]
node = "22.19.0"
flutter = "3.35.4"
pnpm = "10.15.1"
pnpm = "10.14.0"
dart = "3.8.2"
[tools."github:CQLabs/homebrew-dcm"]

View File

@@ -17,7 +17,7 @@ linter:
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
# https://dart.dev/tools/linter-rules
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
@@ -28,6 +28,7 @@ linter:
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
unawaited_futures: true
use_build_context_synchronously: false
require_trailing_commas: true
unrelated_type_equality_checks: true
@@ -47,6 +48,9 @@ analyzer:
# plugins:
# - custom_lint
errors:
unawaited_futures: warning
custom_lint:
debug: true
rules:
@@ -142,170 +146,7 @@ dart_code_metrics:
exclude-paths:
- 'lib/utils/debug_print.dart'
severity: perf
# All rules from "recommended" preset
# Show potential errors
# - avoid-cascade-after-if-null
# - avoid-collection-methods-with-unrelated-types
# - avoid-duplicate-exports
# - avoid-dynamic
# - avoid-missing-enum-constant-in-map
# - avoid-passing-async-when-sync-expected
# - avoid-throw-in-catch-block
- avoid-unused-parameters
# - avoid-unnecessary-type-assertions
# - avoid-unnecessary-type-casts
# - avoid-unrelated-type-assertions
# - avoid-unrelated-type-casts
# - no-empty-block
# - no-equal-then-else
# - prefer-correct-test-file-name
- prefer-const-border-radius
# - prefer-match-file-name
# - prefer-return-await
# - avoid-self-assignment
# - avoid-self-compare
# - avoid-shadowing
# - prefer-iterable-of
# - no-equal-switch-case
# - no-equal-conditions
# - avoid-equal-expressions
# - avoid-missed-calls
# - avoid-unnecessary-negations
# - avoid-unused-generics
# - function-always-returns-null
# - avoid-throw-objects-without-tostring
# - avoid-unsafe-collection-methods
# - prefer-wildcard-pattern
# - no-equal-switch-expression-cases
# - avoid-future-tostring
# - avoid-unassigned-late-fields
# - avoid-nested-futures
# - avoid-generics-shadowing
# - prefer-parentheses-with-if-null
# - no-equal-nested-conditions
# - avoid-shadowed-extension-methods
# - avoid-unnecessary-conditionals
# - avoid-double-slash-imports
# - avoid-map-keys-contains
# - prefer-correct-json-casts
# - avoid-duplicate-mixins
# - avoid-nullable-interpolation
# - avoid-unused-instances
# - prefer-correct-for-loop-increment
# - prefer-public-exception-classes
# - avoid-uncaught-future-errors
# - always-remove-listener
# - avoid-unnecessary-setstate
# - check-for-equals-in-render-object-setters
# - consistent-update-render-object
# - use-setstate-synchronously
# - avoid-incomplete-copy-with
# - proper-super-calls
# - dispose-fields
# - avoid-empty-setstate
# - avoid-state-constructors
# - avoid-recursive-widget-calls
# - avoid-missing-image-alt
# - avoid-passing-self-as-argument
# - avoid-unnecessary-if
# - avoid-unconditional-break
# - avoid-referencing-discarded-variables
# - avoid-unnecessary-local-late
# - avoid-wildcard-cases-with-enums
# - match-getter-setter-field-names
# - avoid-accessing-collections-by-constant-index
# - prefer-unique-test-names
# - avoid-duplicate-cascades
# - prefer-specific-cases-first
# - avoid-duplicate-switch-case-conditions
# - prefer-explicit-function-type
# - avoid-misused-test-matchers
# - avoid-duplicate-test-assertions
# - prefer-switch-with-enums
# - prefer-any-or-every
# - avoid-duplicate-map-keys
# - avoid-nullable-tostring
# - avoid-undisposed-instances
# - avoid-duplicate-initializers
# - avoid-unassigned-stream-subscriptions
# - avoid-empty-test-groups
# - avoid-not-encodable-in-to-json
# - avoid-contradictory-expressions
# - avoid-excessive-expressions
# - prefer-private-extension-type-field
# - avoid-renaming-representation-getters
# - avoid-empty-spread
# - avoid-unnecessary-gesture-detector
# - avoid-missing-completer-stack-trace
# - avoid-casting-to-extension-type
# - prefer-overriding-parent-equality
# - avoid-missing-controller
# - avoid-unknown-pragma
# - avoid-conditions-with-boolean-literals
# - avoid-multi-assignment
# - avoid-collection-equality-checks
# - avoid-only-rethrow
# - avoid-incorrect-image-opacity
# - avoid-misused-set-literals
# - dispose-class-fields
# - avoid-suspicious-super-overrides
# - avoid-assignments-as-conditions
# - avoid-unused-assignment
# - avoid-unnecessary-overrides
# - avoid-implicitly-nullable-extension-types
# Enable with the next release
# - avoid-late-final-reassignment
# - avoid-duplicate-constant-values
# - function-always-returns-same-value
# - avoid-flexible-outside-flex
# - avoid-unnecessary-patterns
# - use-closest-build-context
# - avoid-commented-out-code
# - avoid-recursive-tostring
# - avoid-enum-values-by-index
# - avoid-constant-assert-conditions
# - avoid-inconsistent-digit-separators
# - pass-existing-future-to-future-builder
# - pass-existing-stream-to-stream-builder
# Code simplification
# - avoid-redundant-async
# - avoid-redundant-else
# - avoid-unnecessary-nullable-return-type
# - avoid-redundant-pragma-inline
# - avoid-nested-records
# - avoid-redundant-positional-field-name
# - avoid-explicit-pattern-field-name
# - prefer-simpler-patterns-null-check
# - avoid-unnecessary-return
# - avoid-duplicate-patterns
# - avoid-keywords-in-wildcard-pattern
# - avoid-unnecessary-futures
# - avoid-unnecessary-reassignment
# - avoid-unnecessary-call
# - avoid-unnecessary-stateful-widgets
# - prefer-dedicated-media-query-methods
# - avoid-unnecessary-overrides-in-state
# - move-variable-closer-to-its-usage
# - avoid-nullable-parameters-with-default-values
# - prefer-null-aware-spread
# - avoid-inferrable-type-arguments
# - avoid-unnecessary-super
# - avoid-unnecessary-collections
# - avoid-unnecessary-extends
# - avoid-unnecessary-enum-arguments
# - prefer-contains
# Enable with the next release
# - prefer-simpler-boolean-expressions
# - prefer-spacing
# - avoid-unnecessary-continue
# - avoid-unnecessary-compare-to
# Style
# - prefer-trailing-comma
# - unnecessary-trailing-comma
- prefer-declaring-const-constructor
# - prefer-single-widget-per-file
- prefer-switch-expression
# - prefer-prefixed-global-constants
# - prefer-correct-callback-field-name

View File

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

View File

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

View File

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

View File

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

View File

@@ -33,7 +33,7 @@ class SearchApiRepository extends ApiRepository {
personIds: filter.people.map((e) => e.id).toList(),
type: type,
page: page,
size: 100,
size: 1000,
),
);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -211,7 +211,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget {
if (currentUser == null) {
return;
}
ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
unawaited(ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id));
},
child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(),
),

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -54,9 +56,11 @@ class DriftBackupOptionsPage extends ConsumerWidget {
);
final backupNotifier = ref.read(driftBackupProvider.notifier);
backupNotifier.cancel().then((_) {
backupNotifier.startBackup(currentUser.id);
});
unawaited(
backupNotifier.cancel().then((_) {
backupNotifier.startBackup(currentUser.id);
}),
);
}
},
child: Scaffold(

View File

@@ -1,12 +1,12 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
import 'package:immich_mobile/utils/bytes_units.dart';
import 'package:path/path.dart' as path;
@@ -163,8 +163,8 @@ class DriftUploadDetailPage extends ConsumerWidget {
);
}
Future<void> _showFileDetailDialog(BuildContext context, DriftUploadStatus item) async {
showDialog(
Future<void> _showFileDetailDialog(BuildContext context, DriftUploadStatus item) {
return showDialog(
context: context,
builder: (context) => FileDetailDialog(uploadStatus: item),
);

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -53,39 +55,41 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
final backgroundManager = ref.read(backgroundSyncProvider);
final backupProvider = ref.read(driftBackupProvider.notifier);
ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then(
(_) async {
try {
wsProvider.connect();
infoProvider.getServerInfo();
unawaited(
ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then(
(_) async {
try {
wsProvider.connect();
unawaited(infoProvider.getServerInfo());
if (Store.isBetaTimelineEnabled) {
await Future.wait([backgroundManager.syncLocal(), backgroundManager.syncRemote()]);
await Future.wait([
backgroundManager.hashAssets().then((_) {
_resumeBackup(backupProvider);
}),
_resumeBackup(backupProvider),
]);
if (Store.isBetaTimelineEnabled) {
await Future.wait([backgroundManager.syncLocal(), backgroundManager.syncRemote()]);
await Future.wait([
backgroundManager.hashAssets().then((_) {
_resumeBackup(backupProvider);
}),
_resumeBackup(backupProvider),
]);
if (Store.get(StoreKey.syncAlbums, false)) {
await backgroundManager.syncLinkedAlbum();
if (Store.get(StoreKey.syncAlbums, false)) {
await backgroundManager.syncLinkedAlbum();
}
}
} catch (e) {
log.severe('Failed establishing connection to the server: $e');
}
} catch (e) {
log.severe('Failed establishing connection to the server: $e');
}
},
onError: (exception) => {
log.severe('Failed to update auth info with access token: $accessToken'),
ref.read(authProvider.notifier).logout(),
context.replaceRoute(const LoginRoute()),
},
},
onError: (exception) => {
log.severe('Failed to update auth info with access token: $accessToken'),
ref.read(authProvider.notifier).logout(),
context.replaceRoute(const LoginRoute()),
},
),
);
} else {
log.severe('Missing crucial offline login info - Logging out completely');
ref.read(authProvider.notifier).logout();
context.replaceRoute(const LoginRoute());
unawaited(ref.read(authProvider.notifier).logout());
unawaited(context.replaceRoute(const LoginRoute()));
return;
}
@@ -95,11 +99,11 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
final needBetaMigration = Store.get(StoreKey.needBetaMigration, false);
if (needBetaMigration) {
await Store.put(StoreKey.needBetaMigration, false);
context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: true)]);
unawaited(context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: true)]));
return;
}
context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute());
unawaited(context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()));
}
if (Store.isBetaTimelineEnabled) {
@@ -109,7 +113,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
final hasPermission = await ref.read(galleryPermissionNotifier.notifier).hasPermission;
if (hasPermission) {
// Resume backup (if enable) then navigate
ref.watch(backupProvider.notifier).resumeBackup();
unawaited(ref.watch(backupProvider.notifier).resumeBackup());
}
}
@@ -119,7 +123,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
if (isEnableBackup) {
final currentUser = Store.tryGet(StoreKey.currentUser);
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:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'edit.page.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:auto_route/auto_route.dart';
/// A widget for cropping an image.
/// This widget uses [HookWidget] to manage its lifecycle and state. It allows
@@ -35,7 +38,7 @@ class CropImagePage extends HookWidget {
icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24),
onPressed: () async {
final croppedImage = await cropController.croppedImage();
context.pushRoute(EditImageRoute(asset: asset, image: croppedImage, isEdited: true));
unawaited(context.pushRoute(EditImageRoute(asset: asset, image: croppedImage, isEdited: true)));
},
),
],

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -179,7 +181,7 @@ class _DriftCreateAlbumPageState extends ConsumerState<DriftCreateAlbumPage> {
if (album != null) {
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:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -139,7 +141,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
toastType: ToastType.success,
);
context.pushRoute(const DriftAlbumsRoute());
unawaited(context.pushRoute(const DriftAlbumsRoute()));
} catch (e) {
ImmichToast.show(
context: context,
@@ -161,12 +163,12 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
setState(() {
_album = _album.copyWith(name: result.name, description: result.description ?? '');
});
HapticFeedback.mediumImpact();
unawaited(HapticFeedback.mediumImpact());
}
}
Future<void> showActivity(BuildContext context) async {
context.pushRoute(const DriftActivitiesRoute());
unawaited(context.pushRoute(const DriftActivitiesRoute()));
}
void showOptionSheet(BuildContext context) {
@@ -207,7 +209,7 @@ class _RemoteAlbumPageState extends ConsumerState<RemoteAlbumPage> {
},
onCreateSharedLink: () async {
context.pop();
context.pushRoute(SharedLinkEditRoute(albumId: _album.id));
unawaited(context.pushRoute(SharedLinkEditRoute(albumId: _album.id)));
},
onShowOptions: () {
context.pop();

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:crop_image/crop_image.dart';
import 'package:easy_localization/easy_localization.dart';
@@ -34,7 +36,7 @@ class DriftCropImagePage extends HookWidget {
icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24),
onPressed: () async {
final croppedImage = await cropController.croppedImage();
context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true));
unawaited(context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true)));
},
),
],

View File

@@ -65,7 +65,7 @@ class DriftEditImagePage extends ConsumerWidget {
Logger("SaveEditedImage").warning("Failed to retrieve the saved image back from OS", e);
}
ref.read(backgroundSyncProvider).syncLocal(full: true);
unawaited(ref.read(backgroundSyncProvider).syncLocal(full: true));
context.navigator.popUntil((route) => route.isFirst);
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),
onPressed: () async {
final filteredImage = await applyFilterAndConvert(colorFilter.value);
context.pushRoute(DriftEditImageRoute(asset: asset, image: filteredImage, isEdited: true));
unawaited(context.pushRoute(DriftEditImageRoute(asset: asset, image: filteredImage, isEdited: true)));
},
),
],

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,8 +9,8 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart';
import 'package:immich_mobile/presentation/widgets/map/map_utils.dart';
import 'package:immich_mobile/presentation/widgets/map/map.state.dart';
import 'package:immich_mobile/presentation/widgets/map/map_utils.dart';
import 'package:immich_mobile/utils/async_mutex.dart';
import 'package:immich_mobile/utils/debounce.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
@@ -114,12 +114,14 @@ class _DriftMapState extends ConsumerState<DriftMap> {
}
final bounds = await controller.getVisibleRegion();
_reloadMutex.run(() async {
if (mounted && ref.read(mapStateProvider.notifier).setBounds(bounds)) {
final markers = await ref.read(mapMarkerProvider(bounds).future);
await reloadMarkers(markers);
}
});
unawaited(
_reloadMutex.run(() async {
if (mounted && ref.read(mapStateProvider.notifier).setBounds(bounds)) {
final markers = await ref.read(mapMarkerProvider(bounds).future);
await reloadMarkers(markers);
}
}),
);
}
Future<void> reloadMarkers(Map<String, dynamic> markers) async {
@@ -147,7 +149,7 @@ class _DriftMapState extends ConsumerState<DriftMap> {
final controller = mapController;
if (controller != null && location != null) {
controller.animateCamera(
await controller.animateCamera(
CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), MapUtils.mapZoomToAssetLevel),
duration: const Duration(milliseconds: 800),
);

View File

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

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:auto_route/auto_route.dart';
@@ -15,8 +16,8 @@ import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'
import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart';
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
import 'package:immich_mobile/routing/router.dart';
@@ -156,11 +157,13 @@ class _AssetTileWidget extends ConsumerWidget {
await ref.read(timelineServiceProvider).loadAssets(assetIndex, 1);
ref.read(isPlayingMotionVideoProvider.notifier).playing = false;
AssetViewer.setAsset(ref, asset);
ctx.pushRoute(
AssetViewerRoute(
initialIndex: assetIndex,
timelineService: ref.read(timelineServiceProvider),
heroOffset: heroOffset,
unawaited(
ctx.pushRoute(
AssetViewerRoute(
initialIndex: assetIndex,
timelineService: ref.read(timelineServiceProvider),
heroOffset: heroOffset,
),
),
);
}

View File

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

View File

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

View File

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

View File

@@ -104,7 +104,7 @@ class ShareIntentUploadStateNotifier extends StateNotifier<List<ShareIntentAttac
Future<void> upload(File file) async {
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 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:background_downloader/background_downloader.dart';
import 'package:flutter/material.dart';
@@ -64,7 +66,7 @@ class ActionNotifier extends Notifier<void> {
void _downloadLivePhotoCallback(TaskStatusUpdate update) async {
if (update.status == TaskStatus.complete) {
final livePhotosId = LivePhotosMetadata.fromJson(update.task.metaData).id;
_downloadService.saveLivePhotos(update.task, livePhotosId);
unawaited(_downloadService.saveLivePhotos(update.task, livePhotosId));
}
}
@@ -122,7 +124,7 @@ class ActionNotifier extends Notifier<void> {
if (assets.length > 1) {
return ActionResult(count: assets.length, success: false, error: 'Cannot troubleshoot multiple assets');
}
context.pushRoute(AssetTroubleshootRoute(asset: assets.first));
unawaited(context.pushRoute(AssetTroubleshootRoute(asset: assets.first)));
return ActionResult(count: assets.length, success: true);
}

View File

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

View File

@@ -1,17 +1,18 @@
import 'dart:async';
import 'dart:io';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/exif.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/asset.entity.dart' as asset_entity;
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/response_extensions.dart';
import 'package:immich_mobile/repositories/asset_api.repository.dart';
import 'package:immich_mobile/utils/hash.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/response_extensions.dart';
import 'package:share_plus/share_plus.dart';
final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository(ref.watch(assetApiRepositoryProvider)));
@@ -106,15 +107,17 @@ class AssetMediaRepository {
// we dont want to await the share result since the
// "preparing" dialog will not disappear unti
Share.shareXFiles(downloadedXFiles).then((result) async {
for (var file in downloadedXFiles) {
try {
await File(file.path).delete();
} catch (e) {
_log.warning("Failed to delete temporary file: ${file.path}", e);
unawaited(
Share.shareXFiles(downloadedXFiles).then((result) async {
for (var file in downloadedXFiles) {
try {
await File(file.path).delete();
} catch (e) {
_log.warning("Failed to delete temporary file: ${file.path}", e);
}
}
}
});
}),
);
return downloadedXFiles.length;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -50,7 +52,7 @@ class ActionService {
);
Future<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 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -6,8 +8,8 @@ import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/upload_profile_image.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
@@ -54,7 +56,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
ref.read(currentUserProvider.notifier).refresh();
}
ref.read(backupProvider.notifier).updateDiskInfo();
unawaited(ref.read(backupProvider.notifier).updateDiskInfo());
}
}
}

View File

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

View File

@@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/models/map/map_event.model.dart';
import 'package:immich_mobile/widgets/map/map_asset_grid.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/utils/draggable_scroll_controller.dart';
import 'package:immich_mobile/widgets/map/map_asset_grid.dart';
class MapBottomSheet extends HookConsumerWidget {
final Stream<MapEvent> mapEventStream;
@@ -34,7 +34,11 @@ class MapBottomSheet extends HookConsumerWidget {
void handleMapEvents(MapEvent event) async {
if (event is MapCloseBottomSheet) {
sheetController.animateTo(0.1, duration: const Duration(milliseconds: 200), curve: Curves.linearToEaseOut);
await sheetController.animateTo(
0.1,
duration: const Duration(milliseconds: 200),
curve: Curves.linearToEaseOut,
);
}
}

View File

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

View File

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

View File

@@ -393,7 +393,6 @@ Class | Method | HTTP request | Description
- [LoginCredentialDto](doc//LoginCredentialDto.md)
- [LoginResponseDto](doc//LoginResponseDto.md)
- [LogoutResponseDto](doc//LogoutResponseDto.md)
- [MachineLearningAvailabilityChecksDto](doc//MachineLearningAvailabilityChecksDto.md)
- [ManualJobName](doc//ManualJobName.md)
- [MapMarkerResponseDto](doc//MapMarkerResponseDto.md)
- [MapReverseGeocodeResponseDto](doc//MapReverseGeocodeResponseDto.md)

View File

@@ -164,7 +164,6 @@ part 'model/log_level.dart';
part 'model/login_credential_dto.dart';
part 'model/login_response_dto.dart';
part 'model/logout_response_dto.dart';
part 'model/machine_learning_availability_checks_dto.dart';
part 'model/manual_job_name.dart';
part 'model/map_marker_response_dto.dart';
part 'model/map_reverse_geocode_response_dto.dart';

View File

@@ -382,8 +382,6 @@ class ApiClient {
return LoginResponseDto.fromJson(value);
case 'LogoutResponseDto':
return LogoutResponseDto.fromJson(value);
case 'MachineLearningAvailabilityChecksDto':
return MachineLearningAvailabilityChecksDto.fromJson(value);
case 'ManualJobName':
return ManualJobNameTypeTransformer().decode(value);
case 'MapMarkerResponseDto':

View File

@@ -1,115 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class MachineLearningAvailabilityChecksDto {
/// Returns a new [MachineLearningAvailabilityChecksDto] instance.
MachineLearningAvailabilityChecksDto({
required this.enabled,
required this.interval,
required this.timeout,
});
bool enabled;
num interval;
num timeout;
@override
bool operator ==(Object other) => identical(this, other) || other is MachineLearningAvailabilityChecksDto &&
other.enabled == enabled &&
other.interval == interval &&
other.timeout == timeout;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(enabled.hashCode) +
(interval.hashCode) +
(timeout.hashCode);
@override
String toString() => 'MachineLearningAvailabilityChecksDto[enabled=$enabled, interval=$interval, timeout=$timeout]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'enabled'] = this.enabled;
json[r'interval'] = this.interval;
json[r'timeout'] = this.timeout;
return json;
}
/// Returns a new [MachineLearningAvailabilityChecksDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static MachineLearningAvailabilityChecksDto? fromJson(dynamic value) {
upgradeDto(value, "MachineLearningAvailabilityChecksDto");
if (value is Map) {
final json = value.cast<String, dynamic>();
return MachineLearningAvailabilityChecksDto(
enabled: mapValueOfType<bool>(json, r'enabled')!,
interval: num.parse('${json[r'interval']}'),
timeout: num.parse('${json[r'timeout']}'),
);
}
return null;
}
static List<MachineLearningAvailabilityChecksDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <MachineLearningAvailabilityChecksDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = MachineLearningAvailabilityChecksDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, MachineLearningAvailabilityChecksDto> mapFromJson(dynamic json) {
final map = <String, MachineLearningAvailabilityChecksDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = MachineLearningAvailabilityChecksDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of MachineLearningAvailabilityChecksDto-objects as value to a dart map
static Map<String, List<MachineLearningAvailabilityChecksDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<MachineLearningAvailabilityChecksDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = MachineLearningAvailabilityChecksDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'enabled',
'interval',
'timeout',
};
}

View File

@@ -13,16 +13,14 @@ part of openapi.api;
class SystemConfigMachineLearningDto {
/// Returns a new [SystemConfigMachineLearningDto] instance.
SystemConfigMachineLearningDto({
required this.availabilityChecks,
required this.clip,
required this.duplicateDetection,
required this.enabled,
required this.facialRecognition,
this.url,
this.urls = const [],
});
MachineLearningAvailabilityChecksDto availabilityChecks;
CLIPConfig clip;
DuplicateDetectionConfig duplicateDetection;
@@ -31,37 +29,50 @@ class SystemConfigMachineLearningDto {
FacialRecognitionConfig facialRecognition;
/// This property was deprecated in v1.122.0
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? url;
List<String> urls;
@override
bool operator ==(Object other) => identical(this, other) || other is SystemConfigMachineLearningDto &&
other.availabilityChecks == availabilityChecks &&
other.clip == clip &&
other.duplicateDetection == duplicateDetection &&
other.enabled == enabled &&
other.facialRecognition == facialRecognition &&
other.url == url &&
_deepEquality.equals(other.urls, urls);
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(availabilityChecks.hashCode) +
(clip.hashCode) +
(duplicateDetection.hashCode) +
(enabled.hashCode) +
(facialRecognition.hashCode) +
(url == null ? 0 : url!.hashCode) +
(urls.hashCode);
@override
String toString() => 'SystemConfigMachineLearningDto[availabilityChecks=$availabilityChecks, clip=$clip, duplicateDetection=$duplicateDetection, enabled=$enabled, facialRecognition=$facialRecognition, urls=$urls]';
String toString() => 'SystemConfigMachineLearningDto[clip=$clip, duplicateDetection=$duplicateDetection, enabled=$enabled, facialRecognition=$facialRecognition, url=$url, urls=$urls]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'availabilityChecks'] = this.availabilityChecks;
json[r'clip'] = this.clip;
json[r'duplicateDetection'] = this.duplicateDetection;
json[r'enabled'] = this.enabled;
json[r'facialRecognition'] = this.facialRecognition;
if (this.url != null) {
json[r'url'] = this.url;
} else {
// json[r'url'] = null;
}
json[r'urls'] = this.urls;
return json;
}
@@ -75,11 +86,11 @@ class SystemConfigMachineLearningDto {
final json = value.cast<String, dynamic>();
return SystemConfigMachineLearningDto(
availabilityChecks: MachineLearningAvailabilityChecksDto.fromJson(json[r'availabilityChecks'])!,
clip: CLIPConfig.fromJson(json[r'clip'])!,
duplicateDetection: DuplicateDetectionConfig.fromJson(json[r'duplicateDetection'])!,
enabled: mapValueOfType<bool>(json, r'enabled')!,
facialRecognition: FacialRecognitionConfig.fromJson(json[r'facialRecognition'])!,
url: mapValueOfType<String>(json, r'url'),
urls: json[r'urls'] is Iterable
? (json[r'urls'] as Iterable).cast<String>().toList(growable: false)
: const [],
@@ -130,7 +141,6 @@ class SystemConfigMachineLearningDto {
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'availabilityChecks',
'clip',
'duplicateDetection',
'enabled',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More