TF-3986 Change style for over quota flow when send or save draft email

This commit is contained in:
dab246
2025-09-10 13:10:51 +07:00
committed by Dat H. Pham
parent 16f0eed730
commit 77b1222149
8 changed files with 153 additions and 48 deletions
@@ -17,6 +17,7 @@ import 'package:get/get.dart';
import 'package:html_editor_enhanced/html_editor.dart'; import 'package:html_editor_enhanced/html_editor.dart';
import 'package:jmap_dart_client/jmap/account_id.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/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/core/session/session.dart';
import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.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/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/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/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/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/mailbox_dashboard/presentation/model/draggable_app_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_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 BuildContext context,
required FeatureFailure failure required FeatureFailure failure
}) async { }) async {
final errorMessage = getMessageFailure( final messageRecord = getMessageFailure(
appLocalizations: AppLocalizations.of(context), appLocalizations: AppLocalizations.of(context),
exception: failure.exception, exception: failure.exception,
); );
final isIncreaseMySpaceIsDisabled =
!mailboxDashBoardController.validatePremiumIsAvailable() ||
mailboxDashBoardController.validateUserHasIsAlreadyHighestSubscription();
final isPaywallActive = !isIncreaseMySpaceIsDisabled &&
messageRecord.errorType == SetError.overQuota;
await MessageDialogActionManager().showConfirmDialogAction( await MessageDialogActionManager().showConfirmDialogAction(
context, context,
title: '', title: '',
errorMessage, messageRecord.message,
AppLocalizations.of(context).edit, isPaywallActive
cancelTitle: AppLocalizations.of(context).closeAnyway, ? AppLocalizations.of(context).increaseYourSpace
: AppLocalizations.of(context).edit,
cancelTitle: isPaywallActive
? AppLocalizations.of(context).edit
: AppLocalizations.of(context).closeAnyway,
alignCenter: true, alignCenter: true,
outsideDismissible: false, outsideDismissible: false,
autoPerformPopBack: false, autoPerformPopBack: false,
onConfirmAction: () { onConfirmAction: () {
_sendButtonState = ButtonState.enabled; _sendButtonState = ButtonState.enabled;
popBack(); popBack();
_autoFocusFieldWhenLauncher();
if (isPaywallActive) {
mailboxDashBoardController.navigateToPaywall(context);
} else {
_autoFocusFieldWhenLauncher();
}
}, },
onCancelAction: () { onCancelAction: () {
_sendButtonState = ButtonState.enabled; _sendButtonState = ButtonState.enabled;
_closeComposerAction(closeOverlays: true);
if (isPaywallActive) {
popBack();
_autoFocusFieldWhenLauncher();
} else {
_closeComposerAction(closeOverlays: true);
}
}, },
); );
} }
@@ -1282,14 +1307,25 @@ class ComposerController extends BaseController
await _showConfirmDialogWhenSaveMessageToDraftsFailure( await _showConfirmDialogWhenSaveMessageToDraftsFailure(
context: context, context: context,
failure: resultState, failure: resultState,
onConfirmAction: () { onConfirmAction: (isPaywallActive) {
_saveToDraftButtonState = ButtonState.enabled; _saveToDraftButtonState = ButtonState.enabled;
popBack(); popBack();
_autoFocusFieldWhenLauncher();
if (isPaywallActive) {
mailboxDashBoardController.navigateToPaywall(context);
} else {
_autoFocusFieldWhenLauncher();
}
}, },
onCancelAction: () { onCancelAction: (isPaywallActive) {
_saveToDraftButtonState = ButtonState.enabled; _saveToDraftButtonState = ButtonState.enabled;
_closeComposerAction(closeOverlays: true);
if (isPaywallActive) {
popBack();
_autoFocusFieldWhenLauncher();
} else {
_closeComposerAction(closeOverlays: true);
}
}, },
); );
} else { } else {
@@ -2222,31 +2258,62 @@ class ComposerController extends BaseController
Future<void> _showConfirmDialogWhenSaveMessageToDraftsFailure({ Future<void> _showConfirmDialogWhenSaveMessageToDraftsFailure({
required BuildContext context, required BuildContext context,
required FeatureFailure failure, required FeatureFailure failure,
VoidCallback? onConfirmAction, Function(bool)? onConfirmAction,
VoidCallback? onCancelAction, Function(bool)? onCancelAction,
}) async { }) async {
final errorMessage = getMessageFailure( final messageRecord = getMessageFailure(
appLocalizations: AppLocalizations.of(context), appLocalizations: AppLocalizations.of(context),
exception: failure.exception, exception: failure.exception,
isDraft: true, isDraft: true,
); );
final isIncreaseMySpaceIsDisabled =
!mailboxDashBoardController.validatePremiumIsAvailable() ||
mailboxDashBoardController.validateUserHasIsAlreadyHighestSubscription();
final isPaywallActive = !isIncreaseMySpaceIsDisabled &&
messageRecord.errorType == SetError.overQuota;
await MessageDialogActionManager().showConfirmDialogAction( await MessageDialogActionManager().showConfirmDialogAction(
context, context,
title: '', title: '',
errorMessage, messageRecord.message,
AppLocalizations.of(context).edit, isPaywallActive
cancelTitle: AppLocalizations.of(context).closeAnyway, ? AppLocalizations.of(context).increaseYourSpace
: AppLocalizations.of(context).edit,
cancelTitle: isPaywallActive
? AppLocalizations.of(context).edit
: AppLocalizations.of(context).closeAnyway,
alignCenter: true, alignCenter: true,
outsideDismissible: false, outsideDismissible: false,
autoPerformPopBack: false, autoPerformPopBack: false,
onConfirmAction: onConfirmAction ?? () { onConfirmAction: () {
_closeComposerButtonState = ButtonState.enabled; if (onConfirmAction != null) {
popBack(); onConfirmAction(isPaywallActive);
_autoFocusFieldWhenLauncher(); } else {
_closeComposerButtonState = ButtonState.enabled;
popBack();
if (isPaywallActive) {
mailboxDashBoardController.navigateToPaywall(context);
} else {
_autoFocusFieldWhenLauncher();
}
}
}, },
onCancelAction: onCancelAction ?? () { onCancelAction: () {
_closeComposerButtonState = ButtonState.enabled; if (onCancelAction != null) {
_closeComposerAction(closeOverlays: true); onCancelAction(isPaywallActive);
} else {
_closeComposerButtonState = ButtonState.enabled;
if (isPaywallActive) {
popBack();
_autoFocusFieldWhenLauncher();
} else {
_closeComposerAction(closeOverlays: true);
}
}
}, },
); );
} }
@@ -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: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/domain/exceptions/set_method_exception.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.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 { extension HandleMessageFailureExtension on ComposerController {
String getMessageFailure({ ({String message, ErrorType? errorType}) getMessageFailure({
required AppLocalizations appLocalizations, required AppLocalizations appLocalizations,
dynamic exception, dynamic exception,
bool isDraft = false, bool isDraft = false,
}) { }) {
if (exception is! SetMethodException) { final fallbackMessage = isDraft
return isDraft
? appLocalizations.warningMessageWhenSaveEmailToDraftsFailure ? appLocalizations.warningMessageWhenSaveEmailToDraftsFailure
: appLocalizations.warningMessageWhenSendEmailFailure; : appLocalizations.warningMessageWhenSendEmailFailure;
if (exception is! SetMethodException) {
return (message: fallbackMessage, errorType: null);
} }
for (var error in exception.mapErrors.values) { for (final error in exception.mapErrors.values) {
if (error.type != SetError.tooLarge && error.type != SetError.overQuota) { if (error.type == SetError.tooLarge || error.type == SetError.overQuota) {
continue; final message = isDraft
} ? error.toastMessageForSaveEmailAsDraftFailure(
appLocalizations: appLocalizations,
defaultMessage: fallbackMessage,
)
: error.toastMessageForSendEmailFailure(
appLocalizations: appLocalizations,
defaultMessage: fallbackMessage,
);
if (isDraft) { return (message: message, errorType: error.type);
return error.toastMessageForSaveEmailAsDraftFailure(
appLocalizations: appLocalizations,
defaultMessage:appLocalizations.warningMessageWhenSaveEmailToDraftsFailure,
);
} else {
return error.toastMessageForSendEmailFailure(
appLocalizations: appLocalizations,
defaultMessage:appLocalizations.warningMessageWhenSendEmailFailure,
);
} }
} }
return isDraft return (message: fallbackMessage, errorType: null);
? appLocalizations.warningMessageWhenSaveEmailToDraftsFailure
: appLocalizations.warningMessageWhenSendEmailFailure;
} }
} }
@@ -376,6 +376,7 @@ class MailboxDashBoardController extends ReloadableController
if (PlatformInfo.isWeb) { if (PlatformInfo.isWeb) {
listSearchFilterScrollController = ScrollController(); listSearchFilterScrollController = ScrollController();
twakeAppManager.setExecutingBeforeReconnect(false); twakeAppManager.setExecutingBeforeReconnect(false);
isRetryGetPaywallUrl = false;
} }
if (PlatformInfo.isIOS) { if (PlatformInfo.isIOS) {
_registerPendingCurrentEmailIdInNotification(); _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 @override
void handleUrgentExceptionOnMobile({Failure? failure, Exception? exception}) { void handleUrgentExceptionOnMobile({Failure? failure, Exception? exception}) {
SmartDialog.dismiss(); SmartDialog.dismiss();
@@ -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() { void loadPaywallUrl() {
final getPaywallUrlInteractor = getBinding<GetPaywallUrlInteractor>(); final getPaywallUrlInteractor = getBinding<GetPaywallUrlInteractor>();
final jmapUrl = dynamicUrlInterceptors.jmapUrl; final jmapUrl = dynamicUrlInterceptors.jmapUrl;
@@ -37,6 +47,14 @@ extension HandlePaywallExtension on MailboxDashBoardController {
void loadPaywallUrlSuccess(PaywallUrlPattern newPattern) { void loadPaywallUrlSuccess(PaywallUrlPattern newPattern) {
paywallUrlPattern = newPattern; paywallUrlPattern = newPattern;
if (isRetryGetPaywallUrl) {
isRetryGetPaywallUrl = false;
final qualifiedPaywall = paywallUrlPattern!.getQualifiedUrl(
ownerEmail: ownEmailAddress.value,
domainName: RouteUtils.getRootDomain(),
);
AppUtils.launchLink(qualifiedPaywall);
}
} }
void loadPaywallUrlFailure() { void loadPaywallUrlFailure() {
@@ -10,4 +10,12 @@ mixin SaaSPremiumMixin {
final saasAccount = session.getSaaSAccountCapability(accountId); final saasAccount = session.getSaaSAccountCapability(accountId);
return saasAccount?.isPremiumAvailable ?? false; 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;
}
} }
@@ -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:jmap_dart_client/jmap/quotas/quota.dart';
import 'package:tmail_ui_user/features/base/base_controller.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/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/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/state/get_quotas_state.dart';
import 'package:tmail_ui_user/features/quotas/domain/use_case/get_quotas_interactor.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 { bool get isManageMyStorageIsDisabled {
return mailboxDashBoardController.isPremiumAvailable && return !mailboxDashBoardController.validatePremiumIsAvailable() ||
!mailboxDashBoardController.isAlreadyHighestSubscription; mailboxDashBoardController.validateUserHasIsAlreadyHighestSubscription();
} }
void handleManageMyStorage(BuildContext context) { void handleManageMyStorage(BuildContext context) {
@@ -59,7 +59,7 @@ class QuotasBannerWidget extends StatelessWidget {
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
if (!PlatformInfo.isWeb || if (!PlatformInfo.isWeb ||
!_quotasController.isManageMyStorageIsEnabled) _quotasController.isManageMyStorageIsDisabled)
Text( Text(
appLocalizations.quotaBannerWarningSubtitleWithoutPremium, appLocalizations.quotaBannerWarningSubtitleWithoutPremium,
style: QuotasBannerStyles.subTitleTextStyle, style: QuotasBannerStyles.subTitleTextStyle,