From 561f8041ea4e7988f4552e6c6c46ae896dfb3401 Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 14 Apr 2022 11:19:47 +0700 Subject: [PATCH] Fix displaying the external url of the link tag --- core/lib/core.dart | 4 + .../base/dom_transformer.dart | 6 +- .../base/text_transformer.dart | 4 +- ...art => add_tooltip_link_transformers.dart} | 22 +++--- .../dom/blockquoted_transformers.dart | 6 +- .../dom/image_transformers.dart | 8 +- .../dom/meta_transformers.dart | 50 ------------- .../dom/script_transformers.dart | 6 +- .../utils/html_transformer/html_template.dart | 64 ++++++++++++++++ .../html_transformer/html_transform.dart | 14 ++-- .../message_content_transformer.dart | 21 ++++-- .../text/convert_tags_text_transformer.dart | 13 ---- .../text/linebreak_text_transformer.dart | 15 ---- .../text/links_text_transformer.dart | 47 ------------ .../transform_configuration.dart | 28 ++----- .../html_content_viewer_on_web_widget.dart | 73 +++---------------- .../html_content_viewer_widget.dart | 45 +----------- .../data/datasource/html_datasource.dart | 2 + .../datasource_impl/html_datasource_impl.dart | 9 +++ .../email/data/local/html_analyzer.dart | 17 ++++- .../repository/email_repository_impl.dart | 7 ++ .../domain/repository/email_repository.dart | 2 + .../domain/state/get_email_content_state.dart | 5 +- .../get_email_content_interactor.dart | 6 +- .../email/presentation/email_controller.dart | 7 +- 25 files changed, 182 insertions(+), 299 deletions(-) rename core/lib/presentation/utils/html_transformer/dom/{link_transformers.dart => add_tooltip_link_transformers.dart} (55%) delete mode 100644 core/lib/presentation/utils/html_transformer/dom/meta_transformers.dart create mode 100644 core/lib/presentation/utils/html_transformer/html_template.dart delete mode 100644 core/lib/presentation/utils/html_transformer/text/convert_tags_text_transformer.dart delete mode 100644 core/lib/presentation/utils/html_transformer/text/linebreak_text_transformer.dart delete mode 100644 core/lib/presentation/utils/html_transformer/text/links_text_transformer.dart diff --git a/core/lib/core.dart b/core/lib/core.dart index 494c63304..f44c16fbb 100644 --- a/core/lib/core.dart +++ b/core/lib/core.dart @@ -20,6 +20,10 @@ export 'presentation/utils/responsive_utils.dart'; export 'presentation/utils/keyboard_utils.dart'; export 'presentation/utils/style_utils.dart'; export 'presentation/utils/app_toast.dart'; +export 'presentation/utils/html_transformer/html_template.dart'; +export 'presentation/utils/html_transformer/html_transform.dart'; +export 'presentation/utils/html_transformer/transform_configuration.dart'; +export 'presentation/utils/html_transformer/dom/add_tooltip_link_transformers.dart'; export 'data/utils/device_manager.dart'; export 'utils/app_logger.dart'; diff --git a/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart b/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart index 7a137f78c..ff1c2a55b 100644 --- a/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart +++ b/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart @@ -13,8 +13,10 @@ abstract class DomTransformer { Future process( Document document, String message, - Map? mapUrlDownloadCID, - DioClient dioClient + { + Map? mapUrlDownloadCID, + DioClient? dioClient, + } ); /// Adds a HEAD element if necessary diff --git a/core/lib/presentation/utils/html_transformer/base/text_transformer.dart b/core/lib/presentation/utils/html_transformer/base/text_transformer.dart index ac374b85e..af064550d 100644 --- a/core/lib/presentation/utils/html_transformer/base/text_transformer.dart +++ b/core/lib/presentation/utils/html_transformer/base/text_transformer.dart @@ -1,9 +1,7 @@ -import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; - /// Transforms plain text messages. abstract class TextTransformer { const TextTransformer(); - String transform(String text, String message, TransformConfiguration configuration); + String process(String text); } \ No newline at end of file diff --git a/core/lib/presentation/utils/html_transformer/dom/link_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/add_tooltip_link_transformers.dart similarity index 55% rename from core/lib/presentation/utils/html_transformer/dom/link_transformers.dart rename to core/lib/presentation/utils/html_transformer/dom/add_tooltip_link_transformers.dart index 2210f8f34..49d23af78 100644 --- a/core/lib/presentation/utils/html_transformer/dom/link_transformers.dart +++ b/core/lib/presentation/utils/html_transformer/dom/add_tooltip_link_transformers.dart @@ -1,40 +1,36 @@ import 'package:core/data/network/dio_client.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; -import 'package:core/utils/app_logger.dart'; import 'package:html/dom.dart'; -class LinkTransformer extends DomTransformer { +class AddTooltipLinkTransformer extends DomTransformer { - const LinkTransformer(); + const AddTooltipLinkTransformer(); @override Future process( Document document, String message, - Map? mapUrlDownloadCID, - DioClient dioClient + { + Map? mapUrlDownloadCID, + DioClient? dioClient + } ) async { - final linkElements = document.getElementsByTagName('a'); + final linkElements = document.querySelectorAll('a[href^="http"]'); await Future.wait(linkElements.map((linkElement) async { - linkElement.attributes['rel'] = 'noopener noreferrer'; _addToolTipWhenHoverLink(linkElement); })); } void _addToolTipWhenHoverLink(Element element) { - log('LinkTransformer::_addToolTipWhenHoverLink(): Before: ${element.outerHtml}'); final url = element.attributes['href']; final text = element.text; final children = element.children; - if (children.isEmpty && text.isNotEmpty && url?.isNotEmpty == true) { + if (children.isEmpty && text.isNotEmpty && url != null) { final innerHtml = element.innerHtml; final tagClass = element.attributes['class']; element.attributes['class'] = '$tagClass tooltip'; - if (text.isNotEmpty && url != null && url.isNotEmpty) { - element.innerHtml = innerHtml + textHasToolTip(url); - } - log('LinkTransformer::_addToolTipWhenHoverLink(): After: ${element.outerHtml}'); + element.innerHtml = innerHtml + textHasToolTip(url); } } diff --git a/core/lib/presentation/utils/html_transformer/dom/blockquoted_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/blockquoted_transformers.dart index 5dc494393..1fefccf8d 100644 --- a/core/lib/presentation/utils/html_transformer/dom/blockquoted_transformers.dart +++ b/core/lib/presentation/utils/html_transformer/dom/blockquoted_transformers.dart @@ -11,8 +11,10 @@ class BlockQuotedTransformer extends DomTransformer { Future process( Document document, String message, - Map? mapUrlDownloadCID, - DioClient dioClient + { + Map? mapUrlDownloadCID, + DioClient? dioClient + } ) async { final quotedElements = document.getElementsByTagName('blockquote'); await Future.wait(quotedElements.map((quotedElement) async { diff --git a/core/lib/presentation/utils/html_transformer/dom/image_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/image_transformers.dart index 971f58c88..8061ff118 100644 --- a/core/lib/presentation/utils/html_transformer/dom/image_transformers.dart +++ b/core/lib/presentation/utils/html_transformer/dom/image_transformers.dart @@ -14,8 +14,10 @@ class ImageTransformer extends DomTransformer { Future process( Document document, String message, - Map? mapUrlDownloadCID, - DioClient dioClient + { + Map? mapUrlDownloadCID, + DioClient? dioClient + } ) async { final imageElements = document.getElementsByTagName('img'); @@ -29,7 +31,7 @@ class ImageTransformer extends DomTransformer { ) { final cid = src.replaceFirst('cid:', '').trim(); final cidUrlDownload = mapUrlDownloadCID[cid]; - if (cidUrlDownload != null && cidUrlDownload.isNotEmpty) { + if (cidUrlDownload != null && cidUrlDownload.isNotEmpty && dioClient != null) { final imgBase64 = await loadAsyncNetworkImageToBase64(dioClient, cidUrlDownload); if (imgBase64 != null && imgBase64.isNotEmpty) { imageElement.attributes['src'] = 'data:image/jpeg;base64,$imgBase64'; diff --git a/core/lib/presentation/utils/html_transformer/dom/meta_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/meta_transformers.dart deleted file mode 100644 index 0fe0befc9..000000000 --- a/core/lib/presentation/utils/html_transformer/dom/meta_transformers.dart +++ /dev/null @@ -1,50 +0,0 @@ - -import 'package:core/data/network/dio_client.dart'; -import 'package:html/dom.dart'; -import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; - -class MetaTransformer extends DomTransformer { - - static final Element _viewPortMetaElement = Element.html( - ''); - static final Element _contentTypeMetaElement = Element.html( - ''); - - const MetaTransformer(); - - @override - Future process( - Document document, - String message, - Map? mapUrlDownloadCID, - DioClient dioClient - ) async { - final metaElements = document.getElementsByTagName('meta'); - var viewportNeedsToBeAdded = true; - var contentTypeNeedsToBeAdded = true; - - await Future.wait(metaElements.map((metaElement) async { - if (metaElement.attributes['name'] == 'viewport') { - viewportNeedsToBeAdded = false; - metaElement.attributes['content'] = 'width=device-width, initial-scale=1.0'; - } else if (metaElement.attributes['charset'] != null) { - metaElement.attributes['charset'] = 'utf-8'; - } else { - final httpEquiv = metaElement.attributes['http-equiv']; - if (httpEquiv != null && httpEquiv.toLowerCase() == 'content-type') { - contentTypeNeedsToBeAdded = false; - metaElement.attributes['content'] = 'text/html; charset=utf-8'; - } - } - })); - - if (contentTypeNeedsToBeAdded) { - ensureDocumentHeadIsAvailable(document); - document.head!.append(_contentTypeMetaElement); - } - if (viewportNeedsToBeAdded) { - ensureDocumentHeadIsAvailable(document); - document.head!.append(_viewPortMetaElement); - } - } -} \ No newline at end of file diff --git a/core/lib/presentation/utils/html_transformer/dom/script_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/script_transformers.dart index c26e91043..11be37cc3 100644 --- a/core/lib/presentation/utils/html_transformer/dom/script_transformers.dart +++ b/core/lib/presentation/utils/html_transformer/dom/script_transformers.dart @@ -11,8 +11,10 @@ class RemoveScriptTransformer extends DomTransformer { Future process( Document document, String message, - Map? mapUrlDownloadCID, - DioClient dioClient + { + Map? mapUrlDownloadCID, + DioClient? dioClient + } ) async { final scriptElements = document.getElementsByTagName('script'); await Future.wait(scriptElements.map((scriptElement) async { diff --git a/core/lib/presentation/utils/html_transformer/html_template.dart b/core/lib/presentation/utils/html_transformer/html_template.dart new file mode 100644 index 000000000..410a4c0de --- /dev/null +++ b/core/lib/presentation/utils/html_transformer/html_template.dart @@ -0,0 +1,64 @@ + +final tooltipLinkCss = ''' + .tooltip .tooltiptext { + visibility: hidden; + max-width: 400px; + background-color: black; + color: #fff; + text-align: center; + border-radius: 6px; + padding: 5px 8px 5px 8px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + position: absolute; + z-index: 1; + } + .tooltip:hover .tooltiptext { + visibility: visible; + } +'''; + +String generateHtml(String content, { + double? minHeight, + double? minWidth, + String? styleCSS, + String? javaScripts +}) { + return ''' + + + + + + + ${javaScripts ?? ''} + + +
${content}
+ + + '''; +} \ No newline at end of file diff --git a/core/lib/presentation/utils/html_transformer/html_transform.dart b/core/lib/presentation/utils/html_transformer/html_transform.dart index c7d881288..1ab5d8cc4 100644 --- a/core/lib/presentation/utils/html_transformer/html_transform.dart +++ b/core/lib/presentation/utils/html_transformer/html_transform.dart @@ -7,17 +7,17 @@ class HtmlTransform { final String _contentHtml; Map? mapUrlDownloadCID; - DioClient dioClient; + DioClient? dioClient; HtmlTransform( this._contentHtml, - this.dioClient, - this.mapUrlDownloadCID + { + this.mapUrlDownloadCID, + this.dioClient, + } ); /// Transforms this message to HTML code. - /// Optionally specify the [transformConfiguration] to control all aspects of the transformation - /// - in that case other parameters are ignored. Future transformToHtml({TransformConfiguration? transformConfiguration}) async { final document = await transformToDocument(transformConfiguration: transformConfiguration); return document.outerHtml; @@ -26,7 +26,7 @@ class HtmlTransform { /// Transforms this message to Document. Future transformToDocument({TransformConfiguration? transformConfiguration}) async { transformConfiguration ??= TransformConfiguration.create(); - final transformer = MessageContentTransformer(transformConfiguration, dioClient); - return await transformer.toDocument(_contentHtml, mapUrlDownloadCID); + final transformer = MessageContentTransformer(transformConfiguration); + return await transformer.toDocument(_contentHtml, mapUrlDownloadCID: mapUrlDownloadCID, dioClient: dioClient); } } \ No newline at end of file diff --git a/core/lib/presentation/utils/html_transformer/message_content_transformer.dart b/core/lib/presentation/utils/html_transformer/message_content_transformer.dart index 5bd4892d6..e103d918a 100644 --- a/core/lib/presentation/utils/html_transformer/message_content_transformer.dart +++ b/core/lib/presentation/utils/html_transformer/message_content_transformer.dart @@ -7,24 +7,29 @@ import 'package:html/parser.dart' show parse; class MessageContentTransformer { /// The configuration used for the transformation final TransformConfiguration configuration; - final DioClient dioClient; - MessageContentTransformer(this.configuration, this.dioClient); + MessageContentTransformer(this.configuration); Future transformDocument( Document document, String message, - Map? mapUrlDownloadCID, + { + Map? mapUrlDownloadCID, + DioClient? dioClient + } ) async { - await Future.wait(configuration.domTransformers.map((domTransformer) async { - await domTransformer.process(document, message, mapUrlDownloadCID, dioClient); - })); + await Future.wait([ + if (configuration.domTransformers.isNotEmpty) ...configuration.domTransformers.map((domTransformer) async => + domTransformer.process(document, message, mapUrlDownloadCID: mapUrlDownloadCID, dioClient: dioClient)), + if (configuration.textTransformers.isNotEmpty) ...configuration.textTransformers.map((textTransformer) async => + textTransformer.process(message)) + ]); } - Future toDocument(String message, Map? mapUrlDownloadCID) async { + Future toDocument(String message, {Map? mapUrlDownloadCID, DioClient? dioClient}) async { var html = message; final document = parse(html); - await transformDocument(document, message, mapUrlDownloadCID); + await transformDocument(document, message, mapUrlDownloadCID: mapUrlDownloadCID, dioClient: dioClient); return document; } } \ No newline at end of file diff --git a/core/lib/presentation/utils/html_transformer/text/convert_tags_text_transformer.dart b/core/lib/presentation/utils/html_transformer/text/convert_tags_text_transformer.dart deleted file mode 100644 index a235b9c12..000000000 --- a/core/lib/presentation/utils/html_transformer/text/convert_tags_text_transformer.dart +++ /dev/null @@ -1,13 +0,0 @@ - -import 'package:core/presentation/utils/html_transformer/base/text_transformer.dart'; -import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; - -class ConvertTagsTextTransformer implements TextTransformer { - - const ConvertTagsTextTransformer(); - - @override - String transform(String text, String message, TransformConfiguration configuration) { - return text.replaceAll('<', '<').replaceAll('>', '>'); - } -} \ No newline at end of file diff --git a/core/lib/presentation/utils/html_transformer/text/linebreak_text_transformer.dart b/core/lib/presentation/utils/html_transformer/text/linebreak_text_transformer.dart deleted file mode 100644 index 98abf9a6b..000000000 --- a/core/lib/presentation/utils/html_transformer/text/linebreak_text_transformer.dart +++ /dev/null @@ -1,15 +0,0 @@ - -import 'package:core/presentation/utils/html_transformer/base/text_transformer.dart'; -import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; - -class LineBreakTextTransformer extends TextTransformer { - - const LineBreakTextTransformer(); - - @override - String transform(String text, String message, TransformConfiguration configuration) { - text = text.replaceAll('\r\n', '
'); - text = text.replaceAll('\n', '
'); - return text; - } -} \ No newline at end of file diff --git a/core/lib/presentation/utils/html_transformer/text/links_text_transformer.dart b/core/lib/presentation/utils/html_transformer/text/links_text_transformer.dart deleted file mode 100644 index 6864133a1..000000000 --- a/core/lib/presentation/utils/html_transformer/text/links_text_transformer.dart +++ /dev/null @@ -1,47 +0,0 @@ - -import 'package:core/presentation/utils/html_transformer/base/text_transformer.dart'; -import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; - -class LinksTextTransformer extends TextTransformer { - - static final RegExp schemeRegEx = RegExp(r'[a-z]{3,6}://'); - // Not a perfect but good enough regular expression to match URLs in text. - // It also matches a space at the beginning and a dot at the end, - // so this is filtered out manually in the found matches - static final RegExp linkRegEx = RegExp( - r'(([a-z]{3,6}:\/\/)|(^|\s))([a-zA-Z0-9\-]+\.)+[a-z]{2,13}([\?\/]+[\.\?\=\&\%\/\w\+\-]*)?'); - const LinksTextTransformer(); - - @override - String transform(String text, String message, TransformConfiguration configuration) { - final matches = linkRegEx.allMatches(text); - if (matches.isEmpty) { - return text; - } - final buffer = StringBuffer(); - var end = 0; - for (final match in matches) { - if (match.end < text.length && text[match.end] == '@') { - // this is an email address, abort abort! - continue; - } - final originalGroup = match.group(0)!; - final group = originalGroup.trimLeft(); - final start = match.start + originalGroup.length - group.length; - buffer.write(text.substring(end, start)); - final endsWithDot = group.endsWith('.'); - final urlText = - endsWithDot ? group.substring(0, group.length - 1) : group; - buffer.write('')..write(urlText)..write(''); - end = endsWithDot ? match.end - 1 : match.end; - } - if (end < text.length) { - buffer.write(text.substring(end)); - } - return buffer.toString(); - } -} \ No newline at end of file diff --git a/core/lib/presentation/utils/html_transformer/transform_configuration.dart b/core/lib/presentation/utils/html_transformer/transform_configuration.dart index a7b66c43a..63c841706 100644 --- a/core/lib/presentation/utils/html_transformer/transform_configuration.dart +++ b/core/lib/presentation/utils/html_transformer/transform_configuration.dart @@ -3,12 +3,7 @@ import 'package:core/presentation/utils/html_transformer/base/dom_transformer.da import 'package:core/presentation/utils/html_transformer/base/text_transformer.dart'; import 'package:core/presentation/utils/html_transformer/dom/blockquoted_transformers.dart'; import 'package:core/presentation/utils/html_transformer/dom/image_transformers.dart'; -import 'package:core/presentation/utils/html_transformer/dom/link_transformers.dart'; -import 'package:core/presentation/utils/html_transformer/dom/meta_transformers.dart'; import 'package:core/presentation/utils/html_transformer/dom/script_transformers.dart'; -import 'package:core/presentation/utils/html_transformer/text/convert_tags_text_transformer.dart'; -import 'package:core/presentation/utils/html_transformer/text/linebreak_text_transformer.dart'; -import 'package:core/presentation/utils/html_transformer/text/links_text_transformer.dart'; /// Contains the configuration for all transformations. class TransformConfiguration { @@ -37,37 +32,26 @@ class TransformConfiguration { /// /// Any specified [customDomTransformers] or [customTextTransformers] are being appended to the standard transformers. static TransformConfiguration create({ - bool? blockExternalImages, - int? maxImageWidth, List? customDomTransformers, List? customTextTransformers }) { - final domTransformers = (customDomTransformers != null) - ? [...standardDomTransformers, ...customDomTransformers] + final domTransformers = (customDomTransformers != null && customDomTransformers.isNotEmpty) + ? [...customDomTransformers] : [...standardDomTransformers]; - final textTransformers = (customTextTransformers != null) - ? [...standardTextTransformers, ...customTextTransformers] + final textTransformers = (customTextTransformers != null && customTextTransformers.isNotEmpty) + ? [...customTextTransformers] : standardTextTransformers; - maxImageWidth ??= standardMaxImageWidth; return TransformConfiguration( domTransformers, textTransformers ); } - static const int? standardMaxImageWidth = null; - static const List standardDomTransformers = [ - MetaTransformer(), RemoveScriptTransformer(), - ImageTransformer(), - LinkTransformer(), BlockQuotedTransformer(), + ImageTransformer(), ]; - static const List standardTextTransformers = [ - ConvertTagsTextTransformer(), - LinksTextTransformer(), - LineBreakTextTransformer(), - ]; + static const List standardTextTransformers = []; } \ No newline at end of file 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 f62cf5b9c..403c0d31c 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 @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:math' as math; import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/utils/html_transformer/html_template.dart'; import 'package:core/presentation/views/html_viewer/html_viewer_controller_for_web.dart'; import 'package:core/utils/app_logger.dart'; import 'package:flutter/material.dart'; @@ -36,16 +37,16 @@ class _HtmlContentViewerOnWebState extends State { /// The view ID for the IFrameElement. Must be unique. late String createdViewId; - /// The actual height of the editor, used to automatically set the height + /// The actual height of the content view, used to automatically set the height late double actualHeight; - /// The actual width of the editor, used to automatically set the width + /// The actual width of the content view, used to automatically set the width late double actualWidth; Future? webInit; String? _htmlData; bool _isLoading = true; - int minHeight = 100; - int minWidth = 300; + double minHeight = 100; + double minWidth = 300; @override void initState() { @@ -64,7 +65,7 @@ class _HtmlContentViewerOnWebState extends State { } String _generateHtmlDocument(String content) { - final htmlScripts = ''' + final webViewActionScripts = ''' '''; - final tooltipLinkCss = ''' - .tooltip .tooltiptext { - visibility: hidden; - max-width: 400px; - background-color: black; - color: #fff; - text-align: center; - border-radius: 6px; - padding: 5px 8px 5px 8px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - position: absolute; - z-index: 1; - } - .tooltip:hover .tooltiptext { - visibility: visible; - } - '''; - - final htmlTemplate = ''' - - - - - - - $htmlScripts - - -
$content
- - - '''; + final htmlTemplate = generateHtml(content, + minHeight: minHeight, + minWidth: minWidth, + styleCSS: tooltipLinkCss, + javaScripts: webViewActionScripts); return htmlTemplate; } diff --git a/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart b/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart index 1da39c801..56b6f6dae 100644 --- a/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart +++ b/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart @@ -43,7 +43,8 @@ class _HtmlContentViewState extends State { double? _webViewHeight = 1.0; double? _webViewWidth = 1.0; - int minHeight = 100; + double minHeight = 100; + double minWidth = 300; String? _htmlData; late WebViewController _webViewController; bool _isLoading = true; @@ -56,47 +57,7 @@ class _HtmlContentViewState extends State { } String _generateHtmlDocument(String content) { - final htmlTemplate = ''' - - - - - - - - - -
$content
- - - '''; + final htmlTemplate = generateHtml(content); return htmlTemplate; } diff --git a/lib/features/email/data/datasource/html_datasource.dart b/lib/features/email/data/datasource/html_datasource.dart index 5f736cc6a..bcf7b6792 100644 --- a/lib/features/email/data/datasource/html_datasource.dart +++ b/lib/features/email/data/datasource/html_datasource.dart @@ -6,4 +6,6 @@ abstract class HtmlDataSource { EmailContent emailContent, Map mapUrlDownloadCID ); + + Future addTooltipWhenHoverOnLink(EmailContent emailContent); } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/html_datasource_impl.dart b/lib/features/email/data/datasource_impl/html_datasource_impl.dart index 4893aa60b..94fb67403 100644 --- a/lib/features/email/data/datasource_impl/html_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/html_datasource_impl.dart @@ -21,4 +21,13 @@ class HtmlDataSourceImpl extends HtmlDataSource { throw error; }); } + + @override + Future addTooltipWhenHoverOnLink(EmailContent emailContent) { + return Future.sync(() async { + return await _htmlAnalyzer.addTooltipWhenHoverOnLink(emailContent); + }).catchError((error) { + throw error; + }); + } } \ No newline at end of file diff --git a/lib/features/email/data/local/html_analyzer.dart b/lib/features/email/data/local/html_analyzer.dart index 81b3444a9..d109e8004 100644 --- a/lib/features/email/data/local/html_analyzer.dart +++ b/lib/features/email/data/local/html_analyzer.dart @@ -1,6 +1,5 @@ import 'package:core/core.dart'; -import 'package:core/presentation/utils/html_transformer/html_transform.dart'; import 'package:model/model.dart'; class HtmlAnalyzer { @@ -14,12 +13,24 @@ class HtmlAnalyzer { case EmailContentType.textHtml: final htmlTransform = HtmlTransform( emailContent.content, - dioClient, - mapUrlDownloadCID); + dioClient: dioClient, + mapUrlDownloadCID: mapUrlDownloadCID); final htmlContent = await htmlTransform.transformToHtml(); return EmailContent(emailContent.type, htmlContent); default: return emailContent; } } + + Future addTooltipWhenHoverOnLink(EmailContent emailContent) async { + switch(emailContent.type) { + case EmailContentType.textHtml: + final htmlTransform = HtmlTransform(emailContent.content); + final htmlContent = await htmlTransform.transformToHtml( + transformConfiguration: TransformConfiguration.create(customDomTransformers: [AddTooltipLinkTransformer()])); + return EmailContent(emailContent.type, htmlContent); + default: + return emailContent; + } + } } \ 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 fac295e5b..ee904d481 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -124,4 +124,11 @@ class EmailRepositoryImpl extends EmailRepository { Future deleteEmailPermanently(AccountId accountId, EmailId emailId) { return emailDataSource.deleteEmailPermanently(accountId, emailId); } + + @override + Future> addTooltipWhenHoverOnLink(List emailContents) { + return Future.wait(emailContents + .map((emailContent) => _htmlDataSource.addTooltipWhenHoverOnLink(emailContent)) + .toList()); + } } \ 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 08ef59b6e..8553ba445 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -52,6 +52,8 @@ abstract class EmailRepository { AccountId accountId ); + Future> addTooltipWhenHoverOnLink(List emailContents); + Future saveEmailAsDrafts(AccountId accountId, Email email); Future removeEmailDrafts(AccountId accountId, EmailId emailId); diff --git a/lib/features/email/domain/state/get_email_content_state.dart b/lib/features/email/domain/state/get_email_content_state.dart index 2726c39a8..e9ee5e491 100644 --- a/lib/features/email/domain/state/get_email_content_state.dart +++ b/lib/features/email/domain/state/get_email_content_state.dart @@ -3,12 +3,13 @@ import 'package:model/model.dart'; class GetEmailContentSuccess extends UIState { final List emailContents; + final List emailContentsDisplayed; final List attachments; - GetEmailContentSuccess(this.emailContents, this.attachments); + GetEmailContentSuccess(this.emailContents, this.emailContentsDisplayed, this.attachments); @override - List get props => [emailContents, attachments]; + List get props => [emailContents, emailContentsDisplayed, attachments]; } class GetEmailContentFailure extends FeatureFailure { diff --git a/lib/features/email/domain/usecases/get_email_content_interactor.dart b/lib/features/email/domain/usecases/get_email_content_interactor.dart index f6d1abda0..023325e68 100644 --- a/lib/features/email/domain/usecases/get_email_content_interactor.dart +++ b/lib/features/email/domain/usecases/get_email_content_interactor.dart @@ -1,4 +1,5 @@ import 'package:core/core.dart'; +import 'package:flutter/foundation.dart'; import 'package:model/model.dart'; import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; @@ -22,7 +23,10 @@ class GetEmailContentInteractor { email.allAttachments.listAttachmentsDisplayedInContent, baseDownloadUrl, accountId); - yield Right(GetEmailContentSuccess(newEmailContents, email.allAttachments)); + final newEmailContentsDisplayed = kIsWeb + ? await emailRepository.addTooltipWhenHoverOnLink(newEmailContents) + : newEmailContents; + yield Right(GetEmailContentSuccess(newEmailContents, newEmailContentsDisplayed, email.allAttachments)); } else { yield Left(GetEmailContentFailure(null)); } diff --git a/lib/features/email/presentation/email_controller.dart b/lib/features/email/presentation/email_controller.dart index 401d90a61..c845bd766 100644 --- a/lib/features/email/presentation/email_controller.dart +++ b/lib/features/email/presentation/email_controller.dart @@ -65,6 +65,7 @@ class EmailController extends BaseController { final emailContents = [].obs; final attachments = [].obs; EmailId? _currentEmailId; + List? initialEmailContents; PresentationMailbox? get currentMailbox => mailboxDashBoardController.selectedMailbox.value; @@ -152,7 +153,8 @@ class EmailController extends BaseController { } void _getEmailContentSuccess(GetEmailContentSuccess success) { - emailContents.value = success.emailContents; + emailContents.value = success.emailContentsDisplayed; + initialEmailContents = success.emailContents; attachments.value = success.attachments; } @@ -161,6 +163,7 @@ class EmailController extends BaseController { emailAddressExpandMode.value = ExpandMode.COLLAPSE; isDisplayFullEmailAddress.value = false; emailContents.clear(); + initialEmailContents?.clear(); attachments.clear(); } @@ -592,7 +595,7 @@ class EmailController extends BaseController { final arguments = ComposerArguments( emailActionType: emailActionType, presentationEmail: mailboxDashBoardController.selectedEmail.value!, - emailContents: emailContents, + emailContents: initialEmailContents, attachments: attachments, mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role);