Browse Source

Merge branch 'main' into pull_down_fix

Neeraj Gupta 1 year ago
parent
commit
9e75d4c4d2
50 changed files with 382 additions and 149 deletions
  1. 1 1
      analysis_options.yaml
  2. 1 1
      lib/core/configuration.dart
  3. 3 3
      lib/core/error-reporting/super_logging.dart
  4. 1 0
      lib/db/file_updation_db.dart
  5. 22 0
      lib/db/files_db.dart
  6. 4 4
      lib/main.dart
  7. 3 3
      lib/models/search/search_types.dart
  8. 2 2
      lib/services/billing_service.dart
  9. 1 1
      lib/services/entity_service.dart
  10. 9 5
      lib/services/files_service.dart
  11. 1 1
      lib/services/hidden_service.dart
  12. 9 5
      lib/services/local_authentication_service.dart
  13. 147 0
      lib/services/local_file_update_service.dart
  14. 9 8
      lib/services/user_service.dart
  15. 1 1
      lib/ui/account/delete_account_page.dart
  16. 3 3
      lib/ui/account/password_entry_page.dart
  17. 1 1
      lib/ui/account/verify_recovery_page.dart
  18. 5 2
      lib/ui/actions/collection/collection_file_actions.dart
  19. 16 7
      lib/ui/actions/collection/collection_sharing_actions.dart
  20. 4 1
      lib/ui/actions/file/file_actions.dart
  21. 6 6
      lib/ui/collections/album/vertical_list.dart
  22. 1 1
      lib/ui/collections/new_album_icon.dart
  23. 1 2
      lib/ui/components/bottom_action_bar/bottom_action_bar_widget.dart
  24. 1 1
      lib/ui/components/buttons/button_widget.dart
  25. 1 1
      lib/ui/payment/payment_web_page.dart
  26. 1 1
      lib/ui/payment/store_subscription_page.dart
  27. 21 17
      lib/ui/payment/stripe_subscription_page.dart
  28. 1 1
      lib/ui/payment/view_add_on_widget.dart
  29. 6 2
      lib/ui/settings/advanced_settings_screen.dart
  30. 1 1
      lib/ui/settings/backup/backup_folder_selection_page.dart
  31. 4 4
      lib/ui/settings/backup/backup_section_widget.dart
  32. 3 3
      lib/ui/settings/debug_section_widget.dart
  33. 1 1
      lib/ui/settings/general_section_widget.dart
  34. 1 1
      lib/ui/settings/security_section_widget.dart
  35. 4 1
      lib/ui/sharing/manage_album_participant.dart
  36. 1 1
      lib/ui/sharing/pickers/device_limit_picker_page.dart
  37. 1 1
      lib/ui/sharing/pickers/link_expiry_picker_page.dart
  38. 24 15
      lib/ui/viewer/actions/file_selection_actions_widget.dart
  39. 6 6
      lib/ui/viewer/file/file_app_bar.dart
  40. 7 3
      lib/ui/viewer/file_details/favorite_widget.dart
  41. 1 1
      lib/ui/viewer/gallery/empty_album_state.dart
  42. 5 0
      lib/ui/viewer/gallery/gallery.dart
  43. 10 7
      lib/ui/viewer/gallery/gallery_app_bar_widget.dart
  44. 1 1
      lib/ui/viewer/gallery/hooks/add_photos_sheet.dart
  45. 1 1
      lib/ui/viewer/location/location_screen.dart
  46. 8 5
      lib/utils/delete_file_util.dart
  47. 3 3
      lib/utils/file_util.dart
  48. 8 6
      lib/utils/magic_util.dart
  49. 6 5
      pubspec.lock
  50. 4 2
      pubspec.yaml

+ 1 - 1
analysis_options.yaml

@@ -61,7 +61,7 @@ analyzer:
     cancel_subscriptions: error
 
 
-    unawaited_futures: ignore # convert to warning after fixing existing issues
+    unawaited_futures: warning # convert to warning after fixing existing issues
     invalid_dependency: info
     use_build_context_synchronously: ignore # experimental lint, requires many changes
     prefer_interpolation_to_compose_strings: ignore # later too many warnings

+ 1 - 1
lib/core/configuration.dart

@@ -171,7 +171,7 @@ class Configuration {
       SearchService.instance.clearCache();
       Bus.instance.fire(UserLoggedOutEvent());
     } else {
-      _preferences.setBool("auto_logout", true);
+      await _preferences.setBool("auto_logout", true);
     }
   }
 

+ 3 - 3
lib/core/error-reporting/super_logging.dart

@@ -171,7 +171,7 @@ class SuperLogging {
       await setupLogDir();
     }
     if (sentryIsEnabled) {
-      setupSentry();
+      setupSentry().ignore();
     }
 
     Logger.root.level = Level.ALL;
@@ -266,7 +266,7 @@ class SuperLogging {
 
     // add error to sentry queue
     if (sentryIsEnabled && rec.error != null) {
-      _sendErrorToSentry(rec.error!, null);
+      _sendErrorToSentry(rec.error!, null).ignore();
     }
   }
 
