Merge branch 'main' into pull_down_fix

This commit is contained in:
Neeraj Gupta 2023-12-04 10:27:29 +05:30
commit 9e75d4c4d2
50 changed files with 382 additions and 149 deletions

View file

@ -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

View file

@ -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);
}
}

View file

@ -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) {

View file

@ -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';

View file

@ -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(

View file

@ -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();

View file

@ -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:

View file

@ -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();
}

View file

@ -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),

View file

@ -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(

View file

@ -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();

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

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

View file

@ -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;
}
}

View file

@ -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) &&

View file

@ -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,
);
}
}

View file

@ -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;
}
}

View file

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

View file

@ -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,

View file

@ -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();
}
}
}

View file

@ -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"),
);

View file

@ -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),
);

View file

@ -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) {

View file

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

View file

@ -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,
),
);
},
),

View file

@ -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);
}
}

View file

@ -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();
},
);
}

View file

@ -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,

View file

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

View file

@ -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();
}
}
}

View file

@ -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

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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),
],
),
),
),
),
),

View file

@ -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);
}
}
}

View file

@ -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,
),
);
}
}

View file

@ -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);
}
},
),

View file

@ -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(() {});
}
});
});
}

View file

@ -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);
}
}

View file

@ -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,

View file

@ -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);
}
}
},

View file

@ -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();
}

View file

@ -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() +

View file

@ -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();
}

View file

@ -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:

View file

@ -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