Fix displaying the external url of the link tag

This commit is contained in:
dab246
2022-04-14 11:19:47 +07:00
committed by Dat H. Pham
parent 701502c6b7
commit 561f8041ea
25 changed files with 182 additions and 299 deletions
+4
View File
@@ -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';
@@ -13,8 +13,10 @@ abstract class DomTransformer {
Future<void> process(
Document document,
String message,
Map<String, String>? mapUrlDownloadCID,
DioClient dioClient
{
Map<String, String>? mapUrlDownloadCID,
DioClient? dioClient,
}
);
/// Adds a HEAD element if necessary
@@ -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);
}
@@ -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<void> process(
Document document,
String message,
Map<String, String>? mapUrlDownloadCID,
DioClient dioClient
{
Map<String, String>? 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);
}
}
@@ -11,8 +11,10 @@ class BlockQuotedTransformer extends DomTransformer {
Future<void> process(
Document document,
String message,
Map<String, String>? mapUrlDownloadCID,
DioClient dioClient
{
Map<String, String>? mapUrlDownloadCID,
DioClient? dioClient
}
) async {
final quotedElements = document.getElementsByTagName('blockquote');
await Future.wait(quotedElements.map((quotedElement) async {
@@ -14,8 +14,10 @@ class ImageTransformer extends DomTransformer {
Future<void> process(
Document document,
String message,
Map<String, String>? mapUrlDownloadCID,
DioClient dioClient
{
Map<String, String>? 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';
@@ -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(
'<meta name="viewport" content="width=device-width, initial-scale=1.0">');
static final Element _contentTypeMetaElement = Element.html(
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8">');
const MetaTransformer();
@override
Future<void> process(
Document document,
String message,
Map<String, String>? 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);
}
}
}
@@ -11,8 +11,10 @@ class RemoveScriptTransformer extends DomTransformer {
Future<void> process(
Document document,
String message,
Map<String, String>? mapUrlDownloadCID,
DioClient dioClient
{
Map<String, String>? mapUrlDownloadCID,
DioClient? dioClient
}
) async {
final scriptElements = document.getElementsByTagName('script');
await Future.wait(scriptElements.map((scriptElement) async {
@@ -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 '''
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
.tmail-content {
min-height: ${minHeight ?? 0}px;
min-width: ${minWidth ?? 0}px;
color: #000000;
font-family: Inter;
font-size: 16px;
font-style: normal;
}
table {
width: 100%;
max-width: 100%;
}
td {
padding: 13px;
margin: 0px;
}
th {
padding: 13px;
margin: 0px;
}
${styleCSS ?? ''}
</style>
${javaScripts ?? ''}
</head>
<body>
<div class="tmail-content">${content}</div>
</body>
</html>
''';
}
@@ -7,17 +7,17 @@ class HtmlTransform {
final String _contentHtml;
Map<String, String>? 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<String> 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<Document> 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);
}
}
@@ -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<void> transformDocument(
Document document,
String message,
Map<String, String>? mapUrlDownloadCID,
{
Map<String, String>? 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<Document> toDocument(String message, Map<String, String>? mapUrlDownloadCID) async {
Future<Document> toDocument(String message, {Map<String, String>? 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;
}
}
@@ -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('<', '&lt;').replaceAll('>', '&gt;');
}
}
@@ -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', '<br/>');
text = text.replaceAll('\n', '<br/>');
return text;
}
}
@@ -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('<a href="');
if (!group.startsWith(schemeRegEx)) {
buffer.write('https://');
}
buffer..write(urlText)..write('">')..write(urlText)..write('</a>');
end = endsWithDot ? match.end - 1 : match.end;
}
if (end < text.length) {
buffer.write(text.substring(end));
}
return buffer.toString();
}
}
@@ -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<DomTransformer>? customDomTransformers,
List<TextTransformer>? 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<DomTransformer> standardDomTransformers = [
MetaTransformer(),
RemoveScriptTransformer(),
ImageTransformer(),
LinkTransformer(),
BlockQuotedTransformer(),
ImageTransformer(),
];
static const List<TextTransformer> standardTextTransformers = [
ConvertTagsTextTransformer(),
LinksTextTransformer(),
LineBreakTextTransformer(),
];
static const List<TextTransformer> standardTextTransformers = [];
}
@@ -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<HtmlContentViewerOnWeb> {
/// 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<bool>? 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<HtmlContentViewerOnWeb> {
}
String _generateHtmlDocument(String content) {
final htmlScripts = '''
final webViewActionScripts = '''
<script type="text/javascript">
window.parent.addEventListener('message', handleMessage, false);
window.addEventListener('click', handleOnClickLink, true);
@@ -122,63 +123,11 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
</script>
''';
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 = '''
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
#editor {
outline: 0px solid transparent;
min-height: ${minHeight}px;
min-width: ${minWidth}px;
color: #000000;
font-family: Inter;
font-size: 16px;
font-style: normal;
}
table {
width: 100%;
max-width: 100%;
}
td {
padding: 13px;
margin: 0px;
}
th {
padding: 13px;
margin: 0px;
}
$tooltipLinkCss
</style>
$htmlScripts
</head>
<body>
<div id="editor">$content</div>
</body>
</html>
''';
final htmlTemplate = generateHtml(content,
minHeight: minHeight,
minWidth: minWidth,
styleCSS: tooltipLinkCss,
javaScripts: webViewActionScripts);
return htmlTemplate;
}
@@ -43,7 +43,8 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
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<HtmlContentViewer> {
}
String _generateHtmlDocument(String content) {
final htmlTemplate = '''
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
#editor {
outline: 0px solid transparent;
min-height: ${minHeight}px;
min-width: 300px;
color: #182952;
font-family: verdana;
}
table {
width: 100%;
max-width: 100%;
}
td {
padding: 13px;
margin: 0px;
}
th {
padding: 13px;
margin: 0px;
}
</style>
<script>
var documentHeight;
function onLoaded() {
documentHeight = document.body.scrollHeight;
document.execCommand("styleWithCSS", false, true);
}
</script>
</head>
<body onload="onLoaded();">
<div id="editor">$content</div>
</body>
</html>
''';
final htmlTemplate = generateHtml(content);
return htmlTemplate;
}
@@ -6,4 +6,6 @@ abstract class HtmlDataSource {
EmailContent emailContent,
Map<String, String> mapUrlDownloadCID
);
Future<EmailContent> addTooltipWhenHoverOnLink(EmailContent emailContent);
}
@@ -21,4 +21,13 @@ class HtmlDataSourceImpl extends HtmlDataSource {
throw error;
});
}
@override
Future<EmailContent> addTooltipWhenHoverOnLink(EmailContent emailContent) {
return Future.sync(() async {
return await _htmlAnalyzer.addTooltipWhenHoverOnLink(emailContent);
}).catchError((error) {
throw error;
});
}
}
@@ -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<EmailContent> 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;
}
}
}
@@ -124,4 +124,11 @@ class EmailRepositoryImpl extends EmailRepository {
Future<bool> deleteEmailPermanently(AccountId accountId, EmailId emailId) {
return emailDataSource.deleteEmailPermanently(accountId, emailId);
}
@override
Future<List<EmailContent>> addTooltipWhenHoverOnLink(List<EmailContent> emailContents) {
return Future.wait(emailContents
.map((emailContent) => _htmlDataSource.addTooltipWhenHoverOnLink(emailContent))
.toList());
}
}
@@ -52,6 +52,8 @@ abstract class EmailRepository {
AccountId accountId
);
Future<List<EmailContent>> addTooltipWhenHoverOnLink(List<EmailContent> emailContents);
Future<Email?> saveEmailAsDrafts(AccountId accountId, Email email);
Future<bool> removeEmailDrafts(AccountId accountId, EmailId emailId);
@@ -3,12 +3,13 @@ import 'package:model/model.dart';
class GetEmailContentSuccess extends UIState {
final List<EmailContent> emailContents;
final List<EmailContent> emailContentsDisplayed;
final List<Attachment> attachments;
GetEmailContentSuccess(this.emailContents, this.attachments);
GetEmailContentSuccess(this.emailContents, this.emailContentsDisplayed, this.attachments);
@override
List<Object?> get props => [emailContents, attachments];
List<Object?> get props => [emailContents, emailContentsDisplayed, attachments];
}
class GetEmailContentFailure extends FeatureFailure {
@@ -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<Failure, Success>(GetEmailContentSuccess(newEmailContents, email.allAttachments));
final newEmailContentsDisplayed = kIsWeb
? await emailRepository.addTooltipWhenHoverOnLink(newEmailContents)
: newEmailContents;
yield Right<Failure, Success>(GetEmailContentSuccess(newEmailContents, newEmailContentsDisplayed, email.allAttachments));
} else {
yield Left(GetEmailContentFailure(null));
}
@@ -65,6 +65,7 @@ class EmailController extends BaseController {
final emailContents = <EmailContent>[].obs;
final attachments = <Attachment>[].obs;
EmailId? _currentEmailId;
List<EmailContent>? 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);