diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 500bfb039..64c5d88d5 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -17,6 +17,7 @@ import 'package:get/get.dart'; import 'package:html_editor_enhanced/html_editor.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/error/method/error_method_response.dart'; +import 'package:jmap_dart_client/jmap/core/error/set_error.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; @@ -98,6 +99,7 @@ import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_by_id_on_web_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_paywall_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/draggable_app_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; @@ -997,27 +999,50 @@ class ComposerController extends BaseController required BuildContext context, required FeatureFailure failure }) async { - final errorMessage = getMessageFailure( + final messageRecord = getMessageFailure( appLocalizations: AppLocalizations.of(context), exception: failure.exception, ); + + final isIncreaseMySpaceIsDisabled = + !mailboxDashBoardController.validatePremiumIsAvailable() || + mailboxDashBoardController.validateUserHasIsAlreadyHighestSubscription(); + + final isPaywallActive = !isIncreaseMySpaceIsDisabled && + messageRecord.errorType == SetError.overQuota; + await MessageDialogActionManager().showConfirmDialogAction( context, title: '', - errorMessage, - AppLocalizations.of(context).edit, - cancelTitle: AppLocalizations.of(context).closeAnyway, + messageRecord.message, + isPaywallActive + ? AppLocalizations.of(context).increaseYourSpace + : AppLocalizations.of(context).edit, + cancelTitle: isPaywallActive + ? AppLocalizations.of(context).edit + : AppLocalizations.of(context).closeAnyway, alignCenter: true, outsideDismissible: false, autoPerformPopBack: false, onConfirmAction: () { _sendButtonState = ButtonState.enabled; popBack(); - _autoFocusFieldWhenLauncher(); + + if (isPaywallActive) { + mailboxDashBoardController.navigateToPaywall(context); + } else { + _autoFocusFieldWhenLauncher(); + } }, onCancelAction: () { _sendButtonState = ButtonState.enabled; - _closeComposerAction(closeOverlays: true); + + if (isPaywallActive) { + popBack(); + _autoFocusFieldWhenLauncher(); + } else { + _closeComposerAction(closeOverlays: true); + } }, ); } @@ -1282,14 +1307,25 @@ class ComposerController extends BaseController await _showConfirmDialogWhenSaveMessageToDraftsFailure( context: context, failure: resultState, - onConfirmAction: () { + onConfirmAction: (isPaywallActive) { _saveToDraftButtonState = ButtonState.enabled; popBack(); - _autoFocusFieldWhenLauncher(); + + if (isPaywallActive) { + mailboxDashBoardController.navigateToPaywall(context); + } else { + _autoFocusFieldWhenLauncher(); + } }, - onCancelAction: () { + onCancelAction: (isPaywallActive) { _saveToDraftButtonState = ButtonState.enabled; - _closeComposerAction(closeOverlays: true); + + if (isPaywallActive) { + popBack(); + _autoFocusFieldWhenLauncher(); + } else { + _closeComposerAction(closeOverlays: true); + } }, ); } else { @@ -2222,31 +2258,62 @@ class ComposerController extends BaseController Future _showConfirmDialogWhenSaveMessageToDraftsFailure({ required BuildContext context, required FeatureFailure failure, - VoidCallback? onConfirmAction, - VoidCallback? onCancelAction, + Function(bool)? onConfirmAction, + Function(bool)? onCancelAction, }) async { - final errorMessage = getMessageFailure( + final messageRecord = getMessageFailure( appLocalizations: AppLocalizations.of(context), exception: failure.exception, isDraft: true, ); + + final isIncreaseMySpaceIsDisabled = + !mailboxDashBoardController.validatePremiumIsAvailable() || + mailboxDashBoardController.validateUserHasIsAlreadyHighestSubscription(); + + final isPaywallActive = !isIncreaseMySpaceIsDisabled && + messageRecord.errorType == SetError.overQuota; + await MessageDialogActionManager().showConfirmDialogAction( context, title: '', - errorMessage, - AppLocalizations.of(context).edit, - cancelTitle: AppLocalizations.of(context).closeAnyway, + messageRecord.message, + isPaywallActive + ? AppLocalizations.of(context).increaseYourSpace + : AppLocalizations.of(context).edit, + cancelTitle: isPaywallActive + ? AppLocalizations.of(context).edit + : AppLocalizations.of(context).closeAnyway, alignCenter: true, outsideDismissible: false, autoPerformPopBack: false, - onConfirmAction: onConfirmAction ?? () { - _closeComposerButtonState = ButtonState.enabled; - popBack(); - _autoFocusFieldWhenLauncher(); + onConfirmAction: () { + if (onConfirmAction != null) { + onConfirmAction(isPaywallActive); + } else { + _closeComposerButtonState = ButtonState.enabled; + popBack(); + + if (isPaywallActive) { + mailboxDashBoardController.navigateToPaywall(context); + } else { + _autoFocusFieldWhenLauncher(); + } + } }, - onCancelAction: onCancelAction ?? () { - _closeComposerButtonState = ButtonState.enabled; - _closeComposerAction(closeOverlays: true); + onCancelAction: () { + if (onCancelAction != null) { + onCancelAction(isPaywallActive); + } else { + _closeComposerButtonState = ButtonState.enabled; + + if (isPaywallActive) { + popBack(); + _autoFocusFieldWhenLauncher(); + } else { + _closeComposerAction(closeOverlays: true); + } + } }, ); } diff --git a/lib/features/composer/presentation/extensions/handle_message_failure_extension.dart b/lib/features/composer/presentation/extensions/handle_message_failure_extension.dart index 4cc86e53c..9c2b2fb91 100644 --- a/lib/features/composer/presentation/extensions/handle_message_failure_extension.dart +++ b/lib/features/composer/presentation/extensions/handle_message_failure_extension.dart @@ -1,4 +1,5 @@ +import 'package:jmap_dart_client/jmap/core/error/error_type.dart'; import 'package:jmap_dart_client/jmap/core/error/set_error.dart'; import 'package:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; @@ -7,37 +8,35 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; extension HandleMessageFailureExtension on ComposerController { - String getMessageFailure({ + ({String message, ErrorType? errorType}) getMessageFailure({ required AppLocalizations appLocalizations, dynamic exception, bool isDraft = false, }) { - if (exception is! SetMethodException) { - return isDraft + final fallbackMessage = isDraft ? appLocalizations.warningMessageWhenSaveEmailToDraftsFailure : appLocalizations.warningMessageWhenSendEmailFailure; + + if (exception is! SetMethodException) { + return (message: fallbackMessage, errorType: null); } - for (var error in exception.mapErrors.values) { - if (error.type != SetError.tooLarge && error.type != SetError.overQuota) { - continue; - } + for (final error in exception.mapErrors.values) { + if (error.type == SetError.tooLarge || error.type == SetError.overQuota) { + final message = isDraft + ? error.toastMessageForSaveEmailAsDraftFailure( + appLocalizations: appLocalizations, + defaultMessage: fallbackMessage, + ) + : error.toastMessageForSendEmailFailure( + appLocalizations: appLocalizations, + defaultMessage: fallbackMessage, + ); - if (isDraft) { - return error.toastMessageForSaveEmailAsDraftFailure( - appLocalizations: appLocalizations, - defaultMessage:appLocalizations.warningMessageWhenSaveEmailToDraftsFailure, - ); - } else { - return error.toastMessageForSendEmailFailure( - appLocalizations: appLocalizations, - defaultMessage:appLocalizations.warningMessageWhenSendEmailFailure, - ); + return (message: message, errorType: error.type); } } - return isDraft - ? appLocalizations.warningMessageWhenSaveEmailToDraftsFailure - : appLocalizations.warningMessageWhenSendEmailFailure; + return (message: fallbackMessage, errorType: null); } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index 5501781ac..eef703486 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -376,6 +376,7 @@ class MailboxDashBoardController extends ReloadableController if (PlatformInfo.isWeb) { listSearchFilterScrollController = ScrollController(); twakeAppManager.setExecutingBeforeReconnect(false); + isRetryGetPaywallUrl = false; } if (PlatformInfo.isIOS) { _registerPendingCurrentEmailIdInNotification(); @@ -559,6 +560,18 @@ class MailboxDashBoardController extends ReloadableController } } + @override + void handleErrorViewState(Object error, StackTrace stackTrace) { + super.handleErrorViewState(error, stackTrace); + isRetryGetPaywallUrl = false; + } + + @override + void handleUrgentExceptionOnWeb({Failure? failure, Exception? exception}) { + super.handleUrgentExceptionOnWeb(failure: failure, exception: exception); + isRetryGetPaywallUrl = false; + } + @override void handleUrgentExceptionOnMobile({Failure? failure, Exception? exception}) { SmartDialog.dismiss(); diff --git a/lib/features/mailbox_dashboard/presentation/extensions/handle_paywall_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/handle_paywall_extension.dart index d4dff25bb..d1ad9da93 100644 --- a/lib/features/mailbox_dashboard/presentation/extensions/handle_paywall_extension.dart +++ b/lib/features/mailbox_dashboard/presentation/extensions/handle_paywall_extension.dart @@ -20,6 +20,16 @@ extension HandlePaywallExtension on MailboxDashBoardController { ); } + bool validateUserHasIsAlreadyHighestSubscription() { + if (accountId.value == null || sessionCurrent == null) { + return false; + } + return isAlreadyHighestSubscription( + accountId: accountId.value, + session: sessionCurrent, + ); + } + void loadPaywallUrl() { final getPaywallUrlInteractor = getBinding(); final jmapUrl = dynamicUrlInterceptors.jmapUrl; @@ -37,6 +47,14 @@ extension HandlePaywallExtension on MailboxDashBoardController { void loadPaywallUrlSuccess(PaywallUrlPattern newPattern) { paywallUrlPattern = newPattern; + if (isRetryGetPaywallUrl) { + isRetryGetPaywallUrl = false; + final qualifiedPaywall = paywallUrlPattern!.getQualifiedUrl( + ownerEmail: ownEmailAddress.value, + domainName: RouteUtils.getRootDomain(), + ); + AppUtils.launchLink(qualifiedPaywall); + } } void loadPaywallUrlFailure() { diff --git a/lib/features/mailbox_dashboard/presentation/extensions/validate_saas_premium_available_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/validate_saas_premium_available_extension.dart deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/features/paywall/presentation/saas_premium_mixin.dart b/lib/features/paywall/presentation/saas_premium_mixin.dart index d3db21b55..3b449b6ee 100644 --- a/lib/features/paywall/presentation/saas_premium_mixin.dart +++ b/lib/features/paywall/presentation/saas_premium_mixin.dart @@ -10,4 +10,12 @@ mixin SaaSPremiumMixin { final saasAccount = session.getSaaSAccountCapability(accountId); return saasAccount?.isPremiumAvailable ?? false; } + + bool isAlreadyHighestSubscription({Session? session, AccountId? accountId}) { + if (accountId == null || session == null) { + return false; + } + final saasAccount = session.getSaaSAccountCapability(accountId); + return saasAccount?.isAlreadyHighestSubscription ?? false; + } } diff --git a/lib/features/quotas/presentation/quotas_controller.dart b/lib/features/quotas/presentation/quotas_controller.dart index 931c01bec..2b7b09358 100644 --- a/lib/features/quotas/presentation/quotas_controller.dart +++ b/lib/features/quotas/presentation/quotas_controller.dart @@ -7,7 +7,7 @@ import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart import 'package:jmap_dart_client/jmap/quotas/quota.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/validate_saas_premium_available_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_paywall_extension.dart'; import 'package:tmail_ui_user/features/quotas/domain/extensions/list_quotas_extensions.dart'; import 'package:tmail_ui_user/features/quotas/domain/state/get_quotas_state.dart'; import 'package:tmail_ui_user/features/quotas/domain/use_case/get_quotas_interactor.dart'; @@ -83,9 +83,9 @@ class QuotasController extends BaseController { } } - bool get isManageMyStorageIsEnabled { - return mailboxDashBoardController.isPremiumAvailable && - !mailboxDashBoardController.isAlreadyHighestSubscription; + bool get isManageMyStorageIsDisabled { + return !mailboxDashBoardController.validatePremiumIsAvailable() || + mailboxDashBoardController.validateUserHasIsAlreadyHighestSubscription(); } void handleManageMyStorage(BuildContext context) { diff --git a/lib/features/quotas/presentation/widget/quotas_banner_widget.dart b/lib/features/quotas/presentation/widget/quotas_banner_widget.dart index 3d30e7371..bf9ef196a 100644 --- a/lib/features/quotas/presentation/widget/quotas_banner_widget.dart +++ b/lib/features/quotas/presentation/widget/quotas_banner_widget.dart @@ -59,7 +59,7 @@ class QuotasBannerWidget extends StatelessWidget { ), const SizedBox(height: 8), if (!PlatformInfo.isWeb || - !_quotasController.isManageMyStorageIsEnabled) + _quotasController.isManageMyStorageIsDisabled) Text( appLocalizations.quotaBannerWarningSubtitleWithoutPremium, style: QuotasBannerStyles.subTitleTextStyle,