From a7d013cfc7a5d35097cc50757f0674ee5a918404 Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 7 Apr 2022 14:45:01 +0700 Subject: [PATCH] TF-83 Fix show message constraints when sending a message --- core/lib/presentation/utils/app_toast.dart | 14 +-- lib/features/base/base_controller.dart | 4 +- .../mixin/message_dialog_action_mixin.dart | 58 ++++++++++ .../presentation/composer_controller.dart | 109 +++++++----------- .../presentation/composer_view_web.dart | 2 +- lib/l10n/intl_messages.arb | 34 ++++-- lib/main/localizations/app_localizations.dart | 29 +++-- .../extensions/list_attachment_extension.dart | 8 +- 8 files changed, 162 insertions(+), 96 deletions(-) create mode 100644 lib/features/base/mixin/message_dialog_action_mixin.dart diff --git a/core/lib/presentation/utils/app_toast.dart b/core/lib/presentation/utils/app_toast.dart index e3870b495..f7fc02b8a 100644 --- a/core/lib/presentation/utils/app_toast.dart +++ b/core/lib/presentation/utils/app_toast.dart @@ -20,23 +20,23 @@ class AppToast { gravity: ToastGravity.BOTTOM); } - void showSuccessToast(String message) { + void showSuccessToast(String message, {bool isToastLengthLong = false}) { Fluttertoast.showToast( msg: message, fontSize: 16, textColor: Colors.white, backgroundColor: AppColor.toastSuccessBackgroundColor, - toastLength: Toast.LENGTH_SHORT, + toastLength: isToastLengthLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM); } - void showErrorToast(String message) { + void showErrorToast(String message, {bool isToastLengthLong = false}) { Fluttertoast.showToast( msg: message, fontSize: 16, textColor: Colors.white, backgroundColor: AppColor.toastErrorBackgroundColor, - toastLength: Toast.LENGTH_SHORT, + toastLength: isToastLengthLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM); } @@ -64,7 +64,7 @@ class AppToast { void showToastWithIcon(BuildContext context, { String? message, String? icon, Color? bgColor, Color? textColor, double? radius, EdgeInsets? padding, - TextStyle? textStyle, double? widthToast}) { + TextStyle? textStyle, double? widthToast, Duration? toastLength}) { double sizeWidth = context.width > 360 ? 360 : context.width - 40; final toast = Material( color: bgColor ?? Colors.white, @@ -82,7 +82,7 @@ class AppToast { mainAxisSize: MainAxisSize.min, children: [ if (icon != null) SvgPicture.asset(icon, width: 24, height: 24, fit: BoxFit.fill), - SizedBox(width: 10.0), + if (icon != null) SizedBox(width: 10.0), Expanded(child: Text( message ?? '', style: textStyle ?? TextStyle(fontSize: 15, color: textColor ?? Colors.black))), @@ -94,7 +94,7 @@ class AppToast { fToast.showToast( child: toast, gravity: ToastGravity.BOTTOM, - toastDuration: Duration(seconds: 2), + toastDuration: toastLength ?? Duration(seconds: 2), ); } } diff --git a/lib/features/base/base_controller.dart b/lib/features/base/base_controller.dart index df240ac34..d9498eeb3 100644 --- a/lib/features/base/base_controller.dart +++ b/lib/features/base/base_controller.dart @@ -2,13 +2,13 @@ import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:core/core.dart'; import 'package:dartz/dartz.dart'; import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_mixin.dart'; -abstract class BaseController extends GetxController { +abstract class BaseController extends GetxController with MessageDialogActionMixin { final viewState = Rx>(Right(UIState.idle)); final connectivityResult = Rxn(); void consumeState(Stream> newStateStream) async { - log('BaseController::consumeState():'); newStateStream.listen( (state) => onData(state), onError: (error) => onError(error), diff --git a/lib/features/base/mixin/message_dialog_action_mixin.dart b/lib/features/base/mixin/message_dialog_action_mixin.dart new file mode 100644 index 000000000..b945a6671 --- /dev/null +++ b/lib/features/base/mixin/message_dialog_action_mixin.dart @@ -0,0 +1,58 @@ + +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pointer_interceptor/pointer_interceptor.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; + +mixin MessageDialogActionMixin { + + final _responsiveUtils = Get.find(); + final _imagePaths = Get.find(); + + void showConfirmDialogAction( + BuildContext context, + String message, + String actionName, + Function onConfirmAction, + {bool hasCancelButton = true, Widget? icon} + ) { + if (_responsiveUtils.isMobile(context)) { + (ConfirmationDialogActionSheetBuilder(context) + ..messageText(message) + ..styleConfirmButton(TextStyle(fontSize: 20, fontWeight: FontWeight.normal, color: Colors.black)) + ..onCancelAction(AppLocalizations.of(context).cancel, () => popBack()) + ..onConfirmAction(actionName, () { + popBack(); + onConfirmAction.call(); + })).show(); + } else { + showDialog( + context: context, + barrierColor: AppColor.colorDefaultCupertinoActionSheet, + builder: (BuildContext context) => PointerInterceptor(child: (ConfirmDialogBuilder(_imagePaths) + ..key(Key('confirm_dialog_action')) + ..title(AppLocalizations.of(context).sending_failed) + ..content(message) + ..addIcon(icon) + ..colorConfirmButton(AppColor.colorTextButton) + ..colorCancelButton(AppColor.colorCancelButton) + ..paddingTitle(icon != null ? EdgeInsets.only(top: 34) : EdgeInsets.only(top: 10)) + ..marginIcon(EdgeInsets.zero) + ..paddingContent(EdgeInsets.only(left: 44, right: 44, bottom: 24, top: 8)) + ..paddingButton(hasCancelButton ? null : EdgeInsets.only(bottom: 16, left: 44, right: 44)) + ..styleTitle(TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black)) + ..styleContent(TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail)) + ..styleTextCancelButton(TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton)) + ..styleTextConfirmButton(TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: Colors.white)) + ..onConfirmButtonAction(actionName, () { + popBack(); + onConfirmAction.call(); + }) + ..onCancelButtonAction(hasCancelButton ? AppLocalizations.of(context).cancel : '', () => popBack()) + ..onCloseButtonAction(() => popBack())) + .build())); + } + } +} \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 5a1566b8c..2773e53f6 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -6,6 +6,7 @@ import 'package:dartz/dartz.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:enough_html_editor/enough_html_editor.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:filesize/filesize.dart'; import 'package:fk_user_agent/fk_user_agent.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -23,7 +24,6 @@ import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/composer/domain/model/auto_complete_pattern.dart'; import 'package:tmail_ui_user/features/composer/domain/model/contact_suggestion_source.dart'; @@ -55,7 +55,6 @@ class ComposerController extends BaseController { final mailboxDashBoardController = Get.find(); final _appToast = Get.find(); final _imagePaths = Get.find(); - final _responsiveUtils = Get.find(); final expandModeAttachments = ExpandMode.EXPAND.obs; final composerArguments = Rxn(); @@ -386,11 +385,11 @@ class ComposerController extends BaseController { clearFocusEditor(context); if (!isEnableEmailSendButton.value) { - _showConfirmDialogSendEmailFailed( - context, + showConfirmDialogAction(context, AppLocalizations.of(context).message_dialog_send_email_without_recipient, AppLocalizations.of(context).add_recipients, () => {}, + icon: SvgPicture.asset(_imagePaths.icSendToastError, fit: BoxFit.fill), hasCancelButton: false); return; } @@ -399,10 +398,8 @@ class ComposerController extends BaseController { final listEmailAddressInvalid = allListEmailAddress .where((emailAddress) => !GetUtils.isEmail(emailAddress.emailAddress)) .toList(); - if (listEmailAddressInvalid.isNotEmpty) { - _showConfirmDialogSendEmailFailed( - context, + showConfirmDialogAction(context, AppLocalizations.of(context).message_dialog_send_email_with_email_address_invalid, AppLocalizations.of(context).fix_email_addresses, () { @@ -410,16 +407,30 @@ class ComposerController extends BaseController { ccAddressExpandMode.value = ExpandMode.EXPAND; bccAddressExpandMode.value = ExpandMode.EXPAND; }, + icon: SvgPicture.asset(_imagePaths.icSendToastError, fit: BoxFit.fill), hasCancelButton: false); return; } if (subjectEmail.value == null || subjectEmail.isEmpty == true) { - _showConfirmDialogSendEmailFailed( - context, + showConfirmDialogAction(context, AppLocalizations.of(context).message_dialog_send_email_without_a_subject, AppLocalizations.of(context).send_anyway, - () => _handleSendMessages(context)); + () => _handleSendMessages(context), + icon: SvgPicture.asset(_imagePaths.icEmpty, fit: BoxFit.fill), + ); + return; + } + + if (!_validateAttachmentsSize()) { + showConfirmDialogAction( + context, + AppLocalizations.of(context).message_dialog_send_email_exceeds_maximum_size('${ + filesize(mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value ?? 0, 0)}'), + AppLocalizations.of(context).got_it, + () => {}, + icon: SvgPicture.asset(_imagePaths.icSendToastError, fit: BoxFit.fill), + hasCancelButton: false); return; } @@ -512,6 +523,10 @@ class ComposerController extends BaseController { consumeState(_localFilePickerInteractor.execute(fileType: fileType)); } + void openFilePickerByTypeOnWeb(BuildContext context, FileType fileType) async { + consumeState(_localFilePickerInteractor.execute(fileType: fileType)); + } + void _pickFileFailure(Failure failure) { if (failure is LocalFilePickerFailure) { if (currentContext != null) { @@ -522,32 +537,34 @@ class ComposerController extends BaseController { void _pickFileSuccess(Success success) { if (success is LocalFilePickerSuccess) { - if (_validateAttachmentsSize(success.pickedFiles)) { + if (_validateAttachmentsSize(listFiles: success.pickedFiles)) { _uploadAttachmentsAction(success.pickedFiles); } else { if (currentContext != null) { - _appToast.showErrorToast(AppLocalizations.of(currentContext!).the_total_size_of_attachments_in_an_email_exceeds_the_limit); + showConfirmDialogAction( + currentContext!, + AppLocalizations.of(currentContext!).message_dialog_upload_attachments_exceeds_maximum_size('${ + filesize(mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value ?? 0, 0)}'), + AppLocalizations.of(currentContext!).got_it, + () => {}, + hasCancelButton: false); } } } } - bool _validateAttachmentsSize(List listFiles) { - num currentTotalSize = 0; - if (attachments.isNotEmpty) { - final currentListSize = attachments - .map((attachment) => attachment.size?.value ?? 0) - .toList(); - currentTotalSize = currentListSize.reduce((sum, size) => sum + size); - log('ComposerController::checkingAttachmentsLimit(): currentTotalSize: $currentTotalSize'); + bool _validateAttachmentsSize({List? listFiles}) { + final currentTotalAttachmentsSize = attachments.totalSize(); + log('ComposerController::_validateAttachmentsSize(): $currentTotalAttachmentsSize'); + num uploadedTotalSize = 0; + if (listFiles != null && listFiles.isNotEmpty) { + final uploadedListSize = listFiles.map((file) => file.fileSize).toList(); + uploadedTotalSize = uploadedListSize.reduce((sum, size) => sum + size); + log('ComposerController::_validateAttachmentsSize(): uploadedTotalSize: $uploadedTotalSize'); } - final uploadedListSize = listFiles.map((file) => file.fileSize).toList(); - final uploadedTotalSize = uploadedListSize.reduce((sum, size) => sum + size); - log('ComposerController::checkingAttachmentsLimit(): uploadedTotalSize: $uploadedTotalSize'); - - final totalSizeReadyToUpload = currentTotalSize + uploadedTotalSize; - log('ComposerController::checkingAttachmentsLimit(): totalSizeReadyToUpload: $totalSizeReadyToUpload'); + final totalSizeReadyToUpload = currentTotalAttachmentsSize + uploadedTotalSize; + log('ComposerController::_validateAttachmentsSize(): totalSizeReadyToUpload: $totalSizeReadyToUpload'); final maxSizeAttachmentsPerEmail = mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value; if (maxSizeAttachmentsPerEmail != null) { @@ -743,46 +760,6 @@ class ComposerController extends BaseController { expandModeAttachments.value = newExpandMode; } - void _showConfirmDialogSendEmailFailed(BuildContext context, String message, - String actionName, Function? onConfirmAction, {bool hasCancelButton = true}) { - if (_responsiveUtils.isMobile(context)) { - (ConfirmationDialogActionSheetBuilder(context) - ..messageText(message) - ..styleConfirmButton(TextStyle(fontSize: 20, fontWeight: FontWeight.normal, color: Colors.black)) - ..onCancelAction(AppLocalizations.of(context).cancel, () => popBack()) - ..onConfirmAction(actionName, () { - popBack(); - onConfirmAction?.call(); - })).show(); - } else { - showDialog( - context: context, - barrierColor: AppColor.colorDefaultCupertinoActionSheet, - builder: (BuildContext context) => PointerInterceptor(child: (ConfirmDialogBuilder(_imagePaths) - ..key(Key('confirm_dialog_send_message')) - ..title(AppLocalizations.of(context).sending_failed) - ..content(message) - ..addIcon(SvgPicture.asset(_imagePaths.icSendToastError, fit: BoxFit.fill)) - ..colorConfirmButton(AppColor.colorTextButton) - ..colorCancelButton(AppColor.colorCancelButton) - ..paddingTitle(EdgeInsets.only(top: 34)) - ..marginIcon(EdgeInsets.zero) - ..paddingContent(EdgeInsets.only(left: 44, right: 44, bottom: 24, top: 8)) - ..paddingButton(hasCancelButton ? null : EdgeInsets.only(bottom: 16, left: 44, right: 44)) - ..styleTitle(TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black)) - ..styleContent(TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail)) - ..styleTextCancelButton(TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton)) - ..styleTextConfirmButton(TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: Colors.white)) - ..onConfirmButtonAction(actionName, () { - popBack(); - onConfirmAction?.call(); - }) - ..onCancelButtonAction(hasCancelButton ? AppLocalizations.of(context).cancel : '', () => popBack()) - ..onCloseButtonAction(() => popBack())) - .build())); - } - } - void addEmailAddressType(PrefixEmailAddress prefixEmailAddress) { listEmailAddressType.add(prefixEmailAddress); } diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index 4d13b38f7..8a37551c8 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -410,7 +410,7 @@ class ComposerView extends GetWidget { buildIconWeb( icon: SvgPicture.asset(imagePaths.icAttachmentsComposer, color: AppColor.colorTextButton, fit: BoxFit.fill), tooltip: AppLocalizations.of(context).attach_file, - onTap: () => controller.openFilePickerByType(context, FileType.any)), + onTap: () => controller.openFilePickerByTypeOnWeb(context, FileType.any)), ]) ) ); diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 86861fc30..c1eee9fa5 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-04-05T16:47:07.723009", + "@@last_modified": "2022-04-07T14:40:38.896425", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -894,12 +894,6 @@ "placeholders_order": [], "placeholders": {} }, - "the_total_size_of_attachments_in_an_email_exceeds_the_limit": "The total size of attachments in an email exceeds the limit.", - "@the_total_size_of_attachments_in_an_email_exceeds_the_limit": { - "type": "text", - "placeholders_order": [], - "placeholders": {} - }, "your_download_has_started": "Your download has started", "@your_download_has_started": { "type": "text", @@ -1015,5 +1009,31 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "message_dialog_send_email_exceeds_maximum_size": "Your message could not be sent because it exceeds the maximum size of {maxSize}", + "@message_dialog_send_email_exceeds_maximum_size": { + "type": "text", + "placeholders_order": [ + "maxSize" + ], + "placeholders": { + "maxSize": {} + } + }, + "message_dialog_upload_attachments_exceeds_maximum_size": "You have reached the maximum file size. Please upload files that total size is less than {maxSize}", + "@message_dialog_upload_attachments_exceeds_maximum_size": { + "type": "text", + "placeholders_order": [ + "maxSize" + ], + "placeholders": { + "maxSize": {} + } + }, + "got_it": "Got it", + "@got_it": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 27f46f72e..40b7c41ff 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -950,13 +950,6 @@ class AppLocalizations { name: 'fix_email_addresses'); } - String get the_total_size_of_attachments_in_an_email_exceeds_the_limit { - return Intl.message( - 'The total size of attachments in an email exceeds the limit.', - name: 'the_total_size_of_attachments_in_an_email_exceeds_the_limit' - ); - } - String get your_download_has_started { return Intl.message( 'Your download has started', @@ -1069,4 +1062,26 @@ class AppLocalizations { 'Version', name: 'version'); } + + String message_dialog_send_email_exceeds_maximum_size(String maxSize) { + return Intl.message( + 'Your message could not be sent because it exceeds the maximum size of $maxSize', + name: 'message_dialog_send_email_exceeds_maximum_size', + args: [maxSize] + ); + } + + String message_dialog_upload_attachments_exceeds_maximum_size(String maxSize) { + return Intl.message( + 'You have reached the maximum file size. Please upload files that total size is less than $maxSize', + name: 'message_dialog_upload_attachments_exceeds_maximum_size', + args: [maxSize] + ); + } + + String get got_it { + return Intl.message( + 'Got it', + name: 'got_it'); + } } \ No newline at end of file diff --git a/model/lib/extensions/list_attachment_extension.dart b/model/lib/extensions/list_attachment_extension.dart index f4e964181..6632152ae 100644 --- a/model/lib/extensions/list_attachment_extension.dart +++ b/model/lib/extensions/list_attachment_extension.dart @@ -3,12 +3,8 @@ import 'package:model/model.dart'; extension ListAttachmentExtension on List { num totalSize() { - num totalSize = 0; - forEach((attachment) { - if (attachment.size != null) { - totalSize += attachment.size!.value; - } - }); + final currentListSize = map((attachment) => attachment.size?.value ?? 0).toList(); + final totalSize = currentListSize.reduce((sum, size) => sum + size); return totalSize; }