diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 53770fffd..0f3feb2c7 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -79,6 +79,7 @@ import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email. import 'package:tmail_ui_user/features/sending_queue/presentation/model/sending_email_arguments.dart'; import 'package:tmail_ui_user/features/server_settings/domain/state/get_always_read_receipt_setting_state.dart'; import 'package:tmail_ui_user/features/server_settings/domain/usecases/get_always_read_receipt_setting_interactor.dart'; +import 'package:tmail_ui_user/features/upload/domain/extensions/list_file_info_extension.dart'; import 'package:tmail_ui_user/features/upload/domain/model/upload_task_id.dart'; import 'package:tmail_ui_user/features/upload/domain/state/attachment_upload_state.dart'; import 'package:tmail_ui_user/features/upload/domain/state/local_file_picker_state.dart'; @@ -264,7 +265,7 @@ class ComposerController extends BaseController { success is TransformHtmlEmailContentSuccess) { emailContentsViewState.value = Right(success); } else if (success is LocalFilePickerSuccess) { - _pickFileSuccess(success); + _handlePickFileSuccess(success); } else if (success is GetEmailContentSuccess) { _getEmailContentSuccess(success); } else if (success is GetEmailContentFromCacheSuccess) { @@ -877,7 +878,7 @@ class ComposerController extends BaseController { return; } - if (!uploadController.hasEnoughMaxAttachmentSize()) { + if (uploadController.isExceededMaxSizeAttachmentsPerEmail()) { showConfirmDialogAction( context, AppLocalizations.of(context).message_dialog_send_email_exceeds_maximum_size( @@ -1072,29 +1073,21 @@ class ComposerController extends BaseController { } } - void _pickFileSuccess(LocalFilePickerSuccess success) { - if (uploadController.hasEnoughMaxAttachmentSize(fileInfoTotalSize: uploadController.getTotalSizeFromListFileInfo(success.pickedFiles))) { - _uploadAttachmentsAction(success.pickedFiles); - } else { - if (currentContext != null) { - showConfirmDialogAction( - currentContext!, - AppLocalizations.of(currentContext!).message_dialog_upload_attachments_exceeds_maximum_size( - filesize(mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value ?? 0, 0)), - AppLocalizations.of(currentContext!).got_it, - onConfirmAction: () => {isSendEmailLoading.value = false}, - title: AppLocalizations.of(currentContext!).maximum_files_size, - hasCancelButton: false); - } - } + void _handlePickFileSuccess(LocalFilePickerSuccess success) { + uploadController.validateTotalSizeAttachmentsBeforeUpload( + listFileInfo: success.pickedFiles, + callbackAction: () => _uploadAttachmentsAction(success.pickedFiles) + ); } - void _uploadAttachmentsAction(List pickedFiles) async { + void _uploadAttachmentsAction(List pickedFiles) { final session = mailboxDashBoardController.sessionCurrent; final accountId = mailboxDashBoardController.accountId.value; if (session != null && accountId != null) { final uploadUri = session.getUploadUri(accountId, jmapUrl: _dynamicUrlInterceptors.jmapUrl); uploadController.justUploadAttachmentsAction(pickedFiles, uploadUri); + } else { + log('ComposerController::_uploadAttachmentsAction: SESSION OR ACCOUNT_ID is NULL'); } } @@ -1303,7 +1296,7 @@ class ComposerController extends BaseController { } if (listFileAttachmentSharedMediaFile.isNotEmpty) { final listFile = covertListSharedMediaFileToFileInfo(listSharedMediaFile); - if (uploadController.hasEnoughMaxAttachmentSize(fileInfoTotalSize: uploadController.getTotalSizeFromListFileInfo(listFile))) { + if (!uploadController.isExceededMaxSizeAttachmentsPerEmail(totalSizePreparedFiles: listFile.totalSize)) { _uploadAttachmentsAction(listFile); } else { if (currentContext != null) { @@ -1778,7 +1771,7 @@ class ComposerController extends BaseController { } void _uploadInlineAttachmentsAction(FileInfo pickedFile, {bool fromFileShared = false}) async { - if (uploadController.hasEnoughMaxAttachmentSize(fileInfoTotalSize: uploadController.getTotalSizeFromListFileInfo([pickedFile]))) { + if (!uploadController.isExceededMaxSizeAttachmentsPerEmail(totalSizePreparedFiles: pickedFile.fileSize)) { final session = mailboxDashBoardController.sessionCurrent; final accountId = mailboxDashBoardController.accountId.value; if (session != null && accountId != null) { @@ -1985,7 +1978,7 @@ class ComposerController extends BaseController { } void _addAttachmentFromDragAndDrop({required FileInfo fileInfo}) { - if (uploadController.hasEnoughMaxAttachmentSize(fileInfoTotalSize: uploadController.getTotalSizeFromListFileInfo([fileInfo]))) { + if (!uploadController.isExceededMaxSizeAttachmentsPerEmail(totalSizePreparedFiles: fileInfo.fileSize)) { _uploadAttachmentsAction([fileInfo]); } else { if (currentContext != null) { @@ -2094,7 +2087,7 @@ class ComposerController extends BaseController { void addAttachmentFromDropZone(Attachment attachment) { log('ComposerController::addAttachmentFromDropZone: $attachment'); - if (uploadController.hasEnoughMaxAttachmentSize(fileInfoTotalSize: attachment.size?.value)) { + if (!uploadController.isExceededMaxSizeAttachmentsPerEmail(totalSizePreparedFiles: attachment.size?.value ?? 0)) { uploadController.initializeUploadAttachments([attachment]); } else { if (currentContext != null) { @@ -2173,7 +2166,7 @@ class ComposerController extends BaseController { _closeComposerAction(result: draftArgs); _closeComposerButtonState = ButtonState.enabled; } - + void _getAlwaysReadReceiptSetting() { final accountId = mailboxDashBoardController.accountId.value; if (accountId != null) { diff --git a/lib/features/email/presentation/widgets/email_attachments_widget.dart b/lib/features/email/presentation/widgets/email_attachments_widget.dart index ba896651c..06e6566ae 100644 --- a/lib/features/email/presentation/widgets/email_attachments_widget.dart +++ b/lib/features/email/presentation/widgets/email_attachments_widget.dart @@ -64,7 +64,7 @@ class EmailAttachmentsWidget extends StatelessWidget { child: Text( AppLocalizations.of(context).titleHeaderAttachment( attachments.length, - filesize(attachments.totalSize(), 1) + filesize(attachments.totalSize, 1) ), style: const TextStyle( fontSize: EmailAttachmentsStyles.headerTextSize, diff --git a/lib/features/upload/domain/extensions/list_file_info_extension.dart b/lib/features/upload/domain/extensions/list_file_info_extension.dart new file mode 100644 index 000000000..438a30a6a --- /dev/null +++ b/lib/features/upload/domain/extensions/list_file_info_extension.dart @@ -0,0 +1,7 @@ +import 'package:model/upload/file_info.dart'; + +extension ListFileInfoExtension on List { + List get listSize => map((file) => file.fileSize).toList(); + + num get totalSize => listSize.reduce((sum, size) => sum + size); +} \ No newline at end of file diff --git a/lib/features/upload/presentation/controller/upload_controller.dart b/lib/features/upload/presentation/controller/upload_controller.dart index 501643474..eb591053b 100644 --- a/lib/features/upload/presentation/controller/upload_controller.dart +++ b/lib/features/upload/presentation/controller/upload_controller.dart @@ -1,12 +1,15 @@ import 'package:async/async.dart'; import 'package:collection/collection.dart'; +import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; +import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart'; import 'package:model/email/attachment.dart'; @@ -18,6 +21,7 @@ import 'package:tmail_ui_user/features/base/state/base_ui_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/upload_attachment_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/upload/domain/extensions/list_file_info_extension.dart'; import 'package:tmail_ui_user/features/upload/domain/model/upload_task_id.dart'; import 'package:tmail_ui_user/features/upload/domain/state/attachment_upload_state.dart'; import 'package:tmail_ui_user/features/upload/presentation/extensions/upload_attachment_extension.dart'; @@ -26,6 +30,7 @@ import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_sta import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_status.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; +import 'package:tmail_ui_user/main/utils/app_config.dart'; class UploadController extends BaseController { @@ -270,31 +275,96 @@ class UploadController extends BaseController { } } - bool hasEnoughMaxAttachmentSize({num? fileInfoTotalSize}) { - final currentTotalAttachmentsSize = attachmentsUploaded.totalSize(); - final totalInlineAttachmentsSize = inlineAttachmentsUploaded.totalSize(); - log('UploadController::_validateAttachmentsSize(): $currentTotalAttachmentsSize'); - log('UploadController::_validateAttachmentsSize(): totalInlineAttachmentsSize: $totalInlineAttachmentsSize'); - num uploadedTotalSize = fileInfoTotalSize ?? 0; - - final totalSizeReadyToUpload = currentTotalAttachmentsSize + - totalInlineAttachmentsSize + - uploadedTotalSize; - log('UploadController::_validateAttachmentsSize(): totalSizeReadyToUpload: $totalSizeReadyToUpload'); - + bool isExceededMaxSizeAttachmentsPerEmail({num totalSizePreparedFiles = 0}) { + final currentTotalSize = attachmentsUploaded.totalSize + inlineAttachmentsUploaded.totalSize + totalSizePreparedFiles; final maxSizeAttachmentsPerEmail = _mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value; + log('UploadController::isExceededMaxSizeAttachmentsPerEmail(): currentTotalSize = $currentTotalSize | maxSizeAttachmentsPerEmail = $maxSizeAttachmentsPerEmail'); if (maxSizeAttachmentsPerEmail != null) { - return totalSizeReadyToUpload <= maxSizeAttachmentsPerEmail; + return currentTotalSize > maxSizeAttachmentsPerEmail; } else { return false; } } - num getTotalSizeFromListFileInfo(List listFiles) { - final uploadedListSize = listFiles.map((file) => file.fileSize).toList(); - num totalSize = uploadedListSize.reduce((sum, size) => sum + size); - log('UploadController::_getTotalSizeFromListFileInfo():totalSize: $totalSize'); - return totalSize; + bool isExceededMaxSizeFilesAttachedInComposer({num totalSizePreparedFiles = 0}) { + final currentTotalSizeAttachments = attachmentsUploaded.totalSize + totalSizePreparedFiles; + const maximumBytesSizeFileAttachedInComposer = AppConfig.maximumMegabytesSizeFileAttachedInComposer * 1024 * 1024; + log('UploadController::isExceededMaxSizeFilesAttachedInComposer(): currentTotalSizeAttachments = $currentTotalSizeAttachments | maximumBytesSizeFileAttachedInComposer = $maximumBytesSizeFileAttachedInComposer'); + return currentTotalSizeAttachments > maximumBytesSizeFileAttachedInComposer; + } + + void validateTotalSizeAttachmentsBeforeUpload({ + required List listFileInfo, + VoidCallback? callbackAction + }) { + final totalSizeListFiles = listFileInfo.totalSize; + log('UploadController::_validateTotalSizeAttachmentsBeforeUpload: totalSizeListFiles = $totalSizeListFiles'); + if (isExceededMaxSizeAttachmentsPerEmail(totalSizePreparedFiles: totalSizeListFiles)) { + if (currentContext == null) { + log('UploadController::_validateTotalSizeAttachmentsBeforeUpload: CONTEXT IS NULL'); + return; + } + + _showConfirmDialogWhenExceededMaxSizeAttachmentsPerEmail(context: currentContext!); + return; + } + + if (isExceededMaxSizeFilesAttachedInComposer(totalSizePreparedFiles: totalSizeListFiles)) { + if (currentContext == null) { + log('UploadController::_validateTotalSizeAttachmentsBeforeUpload: CONTEXT IS NULL'); + return; + } + + _showWarningDialogWhenExceededMaxSizeFilesAttachedInComposer( + context: currentContext!, + confirmAction: callbackAction + ); + return; + } + + callbackAction?.call(); + } + + void _showConfirmDialogWhenExceededMaxSizeAttachmentsPerEmail({required BuildContext context}) { + final maxSizeAttachmentsPerEmail = filesize(_mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value ?? 0, 0); + showConfirmDialogAction( + context, + AppLocalizations.of(context).message_dialog_upload_attachments_exceeds_maximum_size(maxSizeAttachmentsPerEmail), + AppLocalizations.of(context).got_it, + title: AppLocalizations.of(context).maximum_files_size, + hasCancelButton: false); + } + + void _showWarningDialogWhenExceededMaxSizeFilesAttachedInComposer({ + required BuildContext context, + VoidCallback? confirmAction, + }) { + showConfirmDialogAction( + context, + title: '', + AppLocalizations.of(context).messageWarningDialogWhenExceedMaximumFileSizeComposer, + AppLocalizations.of(context).continueAction, + cancelTitle: AppLocalizations.of(context).cancel, + alignCenter: true, + onConfirmAction: confirmAction, + icon: SvgPicture.asset( + imagePaths.icQuotasWarning, + width: 40, + height: 40, + colorFilter: AppColor.colorBackgroundQuotasWarning.asFilter(), + ), + messageStyle: Theme.of(context).textTheme.labelMedium?.copyWith( + fontSize: 14, + color: Colors.black + ), + actionStyle: Theme.of(context).textTheme.labelMedium?.copyWith( + fontSize: 17, + color: Colors.white + ), + cancelStyle: Theme.of(context).textTheme.labelMedium?.copyWith( + fontSize: 17, + color: Colors.black + )); } bool get allUploadAttachmentsCompleted { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index ccd04c792..fe33404be 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -3238,5 +3238,17 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "messageWarningDialogWhenExceedMaximumFileSizeComposer": "Your message is larger than the size generally accepted by third party email systems. If you confirm sending this mail, there is a risk that it gets rejected by your recipient system.", + "@messageWarningDialogWhenExceedMaximumFileSizeComposer": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "continueAction": "Continue", + "@continueAction": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 357766de3..6d98e60e1 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -3935,5 +3935,17 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "messageWarningDialogWhenExceedMaximumFileSizeComposer": "Votre message dépasse la taille généralement acceptée pour un email. \nSi vous confirmez l'envoi il y a un risque que le mail soit rejeté par le système de votre interlocuteur.", + "@messageWarningDialogWhenExceedMaximumFileSizeComposer": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "continueAction": "Continuer", + "@continueAction": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 6076c1809..0779e9173 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2024-04-05T12:00:06.899366", + "@@last_modified": "2024-02-29T16:08:18.479839", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -3749,5 +3749,17 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "messageWarningDialogWhenExceedMaximumFileSizeComposer": "Your message is larger than the size generally accepted by third party email systems. If you confirm sending this mail, there is a risk that it gets rejected by your recipient system.", + "@messageWarningDialogWhenExceedMaximumFileSizeComposer": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "continueAction": "Continue", + "@continueAction": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_vi.arb b/lib/l10n/intl_vi.arb index d2b6308f8..07f4cf948 100644 --- a/lib/l10n/intl_vi.arb +++ b/lib/l10n/intl_vi.arb @@ -3941,5 +3941,17 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "messageWarningDialogWhenExceedMaximumFileSizeComposer": "Thư của bạn lớn hơn kích thước thường được hệ thống email của bên thứ ba chấp nhận. Nếu bạn xác nhận đã gửi thư này, có nguy cơ nó sẽ bị hệ thống người nhận từ chối.", + "@messageWarningDialogWhenExceedMaximumFileSizeComposer": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "continueAction": "Tiếp tục", + "@continueAction": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 5ceea1c55..5c446ce03 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -3916,4 +3916,18 @@ class AppLocalizations { name: 'zoomOut', ); } + + String get messageWarningDialogWhenExceedMaximumFileSizeComposer { + return Intl.message( + 'Your message is larger than the size generally accepted by third party email systems. If you confirm sending this mail, there is a risk that it gets rejected by your recipient system.', + name: 'messageWarningDialogWhenExceedMaximumFileSizeComposer', + ); + } + + String get continueAction { + return Intl.message( + 'Continue', + name: 'continueAction', + ); + } } \ No newline at end of file diff --git a/lib/main/utils/app_config.dart b/lib/main/utils/app_config.dart index a4634f5a1..f4f65fa6b 100644 --- a/lib/main/utils/app_config.dart +++ b/lib/main/utils/app_config.dart @@ -8,6 +8,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class AppConfig { static const int limitCharToStartSearch = 3; + static const int maximumMegabytesSizeFileAttachedInComposer = 10; static const String appDashboardConfigurationPath = "configurations/app_dashboard.json"; static const String appFCMConfigurationPath = "configurations/env.fcm"; diff --git a/model/lib/extensions/list_attachment_extension.dart b/model/lib/extensions/list_attachment_extension.dart index e882fa117..e8f788170 100644 --- a/model/lib/extensions/list_attachment_extension.dart +++ b/model/lib/extensions/list_attachment_extension.dart @@ -6,7 +6,7 @@ import 'package:model/extensions/attachment_extension.dart'; extension ListAttachmentExtension on List { - num totalSize() { + num get totalSize { if (isNotEmpty) { final currentListSize = map((attachment) => attachment.size?.value ?? 0).toList(); final totalSize = currentListSize.reduce((sum, size) => sum + size);