@@ -303,7 +303,7 @@ class SuperLogging {
   static Future<void> setupSentry() async {
     await for (final error in sentryQueueControl.stream.asBroadcastStream()) {
       try {
-        Sentry.captureException(
+        await Sentry.captureException(
           error,
         );
       } catch (e) {

+ 1 - 0
lib/db/file_updation_db.dart

@@ -14,6 +14,7 @@ class FileUpdationDB {
   static const tableName = 're_upload_tracker';
   static const columnLocalID = 'local_id';
   static const columnReason = 'reason';
+  static const livePhotoSize = 'livePhotoSize';
 
   static const modificationTimeUpdated = 'modificationTimeUpdated';
 

+ 22 - 0
lib/db/files_db.dart

@@ -1449,6 +1449,28 @@ class FilesDB {
     return result;
   }
 
+  // For a given userID, return unique uploadedFileId for the given userID
+  Future<List<String>> getLivePhotosWithBadSize(
+    int userId,
+    int sizeInBytes,
+  ) async {
+    final db = await instance.database;
+    final rows = await db.query(
+      filesTable,
+      columns: [columnLocalID],
+      distinct: true,
+      where: '$columnOwnerID = ? AND '
+          '($columnFileSize IS NULL OR $columnFileSize = ?) AND '
+          '$columnFileType = ? AND $columnLocalID IS NOT NULL',
+      whereArgs: [userId, sizeInBytes, getInt(FileType.livePhoto)],
+    );
+    final result = <String>[];
+    for (final row in rows) {
+      result.add(row[columnLocalID] as String);
+    }
+    return result;
+  }
+
   // updateSizeForUploadIDs takes a map of upploadedFileID and fileSize and
   // update the fileSize for the given uploadedFileID
   Future<void> updateSizeForUploadIDs(

+ 4 - 4
lib/main.dart

@@ -65,8 +65,8 @@ void main() async {
   MediaKit.ensureInitialized();
   final savedThemeMode = await AdaptiveTheme.getThemeMode();
   await _runInForeground(savedThemeMode);
-  BackgroundFetch.registerHeadlessTask(_headlessTaskHandler);
-  FlutterDisplayMode.setHighRefreshRate();
+  unawaited(BackgroundFetch.registerHeadlessTask(_headlessTaskHandler));
+  FlutterDisplayMode.setHighRefreshRate().ignore();
 }
 
 Future<void> _runInForeground(AdaptiveThemeMode? savedThemeMode) async {
@@ -129,7 +129,7 @@ Future<void> _runInBackground(String taskId) async {
     _scheduleSuicide(kBGTaskTimeout, taskId); // To prevent OS from punishing us
   }
   await _init(true, via: 'runViaBackgroundTask');
-  UpdateService.instance.showUpdateNotification();
+  UpdateService.instance.showUpdateNotification().ignore();
   await _sync('bgSync');
   BackgroundFetch.finish(taskId);
 }
@@ -158,7 +158,7 @@ Future<void> _init(bool isBackground, {String via = ''}) async {
     AppLifecycleService.instance.onAppInForeground('init via: $via');
   }
   // Start workers asynchronously. No need to wait for them to start
-  Computer.shared().turnOn(workersCount: 4, verbose: kDebugMode);
+  Computer.shared().turnOn(workersCount: 4, verbose: kDebugMode).ignore();
   CryptoUtil.init();
   await NetworkClient.instance.init();
   await Configuration.instance.init();

+ 3 - 3
lib/models/search/search_types.dart

@@ -181,7 +181,7 @@ extension SectionTypeExtensions on SectionType {
     switch (this) {
       case SectionType.contacts:
         return () async {
-          shareText(
+          await shareText(
             S.of(context).shareTextRecommendUsingEnte,
           );
         };
@@ -210,7 +210,7 @@ extension SectionTypeExtensions on SectionType {
               try {
                 final Collection c =
                     await CollectionsService.instance.createAlbum(text);
-                routeToPage(
+                await routeToPage(
                   context,
                   CollectionPage(CollectionWithThumbnail(c, null)),
                 );
@@ -222,7 +222,7 @@ extension SectionTypeExtensions on SectionType {
             },
           );
           if (result is Exception) {
-            showGenericErrorDialog(context: context, error: result);
+            await showGenericErrorDialog(context: context, error: result);
           }
         };
       default:

+ 2 - 2
lib/services/billing_service.dart

@@ -184,7 +184,7 @@ class BillingService {
     try {
       final String jwtToken = await UserService.instance.getFamiliesToken();
       final bool familyExist = userDetails.isPartOfFamily();
-      Navigator.of(context).push(
+      await Navigator.of(context).push(
         MaterialPageRoute(
           builder: (BuildContext context) {
             return WebPage(
@@ -196,7 +196,7 @@ class BillingService {
       );
     } catch (e) {
       await dialog.hide();
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
     }
     await dialog.hide();
   }

+ 1 - 1
lib/services/entity_service.dart

@@ -163,7 +163,7 @@ class EntityService {
         encryptedKey = response.encryptedKey;
         header = response.header;
         await _prefs.setString(_getEntityKeyPrefix(type), encryptedKey);
-        _prefs.setString(_getEntityHeaderPrefix(type), header);
+        await _prefs.setString(_getEntityHeaderPrefix(type), header);
       }
       final entityKey = CryptoUtil.decryptSync(
         Sodium.base642bin(encryptedKey),

+ 9 - 5
lib/services/files_service.dart

@@ -49,11 +49,7 @@ class FilesService {
       if (uploadIDsWithMissingSize.isEmpty) {
         return Future.value(true);
       }
-      final batchedFiles = uploadIDsWithMissingSize.chunks(1000);
-      for (final batch in batchedFiles) {
-        final Map<int, int> uploadIdToSize = await getFilesSizeFromInfo(batch);
-        await _filesDB.updateSizeForUploadIDs(uploadIdToSize);
-      }
+      await backFillSizes(uploadIDsWithMissingSize);
       return Future.value(true);
     } catch (e, s) {
       _logger.severe("error during has migrated sizes", e, s);
@@ -61,6 +57,14 @@ class FilesService {
     }
   }
 
+  Future<void> backFillSizes(List<int> uploadIDsWithMissingSize) async {
+    final batchedFiles = uploadIDsWithMissingSize.chunks(1000);
+    for (final batch in batchedFiles) {
+      final Map<int, int> uploadIdToSize = await getFilesSizeFromInfo(batch);
+      await _filesDB.updateSizeForUploadIDs(uploadIdToSize);
+    }
+  }
+
   Future<Map<int, int>> getFilesSizeFromInfo(List<int> uploadedFileID) async {
     try {
       final response = await _enteDio.post(

+ 1 - 1
lib/services/hidden_service.dart

@@ -154,7 +154,7 @@ extension HiddenService on CollectionsService {
     } catch (e, s) {
       _logger.severe("Could not hide", e, s);
       await dialog.hide();
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
       return false;
     } finally {
       await dialog.hide();

+ 9 - 5
lib/services/local_authentication_service.dart

@@ -1,3 +1,5 @@
+import "dart:async";
+
 import 'package:flutter/material.dart';
 import 'package:local_auth/local_auth.dart';
 import 'package:photos/core/configuration.dart';
@@ -22,7 +24,7 @@ class LocalAuthenticationService {
         Configuration.instance.shouldShowLockScreen(),
       );
       if (!result) {
-        showToast(context, infoMessage);
+        unawaited(showToast(context, infoMessage));
         return false;
       } else {
         return true;
@@ -54,10 +56,12 @@ class LocalAuthenticationService {
             .setEnabled(Configuration.instance.shouldShowLockScreen());
       }
     } else {
-      showErrorDialog(
-        context,
-        errorDialogTitle,
-        errorDialogContent,
+      unawaited(
+        showErrorDialog(
+          context,
+          errorDialogTitle,
+          errorDialogContent,
+        ),
       );
     }
     return false;

+ 147 - 0
lib/services/local_file_update_service.dart

@@ -7,8 +7,10 @@ import "package:photos/core/configuration.dart";
 import 'package:photos/core/errors.dart';
 import 'package:photos/db/file_updation_db.dart';
 import 'package:photos/db/files_db.dart';
+import "package:photos/extensions/stop_watch.dart";
 import 'package:photos/models/file/file.dart';
 import 'package:photos/models/file/file_type.dart';
+import "package:photos/services/files_service.dart";
 import 'package:photos/utils/file_uploader_util.dart';
 import 'package:photos/utils/file_util.dart';
 import 'package:shared_preferences/shared_preferences.dart';
@@ -19,6 +21,9 @@ class LocalFileUpdateService {
   late FileUpdationDB _fileUpdationDB;
   late SharedPreferences _prefs;
   late Logger _logger;
+  final String _iosLivePhotoSizeMigrationDone = 'fm_ios_live_photo_size';
+  final String _doneLivePhotoImport = 'fm_import_ios_live_photo_size';
+  static int fourMBWithChunkSize = 4194338;
   final List<String> _oldMigrationKeys = [
     'fm_badCreationTime',
     'fm_badCreationTimeCompleted',
@@ -52,6 +57,8 @@ class LocalFileUpdateService {
       await _markFilesWhichAreActuallyUpdated();
       if (Platform.isAndroid) {
         _cleanUpOlderMigration().ignore();
+      } else {
+        await _handleLivePhotosSizedCheck();
       }
     } catch (e, s) {
       _logger.severe('failed to perform migration', e, s);
@@ -199,6 +206,146 @@ class LocalFileUpdateService {
     );
   }
 
+  Future<void> _handleLivePhotosSizedCheck() async {
+    try {
+      if (_prefs.containsKey(_iosLivePhotoSizeMigrationDone)) {
+        return;
+      }
+      await _importLivePhotoReUploadCandidates();
+      final sTime = DateTime.now().microsecondsSinceEpoch;
+      // singleRunLimit indicates number of files to check during single
+      // invocation of this method. The limit act as a crude way to limit the
+      // resource consumed by the method
+      const int singleRunLimit = 50;
+      final localIDsToProcess =
+          await _fileUpdationDB.getLocalIDsForPotentialReUpload(
+        singleRunLimit,
+        FileUpdationDB.livePhotoSize,
+      );
+      if (localIDsToProcess.isNotEmpty) {
+        await _checkLivePhotoWithLowOrUnknownSize(
+          localIDsToProcess,
+        );
+        final eTime = DateTime.now().microsecondsSinceEpoch;
+        final d = Duration(microseconds: eTime - sTime);
+        _logger.info(
+          'Performed hashCheck for ${localIDsToProcess.length} livePhoto files '
+          'completed in ${d.inSeconds.toString()} secs',
+        );
+      } else {
+        _prefs.setBool(_iosLivePhotoSizeMigrationDone, true);
+      }
+    } catch (e, s) {
+      _logger.severe('error while checking livePhotoSize check', e, s);
+    }
+  }
+
+  Future<void> _checkLivePhotoWithLowOrUnknownSize(
+    List<String> localIDsToProcess,
+  ) async {
+    final int userID = Configuration.instance.getUserID()!;
+    final List<EnteFile> result =
+        await FilesDB.instance.getLocalFiles(localIDsToProcess);
+    final List<EnteFile> localFilesForUser = [];
+    final Set<String> localIDsWithFile = {};
+    final Set<int> missingSizeIDs = {};
+    for (EnteFile file in result) {
+      if (file.ownerID == null || file.ownerID == userID) {
+        localFilesForUser.add(file);
+        localIDsWithFile.add(file.localID!);
+        if (file.isUploaded && file.fileSize == null) {
+          missingSizeIDs.add(file.uploadedFileID!);
+        }
+      }
+    }
+    if (missingSizeIDs.isNotEmpty) {
+      await FilesService.instance.backFillSizes(missingSizeIDs.toList());
+      _logger.info('sizes back fill for ${missingSizeIDs.length} files');
+      // return early, let the check run in the next batch
+      return;
+    }
+
+    final Set<String> processedIDs = {};
+    // if a file for localID doesn't exist, then mark it as processed
+    // otherwise the app will be stuck in retrying same set of ids
+
+    for (String localID in localIDsToProcess) {
+      if (!localIDsWithFile.contains(localID)) {
+        processedIDs.add(localID);
+      }
+    }
+    _logger.info(" check ${localIDsToProcess.length} files for livePhotoSize, "
+        "missing file cnt ${processedIDs.length}");
+
+    for (EnteFile file in localFilesForUser) {
+      if (file.fileSize == null) {
+        _logger.info('fileSize still null, skip this file');
+        continue;
+      } else if (file.fileType != FileType.livePhoto) {
+        _logger.severe('fileType is not livePhoto, skip this file');
+        continue;
+      } else if (file.fileSize! != fourMBWithChunkSize) {
+        // back-filled size is not of our interest
+        processedIDs.add(file.localID!);
+        continue;
+      }
+      if (processedIDs.contains(file.localID)) {
+        continue;
+      }
+      try {
+        final MediaUploadData uploadData = await getUploadData(file);
+        _logger.info(
+            'Found livePhoto on local with hash ${uploadData.hashData?.fileHash ?? "null"} and existing hash ${file.hash ?? "null"}');
+        await clearCache(file);
+        await FilesDB.instance.markFilesForReUpload(
+          userID,
+          file.localID!,
+          file.title,
+          file.location,
+          file.creationTime!,
+          file.modificationTime!,
+          file.fileType,
+        );
+        processedIDs.add(file.localID!);
+      } on InvalidFileError catch (e) {
+        if (e.reason == InvalidReason.livePhotoToImageTypeChanged ||
+            e.reason == InvalidReason.imageToLivePhotoTypeChanged) {
+          // let existing file update check handle this case
+          _fileUpdationDB.insertMultiple(
+            [file.localID!],
+            FileUpdationDB.modificationTimeUpdated,
+          ).ignore();
+        } else {
+          _logger.severe("livePhoto check failed: invalid file ${file.tag}", e);
+        }
+        processedIDs.add(file.localID!);
+      } catch (e) {
+        _logger.severe("livePhoto check failed", e);
+      } finally {}
+    }
+    await _fileUpdationDB.deleteByLocalIDs(
+      processedIDs.toList(),
+      FileUpdationDB.livePhotoSize,
+    );
+  }
+
+  Future<void> _importLivePhotoReUploadCandidates() async {
+    if (_prefs.containsKey(_doneLivePhotoImport)) {
+      return;
+    }
+    _logger.info('_importLivePhotoReUploadCandidates');
+    final EnteWatch watch = EnteWatch("_importLivePhotoReUploadCandidates");
+    final int ownerID = Configuration.instance.getUserID()!;
+    final List<String> localIDs = await FilesDB.instance
+        .getLivePhotosWithBadSize(ownerID, fourMBWithChunkSize);
+    await _fileUpdationDB.insertMultiple(
+      localIDs,
+      FileUpdationDB.livePhotoSize,
+    );
+    watch.log("imported ${localIDs.length} files");
+    await _prefs.setBool(_doneLivePhotoImport, true);
+  }
+
   Future<MediaUploadData> getUploadData(EnteFile file) async {
     final mediaUploadData = await getUploadDataFromEnteFile(file);
     // delete the file from app's internal cache if it was copied to app

+ 9 - 8
lib/services/user_service.dart

@@ -338,7 +338,7 @@ class UserService {
         Widget page;
         final String twoFASessionID = response.data["twoFactorSessionID"];
         if (twoFASessionID.isNotEmpty) {
-          setTwoFactor(value: true);
+          await setTwoFactor(value: true);
           page = TwoFactorAuthenticationPage(twoFASessionID);
         } else {
           await _saveConfiguration(response);
@@ -354,7 +354,7 @@ class UserService {
             );
           }
         }
-        Navigator.of(context).pushAndRemoveUntil(
+        await Navigator.of(context).pushAndRemoveUntil(
           MaterialPageRoute(
             builder: (BuildContext context) {
               return page;
@@ -740,9 +740,10 @@ class UserService {
       );
       await dialog.hide();
       if (response.statusCode == 200) {
-        showShortToast(context, S.of(context).authenticationSuccessful);
+        showShortToast(context, S.of(context).authenticationSuccessful)
+            .ignore();
         await _saveConfiguration(response);
-        Navigator.of(context).pushAndRemoveUntil(
+        await Navigator.of(context).pushAndRemoveUntil(
           MaterialPageRoute(
             builder: (BuildContext context) {
               return const PasswordReentryPage();
@@ -755,8 +756,8 @@ class UserService {
       await dialog.hide();
       _logger.severe(e);
       if (e.response != null && e.response!.statusCode == 404) {
-        showToast(context, "Session expired");
-        Navigator.of(context).pushAndRemoveUntil(
+        showToast(context, "Session expired").ignore();
+        await Navigator.of(context).pushAndRemoveUntil(
           MaterialPageRoute(
             builder: (BuildContext context) {
               return const LoginPage();
@@ -809,7 +810,7 @@ class UserService {
     } on DioError catch (e) {
       _logger.severe(e);
       if (e.response != null && e.response!.statusCode == 404) {
-        showToast(context, S.of(context).sessionExpired);
+        showToast(context, S.of(context).sessionExpired).ignore();
         Navigator.of(context).pushAndRemoveUntil(
           MaterialPageRoute(
             builder: (BuildContext context) {
@@ -959,7 +960,7 @@ class UserService {
     try {
       recoveryKey = await getOrCreateRecoveryKey(context);
     } catch (e) {
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
       return false;
     }
     final dialog = createProgressDialog(context, S.of(context).verifying);

+ 1 - 1
lib/ui/account/delete_account_page.dart

@@ -289,7 +289,7 @@ class _DeleteAccountPageState extends State<DeleteAccountPage> {
       showShortToast(context, S.of(context).yourAccountHasBeenDeleted);
     } catch (e, s) {
       Logger("DeleteAccount").severe("failed to delete", e, s);
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
     }
   }
 

+ 3 - 3
lib/ui/account/password_entry_page.dart

@@ -404,7 +404,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
     } catch (e, s) {
       _logger.severe(e, s);
       await dialog.hide();
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
     }
   }
 
@@ -456,7 +456,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
         } catch (e, s) {
           _logger.severe(e, s);
           await dialog.hide();
-          showGenericErrorDialog(context: context, error: e);
+          await showGenericErrorDialog(context: context, error: e);
         }
       }
 
@@ -481,7 +481,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
           S.of(context).sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease,
         );
       } else {
-        showGenericErrorDialog(context: context, error: e);
+        await showGenericErrorDialog(context: context, error: e);
       }
     }
   }

+ 1 - 1
lib/ui/account/verify_recovery_page.dart

@@ -109,7 +109,7 @@ class _VerifyRecoveryPageState extends State<VerifyRecoveryPage> {
           ),
         );
       } catch (e) {
-        showGenericErrorDialog(context: context, error: e);
+        await showGenericErrorDialog(context: context, error: e);
         return;
       }
     }

+ 5 - 2
lib/ui/actions/collection/collection_file_actions.dart

@@ -73,7 +73,10 @@ extension CollectionFileActions on CollectionActions {
     );
     if (actionResult?.action != null &&
         actionResult!.action == ButtonAction.error) {
-      showGenericErrorDialog(context: bContext, error: actionResult.exception);
+      await showGenericErrorDialog(
+        context: bContext,
+        error: actionResult.exception,
+      );
     } else {
       selectedFiles.clearAll();
     }
@@ -187,7 +190,7 @@ extension CollectionFileActions on CollectionActions {
     } catch (e, s) {
       logger.severe("Failed to add to album", e, s);
       await dialog?.hide();
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
       rethrow;
     }
   }

+ 16 - 7
lib/ui/actions/collection/collection_sharing_actions.dart

@@ -52,7 +52,7 @@ class CollectionActions {
         _showUnSupportedAlert(context);
       } else {
         logger.severe("Failed to update shareUrl collection", e);
-        showGenericErrorDialog(context: context, error: e);
+        await showGenericErrorDialog(context: context, error: e);
       }
       return false;
     }
@@ -93,7 +93,10 @@ class CollectionActions {
     );
     if (actionResult?.action != null) {
       if (actionResult!.action == ButtonAction.error) {
-        showGenericErrorDialog(context: context, error: actionResult.exception);
+        await showGenericErrorDialog(
+          context: context,
+          error: actionResult.exception,
+        );
       }
       return actionResult.action == ButtonAction.first;
     } else {
@@ -142,7 +145,7 @@ class CollectionActions {
       return collection;
     } catch (e, s) {
       dialog.hide();
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
       logger.severe("Failing to create link for selected files", e, s);
     }
     return null;
@@ -183,7 +186,10 @@ class CollectionActions {
     );
     if (actionResult?.action != null) {
       if (actionResult!.action == ButtonAction.error) {
-        showGenericErrorDialog(context: context, error: actionResult.exception);
+        await showGenericErrorDialog(
+          context: context,
+          error: actionResult.exception,
+        );
       }
       return actionResult.action == ButtonAction.first;
     }
@@ -230,7 +236,7 @@ class CollectionActions {
     } catch (e) {
       await dialog?.hide();
       logger.severe("Failed to get public key", e);
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
       return false;
     }
     // getPublicKey can return null when no user is associated with given
@@ -272,7 +278,7 @@ class CollectionActions {
           _showUnSupportedAlert(context);
         } else {
           logger.severe("failed to share collection", e);
-          showGenericErrorDialog(context: context, error: e);
+          await showGenericErrorDialog(context: context, error: e);
         }
         return false;
       }
@@ -353,7 +359,10 @@ class CollectionActions {
     );
     if (actionResult?.action != null &&
         actionResult!.action == ButtonAction.error) {
-      showGenericErrorDialog(context: bContext, error: actionResult.exception);
+      await showGenericErrorDialog(
+        context: bContext,
+        error: actionResult.exception,
+      );
       return false;
     }
     if ((actionResult?.action != null) &&

+ 4 - 1
lib/ui/actions/file/file_actions.dart

@@ -125,7 +125,10 @@ Future<void> showSingleFileDeleteSheet(
   );
   if (actionResult?.action != null &&
       actionResult!.action == ButtonAction.error) {
-    showGenericErrorDialog(context: context, error: actionResult.exception);
+    await showGenericErrorDialog(
+      context: context,
+      error: actionResult.exception,
+    );
   }
 }
 

+ 6 - 6
lib/ui/collections/album/vertical_list.dart

@@ -109,7 +109,7 @@ class AlbumVerticalListWidget extends StatelessWidget {
         textCapitalization: TextCapitalization.words,
       );
       if (result is Exception) {
-        showGenericErrorDialog(
+        await showGenericErrorDialog(
           context: context,
           error: result,
         );
@@ -311,7 +311,7 @@ class AlbumVerticalListWidget extends StatelessWidget {
           );
           return true;
         } catch (e) {
-          showGenericErrorDialog(context: context, error: e);
+          await showGenericErrorDialog(context: context, error: e);
           return false;
         }
       }
@@ -334,7 +334,7 @@ class AlbumVerticalListWidget extends StatelessWidget {
           ),
         );
       } else {
-        showGenericErrorDialog(context: context, error: result);
+        await showGenericErrorDialog(context: context, error: result);
         _logger.severe("Cannot share collections owned by others");
       }
     }
@@ -356,7 +356,7 @@ class AlbumVerticalListWidget extends StatelessWidget {
       showGenericErrorDialog(
         context: context,
         error: Exception("Can not share collection owned by others"),
-      );
+      ).ignore();
       _logger.severe("Cannot share collections owned by others");
     }
     return Future.value(true);
@@ -413,7 +413,7 @@ class AlbumVerticalListWidget extends StatelessWidget {
     } catch (e, s) {
       _logger.severe("Could not move to album", e, s);
       await dialog.hide();
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
       return false;
     }
   }
@@ -442,7 +442,7 @@ class AlbumVerticalListWidget extends StatelessWidget {
     } catch (e, s) {
       _logger.severe("Could not move to album", e, s);
       await dialog.hide();
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
       return false;
     }
   }

+ 1 - 1
lib/ui/collections/new_album_icon.dart

@@ -55,7 +55,7 @@ class NewAlbumIcon extends StatelessWidget {
           },
         );
         if (result is Exception) {
-          showGenericErrorDialog(context: context, error: result);
+          await showGenericErrorDialog(context: context, error: result);
         }
       },
     );

+ 1 - 2
lib/ui/components/bottom_action_bar/bottom_action_bar_widget.dart

@@ -49,13 +49,12 @@ class BottomActionBarWidget extends StatelessWidget {
       child: Column(
         mainAxisSize: MainAxisSize.min,
         children: [
-          const SizedBox(height: 12),
+          const SizedBox(height: 8),
           FileSelectionActionsWidget(
             galleryType,
             selectedFiles,
             collection: collection,
           ),
-          const SizedBox(height: 20),
           const DividerWidget(dividerType: DividerType.bottomBar),
           ActionBarWidget(
             selectedFiles: selectedFiles,

+ 1 - 1
lib/ui/components/buttons/button_widget.dart

@@ -498,7 +498,7 @@ class _ButtonChildWidgetState extends State<ButtonChildWidget> {
       } else if (exception != null) {
         //This is to show the execution was unsuccessful if the dialog is manually
         //closed before the execution completes.
-        showGenericErrorDialog(context: context, error: exception);
+        showGenericErrorDialog(context: context, error: exception).ignore();
       }
     }
   }

+ 1 - 1
lib/ui/payment/payment_web_page.dart

@@ -190,7 +190,7 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
     } else {
       // should never reach here
       _logger.severe("unexpected status", uri.toString());
-      showGenericErrorDialog(
+      await showGenericErrorDialog(
         context: context,
         error: Exception("expected payment status $paymentStatus"),
       );

+ 1 - 1
lib/ui/payment/store_subscription_page.dart

@@ -528,7 +528,7 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
                     response.notFoundIDs.toString();
                 _logger.severe(errMsg);
                 await _dialog.hide();
-                showGenericErrorDialog(
+                await showGenericErrorDialog(
                   context: context,
                   error: Exception(errMsg),
                 );

+ 21 - 17
lib/ui/payment/stripe_subscription_page.dart

@@ -303,20 +303,22 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
         await _launchStripePortal();
         break;
       case playStore:
-        launchUrlString(
-          "https://play.google.com/store/account/subscriptions?sku=" +
-              _currentSubscription!.productID +
-              "&package=io.ente.photos",
+        unawaited(
+          launchUrlString(
+            "https://play.google.com/store/account/subscriptions?sku=" +
+                _currentSubscription!.productID +
+                "&package=io.ente.photos",
+          ),
         );
         break;
       case appStore:
-        launchUrlString("https://apps.apple.com/account/billing");
+        unawaited(launchUrlString("https://apps.apple.com/account/billing"));
         break;
       default:
         final String capitalizedWord = paymentProvider.isNotEmpty
             ? '${paymentProvider[0].toUpperCase()}${paymentProvider.substring(1).toLowerCase()}'
             : '';
-        showErrorDialog(
+        await showErrorDialog(
           context,
           S.of(context).sorry,
           S.of(context).contactToManageSubscription(capitalizedWord),
@@ -328,7 +330,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
     await _dialog.show();
     try {
       final String url = await _billingService.getStripeCustomerPortalUrl();
-      Navigator.of(context).push(
+      await Navigator.of(context).push(
         MaterialPageRoute(
           builder: (BuildContext context) {
             return WebPage(S.of(context).paymentDetails, url);
@@ -337,7 +339,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
       ).then((value) => onWebPaymentGoBack);
     } catch (e) {
       await _dialog.hide();
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
     }
     await _dialog.hide();
   }
@@ -382,7 +384,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
           confirmAction = choice!.action == ButtonAction.first;
         }
         if (confirmAction) {
-          toggleStripeSubscription(isRenewCancelled);
+          await toggleStripeSubscription(isRenewCancelled);
         }
       },
     );
@@ -398,11 +400,13 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
           : await _billingService.cancelStripeSubscription();
       await _fetchSub();
     } catch (e) {
-      showShortToast(
-        context,
-        isAutoRenewDisabled
-            ? S.of(context).failedToRenew
-            : S.of(context).failedToCancel,
+      unawaited(
+        showShortToast(
+          context,
+          isAutoRenewDisabled
+              ? S.of(context).failedToRenew
+              : S.of(context).failedToCancel,
+        ),
       );
     }
     await _dialog.hide();
@@ -454,7 +458,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
               if (!_isStripeSubscriber &&
                   _hasActiveSubscription &&
                   _currentSubscription!.productID != freeProductID) {
-                showErrorDialog(
+                await showErrorDialog(
                   context,
                   S.of(context).sorry,
                   S.of(context).cancelOtherSubscription(
@@ -473,7 +477,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
                   "addOnBonus ${convertBytesToReadableFormat(addOnBonus)},"
                   "overshooting by ${convertBytesToReadableFormat(_userDetails.getFamilyOrPersonalUsage() - (plan.storage + addOnBonus))}",
                 );
-                showErrorDialog(
+                await showErrorDialog(
                   context,
                   S.of(context).sorry,
                   S.of(context).youCannotDowngradeToThisPlan,
@@ -495,7 +499,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
                   return;
                 }
               }
-              Navigator.push(
+              await Navigator.push(
                 context,
                 MaterialPageRoute(
                   builder: (BuildContext context) {

+ 1 - 1
lib/ui/payment/view_add_on_widget.dart

@@ -33,7 +33,7 @@ class ViewAddOnButton extends StatelessWidget {
         singleBorderRadius: 4,
         alignCaptionedTextToLeft: true,
         onTap: () async {
-          routeToPage(context, AddOnPage(bonusData!));
+          await routeToPage(context, AddOnPage(bonusData!));
         },
       ),
     );

+ 6 - 2
lib/ui/settings/advanced_settings_screen.dart

@@ -1,3 +1,5 @@
+import "dart:async";
+
 import 'package:flutter/material.dart';
 import "package:photos/core/error-reporting/super_logging.dart";
 import "package:photos/generated/l10n.dart";
@@ -106,8 +108,10 @@ class _AdvancedSettingsScreenState extends State<AdvancedSettingsScreen> {
                                 value: () =>
                                     MemoriesService.instance.showMemories,
                                 onChanged: () async {
-                                  MemoriesService.instance.setShowMemories(
-                                    !MemoriesService.instance.showMemories,
+                                  unawaited(
+                                    MemoriesService.instance.setShowMemories(
+                                      !MemoriesService.instance.showMemories,
+                                    ),
                                   );
                                 },
                               ),

+ 1 - 1
lib/ui/settings/backup/backup_folder_selection_page.dart

@@ -232,7 +232,7 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
     } catch (e, s) {
       _logger.severe("Failed to updated backup folder", e, s);
       await dialog.hide();
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
     }
   }
 

+ 4 - 4
lib/ui/settings/backup/backup_section_widget.dart

@@ -94,7 +94,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
             try {
               status = await SyncService.instance.getBackupStatus();
             } catch (e) {
-              showGenericErrorDialog(context: context, error: e);
+              await showGenericErrorDialog(context: context, error: e);
               return;
             }
 
@@ -128,7 +128,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
               duplicates =
                   await DeduplicationService.instance.getDuplicateFiles();
             } catch (e) {
-              showGenericErrorDialog(context: context, error: e);
+              await showGenericErrorDialog(context: context, error: e);
               return;
             }
 
@@ -217,7 +217,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
           ),
       firstButtonLabel: S.of(context).rateUs,
       firstButtonOnTap: () async {
-        UpdateService.instance.launchReviewUrl();
+        await UpdateService.instance.launchReviewUrl();
       },
       firstButtonType: ButtonType.primary,
       secondButtonLabel: S.of(context).ok,
@@ -225,7 +225,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
         showShortToast(
           context,
           S.of(context).remindToEmptyEnteTrash,
-        );
+        ).ignore();
       },
     );
   }

+ 3 - 3
lib/ui/settings/debug_section_widget.dart

@@ -50,7 +50,7 @@ class DebugSectionWidget extends StatelessWidget {
           trailingIconIsMuted: true,
           onTap: () async {
             await LocalSyncService.instance.resetLocalSync();
-            showShortToast(context, "Done");
+            showShortToast(context, "Done").ignore();
           },
         ),
         sectionOptionSpacing,
@@ -63,8 +63,8 @@ class DebugSectionWidget extends StatelessWidget {
           trailingIconIsMuted: true,
           onTap: () async {
             await IgnoredFilesService.instance.reset();
-            SyncService.instance.sync();
-            showShortToast(context, "Done");
+            SyncService.instance.sync().ignore();
+            showShortToast(context, "Done").ignore();
           },
         ),
         sectionOptionSpacing,

+ 1 - 1
lib/ui/settings/general_section_widget.dart

@@ -68,7 +68,7 @@ class GeneralSectionWidget extends StatelessWidget {
           trailingIconIsMuted: true,
           onTap: () async {
             final locale = await getLocale();
-            routeToPage(
+            await routeToPage(
               context,
               LanguageSelectorPage(
                 appSupportedLocales,

+ 1 - 1
lib/ui/settings/security_section_widget.dart

@@ -279,7 +279,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
       }
       await UserService.instance.updateEmailMFA(isEnabled);
     } catch (e) {
-      showToast(context, S.of(context).somethingWentWrong);
+      showToast(context, S.of(context).somethingWentWrong).ignore();
     }
   }
 }

+ 4 - 1
lib/ui/sharing/manage_album_participant.dart

@@ -132,7 +132,10 @@ class _ManageIndividualParticipantState
                               CollectionParticipantRole.viewer,
                             );
                           } catch (e) {
-                            showGenericErrorDialog(context: context, error: e);
+                            await showGenericErrorDialog(
+                              context: context,
+                              error: e,
+                            );
                           }
                           if (isConvertToViewSuccess && mounted) {
                             // reset value

+ 1 - 1
lib/ui/sharing/pickers/device_limit_picker_page.dart

@@ -137,7 +137,7 @@ class _ItemsWidgetState extends State<ItemsWidget> {
     try {
       await CollectionsService.instance.updateShareUrl(widget.collection, prop);
     } catch (e) {
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
       rethrow;
     }
   }

+ 1 - 1
lib/ui/sharing/pickers/link_expiry_picker_page.dart

@@ -180,7 +180,7 @@ class _ItemsWidgetState extends State<ItemsWidget> {
     try {
       await CollectionsService.instance.updateShareUrl(widget.collection, prop);
     } catch (e) {
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
       rethrow;
     }
   }

+ 24 - 15
lib/ui/viewer/actions/file_selection_actions_widget.dart

@@ -319,21 +319,30 @@ class _FileSelectionActionsWidgetState
     );
 
     if (items.isNotEmpty) {
-      return SizedBox(
-        width: double.infinity,
-        child: Center(
-          child: SingleChildScrollView(
-            physics: const BouncingScrollPhysics(
-              decelerationRate: ScrollDecelerationRate.fast,
-            ),
-            scrollDirection: Axis.horizontal,
-            child: Row(
-              crossAxisAlignment: CrossAxisAlignment.start,
-              children: [
-                const SizedBox(width: 4),
-                ...items,
-                const SizedBox(width: 4),
-              ],
+      final scrollController = ScrollController();
+      // h4ck: https://github.com/flutter/flutter/issues/57920#issuecomment-893970066
+      return MediaQuery(
+        data: MediaQuery.of(context).removePadding(removeBottom: true),
+        child: SafeArea(
+          child: Scrollbar(
+            controller: scrollController,
+            thumbVisibility: true,
+            child: SingleChildScrollView(
+              physics: const BouncingScrollPhysics(
+                decelerationRate: ScrollDecelerationRate.fast,
+              ),
+              scrollDirection: Axis.horizontal,
+              child: Container(
+                padding: const EdgeInsets.only(bottom: 24),
+                child: Row(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    const SizedBox(width: 4),
+                    ...items,
+                    const SizedBox(width: 4),
+                  ],
+                ),
+              ),
             ),
           ),
         ),

+ 6 - 6
lib/ui/viewer/file/file_app_bar.dart

@@ -257,15 +257,15 @@ class FileAppBarState extends State<FileAppBar> {
         },
         onSelected: (dynamic value) async {
           if (value == 1) {
-            _download(widget.file);
+            await _download(widget.file);
           } else if (value == 2) {
             await _toggleFileArchiveStatus(widget.file);
           } else if (value == 3) {
-            _setAs(widget.file);
+            await _setAs(widget.file);
           } else if (value == 4) {
-            _handleHideRequest(context);
+            await _handleHideRequest(context);
           } else if (value == 5) {
-            _handleUnHideRequest(context);
+            await _handleUnHideRequest(context);
           }
         },
       ),
@@ -371,7 +371,7 @@ class FileAppBarState extends State<FileAppBar> {
     } catch (e) {
       _logger.warning("Failed to save file", e);
       await dialog.hide();
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
     } finally {
       PhotoManager.startChangeNotify();
       LocalSyncService.instance.checkAndSync().ignore();
@@ -432,7 +432,7 @@ class FileAppBarState extends State<FileAppBar> {
     } catch (e) {
       dialog.hide();
       _logger.severe("Failed to use as", e);
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
     }
   }
 }

+ 7 - 3
lib/ui/viewer/file_details/favorite_widget.dart

@@ -1,3 +1,5 @@
+import "dart:async";
+
 import "package:flutter/material.dart";
 import "package:like_button/like_button.dart";
 import "package:logging/logging.dart";
@@ -77,9 +79,11 @@ class _FavoriteWidgetState extends State<FavoriteWidget> {
               } catch (e, s) {
                 _logger.severe(e, s);
                 hasError = true;
-                showToast(
-                  context,
-                  S.of(context).sorryCouldNotRemoveFromFavorites,
+                unawaited(
+                  showToast(
+                    context,
+                    S.of(context).sorryCouldNotRemoveFromFavorites,
+                  ),
                 );
               }
             }

+ 1 - 1
lib/ui/viewer/gallery/empty_album_state.dart

@@ -31,7 +31,7 @@ class EmptyAlbumState extends StatelessWidget {
               try {
                 await showAddPhotosSheet(context, c);
               } catch (e) {
-                showGenericErrorDialog(context: context, error: e);
+                await showGenericErrorDialog(context: context, error: e);
               }
             },
           ),

+ 5 - 0
lib/ui/viewer/gallery/gallery.dart

@@ -131,6 +131,11 @@ class GalleryState extends State<Gallery> {
               "Reloaded gallery on soft refresh all files on ${event.reason}",
             );
           }
+          if (event.type == EventType.deletedFromDevice ||
+              event.type == EventType.deletedFromEverywhere ||
+              event.type == EventType.deletedFromRemote) {
+            setState(() {});
+          }
         });
       });
     }

+ 10 - 7
lib/ui/viewer/gallery/gallery_app_bar_widget.dart

@@ -172,7 +172,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
       },
     );
     if (result is Exception) {
-      showGenericErrorDialog(context: context, error: result);
+      await showGenericErrorDialog(context: context, error: result);
     }
   }
 
@@ -204,7 +204,10 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
     );
     if (actionResult?.action != null && mounted) {
       if (actionResult!.action == ButtonAction.error) {
-        showGenericErrorDialog(context: context, error: actionResult.exception);
+        await showGenericErrorDialog(
+          context: context,
+          error: actionResult.exception,
+        );
       } else if (actionResult.action == ButtonAction.first) {
         Navigator.of(context).pop();
       }
@@ -224,7 +227,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
           .getBackupStatus(pathID: widget.deviceCollection!.id);
     } catch (e) {
       await dialog.hide();
-      showGenericErrorDialog(context: context, error: e);
+      unawaited(showGenericErrorDialog(context: context, error: e));
       return;
     }
 
@@ -664,7 +667,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
       } catch (e, s) {
         _logger.severe("failed to trash collection", e, s);
         await dialog.hide();
-        showGenericErrorDialog(context: context, error: e);
+        await showGenericErrorDialog(context: context, error: e);
       }
     } else {
       final bool result = await collectionActions.deleteCollectionSheet(
@@ -691,7 +694,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
       }
     } catch (e, s) {
       _logger.severe("failed to trash collection", e, s);
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
     }
   }
 
@@ -726,7 +729,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
       }
     } catch (e, s) {
       _logger.severe(e, s);
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
     }
   }
 
@@ -736,7 +739,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
       await showAddPhotosSheet(bContext, collection!);
     } catch (e, s) {
       _logger.severe(e, s);
-      showGenericErrorDialog(context: bContext, error: e);
+      await showGenericErrorDialog(context: bContext, error: e);
     }
   }
 

+ 1 - 1
lib/ui/viewer/gallery/hooks/add_photos_sheet.dart

@@ -205,7 +205,7 @@ class AddPhotosPhotoWidget extends StatelessWidget {
       if (e is StateError) {
         final PermissionState ps = await PhotoManager.requestPermissionExtend();
         if (ps != PermissionState.authorized && ps != PermissionState.limited) {
-          showChoiceDialog(
+          await showChoiceDialog(
             context,
             title: context.l10n.grantPermission,
             body: context.l10n.pleaseGrantPermissions,

+ 1 - 1
lib/ui/viewer/location/location_screen.dart

@@ -118,7 +118,7 @@ class LocationScreenPopUpMenu extends StatelessWidget {
                 );
                 Navigator.of(context).pop();
               } catch (e) {
-                showGenericErrorDialog(context: context, error: e);
+                await showGenericErrorDialog(context: context, error: e);
               }
             }
           },

+ 8 - 5
lib/utils/delete_file_util.dart

@@ -102,7 +102,7 @@ Future<void> deleteFilesFromEverywhere(
       await FilesDB.instance.deleteMultipleUploadedFiles(fileIDs);
     } catch (e) {
       _logger.severe(e);
-      showGenericErrorDialog(context: context, error: e);
+      await showGenericErrorDialog(context: context, error: e);
       rethrow;
     }
     for (final collectionID in updatedCollectionIDs) {
@@ -127,9 +127,9 @@ Future<void> deleteFilesFromEverywhere(
       ),
     );
     if (hasLocalOnlyFiles && Platform.isAndroid) {
-      showShortToast(context, S.of(context).filesDeleted);
+      await showShortToast(context, S.of(context).filesDeleted);
     } else {
-      showShortToast(context, S.of(context).movedToTrash);
+      await showShortToast(context, S.of(context).movedToTrash);
     }
   }
   if (uploadedFilesToBeTrashed.isNotEmpty) {
@@ -163,7 +163,7 @@ Future<void> deleteFilesFromRemoteOnly(
     await FilesDB.instance.deleteMultipleUploadedFiles(uploadedFileIDs);
   } catch (e, s) {
     _logger.severe("Failed to delete files from remote", e, s);
-    showGenericErrorDialog(context: context, error: e);
+    await showGenericErrorDialog(context: context, error: e);
     rethrow;
   }
   for (final collectionID in updatedCollectionIDs) {
@@ -626,7 +626,10 @@ Future<void> showDeleteSheet(
   );
   if (actionResult?.action != null &&
       actionResult!.action == ButtonAction.error) {
-    showGenericErrorDialog(context: context, error: actionResult.exception);
+    await showGenericErrorDialog(
+      context: context,
+      error: actionResult.exception,
+    );
   } else {
     selectedFiles.clearAll();
   }

+ 3 - 3
lib/utils/file_util.dart

@@ -204,7 +204,7 @@ Future<File?> _getLivePhotoFromServer(
     return needLiveVideo ? livePhoto.video : livePhoto.image;
   } catch (e, s) {
     _logger.warning("live photo get failed", e, s);
-    _livePhotoDownloadsTracker.remove(downloadID);
+    await _livePhotoDownloadsTracker.remove(downloadID);
     return null;
   }
 }
@@ -348,9 +348,9 @@ Future<Uint8List> compressThumbnail(Uint8List thumbnail) {
 
 Future<void> clearCache(EnteFile file) async {
   if (file.fileType == FileType.video) {
-    VideoCacheManager.instance.removeFile(file.downloadUrl);
+    await VideoCacheManager.instance.removeFile(file.downloadUrl);
   } else {
-    DefaultCacheManager().removeFile(file.downloadUrl);
+    await DefaultCacheManager().removeFile(file.downloadUrl);
   }
   final cachedThumbnail = File(
     Configuration.instance.getThumbnailCacheDirectory() +

+ 8 - 6
lib/utils/magic_util.dart

@@ -1,3 +1,5 @@
+import "dart:async";
+
 import 'package:flutter/material.dart';
 import 'package:logging/logging.dart';
 import 'package:path/path.dart';
@@ -134,7 +136,7 @@ Future<void> updateOrder(
     );
   } catch (e, s) {
     _logger.severe("failed to update order", e, s);
-    showShortToast(context, S.of(context).somethingWentWrong);
+    unawaited(showShortToast(context, S.of(context).somethingWentWrong));
     rethrow;
   }
 }
@@ -160,7 +162,7 @@ Future<void> changeCoverPhoto(
     );
   } catch (e, s) {
     _logger.severe("failed to update cover", e, s);
-    showShortToast(context, S.of(context).somethingWentWrong);
+    unawaited(showShortToast(context, S.of(context).somethingWentWrong));
     rethrow;
   }
 }
@@ -179,7 +181,7 @@ Future<bool> editTime(
     );
     return true;
   } catch (e) {
-    showShortToast(context, S.of(context).somethingWentWrong);
+    showShortToast(context, S.of(context).somethingWentWrong).ignore();
     return false;
   }
 }
@@ -218,7 +220,7 @@ Future<void> editFilename(
   );
   if (result is Exception) {
     _logger.severe("Failed to rename file");
-    showGenericErrorDialog(context: context, error: result);
+    await showGenericErrorDialog(context: context, error: result);
   }
 }
 
@@ -238,7 +240,7 @@ Future<bool> editFileCaption(
     return true;
   } catch (e) {
     if (context != null) {
-      showShortToast(context, S.of(context).somethingWentWrong);
+      unawaited(showShortToast(context, S.of(context).somethingWentWrong));
     }
     return false;
   }
@@ -265,7 +267,7 @@ Future<void> _updatePublicMetadata(
     await FileMagicService.instance.updatePublicMagicMetadata(files, update);
     if (context != null) {
       if (showDoneToast) {
-        showShortToast(context, S.of(context).done);
+        await showShortToast(context, S.of(context).done);
       }
       await dialog?.hide();
     }

+ 6 - 5
pubspec.lock

@@ -503,11 +503,12 @@ packages:
   file_saver:
     dependency: "direct main"
     description:
-      name: file_saver
-      sha256: "591d25e750e3a4b654f7b0293abc2ed857242f82ca7334051b2a8ceeb369dac8"
-      url: "https://pub.dev"
-    source: hosted
-    version: "0.2.8"
+      path: "."
+      ref: HEAD
+      resolved-ref: "01b2e6b6fe520cfa5d2d91342ccbfbaefa8f6482"
+      url: "https://github.com/jesims/file_saver.git"
+    source: git
+    version: "0.2.9"
   firebase_core:
     dependency: "direct main"
     description:

+ 4 - 2
pubspec.yaml

@@ -55,8 +55,10 @@ dependencies:
   extended_image: ^8.1.1
   fade_indexed_stack: ^0.2.2
   fast_base58: ^0.2.1
-  # https://github.com/incrediblezayed/file_saver/issues/86
-  file_saver: 0.2.8
+
+  file_saver:
+    # Use forked version till this PR is merged: https://github.com/incrediblezayed/file_saver/pull/87
+    git: https://github.com/jesims/file_saver.git
   firebase_core: ^2.13.1
   firebase_messaging: ^14.6.2
   fk_user_agent: ^2.0.1