From d304083b2e27113af578ae4127ab58e46d804267 Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 13 Jan 2025 10:02:50 +0700 Subject: [PATCH] TF-3416 Implement preview EML attachment --- core/lib/core.dart | 3 +- core/lib/data/constants/constant.dart | 1 + .../extensions/media_type_extension.dart | 23 -- .../preview/supported_preview_file_types.dart | 4 +- .../extensions/media_type_extension.dart | 50 ++++ core/lib/utils/html/html_template.dart | 151 ++++++++++++ core/lib/utils/html/html_utils.dart | 24 ++ core/lib/utils/preview_eml_file_utils.dart | 179 ++++++++++++++ .../exceptions/local_storage_exception.dart | 1 + .../caching/utils/local_storage_manager.dart | 27 +++ .../presentation/composer_bindings.dart | 8 +- .../data/datasource/email_datasource.dart | 7 + .../email_datasource_impl.dart | 138 +++++++++++ .../email_hive_cache_datasource_impl.dart | 16 ++ .../email_local_storage_datasource_impl.dart | 224 ++++++++++++++++++ .../email/data/network/email_api.dart | 4 +- .../repository/email_repository_impl.dart | 18 +- .../model/preview_email_eml_request.dart | 40 ++++ .../domain/repository/email_repository.dart | 7 + ...et_preview_email_content_shared_state.dart | 19 ++ .../state/parse_email_by_blob_id_state.dart | 6 +- .../preview_email_from_eml_file_state.dart | 19 ++ ...w_email_eml_content_shared_interactor.dart | 24 ++ .../parse_email_by_blob_id_interactor.dart | 5 +- ...review_email_from_eml_file_interactor.dart | 31 +++ .../presentation/bindings/email_bindings.dart | 14 +- .../controller/single_email_controller.dart | 107 ++++++++- .../extensions/attachment_extension.dart | 6 +- .../email_previewer_bindings.dart | 107 +++++++++ .../email_previewer_controller.dart | 41 ++++ .../email_previewer/email_previewer_view.dart | 48 ++++ .../data/exceptions/session_exceptions.dart | 6 +- .../bindings/mailbox_dashboard_bindings.dart | 8 +- .../sending_email_interactor_bindings.dart | 8 +- .../bindings/fcm_interactor_bindings.dart | 8 +- .../twake_welcome_controller.dart | 14 +- .../twake_welcome/twake_welcome_view.dart | 58 ++--- .../domain/constants/thread_constants.dart | 24 ++ .../extensions/media_type_extension.dart | 28 --- .../presentation/model/upload_file_state.dart | 2 +- lib/l10n/intl_messages.arb | 14 +- lib/main.dart | 5 +- lib/main/bindings/core/core_bindings.dart | 4 + lib/main/localizations/app_localizations.dart | 13 + lib/main/pages/app_pages.dart | 10 + lib/main/routes/app_routes.dart | 1 + lib/main/routes/route_utils.dart | 18 +- lib/main/utils/toast_manager.dart | 6 + model/lib/extensions/email_extension.dart | 10 + pubspec.lock | 24 +- pubspec.yaml | 2 +- ...arse_email_by_blob_id_interactor_test.dart | 2 +- .../single_email_controller_test.dart | 8 + .../model/attachment_extension_test.dart | 12 +- 54 files changed, 1489 insertions(+), 148 deletions(-) delete mode 100644 core/lib/domain/extensions/media_type_extension.dart create mode 100644 core/lib/presentation/extensions/media_type_extension.dart create mode 100644 core/lib/utils/preview_eml_file_utils.dart create mode 100644 lib/features/caching/exceptions/local_storage_exception.dart create mode 100644 lib/features/caching/utils/local_storage_manager.dart create mode 100644 lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart create mode 100644 lib/features/email/domain/model/preview_email_eml_request.dart create mode 100644 lib/features/email/domain/state/get_preview_email_content_shared_state.dart create mode 100644 lib/features/email/domain/state/preview_email_from_eml_file_state.dart create mode 100644 lib/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart create mode 100644 lib/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart create mode 100644 lib/features/email_previewer/email_previewer_bindings.dart create mode 100644 lib/features/email_previewer/email_previewer_controller.dart create mode 100644 lib/features/email_previewer/email_previewer_view.dart delete mode 100644 lib/features/upload/domain/extensions/media_type_extension.dart diff --git a/core/lib/core.dart b/core/lib/core.dart index 1d92e3eef..048775c12 100644 --- a/core/lib/core.dart +++ b/core/lib/core.dart @@ -14,9 +14,9 @@ export 'presentation/extensions/compare_string_extension.dart'; export 'presentation/extensions/compare_list_extensions.dart'; export 'presentation/extensions/string_extension.dart'; export 'presentation/extensions/tap_down_details_extension.dart'; -export 'domain/extensions/media_type_extension.dart'; export 'presentation/extensions/map_extensions.dart'; export 'presentation/extensions/either_view_state_extension.dart'; +export 'presentation/extensions/media_type_extension.dart'; // Exceptions export 'domain/exceptions/download_file_exception.dart'; @@ -51,6 +51,7 @@ export 'utils/broadcast_channel/broadcast_channel.dart'; export 'utils/list_utils.dart'; export 'utils/mail/domain.dart'; export 'utils/mail/mail_address.dart'; +export 'utils/preview_eml_file_utils.dart'; // Views export 'presentation/views/text/slogan_builder.dart'; diff --git a/core/lib/data/constants/constant.dart b/core/lib/data/constants/constant.dart index 06b5d45ea..93968f5ad 100644 --- a/core/lib/data/constants/constant.dart +++ b/core/lib/data/constants/constant.dart @@ -9,4 +9,5 @@ class Constant { static const imageType = 'image'; static const textVCardMimeType = 'text/x-vcard'; static const textPlainMimeType = 'text/plain'; + static const emlMimeType = 'message/rfc822'; } \ No newline at end of file diff --git a/core/lib/domain/extensions/media_type_extension.dart b/core/lib/domain/extensions/media_type_extension.dart deleted file mode 100644 index 79e719af4..000000000 --- a/core/lib/domain/extensions/media_type_extension.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:core/domain/preview/document_uti.dart'; -import 'package:core/domain/preview/supported_preview_file_types.dart'; -import 'package:http_parser/http_parser.dart'; - -extension MediaTypeExtension on MediaType { - bool isAndroidSupportedPreview() => SupportedPreviewFileTypes.androidSupportedTypes.contains(mimeType); - - bool isIOSSupportedPreview() => SupportedPreviewFileTypes.iOSSupportedTypes.containsKey(mimeType); - - bool isImageFile() => SupportedPreviewFileTypes.imageMimeTypes.contains(mimeType); - - bool isDocFile() => SupportedPreviewFileTypes.docMimeTypes.contains(mimeType); - - bool isPowerPointFile() => SupportedPreviewFileTypes.pptMimeTypes.contains(mimeType); - - bool isExcelFile() => SupportedPreviewFileTypes.xlsMimeTypes.contains(mimeType); - - bool isZipFile() => SupportedPreviewFileTypes.zipMimeTypes.contains(mimeType); - - bool isPdfFile() => SupportedPreviewFileTypes.pdfMimeTypes.contains(mimeType); - - DocumentUti getDocumentUti() => DocumentUti(SupportedPreviewFileTypes.iOSSupportedTypes[mimeType]); -} diff --git a/core/lib/domain/preview/supported_preview_file_types.dart b/core/lib/domain/preview/supported_preview_file_types.dart index 2689a1d1e..99df0bdbf 100644 --- a/core/lib/domain/preview/supported_preview_file_types.dart +++ b/core/lib/domain/preview/supported_preview_file_types.dart @@ -14,9 +14,7 @@ class SupportedPreviewFileTypes { 'application/msword', 'application/vnd.ms-works']; - static const pdfMimeTypes = [ - 'application/pdf', - 'application/rtf']; + static const rtfMimeTypes = ['application/rtf']; static const xlsMimeTypes = [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', diff --git a/core/lib/presentation/extensions/media_type_extension.dart b/core/lib/presentation/extensions/media_type_extension.dart new file mode 100644 index 000000000..dde1016fd --- /dev/null +++ b/core/lib/presentation/extensions/media_type_extension.dart @@ -0,0 +1,50 @@ +import 'package:core/data/constants/constant.dart'; +import 'package:core/domain/preview/document_uti.dart'; +import 'package:core/domain/preview/supported_preview_file_types.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:http_parser/http_parser.dart'; + +extension MediaTypeExtension on MediaType { + bool isAndroidSupportedPreview() => SupportedPreviewFileTypes.androidSupportedTypes.contains(mimeType); + + bool isIOSSupportedPreview() => SupportedPreviewFileTypes.iOSSupportedTypes.containsKey(mimeType); + + bool isImageFile() => SupportedPreviewFileTypes.imageMimeTypes.contains(mimeType); + + bool isDocFile() => SupportedPreviewFileTypes.docMimeTypes.contains(mimeType); + + bool isPowerPointFile() => SupportedPreviewFileTypes.pptMimeTypes.contains(mimeType); + + bool isExcelFile() => SupportedPreviewFileTypes.xlsMimeTypes.contains(mimeType); + + bool isZipFile() => SupportedPreviewFileTypes.zipMimeTypes.contains(mimeType); + + bool isRtfFile() => SupportedPreviewFileTypes.rtfMimeTypes.contains(mimeType); + + DocumentUti getDocumentUti() => DocumentUti(SupportedPreviewFileTypes.iOSSupportedTypes[mimeType]); + + String getIcon(ImagePaths imagePaths, {String? fileName}) { + if (isPDFFile(fileName: fileName) == true || isRtfFile()) { + return imagePaths.icFilePdf; + } else if (isDocFile()) { + return imagePaths.icFileDocx; + } else if (isExcelFile()) { + return imagePaths.icFileXlsx; + } else if (isPowerPointFile()) { + return imagePaths.icFilePptx; + } else if (isZipFile()) { + return imagePaths.icFileZip; + } else if (isImageFile()) { + return imagePaths.icFilePng; + } else { + return imagePaths.icFileEPup; + } + } + + bool isPDFFile({required String? fileName}) => + mimeType == Constant.pdfMimeType || + (mimeType == Constant.octetStreamMimeType && + fileName?.endsWith(Constant.pdfExtension) == true); + + bool get isEMLFile => mimeType == Constant.emlMimeType; +} diff --git a/core/lib/utils/html/html_template.dart b/core/lib/utils/html/html_template.dart index e0ee7bef8..c9a06e955 100644 --- a/core/lib/utils/html/html_template.dart +++ b/core/lib/utils/html/html_template.dart @@ -1,5 +1,6 @@ class HtmlTemplate { + static const String fontLink = ''; static const String nameClassToolTip = 'tmail-tooltip'; static const String tooltipLinkCss = ''' .$nameClassToolTip .tooltiptext { @@ -64,4 +65,154 @@ class HtmlTemplate { } '''; + + static const String previewEMLFileCssStyle = ''' + + '''; } \ No newline at end of file diff --git a/core/lib/utils/html/html_utils.dart b/core/lib/utils/html/html_utils.dart index 63a212074..4bc73c69e 100644 --- a/core/lib/utils/html/html_utils.dart +++ b/core/lib/utils/html/html_utils.dart @@ -421,4 +421,28 @@ class HtmlUtils { const fileSizeSpan = document.getElementById('file-size'); fileSizeSpan.textContent = formatFileSize(bytesJs.length);'''; } + + static void openNewWindowByUrl( + String url, + { + int width = 800, + int height = 600, + } + ) async { + try { + final screenWidth = html.window.screen?.width ?? width; + final screenHeight = html.window.screen?.height ?? height; + + final left = (screenWidth - width) ~/ 2; + final top = (screenHeight - height) ~/ 2; + + final options = 'width=$width,height=$height,top=$top,left=$left'; + + html.window.open(url, '_blank', options); + + html.Url.revokeObjectUrl(url); + } catch (e) { + logError('AppUtils::openNewWindowByUrl:Exception = $e'); + } + } } diff --git a/core/lib/utils/preview_eml_file_utils.dart b/core/lib/utils/preview_eml_file_utils.dart new file mode 100644 index 000000000..78cc2f502 --- /dev/null +++ b/core/lib/utils/preview_eml_file_utils.dart @@ -0,0 +1,179 @@ + +import 'package:core/data/model/print_attachment.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:core/utils/html/html_template.dart'; +import 'package:core/utils/html/html_utils.dart'; +import 'package:html/dom.dart'; +import 'package:html/parser.dart'; + +class PreviewEmlFileUtils { + + Element? _createEmailElement({ + required String subjectPrefix, + required String fromPrefix, + required String toPrefix, + required String ccPrefix, + required String bccPrefix, + required String replyToPrefix, + required String titleAttachment, + String? subject, + String? senderName, + String? senderEmailAddress, + String? toAddress, + String? ccAddress, + String? bccAddress, + String? replyToAddress, + String? dateTime, + String? attachmentIcon, + String? emailContent, + List? listAttachment, + }) { + try { + return Element.html(''' +