Explorar o código

Merge pull request #856 from ente-io/return_exception_with_buttonAction

ButtonWidget : Return exception with buttonAction on dialogs
Ashil %!s(int64=2) %!d(string=hai) anos
pai
achega
85abf77cfa

+ 11 - 0
lib/models/search/button_result.dart

@@ -0,0 +1,11 @@
+import "package:photos/ui/components/button_widget.dart";
+
+class ButtonResult {
+  ///action can be null when action for the button that is returned when popping
+  ///the widget (dialog, actionSheet) which uses a ButtonWidget isn't
+  ///relevant/useful and so is not assigned a value when an instance of
+  ///ButtonWidget is created.
+  final ButtonAction? action;
+  final Exception? exception;
+  ButtonResult([this.action, this.exception]);
+}

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

@@ -152,10 +152,10 @@ class DeleteAccountPage extends StatelessWidget {
               .deleteAccount(context, challengeResponseStr);
         },
       );
-      if (choice == ButtonAction.error) {
+      if (choice!.action == ButtonAction.error) {
         showGenericErrorDialog(context: context);
       }
-      if (choice != ButtonAction.first) {
+      if (choice.action != ButtonAction.first) {
         return;
       }
       Navigator.of(context).popUntil((route) => route.isFirst);

+ 2 - 2
lib/ui/account/password_reentry_page.dart

@@ -89,7 +89,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
                   "recovery key and regenerate your password (you can use the same one again if you wish).",
               firstButtonLabel: "Use recovery key",
             );
-            if (dialogChoice == ButtonAction.first) {
+            if (dialogChoice!.action == ButtonAction.first) {
               Navigator.of(context).push(
                 MaterialPageRoute(
                   builder: (BuildContext context) {
@@ -109,7 +109,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
               firstButtonLabel: "Contact Support",
               secondButtonLabel: "OK",
             );
-            if (dialogChoice == ButtonAction.first) {
+            if (dialogChoice!.action == ButtonAction.first) {
               await sendLogs(
                 context,
                 "Contact support",

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

@@ -82,7 +82,7 @@ class _VerifyRecoveryPageState extends State<VerifyRecoveryPage> {
         secondButtonLabel: "View recovery key",
         secondButtonAction: ButtonAction.second,
       );
-      if (result == ButtonAction.second) {
+      if (result!.action == ButtonAction.second) {
         await _onViewRecoveryKeyClick();
       }
     }

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

@@ -57,7 +57,8 @@ extension CollectionFileActions on CollectionActions {
           : "Selected items will be removed from this album",
       actionSheetType: ActionSheetType.defaultActionSheet,
     );
-    if (actionResult != null && actionResult == ButtonAction.error) {
+    if (actionResult?.action != null &&
+        actionResult!.action == ButtonAction.error) {
       showGenericErrorDialog(context: bContext);
     } else {
       selectedFiles.clearAll();

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

@@ -54,7 +54,7 @@ class CollectionActions {
   }
 
   Future<bool> disableUrl(BuildContext context, Collection collection) async {
-    final ButtonAction? result = await showActionSheet(
+    final actionResult = await showActionSheet(
       context: context,
       buttons: [
         ButtonWidget(
@@ -80,11 +80,11 @@ class CollectionActions {
       body:
           'This will remove the public link for accessing "${collection.name}".',
     );
-    if (result != null) {
-      if (result == ButtonAction.error) {
+    if (actionResult?.action != null) {
+      if (actionResult!.action == ButtonAction.error) {
         showGenericErrorDialog(context: context);
       }
-      return result == ButtonAction.first;
+      return actionResult.action == ButtonAction.first;
     } else {
       return false;
     }
@@ -140,7 +140,7 @@ class CollectionActions {
     Collection collection,
     User user,
   ) async {
-    final ButtonAction? result = await showActionSheet(
+    final actionResult = await showActionSheet(
       context: context,
       buttons: [
         ButtonWidget(
@@ -168,11 +168,11 @@ class CollectionActions {
       body: '${user.email} will be removed from this shared album\n\nAny '
           'photos added by them will also be removed from the album',
     );
-    if (result != null) {
-      if (result == ButtonAction.error) {
+    if (actionResult?.action != null) {
+      if (actionResult!.action == ButtonAction.error) {
         showGenericErrorDialog(context: context);
       }
-      return result == ButtonAction.first;
+      return actionResult.action == ButtonAction.first;
     }
     return false;
   }
@@ -346,13 +346,14 @@ class CollectionActions {
       ),
       actionSheetType: ActionSheetType.defaultActionSheet,
     );
-    if (actionResult != null && actionResult == ButtonAction.error) {
+    if (actionResult?.action != null &&
+        actionResult!.action == ButtonAction.error) {
       showGenericErrorDialog(context: bContext);
       return false;
     }
-    if (actionResult != null &&
-        (actionResult == ButtonAction.first ||
-            actionResult == ButtonAction.second)) {
+    if ((actionResult?.action != null) &&
+        (actionResult!.action == ButtonAction.first ||
+            actionResult.action == ButtonAction.second)) {
       return true;
     }
     return false;
@@ -364,7 +365,7 @@ class CollectionActions {
     BuildContext context,
     Collection collection,
   ) async {
-    final ButtonAction? result = await showChoiceActionSheet(
+    final actionResult = await showChoiceActionSheet(
       context,
       isCritical: true,
       title: "Delete shared album?",
@@ -372,7 +373,8 @@ class CollectionActions {
       body: "The album will be deleted for everyone\n\nYou will lose access to "
           "shared photos in this album that are owned by others",
     );
-    return result != null && result == ButtonAction.first;
+    return actionResult?.action != null &&
+        actionResult!.action == ButtonAction.first;
   }
 
   /*

+ 2 - 1
lib/ui/components/action_sheet_widget.dart

@@ -3,6 +3,7 @@ import 'dart:ui';
 import 'package:flutter/material.dart';
 import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
 import 'package:photos/core/constants.dart';
+import "package:photos/models/search/button_result.dart";
 import 'package:photos/theme/colors.dart';
 import 'package:photos/theme/effects.dart';
 import 'package:photos/theme/ente_theme.dart';
@@ -15,7 +16,7 @@ enum ActionSheetType {
 }
 
 ///Returns null if dismissed
-Future<ButtonAction?> showActionSheet({
+Future<ButtonResult?> showActionSheet({
   required BuildContext context,
   required List<ButtonWidget> buttons,
   ActionSheetType actionSheetType = ActionSheetType.defaultActionSheet,

+ 36 - 13
lib/ui/components/button_widget.dart

@@ -1,6 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/scheduler.dart';
 import 'package:photos/models/execution_states.dart';
+import "package:photos/models/search/button_result.dart";
 import 'package:photos/models/typedefs.dart';
 import 'package:photos/theme/colors.dart';
 import 'package:photos/theme/ente_theme.dart';
@@ -9,6 +10,7 @@ import 'package:photos/ui/common/loading_widget.dart';
 import 'package:photos/ui/components/models/button_type.dart';
 import 'package:photos/ui/components/models/custom_button_style.dart';
 import 'package:photos/utils/debouncer.dart';
+import "package:photos/utils/dialog_util.dart";
 
 enum ButtonSize {
   small,
@@ -196,6 +198,7 @@ class _ButtonChildWidgetState extends State<ButtonChildWidget> {
   double? widthOfButton;
   final _debouncer = Debouncer(const Duration(milliseconds: 300));
   ExecutionState executionState = ExecutionState.idle;
+  Exception? _exception;
 
   @override
   void initState() {
@@ -404,10 +407,16 @@ class _ButtonChildWidgetState extends State<ButtonChildWidget> {
           });
         }),
       );
-      await widget.onTap!.call().onError((error, stackTrace) {
-        executionState = ExecutionState.error;
-        _debouncer.cancelDebounce();
-      });
+      await widget.onTap!.call().then(
+        (value) {
+          _exception = null;
+        },
+        onError: (error, stackTrace) {
+          executionState = ExecutionState.error;
+          _exception = error as Exception;
+          _debouncer.cancelDebounce();
+        },
+      );
       widget.shouldShowSuccessConfirmation && _debouncer.isActive()
           ? executionState = ExecutionState.successful
           : null;
@@ -436,7 +445,10 @@ class _ButtonChildWidgetState extends State<ButtonChildWidget> {
                       : 0,
                 ), () {
               widget.isInAlert
-                  ? _popWithButtonAction(context, widget.buttonAction)
+                  ? _popWithButtonAction(
+                      context,
+                      buttonAction: widget.buttonAction,
+                    )
                   : null;
               if (mounted) {
                 setState(() {
@@ -453,7 +465,11 @@ class _ButtonChildWidgetState extends State<ButtonChildWidget> {
           widget.isInAlert
               ? Future.delayed(
                   const Duration(seconds: 0),
-                  () => _popWithButtonAction(context, ButtonAction.error),
+                  () => _popWithButtonAction(
+                    context,
+                    buttonAction: ButtonAction.error,
+                    exception: _exception,
+                  ),
                 )
               : null;
         });
@@ -462,18 +478,25 @@ class _ButtonChildWidgetState extends State<ButtonChildWidget> {
       if (widget.isInAlert) {
         Future.delayed(
           Duration(seconds: widget.shouldShowSuccessConfirmation ? 1 : 0),
-          () => _popWithButtonAction(context, widget.buttonAction),
+          () =>
+              _popWithButtonAction(context, buttonAction: widget.buttonAction),
         );
       }
     }
   }
 
-  void _popWithButtonAction(BuildContext context, ButtonAction? buttonAction) {
-    Navigator.of(context).canPop()
-        ? Navigator.of(context).pop(
-            buttonAction,
-          )
-        : null;
+  void _popWithButtonAction(
+    BuildContext context, {
+    required ButtonAction? buttonAction,
+    Exception? exception,
+  }) {
+    if (Navigator.of(context).canPop()) {
+      Navigator.of(context).pop(ButtonResult(widget.buttonAction, exception));
+    } 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);
+    }
   }
 
   void _onTapDown(details) {

+ 2 - 1
lib/ui/components/dialog_widget.dart

@@ -2,6 +2,7 @@ import 'dart:math';
 
 import 'package:flutter/material.dart';
 import 'package:photos/core/constants.dart';
+import "package:photos/models/search/button_result.dart";
 import 'package:photos/models/typedefs.dart';
 import 'package:photos/theme/colors.dart';
 import 'package:photos/theme/effects.dart';
@@ -12,7 +13,7 @@ import 'package:photos/ui/components/text_input_widget.dart';
 import 'package:photos/utils/separators_util.dart';
 
 ///Will return null if dismissed by tapping outside
-Future<ButtonAction?> showDialogWidget({
+Future<ButtonResult?> showDialogWidget({
   required BuildContext context,
   required String title,
   String? body,

+ 2 - 2
lib/ui/home/landing_page_widget.dart

@@ -209,7 +209,7 @@ class _LandingPageWidgetState extends State<LandingPageWidget> {
   Future<void> _showAutoLogoutDialogIfRequired() async {
     final bool autoLogout = Configuration.instance.showAutoLogoutDialog();
     if (autoLogout) {
-      final ButtonAction? result = await showDialogWidget(
+      final result = await showDialogWidget(
         context: context,
         title: "Please login again",
         body: "The developer account we use to publish ente on App Store has "
@@ -225,7 +225,7 @@ class _LandingPageWidgetState extends State<LandingPageWidget> {
         ],
       );
       Configuration.instance.clearAutoLogoutFlag().ignore();
-      if (result != null && result == ButtonAction.first) {
+      if (result?.action != null && result!.action == ButtonAction.first) {
         _navigateToSignInPage();
       }
     }

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

@@ -132,7 +132,7 @@ class ChildSubscriptionWidget extends StatelessWidget {
         }
       },
     );
-    if (choice == ButtonAction.error) {
+    if (choice!.action == ButtonAction.error) {
       await showGenericErrorDialog(context: context);
     }
   }

+ 3 - 3
lib/ui/payment/stripe_subscription_page.dart

@@ -350,7 +350,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
             body: "Are you sure you want to renew?",
             firstButtonLabel: "Yes, Renew",
           );
-          confirmAction = choice == ButtonAction.first;
+          confirmAction = choice!.action == ButtonAction.first;
         } else {
           final choice = await showChoiceDialog(
             context,
@@ -360,7 +360,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
             secondButtonLabel: "No",
             isCritical: true,
           );
-          confirmAction = choice == ButtonAction.first;
+          confirmAction = choice!.action == ButtonAction.first;
         }
         if (confirmAction) {
           toggleStripeSubscription(isRenewCancelled);
@@ -435,7 +435,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
                   body: "Are you sure you want to change your plan?",
                   firstButtonLabel: "Yes",
                 );
-                if (result == ButtonAction.first) {
+                if (result!.action == ButtonAction.first) {
                   stripPurChaseAction = 'update';
                 } else {
                   return;

+ 3 - 3
lib/ui/sharing/manage_album_participant.dart

@@ -108,7 +108,7 @@ class _ManageIndividualParticipantState
               onTap: widget.user.isViewer
                   ? null
                   : () async {
-                      final ButtonAction? result = await showChoiceActionSheet(
+                      final actionResult = await showChoiceActionSheet(
                         context,
                         title: "Change permissions?",
                         firstButtonLabel: "Yes, convert to viewer",
@@ -116,8 +116,8 @@ class _ManageIndividualParticipantState
                             '${widget.user.email} will not be able to add more photos to this album\n\nThey will still be able to remove existing photos added by them',
                         isCritical: true,
                       );
-                      if (result != null) {
-                        if (result == ButtonAction.first) {
+                      if (actionResult?.action != null) {
+                        if (actionResult!.action == ButtonAction.first) {
                           try {
                             isConvertToViewSuccess =
                                 await collectionActions.addEmailToCollection(

+ 2 - 1
lib/ui/tools/editor/image_editor_page.dart

@@ -524,7 +524,8 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
       body: "Do you want to discard the edits you have made?",
       actionSheetType: ActionSheetType.defaultActionSheet,
     );
-    if (actionResult != null && actionResult == ButtonAction.first) {
+    if (actionResult?.action != null &&
+        actionResult!.action == ButtonAction.first) {
       replacePage(context, DetailPage(widget.detailPageConfig));
     }
   }

+ 11 - 8
lib/ui/viewer/actions/file_selection_actions_widget.dart

@@ -428,15 +428,18 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
       body: "You can manage your links in the share tab.",
       actionSheetType: ActionSheetType.defaultActionSheet,
     );
-    if (actionResult != null && actionResult == ButtonAction.first) {
-      await _copyLink();
-    }
-    if (actionResult != null && actionResult == ButtonAction.second) {
-      routeToPage(
-        context,
-        ManageSharedLinkWidget(collection: _cachedCollectionForSharedLink),
-      );
+    if (actionResult?.action != null) {
+      if (actionResult!.action == ButtonAction.first) {
+        await _copyLink();
+      }
+      if (actionResult.action == ButtonAction.second) {
+        routeToPage(
+          context,
+          ManageSharedLinkWidget(collection: _cachedCollectionForSharedLink),
+        );
+      }
     }
+
     if (mounted) {
       setState(() => {});
     }

+ 3 - 2
lib/ui/viewer/file/fading_app_bar.dart

@@ -431,14 +431,15 @@ class FadingAppBarState extends State<FadingAppBar> {
         isInAlert: true,
       ),
     );
-    final ButtonAction? result = await showActionSheet(
+    final actionResult = await showActionSheet(
       context: context,
       buttons: buttons,
       actionSheetType: ActionSheetType.defaultActionSheet,
       body: body,
       bodyHighlight: bodyHighlight,
     );
-    if (result != null && result == ButtonAction.error) {
+    if (actionResult?.action != null &&
+        actionResult!.action == ButtonAction.error) {
       showGenericErrorDialog(context: context);
     }
   }

+ 4 - 4
lib/ui/viewer/gallery/gallery_app_bar_widget.dart

@@ -141,7 +141,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
   }
 
   Future<dynamic> _leaveAlbum(BuildContext context) async {
-    final ButtonAction? result = await showActionSheet(
+    final actionResult = await showActionSheet(
       context: context,
       buttons: [
         ButtonWidget(
@@ -166,10 +166,10 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
       title: "Leave shared album?",
       body: "Photos added by you will be removed from the album",
     );
-    if (result != null && mounted) {
-      if (result == ButtonAction.error) {
+    if (actionResult?.action != null && mounted) {
+      if (actionResult!.action == ButtonAction.error) {
         showGenericErrorDialog(context: context);
-      } else if (result == ButtonAction.first) {
+      } else if (actionResult.action == ButtonAction.first) {
         Navigator.of(context).pop();
       }
     }

+ 24 - 16
lib/utils/delete_file_util.dart

@@ -247,7 +247,7 @@ Future<void> deleteFilesOnDeviceOnly(
 
 Future<bool> deleteFromTrash(BuildContext context, List<File> files) async {
   bool didDeletionStart = false;
-  final result = await showChoiceActionSheet(
+  final actionResult = await showChoiceActionSheet(
     context,
     title: "Permanently delete?",
     body: "This action cannot be undone",
@@ -270,19 +270,20 @@ Future<bool> deleteFromTrash(BuildContext context, List<File> files) async {
       }
     },
   );
-  if (result == ButtonAction.error) {
+
+  if (actionResult?.action == null ||
+      actionResult!.action == ButtonAction.cancel) {
+    return didDeletionStart ? true : false;
+  } else if (actionResult.action == ButtonAction.error) {
     await showGenericErrorDialog(context: context);
     return false;
-  }
-  if (result == null || result == ButtonAction.cancel) {
-    return didDeletionStart ? true : false;
   } else {
     return true;
   }
 }
 
 Future<bool> emptyTrash(BuildContext context) async {
-  final result = await showChoiceActionSheet(
+  final actionResult = await showChoiceActionSheet(
     context,
     title: "Empty trash?",
     body:
@@ -298,11 +299,11 @@ Future<bool> emptyTrash(BuildContext context) async {
       }
     },
   );
-  if (result == ButtonAction.error) {
-    await showGenericErrorDialog(context: context);
+  if (actionResult?.action == null ||
+      actionResult!.action == ButtonAction.cancel) {
     return false;
-  }
-  if (result == null || result == ButtonAction.cancel) {
+  } else if (actionResult.action == ButtonAction.error) {
+    await showGenericErrorDialog(context: context);
     return false;
   } else {
     return true;
@@ -467,7 +468,7 @@ Future<List<String>> _tryDeleteSharedMediaFiles(List<String> localIDs) {
 }
 
 Future<bool> shouldProceedWithDeletion(BuildContext context) async {
-  final choice = await showChoiceActionSheet(
+  final actionResult = await showChoiceActionSheet(
     context,
     title: "Permanently delete from device?",
     body:
@@ -475,10 +476,10 @@ Future<bool> shouldProceedWithDeletion(BuildContext context) async {
     firstButtonLabel: "Delete",
     isCritical: true,
   );
-  if (choice == null) {
+  if (actionResult?.action == null) {
     return false;
   } else {
-    return choice == ButtonAction.first;
+    return actionResult!.action == ButtonAction.first;
   }
 }
 
@@ -528,8 +529,14 @@ Future<void> showDeleteSheet(
           await deleteFilesFromRemoteOnly(
             context,
             selectedFiles.files.toList(),
+          ).then(
+            (value) {
+              showShortToast(context, "Moved to trash");
+            },
+            onError: (e, s) {
+              showGenericErrorDialog(context: context);
+            },
           );
-          showShortToast(context, "Moved to trash");
         },
       ),
     );
@@ -581,14 +588,15 @@ Future<void> showDeleteSheet(
       isInAlert: true,
     ),
   );
-  final ButtonAction? result = await showActionSheet(
+  final actionResult = await showActionSheet(
     context: context,
     buttons: buttons,
     actionSheetType: ActionSheetType.defaultActionSheet,
     body: body,
     bodyHighlight: bodyHighlight,
   );
-  if (result != null && result == ButtonAction.error) {
+  if (actionResult?.action != null &&
+      actionResult!.action == ButtonAction.error) {
     showGenericErrorDialog(context: context);
   } else {
     selectedFiles.clearAll();

+ 6 - 5
lib/utils/dialog_util.dart

@@ -3,6 +3,7 @@ import 'dart:math';
 import 'package:confetti/confetti.dart';
 import 'package:flutter/material.dart';
 import 'package:photos/core/constants.dart';
+import "package:photos/models/search/button_result.dart";
 import 'package:photos/models/typedefs.dart';
 import 'package:photos/theme/colors.dart';
 import 'package:photos/ui/common/loading_widget.dart';
@@ -15,7 +16,7 @@ import 'package:photos/ui/components/models/button_type.dart';
 typedef DialogBuilder = DialogWidget Function(BuildContext context);
 
 ///Will return null if dismissed by tapping outside
-Future<ButtonAction?> showErrorDialog(
+Future<ButtonResult?> showErrorDialog(
   BuildContext context,
   String title,
   String? body, {
@@ -38,7 +39,7 @@ Future<ButtonAction?> showErrorDialog(
 }
 
 ///Will return null if dismissed by tapping outside
-Future<ButtonAction?> showGenericErrorDialog({
+Future<ButtonResult?> showGenericErrorDialog({
   required BuildContext context,
   bool isDismissible = true,
 }) async {
@@ -94,7 +95,7 @@ DialogWidget choiceDialog({
 }
 
 ///Will return null if dismissed by tapping outside
-Future<ButtonAction?> showChoiceDialog(
+Future<ButtonResult?> showChoiceDialog(
   BuildContext context, {
   required String title,
   String? body,
@@ -137,7 +138,7 @@ Future<ButtonAction?> showChoiceDialog(
 }
 
 ///Will return null if dismissed by tapping outside
-Future<ButtonAction?> showChoiceActionSheet(
+Future<ButtonResult?> showChoiceActionSheet(
   BuildContext context, {
   required String title,
   String? body,
@@ -203,7 +204,7 @@ ProgressDialog createProgressDialog(
   return dialog;
 }
 
-Future<ButtonAction?> showConfettiDialog<T>({
+Future<ButtonResult?> showConfettiDialog<T>({
   required BuildContext context,
   required DialogBuilder dialogBuilder,
   bool barrierDismissible = true,

+ 1 - 1
lib/utils/email_util.dart

@@ -158,7 +158,7 @@ Future<void> shareLogs(
       ),
     ],
   );
-  if (result != null && result == ButtonAction.second) {
+  if (result?.action != null && result!.action == ButtonAction.second) {
     final Size size = MediaQuery.of(context).size;
     await Share.shareFiles(
       [zipFilePath],