diff --git a/core/lib/data/model/preview_attachment.dart b/core/lib/data/model/preview_attachment.dart new file mode 100644 index 000000000..979804376 --- /dev/null +++ b/core/lib/data/model/preview_attachment.dart @@ -0,0 +1,24 @@ + +import 'package:equatable/equatable.dart'; + +class PreviewAttachment with EquatableMixin { + final String iconBase64Data; + final String name; + final String size; + final String? link; + + PreviewAttachment({ + required this.iconBase64Data, + required this.name, + required this.size, + this.link, + }); + + @override + List get props => [ + iconBase64Data, + name, + size, + link, + ]; +} \ No newline at end of file diff --git a/core/lib/domain/exceptions/web_session_exception.dart b/core/lib/domain/exceptions/web_session_exception.dart index 8d118d831..a18f482c9 100644 --- a/core/lib/domain/exceptions/web_session_exception.dart +++ b/core/lib/domain/exceptions/web_session_exception.dart @@ -24,3 +24,5 @@ class SaveToWebSessionFailException with EquatableMixin implements Exception { @override List get props => []; } + +class CannotOpenNewWindowException implements Exception {} diff --git a/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart b/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart index c3ac17d67..99e67db9d 100644 --- a/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart +++ b/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart @@ -11,6 +11,8 @@ import 'package:core/utils/html/html_utils.dart'; import 'package:flutter/cupertino.dart'; import 'package:universal_html/html.dart' as html; +typedef OnClickHyperLinkAction = Function(Uri?); + class HtmlContentViewerOnWeb extends StatefulWidget { final String contentHtml; @@ -21,6 +23,8 @@ class HtmlContentViewerOnWeb extends StatefulWidget { /// Handler for mailto: links final Function(Uri?)? mailtoDelegate; + final OnClickHyperLinkAction? onClickHyperLinkAction; + // if widthContent is bigger than width of htmlContent, set this to true let widget able to resize to width of htmlContent final bool allowResizeToDocumentSize; @@ -32,6 +36,7 @@ class HtmlContentViewerOnWeb extends StatefulWidget { this.allowResizeToDocumentSize = true, this.mailtoDelegate, this.direction, + this.onClickHyperLinkAction, }) : super(key: key); @override @@ -55,6 +60,7 @@ class _HtmlContentViewerOnWebState extends State { late final StreamSubscription sizeListener; bool _iframeLoaded = false; static const String iframeOnLoadMessage = 'iframeHasBeenLoaded'; + static const String onClickHyperLinkName = 'onClickHyperLink'; @override void initState() { @@ -113,6 +119,13 @@ class _HtmlContentViewerOnWebState extends State { } } } + + if (data['type'] != null && data['type'].contains('toDart: $onClickHyperLinkName')) { + final link = data['url'] as String?; + if (link != null && mounted) { + widget.onClickHyperLinkAction?.call(Uri.parse(link)); + } + } }); } @@ -173,21 +186,50 @@ class _HtmlContentViewerOnWebState extends State { } } - function handleOnClickEmailLink(e) { - var href = this.href; - window.parent.postMessage(JSON.stringify({"view": "$_createdViewId", "type": "toDart: OpenLink", "url": "" + href}), "*"); - e.preventDefault(); - } + ${widget.mailtoDelegate != null + ? ''' + function handleOnClickEmailLink(e) { + var href = this.href; + window.parent.postMessage(JSON.stringify({"view": "$_createdViewId", "type": "toDart: OpenLink", "url": "" + href}), "*"); + e.preventDefault(); + } + ''' + : ''} + + + + ${widget.onClickHyperLinkAction != null + ? ''' + function onClickHyperLink(e) { + var href = this.href; + window.parent.postMessage(JSON.stringify({"view": "$_createdViewId", "type": "toDart: $onClickHyperLinkName", "url": "" + href}), "*"); + e.preventDefault(); + } + ''' + : ''} function handleOnLoad() { window.parent.postMessage(JSON.stringify({"view": "$_createdViewId", "message": "$iframeOnLoadMessage"}), "*"); window.parent.postMessage(JSON.stringify({"view": "$_createdViewId", "type": "toIframe: getHeight"}), "*"); window.parent.postMessage(JSON.stringify({"view": "$_createdViewId", "type": "toIframe: getWidth"}), "*"); - var emailLinks = document.querySelectorAll('a[href^="mailto:"]'); - for(var i=0; i < emailLinks.length; i++){ - emailLinks[i].addEventListener('click', handleOnClickEmailLink); - } + ${widget.onClickHyperLinkAction != null + ? ''' + var hyperLinks = document.querySelectorAll('a'); + for (var i=0; i < hyperLinks.length; i++){ + hyperLinks[i].addEventListener('click', onClickHyperLink); + } + ''' + : ''} + + ${widget.mailtoDelegate != null + ? ''' + var emailLinks = document.querySelectorAll('a[href^="mailto:"]'); + for (var i=0; i < emailLinks.length; i++){ + emailLinks[i].addEventListener('click', handleOnClickEmailLink); + } + ''' + : ''} } '''; diff --git a/core/lib/utils/html/html_utils.dart b/core/lib/utils/html/html_utils.dart index 4bc73c69e..c6b0a3fcb 100644 --- a/core/lib/utils/html/html_utils.dart +++ b/core/lib/utils/html/html_utils.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:math'; import 'js_interop_stub.dart' if (dart.library.html) 'dart:js_interop'; import 'dart:typed_data'; @@ -11,6 +12,8 @@ import 'package:flutter/material.dart'; import 'package:universal_html/html.dart' as html; class HtmlUtils { + static final random = Random(); + static const lineHeight100Percent = ( script: ''' document.querySelectorAll("*") @@ -422,27 +425,62 @@ class HtmlUtils { fileSizeSpan.textContent = formatFileSize(bytesJs.length);'''; } - static void openNewWindowByUrl( + static bool openNewWindowByUrl( String url, { int width = 800, int height = 600, + bool isFullScreen = false, + bool isCenter = true, } - ) async { + ) { try { + if (isFullScreen) { + final screenWidth = html.window.screen?.width; + final screenHeight = html.window.screen?.height; + + final options = 'width=$screenWidth,height=$screenHeight'; + + html.window.open(url, '_blank', options); + + html.Url.revokeObjectUrl(url); + return true; + } + final screenWidth = html.window.screen?.width ?? width; final screenHeight = html.window.screen?.height ?? height; - final left = (screenWidth - width) ~/ 2; - final top = (screenHeight - height) ~/ 2; + int left, top; + + if (isCenter) { + left = (screenWidth - width) ~/ 2; + top = (screenHeight - height) ~/ 2; + } else { + left = random.nextInt(screenWidth ~/ 2); + top = random.nextInt(screenHeight ~/ 2); + } final options = 'width=$width,height=$height,top=$top,left=$left'; html.window.open(url, '_blank', options); html.Url.revokeObjectUrl(url); + + return true; } catch (e) { logError('AppUtils::openNewWindowByUrl:Exception = $e'); + return false; + } + } + + static void setWindowBrowserTitle(String title) { + try { + final titleElements = html.window.document.getElementsByTagName('title'); + if (titleElements.isNotEmpty) { + titleElements.first.text = title; + } + } catch (e) { + logError('AppUtils::setWindowBrowserTitle:Exception = $e'); } } } diff --git a/core/lib/utils/preview_eml_file_utils.dart b/core/lib/utils/preview_eml_file_utils.dart index 78cc2f502..992e1daa5 100644 --- a/core/lib/utils/preview_eml_file_utils.dart +++ b/core/lib/utils/preview_eml_file_utils.dart @@ -1,5 +1,5 @@ -import 'package:core/data/model/print_attachment.dart'; +import 'package:core/data/model/preview_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'; @@ -26,7 +26,7 @@ class PreviewEmlFileUtils { String? dateTime, String? attachmentIcon, String? emailContent, - List? listAttachment, + List? listAttachment, }) { try { return Element.html(''' @@ -83,23 +83,38 @@ class PreviewEmlFileUtils { } } - String _createAttachmentHtmlTag(PrintAttachment printAttachment) { + String _createAttachmentHtmlTag(PreviewAttachment previewAttachment) { return ''' -
-
- attachment-icon -
-
-
${printAttachment.name}
-
${printAttachment.size}
-
-
+ ${previewAttachment.link?.isNotEmpty == true + ? ''' + +
+ attachment-icon +
+
+
${previewAttachment.name}
+
${previewAttachment.size}
+
+
+ ''' + : ''' +
+
+ attachment-icon +
+
+
${previewAttachment.name}
+
${previewAttachment.size}
+
+
+ '''} + '''; } String _createAttachmentsElement({ required String titleAttachment, - required List listAttachment + required List listAttachment }) { try { return ''' @@ -137,7 +152,7 @@ class PreviewEmlFileUtils { String? ccAddress, String? bccAddress, String? replyToAddress, - List? listAttachment, + List? listAttachment, }) { Document document = parse(HtmlUtils.createTemplateHtmlDocument(title: '$subject - $userName')); diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index 02bb9e822..ae28c6f77 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -26,6 +26,7 @@ import 'package:tmail_ui_user/features/email/domain/model/event_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart'; @@ -191,13 +192,13 @@ abstract class EmailDataSource { Future generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest); - Future sharePreviewEmailEMLContent(String keyStored, String previewEMLContent); + Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer); - Future getPreviewEmailEMLContentShared(String keyStored); + Future getPreviewEmailEMLContentShared(String keyStored); Future removePreviewEmailEMLContentShared(String keyStored); - Future storePreviewEMLContentToSessionStorage(String keyStored, String content); + Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer); - Future getPreviewEMLContentInMemory(String keyStored); + Future getPreviewEMLContentInMemory(String keyStored); } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_datasource_impl.dart index f04a59dc6..eca0d4c57 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:core/data/model/print_attachment.dart'; +import 'package:core/data/model/preview_attachment.dart'; import 'package:core/data/network/download/downloaded_response.dart'; import 'package:core/domain/extensions/datetime_extension.dart'; import 'package:core/presentation/extensions/html_extension.dart'; @@ -37,10 +37,13 @@ import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_reques import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart'; import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/send_email_exception_thrower.dart'; +import 'package:tmail_ui_user/main/routes/app_routes.dart'; +import 'package:tmail_ui_user/main/routes/route_utils.dart'; class EmailDataSourceImpl extends EmailDataSource { @@ -442,20 +445,28 @@ class EmailDataSourceImpl extends EmailDataSource { pattern: email.sentAt?.value.toLocal().toPatternForPrinting(locale) ); - final List listPrintAttachment = []; + final List listPreviewAttachment = []; if (listAttachments.isNotEmpty) { await Future.forEach(listAttachments, (attachment) async { final iconBase64Data = await _fileUtils.convertImageAssetToBase64( attachment.getIcon(_imagePaths)); - final printAttachment = PrintAttachment( + final link = attachment.isEMLFile + ? RouteUtils.createUrlWebLocationBar( + AppRoutes.emailEMLPreviewer, + previewId: attachment.blobId?.value, + ).toString() + : null; + + final previewAttachment = PreviewAttachment( iconBase64Data: iconBase64Data, name: attachment.name.escapeLtGtHtmlString(), size: filesize(attachment.size?.value), + link: link, ); - listPrintAttachment.add(printAttachment); + listPreviewAttachment.add(previewAttachment); }); } @@ -483,7 +494,7 @@ class EmailDataSourceImpl extends EmailDataSource { ccAddress: email.cc?.listEmailAddressToString(isFullEmailAddress: true), bccAddress: email.bcc?.listEmailAddressToString(isFullEmailAddress: true), replyToAddress: email.replyTo?.listEmailAddressToString(isFullEmailAddress: true), - listAttachment: listPrintAttachment, + listAttachment: listPreviewAttachment, ); return previewEmlHtmlDocument; @@ -515,17 +526,17 @@ class EmailDataSourceImpl extends EmailDataSource { } @override - Future sharePreviewEmailEMLContent(String keyStored, String previewEMLContent) { + Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { throw UnimplementedError(); } @override - Future getPreviewEmailEMLContentShared(String keyStored) { + Future getPreviewEmailEMLContentShared(String keyStored) { throw UnimplementedError(); } @override - Future storePreviewEMLContentToSessionStorage(String keyStored, String content) { + Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { throw UnimplementedError(); } @@ -535,7 +546,7 @@ class EmailDataSourceImpl extends EmailDataSource { } @override - Future getPreviewEMLContentInMemory(String keyStored) { + Future getPreviewEMLContentInMemory(String keyStored) { throw UnimplementedError(); } } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart index 3cc3d4bad..afe81c91f 100644 --- a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart @@ -36,6 +36,7 @@ import 'package:tmail_ui_user/features/email/domain/model/event_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/offline_mode/extensions/list_sending_email_hive_cache_extension.dart'; import 'package:tmail_ui_user/features/offline_mode/extensions/sending_email_hive_cache_extension.dart'; @@ -539,12 +540,12 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource { } @override - Future sharePreviewEmailEMLContent(String keyStored, String previewEMLContent) { + Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { throw UnimplementedError(); } @override - Future getPreviewEmailEMLContentShared(String keyStored) { + Future getPreviewEmailEMLContentShared(String keyStored) { throw UnimplementedError(); } @@ -554,12 +555,12 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource { } @override - Future storePreviewEMLContentToSessionStorage(String keyStored, String content) { + Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { throw UnimplementedError(); } @override - Future getPreviewEMLContentInMemory(String keyStored) { + Future getPreviewEMLContentInMemory(String keyStored) { throw UnimplementedError(); } } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart index 6ebb40ef5..37a9d62fa 100644 --- a/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:typed_data'; import 'package:core/data/network/download/downloaded_response.dart'; @@ -29,6 +30,7 @@ import 'package:tmail_ui_user/features/email/domain/model/event_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart'; import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; @@ -164,9 +166,9 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource { } @override - Future sharePreviewEmailEMLContent(String keyStored, String previewEMLContent) { + Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { return Future.sync(() async { - return _localStorageManager.save(keyStored, previewEMLContent); + return _localStorageManager.save(keyStored, jsonEncode(emlPreviewer.toJson())); }).catchError(_exceptionThrower.throwException); } @@ -216,14 +218,15 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource { } @override - Future getPreviewEmailEMLContentShared(String keyStored) { + Future getPreviewEmailEMLContentShared(String keyStored) { return Future.sync(() async { - return _localStorageManager.get(keyStored); + final data = _localStorageManager.get(keyStored); + return EMLPreviewer.fromJson(jsonDecode(data)); }).catchError(_exceptionThrower.throwException); } @override - Future storePreviewEMLContentToSessionStorage(String keyStored, String content) { + Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { throw UnimplementedError(); } @@ -235,7 +238,7 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource { } @override - Future getPreviewEMLContentInMemory(String keyStored) { + Future getPreviewEMLContentInMemory(String keyStored) { throw UnimplementedError(); } } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart index 6cb872596..568b65403 100644 --- a/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:typed_data'; import 'package:core/data/network/download/downloaded_response.dart'; @@ -29,6 +30,7 @@ import 'package:tmail_ui_user/features/email/domain/model/event_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart'; import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; @@ -164,7 +166,7 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource { } @override - Future sharePreviewEmailEMLContent(String keyStored, String previewEMLContent) { + Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { throw UnimplementedError(); } @@ -214,14 +216,14 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource { } @override - Future getPreviewEmailEMLContentShared(String keyStored) { + Future getPreviewEmailEMLContentShared(String keyStored) { throw UnimplementedError(); } @override - Future storePreviewEMLContentToSessionStorage(String keyStored, String content) { + Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { return Future.sync(() async { - return _sessionStorageManager.save(keyStored, content); + return _sessionStorageManager.save(keyStored, jsonEncode(emlPreviewer.toJson())); }).catchError(_exceptionThrower.throwException); } @@ -231,9 +233,10 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource { } @override - Future getPreviewEMLContentInMemory(String keyStored) { + Future getPreviewEMLContentInMemory(String keyStored) { return Future.sync(() async { - return _sessionStorageManager.get(keyStored); + final data = _sessionStorageManager.get(keyStored); + return EMLPreviewer.fromJson(jsonDecode(data)); }).catchError(_exceptionThrower.throwException); } } \ No newline at end of file diff --git a/lib/features/email/data/repository/email_repository_impl.dart b/lib/features/email/data/repository/email_repository_impl.dart index e87e54289..a2601dd25 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -35,6 +35,7 @@ import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_reques import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; @@ -496,12 +497,12 @@ class EmailRepositoryImpl extends EmailRepository { } @override - Future sharePreviewEmailEMLContent(String keyStored, String previewEMLContent) { - return emailDataSource[DataSourceType.local]!.sharePreviewEmailEMLContent(keyStored, previewEMLContent); + Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { + return emailDataSource[DataSourceType.local]!.sharePreviewEmailEMLContent(keyStored, emlPreviewer); } @override - Future getPreviewEmailEMLContentShared(String keyStored) { + Future getPreviewEmailEMLContentShared(String keyStored) { return emailDataSource[DataSourceType.local]!.getPreviewEmailEMLContentShared(keyStored); } @@ -511,12 +512,12 @@ class EmailRepositoryImpl extends EmailRepository { } @override - Future movePreviewEMLContentFromPersistentToMemory(String keyStored, String content) { - return emailDataSource[DataSourceType.session]!.storePreviewEMLContentToSessionStorage(keyStored, content); + Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { + return emailDataSource[DataSourceType.session]!.storePreviewEMLContentToSessionStorage(keyStored, emlPreviewer); } @override - Future getPreviewEMLContentInMemory(String keyStored) { + Future getPreviewEMLContentInMemory(String keyStored) { return emailDataSource[DataSourceType.session]!.getPreviewEMLContentInMemory(keyStored); } } \ No newline at end of file diff --git a/lib/features/email/domain/model/preview_email_eml_request.dart b/lib/features/email/domain/model/preview_email_eml_request.dart index e790b3211..3830d1739 100644 --- a/lib/features/email/domain/model/preview_email_eml_request.dart +++ b/lib/features/email/domain/model/preview_email_eml_request.dart @@ -14,6 +14,7 @@ class PreviewEmailEMLRequest with EquatableMixin { final Locale locale; final AppLocalizations appLocalizations; final String baseDownloadUrl; + final bool isShared; PreviewEmailEMLRequest({ required this.accountId, @@ -23,10 +24,13 @@ class PreviewEmailEMLRequest with EquatableMixin { required this.locale, required this.appLocalizations, required this.baseDownloadUrl, + this.isShared = true, }); String get keyStored => blobId.value; + String get title => '${email.subject?.trim().isNotEmpty == true ? email.subject : appLocalizations.app_name} - ${userName.value}'; + @override List get props => [ accountId, @@ -36,5 +40,6 @@ class PreviewEmailEMLRequest with EquatableMixin { locale, appLocalizations, baseDownloadUrl, + isShared, ]; } \ No newline at end of file diff --git a/lib/features/email/domain/repository/email_repository.dart b/lib/features/email/domain/repository/email_repository.dart index c4d95fa84..e8a3f05db 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -29,6 +29,7 @@ import 'package:tmail_ui_user/features/email/domain/model/event_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; abstract class EmailRepository { @@ -185,13 +186,13 @@ abstract class EmailRepository { Future generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest); - Future sharePreviewEmailEMLContent(String keyStored, String previewEMLContent); + Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer); - Future getPreviewEmailEMLContentShared(String keyStored); + Future getPreviewEmailEMLContentShared(String keyStored); Future removePreviewEmailEMLContentShared(String keyStored); - Future movePreviewEMLContentFromPersistentToMemory(String keyStored, String content); + Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer); - Future getPreviewEMLContentInMemory(String keyStored); + Future getPreviewEMLContentInMemory(String keyStored); } \ No newline at end of file diff --git a/lib/features/email/domain/state/get_preview_email_eml_content_shared_state.dart b/lib/features/email/domain/state/get_preview_email_eml_content_shared_state.dart index b6ce43a18..709437395 100644 --- a/lib/features/email/domain/state/get_preview_email_eml_content_shared_state.dart +++ b/lib/features/email/domain/state/get_preview_email_eml_content_shared_state.dart @@ -1,17 +1,18 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; class GettingPreviewEmailEMLContentShared extends LoadingState {} class GetPreviewEmailEMLContentSharedSuccess extends UIState { final String keyStored; - final String previewEMLContent; + final EMLPreviewer emlPreviewer; - GetPreviewEmailEMLContentSharedSuccess(this.keyStored, this.previewEMLContent); + GetPreviewEmailEMLContentSharedSuccess(this.keyStored, this.emlPreviewer); @override - List get props => [keyStored, previewEMLContent]; + List get props => [keyStored, emlPreviewer]; } class GetPreviewEmailEMLContentSharedFailure extends FeatureFailure { diff --git a/lib/features/email/domain/state/get_preview_eml_content_in_memory_state.dart b/lib/features/email/domain/state/get_preview_eml_content_in_memory_state.dart index b36ea9bb9..b892a7131 100644 --- a/lib/features/email/domain/state/get_preview_eml_content_in_memory_state.dart +++ b/lib/features/email/domain/state/get_preview_eml_content_in_memory_state.dart @@ -1,18 +1,26 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; class GettingPreviewEMLContentInMemory extends LoadingState {} class GetPreviewEMLContentInMemorySuccess extends UIState { - final String previewEMLContent; + final EMLPreviewer emlPreviewer; - GetPreviewEMLContentInMemorySuccess(this.previewEMLContent); + GetPreviewEMLContentInMemorySuccess(this.emlPreviewer); @override - List get props => [previewEMLContent]; + List get props => [emlPreviewer]; } class GetPreviewEMLContentInMemoryFailure extends FeatureFailure { + final String? keyStored; - GetPreviewEMLContentInMemoryFailure(dynamic exception) : super(exception: exception); + GetPreviewEMLContentInMemoryFailure({ + this.keyStored, + dynamic exception, + }) : super(exception: exception); + + @override + List get props => [keyStored, exception]; } \ No newline at end of file diff --git a/lib/features/email/domain/state/preview_email_from_eml_file_state.dart b/lib/features/email/domain/state/preview_email_from_eml_file_state.dart index aa6a9b115..715821b20 100644 --- a/lib/features/email/domain/state/preview_email_from_eml_file_state.dart +++ b/lib/features/email/domain/state/preview_email_from_eml_file_state.dart @@ -1,16 +1,18 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; class PreviewingEmailFromEmlFile extends LoadingState {} class PreviewEmailFromEmlFileSuccess extends UIState { final String storeKey; + final EMLPreviewer emlPreviewer; - PreviewEmailFromEmlFileSuccess(this.storeKey); + PreviewEmailFromEmlFileSuccess(this.storeKey, this.emlPreviewer); @override - List get props => [storeKey]; + List get props => [storeKey, emlPreviewer]; } class PreviewEmailFromEmlFileFailure extends FeatureFailure { diff --git a/lib/features/email/domain/usecases/get_preview_eml_content_in_memory_interactor.dart b/lib/features/email/domain/usecases/get_preview_eml_content_in_memory_interactor.dart index 812b01c64..77eb82d55 100644 --- a/lib/features/email/domain/usecases/get_preview_eml_content_in_memory_interactor.dart +++ b/lib/features/email/domain/usecases/get_preview_eml_content_in_memory_interactor.dart @@ -12,10 +12,13 @@ class GetPreviewEmlContentInMemoryInteractor { Stream> execute(String keyStored) async* { try { yield Right(GettingPreviewEMLContentInMemory()); - final previewEMLContent = await _emailRepository.getPreviewEMLContentInMemory(keyStored); - yield Right(GetPreviewEMLContentInMemorySuccess(previewEMLContent)); + final emlPreviewer = await _emailRepository.getPreviewEMLContentInMemory(keyStored); + yield Right(GetPreviewEMLContentInMemorySuccess(emlPreviewer)); } catch (e) { - yield Left(GetPreviewEMLContentInMemoryFailure(e)); + yield Left(GetPreviewEMLContentInMemoryFailure( + keyStored: keyStored, + exception: e, + )); } } } \ No newline at end of file diff --git a/lib/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart b/lib/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart index 6ed36c19c..7cfd99363 100644 --- a/lib/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart +++ b/lib/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart @@ -3,18 +3,19 @@ import 'package:core/presentation/state/success.dart'; import 'package:dartz/dartz.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/move_preview_eml_content_from_persistent_to_memory_state.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; class MovePreviewEmlContentFromPersistentToMemoryInteractor { final EmailRepository _emailRepository; const MovePreviewEmlContentFromPersistentToMemoryInteractor(this._emailRepository); - Stream> execute(String keyStored, String content) async* { + Stream> execute(String keyStored, EMLPreviewer emlPreviewer) async* { try { yield Right(MovingPreviewEmailEMLContentFromPersistentToMemory()); await Future.wait([ _emailRepository.removePreviewEmailEMLContentShared(keyStored), - _emailRepository.movePreviewEMLContentFromPersistentToMemory(keyStored, content), + _emailRepository.storePreviewEMLContentToSessionStorage(keyStored, emlPreviewer), ]); yield Right(MovePreviewEmailEMLContentFromPersistentToMemorySuccess()); } catch (e) { diff --git a/lib/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart b/lib/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart index c406bb777..c6b536eb5 100644 --- a/lib/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart +++ b/lib/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart @@ -4,6 +4,7 @@ import 'package:dartz/dartz.dart'; import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/preview_email_from_eml_file_state.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; class PreviewEmailFromEmlFileInteractor { final EmailRepository _emailRepository; @@ -21,9 +22,18 @@ class PreviewEmailFromEmlFileInteractor { final keyStored = previewEmailEMLRequest.keyStored; - await _emailRepository.sharePreviewEmailEMLContent(keyStored, previewEMLContent); + final emlPreviewer = EMLPreviewer( + title: previewEmailEMLRequest.title, + content: previewEMLContent, + ); - yield Right(PreviewEmailFromEmlFileSuccess(keyStored)); + if (previewEmailEMLRequest.isShared) { + await _emailRepository.sharePreviewEmailEMLContent(keyStored, emlPreviewer); + } else { + await _emailRepository.storePreviewEMLContentToSessionStorage(keyStored, emlPreviewer); + } + + yield Right(PreviewEmailFromEmlFileSuccess(keyStored, emlPreviewer)); } catch (e) { yield Left(PreviewEmailFromEmlFileFailure(e)); } diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 3ba95fd01..607f98096 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -2016,12 +2016,16 @@ class SingleEmailController extends BaseController with AppLoaderMixin { SmartDialog.dismiss(); if (PlatformInfo.isWeb) { - HtmlUtils.openNewWindowByUrl( + bool isOpen = HtmlUtils.openNewWindowByUrl( RouteUtils.createUrlWebLocationBar( AppRoutes.emailEMLPreviewer, previewId: success.storeKey ).toString(), ); + + if (!isOpen) { + toastManager.showMessageFailure(PreviewEmailFromEmlFileFailure(CannotOpenNewWindowException())); + } } } diff --git a/lib/features/email/presentation/model/eml_previewer.dart b/lib/features/email/presentation/model/eml_previewer.dart new file mode 100644 index 000000000..d358906a8 --- /dev/null +++ b/lib/features/email/presentation/model/eml_previewer.dart @@ -0,0 +1,20 @@ + +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'eml_previewer.g.dart'; + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class EMLPreviewer with EquatableMixin { + final String title; + final String content; + + EMLPreviewer({required this.title, required this.content}); + + factory EMLPreviewer.fromJson(Map json) => _$EMLPreviewerFromJson(json); + + Map toJson() => _$EMLPreviewerToJson(this); + + @override + List get props => [title, content]; +} \ No newline at end of file diff --git a/lib/features/email_previewer/email_previewer_bindings.dart b/lib/features/email_previewer/email_previewer_bindings.dart index ac4924d83..41dea591b 100644 --- a/lib/features/email_previewer/email_previewer_bindings.dart +++ b/lib/features/email_previewer/email_previewer_bindings.dart @@ -23,6 +23,8 @@ import 'package:tmail_ui_user/features/email/domain/repository/email_repository. import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_eml_content_in_memory_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/remove_preview_email_eml_content_shared_interactor.dart'; import 'package:tmail_ui_user/features/email_previewer/email_previewer_controller.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; @@ -46,7 +48,9 @@ class EmailPreviewerBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), - Get.find())); + Get.find(), + Get.find(), + Get.find())); } @override @@ -102,6 +106,10 @@ class EmailPreviewerBindings extends BaseBindings { Get.find())); Get.lazyPut(() => GetPreviewEmlContentInMemoryInteractor( Get.find())); + Get.lazyPut(() => ParseEmailByBlobIdInteractor( + Get.find())); + Get.lazyPut(() => PreviewEmailFromEmlFileInteractor( + Get.find())); } @override diff --git a/lib/features/email_previewer/email_previewer_controller.dart b/lib/features/email_previewer/email_previewer_controller.dart index 3c075c9fc..3f900a81a 100644 --- a/lib/features/email_previewer/email_previewer_controller.dart +++ b/lib/features/email_previewer/email_previewer_controller.dart @@ -1,33 +1,60 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/html/html_utils.dart'; import 'package:dartz/dartz.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart'; import 'package:tmail_ui_user/features/caching/exceptions/local_storage_exception.dart'; import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart'; +import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_preview_email_eml_content_shared_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_preview_eml_content_in_memory_state.dart'; +import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart'; +import 'package:tmail_ui_user/features/email/domain/state/preview_email_from_eml_file_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_eml_content_in_memory_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/remove_preview_email_eml_content_shared_interactor.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; +import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; +import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_state.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/routes/route_utils.dart'; -class EmailPreviewerController extends BaseController { +class EmailPreviewerController extends ReloadableController { final GetPreviewEmailEMLContentSharedInteractor _getPreviewEmailEMLContentSharedInteractor; final MovePreviewEmlContentFromPersistentToMemoryInteractor _movePreviewEmlContentFromPersistentToMemoryInteractor; final RemovePreviewEmailEmlContentSharedInteractor _removePreviewEmailEmlContentSharedInteractor; final GetPreviewEmlContentInMemoryInteractor _getPreviewEmlContentInMemoryInteractor; + final ParseEmailByBlobIdInteractor _parseEmailByBlobIdInteractor; + final PreviewEmailFromEmlFileInteractor _previewEmailFromEmlFileInteractor; final emlContentViewState = Rx>(Right(UIState.idle)); + Session? _session; + AccountId? _accountId; + String? _keyStored; + EmailPreviewerController( this._getPreviewEmailEMLContentSharedInteractor, this._movePreviewEmlContentFromPersistentToMemoryInteractor, this._removePreviewEmailEmlContentSharedInteractor, this._getPreviewEmlContentInMemoryInteractor, + this._parseEmailByBlobIdInteractor, + this._previewEmailFromEmlFileInteractor, ); @override @@ -42,17 +69,33 @@ class EmailPreviewerController extends BaseController { _getPreviewEmailEMLContentShared(); } + @override + void onClose() { + _accountId = null; + _session = null; + _keyStored = null; + super.onClose(); + } + @override void handleSuccessViewState(Success success) { if (success is GettingPreviewEmailEMLContentShared || - success is GettingPreviewEMLContentInMemory || - success is GetPreviewEMLContentInMemorySuccess) { + success is GettingPreviewEMLContentInMemory) { emlContentViewState.value = Right(success); + } else if (success is GetPreviewEMLContentInMemorySuccess) { + emlContentViewState.value = Right(success); + _updateWindowBrowserTitle(success.emlPreviewer.title); } else if (success is GetPreviewEmailEMLContentSharedSuccess) { emlContentViewState.value = Right(success); + _updateWindowBrowserTitle(success.emlPreviewer.title); _movePreviewEmlContentFromPersistentToMemory( success.keyStored, - success.previewEMLContent); + success.emlPreviewer); + } else if (success is ParseEmailByBlobIdSuccess) { + _handleParseEmailByBlobIdSuccess(success); + } else if (success is PreviewEmailFromEmlFileSuccess) { + emlContentViewState.value = Right(success); + _updateWindowBrowserTitle(success.emlPreviewer.title); } else { super.handleSuccessViewState(success); } @@ -61,22 +104,46 @@ class EmailPreviewerController extends BaseController { @override void handleFailureViewState(Failure failure) { if (failure is GetPreviewEmailEMLContentSharedFailure) { - emlContentViewState.value = Left(failure); - - if (failure.keyStored == null) return; + if (failure.keyStored == null) { + emlContentViewState.value = Left(failure); + return; + } if (failure.exception is NotFoundDataWithThisKeyException) { _getPreviewEMLContentInMemory(failure.keyStored!); } else { + emlContentViewState.value = Left(failure); _removePreviewEmlContentShared(failure.keyStored!); } } else if (failure is GetPreviewEMLContentInMemoryFailure) { + if (failure.exception is NotFoundDataWithThisKeyException) { + _keyStored = failure.keyStored; + reload(); + } else { + emlContentViewState.value = Left(failure); + } + } else if (failure is GetCredentialFailure || + failure is GetStoredTokenOidcFailure || + failure is GetAuthenticatedAccountFailure || + failure is GetSessionFailure || + failure is ParseEmailByBlobIdFailure || + failure is PreviewEmailFromEmlFileFailure) { emlContentViewState.value = Left(failure); - } else { + }else { super.handleFailureViewState(failure); } } + @override + void handleReloaded(Session session) { + _session = session; + _accountId = session.accountId; + + if (_keyStored != null) { + _parseEmailByBlobId(session.accountId, _keyStored!); + } + } + void _getPreviewEmailEMLContentShared() { final parameters = Get.parameters; @@ -97,12 +164,12 @@ class EmailPreviewerController extends BaseController { void _movePreviewEmlContentFromPersistentToMemory( String keyStored, - String content, + EMLPreviewer emlPreviewer, ) { consumeState( _movePreviewEmlContentFromPersistentToMemoryInteractor.execute( keyStored, - content, + emlPreviewer, ), ); } @@ -114,4 +181,72 @@ class EmailPreviewerController extends BaseController { void _getPreviewEMLContentInMemory(String keyStored) { consumeState(_getPreviewEmlContentInMemoryInteractor.execute(keyStored)); } + + Future onClickHyperLink(Uri? uri) async { + if (uri == null) return; + + bool isEMlPreview = uri.toString().startsWith(RouteUtils.emailEMLPreviewerRoutePath); + final url = _standardizeURL(uri.toString()); + + bool isOpen = HtmlUtils.openNewWindowByUrl( + url, + isFullScreen: !isEMlPreview, + isCenter: false, + ); + + if (!isOpen && currentOverlayContext != null && currentContext != null) { + appToast.showToastErrorMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).cannotOpenNewWindow); + } + } + + String _standardizeURL(String url) { + if (url.startsWith('${RouteUtils.mailtoPrefix}:')) { + final mailtoLink = '${RouteUtils.baseOriginUrl}/mailto?uri=$url'; + return mailtoLink; + } + return url; + } + + void _parseEmailByBlobId(AccountId accountId, String blobId) { + consumeState(_parseEmailByBlobIdInteractor.execute( + accountId, + Id(blobId), + )); + } + + void _handleParseEmailByBlobIdSuccess(ParseEmailByBlobIdSuccess success) { + if (_session == null) { + consumeState(Stream.value(Left(PreviewEmailFromEmlFileFailure(NotFoundSessionException())))); + return; + } + + if (_accountId == null) { + consumeState(Stream.value(Left(PreviewEmailFromEmlFileFailure(NotFoundAccountIdException())))); + return; + } + + if (currentContext == null) { + consumeState(Stream.value(Left(PreviewEmailFromEmlFileFailure(NotFoundContextException())))); + return; + } + + consumeState(_previewEmailFromEmlFileInteractor.execute( + PreviewEmailEMLRequest( + accountId: _accountId!, + userName: _session!.username, + blobId: success.blobId, + email: success.email, + locale: Localizations.localeOf(currentContext!), + appLocalizations: AppLocalizations.of(currentContext!), + baseDownloadUrl: _session!.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl), + isShared: false, + ), + )); + } + + void _updateWindowBrowserTitle(String title) { + HtmlUtils.setWindowBrowserTitle(title); + } } \ No newline at end of file diff --git a/lib/features/email_previewer/email_previewer_view.dart b/lib/features/email_previewer/email_previewer_view.dart index d04032530..9920daaf4 100644 --- a/lib/features/email_previewer/email_previewer_view.dart +++ b/lib/features/email_previewer/email_previewer_view.dart @@ -5,8 +5,10 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_preview_email_eml_content_shared_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_preview_eml_content_in_memory_state.dart'; +import 'package:tmail_ui_user/features/email/domain/state/preview_email_from_eml_file_state.dart'; import 'package:tmail_ui_user/features/email_previewer/email_previewer_controller.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/utils/app_utils.dart'; class EmailPreviewerView extends GetWidget { @@ -27,13 +29,25 @@ class EmailPreviewerView extends GetWidget { return HtmlContentViewerOnWeb( widthContent: context.width, heightContent: context.height, - contentHtml: success.previewEMLContent, + contentHtml: success.emlPreviewer.content, + direction: AppUtils.getCurrentDirection(context), + onClickHyperLinkAction: controller.onClickHyperLink, ); } else if (success is GetPreviewEMLContentInMemorySuccess) { return HtmlContentViewerOnWeb( widthContent: context.width, heightContent: context.height, - contentHtml: success.previewEMLContent, + contentHtml: success.emlPreviewer.content, + direction: AppUtils.getCurrentDirection(context), + onClickHyperLinkAction: controller.onClickHyperLink, + ); + } else if (success is PreviewEmailFromEmlFileSuccess) { + return HtmlContentViewerOnWeb( + widthContent: context.width, + heightContent: context.height, + contentHtml: success.emlPreviewer.content, + direction: AppUtils.getCurrentDirection(context), + onClickHyperLinkAction: controller.onClickHyperLink, ); } else { return const Center( diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index d531aa332..cefbf8b58 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2025-01-10T23:14:09.007935", + "@@last_modified": "2025-01-13T15:53:39.843822", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -4101,5 +4101,11 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "cannotOpenNewWindow": "Cannot open new window", + "@cannotOpenNewWindow": { + "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 1531225ba..1796a4c42 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -4307,4 +4307,11 @@ class AppLocalizations { name: 'previewEmailFromEMLFileFailed', ); } + + String get cannotOpenNewWindow { + return Intl.message( + 'Cannot open new window', + name: 'cannotOpenNewWindow', + ); + } } diff --git a/lib/main/routes/route_utils.dart b/lib/main/routes/route_utils.dart index 2188dd3e3..2493cfb70 100644 --- a/lib/main/routes/route_utils.dart +++ b/lib/main/routes/route_utils.dart @@ -252,4 +252,7 @@ abstract class RouteUtils { || navigationRouter.subject?.isNotEmpty == true || navigationRouter.body?.isNotEmpty == true; } + + static String get emailEMLPreviewerRoutePath => + _createEmailEMLPreviewerServicePath('$baseOriginUrl${AppRoutes.emailEMLPreviewer}').path; } \ No newline at end of file diff --git a/lib/main/utils/toast_manager.dart b/lib/main/utils/toast_manager.dart index 8f15f06e7..2cd39b5cf 100644 --- a/lib/main/utils/toast_manager.dart +++ b/lib/main/utils/toast_manager.dart @@ -1,3 +1,4 @@ +import 'package:core/domain/exceptions/web_session_exception.dart'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/utils/app_toast.dart'; import 'package:core/utils/app_logger.dart'; @@ -89,7 +90,11 @@ class ToastManager { } else if (failure is ParseEmailByBlobIdFailure) { message = AppLocalizations.of(currentContext!).parseEmailByBlobIdFailed; } else if (failure is PreviewEmailFromEmlFileFailure) { - message = AppLocalizations.of(currentContext!).previewEmailFromEMLFileFailed; + if (failure.exception is CannotOpenNewWindowException) { + message = AppLocalizations.of(currentContext!).cannotOpenNewWindow; + } else { + message = AppLocalizations.of(currentContext!).previewEmailFromEMLFileFailed; + } } if (message?.isNotEmpty == true) {