TF-457 Display signature, bcc email address in composer

This commit is contained in:
dab246
2022-04-29 15:22:11 +07:00
committed by Dat H. Pham
parent e059b87dc0
commit f993eeb888
9 changed files with 206 additions and 70 deletions
@@ -0,0 +1,24 @@
import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:html/dom.dart';
class SignatureTransformer extends DomTransformer {
const SignatureTransformer();
@override
Future<void> process(
Document document,
String message,
{
Map<String, String>? mapUrlDownloadCID,
DioClient? dioClient
}
) async {
final signatureElements = document.querySelectorAll('div.tmail-signature');
await Future.wait(signatureElements.map((element) async {
element.attributes['class'] = 'tmail-signature-blocked';
}));
}
}
@@ -1,6 +1,5 @@
import 'package:core/core.dart';
import 'package:core/presentation/utils/html_transformer/message_content_transformer.dart';
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
import 'package:html/dom.dart';
class HtmlTransform {
@@ -1,5 +1,4 @@
import 'package:core/core.dart';
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
import 'package:html/dom.dart';
import 'package:html/parser.dart' show parse;
@@ -4,6 +4,7 @@ import 'package:core/presentation/utils/html_transformer/base/text_transformer.d
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/script_transformers.dart';
import 'package:core/presentation/utils/html_transformer/dom/sigature_transformers.dart';
/// Contains the configuration for all transformations.
class TransformConfiguration {
@@ -49,6 +50,7 @@ class TransformConfiguration {
static const List<DomTransformer> standardDomTransformers = [
RemoveScriptTransformer(),
SignatureTransformer(),
BlockQuotedTransformer(),
ImageTransformer(),
];
@@ -58,6 +58,7 @@ class ComposerController extends BaseController {
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
final _appToast = Get.find<AppToast>();
final _imagePaths = Get.find<ImagePaths>();
final _responsiveUtils = Get.find<ResponsiveUtils>();
final expandModeAttachments = ExpandMode.COLLAPSE.obs;
final composerArguments = Rxn<ComposerArguments>();
@@ -92,7 +93,8 @@ class ComposerController extends BaseController {
List<EmailAddress> listBccEmailAddress = <EmailAddress>[];
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.localContact;
HtmlEditorApi? htmlEditorApi;
final html_editor_browser.HtmlEditorController htmlControllerBrowser = html_editor_browser.HtmlEditorController(processNewLineAsBr: true);
final html_editor_browser.HtmlEditorController htmlControllerBrowser =
html_editor_browser.HtmlEditorController(processNewLineAsBr: true);
final subjectEmailInputController = TextEditingController();
final toEmailAddressController = TextEditingController();
@@ -108,14 +110,21 @@ class ComposerController extends BaseController {
void setSubjectEmail(String subject) => subjectEmail.value = subject;
Future<String> _getEmailBodyText({bool onlyText = false}) async {
Future<String> _getEmailBodyText({bool changedEmail = false}) async {
if (kIsWeb) {
return await htmlControllerBrowser.getText();
} else {
if (onlyText) {
return (await htmlEditorApi?.getText()) ?? '';
if (changedEmail) {
return await htmlEditorApi?.getText() ?? '';
} else {
return (await htmlEditorApi?.getFullHtml()) ?? '';
final content = await htmlEditorApi?.getText() ?? '';
if (_isMobileApp && identitySelected.value?.textSignature?.value.isNotEmpty == true) {
final newContent = '$content<br><br><br>--<br><br><div>${identitySelected.value?.textSignature?.value}</div><br>';
log('ComposerController::_generateEmail()_MOBILE: $newContent');
return newContent;
} else {
return content;
}
}
}
}
@@ -328,9 +337,12 @@ class ComposerController extends BaseController {
if (arguments.presentationEmail != null && userProfile != null) {
final userEmailAddress = EmailAddress(null, userProfile.email);
final recipients = arguments.presentationEmail!.generateRecipientsEmailAddressForComposer(arguments.emailActionType, arguments.mailboxRole);
final recipients = arguments.presentationEmail!.generateRecipientsEmailAddressForComposer(
arguments.emailActionType,
arguments.mailboxRole);
if (arguments.mailboxRole == PresentationMailbox.roleSent || arguments.emailActionType == EmailActionType.edit) {
if (arguments.mailboxRole == PresentationMailbox.roleSent
|| arguments.emailActionType == EmailActionType.edit) {
listToEmailAddress = recipients.value1;
listCcEmailAddress = recipients.value2;
listBccEmailAddress = recipients.value3;
@@ -362,7 +374,8 @@ class ComposerController extends BaseController {
_updateStatusEmailSendButton();
}
void updateListEmailAddress(PrefixEmailAddress prefixEmailAddress, List<EmailAddress> newListEmailAddress) {
void updateListEmailAddress(PrefixEmailAddress prefixEmailAddress,
List<EmailAddress> newListEmailAddress) {
switch(prefixEmailAddress) {
case PrefixEmailAddress.to:
listToEmailAddress = newListEmailAddress;
@@ -395,22 +408,31 @@ class ComposerController extends BaseController {
final headerEmailQuotedAsHtml = headerEmailQuoted != null ? headerEmailQuoted.addBlockTag('cite') : '';
final trustAsHtml = arguments.emailContents?.asHtmlString ?? '';
final emailQuotedHtml = '<p></br></p>$headerEmailQuotedAsHtml${trustAsHtml.addBlockQuoteTag()}';
final emailQuotedHtml = '<p></br></p>$headerEmailQuotedAsHtml${trustAsHtml.addBlockQuoteTag()}</br></br></br>';
return emailQuotedHtml;
}
Future<Email> _generateEmail(Map<Role, MailboxId> mapDefaultMailboxId, UserProfile userProfile, {bool asDrafts = false}) async {
Future<Email> _generateEmail(Map<Role, MailboxId> mapDefaultMailboxId,
UserProfile userProfile, {bool asDrafts = false}) async {
final generateEmailId = EmailId(Id(_uuid.v1()));
final outboxMailboxId = mapDefaultMailboxId[PresentationMailbox.roleOutbox];
final draftMailboxId = mapDefaultMailboxId[PresentationMailbox.roleDrafts];
final listFromEmailAddress = {
EmailAddress(null, userProfile.email)
};
Set<EmailAddress> listFromEmailAddress = {EmailAddress(null, userProfile.email)};
if (identitySelected.value?.email?.isNotEmpty == true) {
listFromEmailAddress = {EmailAddress(
identitySelected.value?.name,
identitySelected.value?.email)};
}
Set<EmailAddress> listReplyToEmailAddress = {EmailAddress(null, userProfile.email)};
if (identitySelected.value?.replyTo?.isNotEmpty == true) {
listReplyToEmailAddress = identitySelected.value!.replyTo!;
}
final generatePartId = PartId(_uuid.v1());
final generateBlobId = Id(_uuid.v1());
final emailBodyText = await _getEmailBodyText();
var emailBodyText = await _getEmailBodyText();
log('ComposerController::_generateEmail(): $emailBodyText');
final userAgent = await userAgentPlatform;
return Email(
@@ -420,7 +442,7 @@ class ComposerController extends BaseController {
to: listToEmailAddress.toSet(),
cc: listCcEmailAddress.toSet(),
bcc: listBccEmailAddress.toSet(),
replyTo: listFromEmailAddress,
replyTo: listReplyToEmailAddress,
keywords: asDrafts ? {KeyWordIdentifier.emailDraft : true} : null,
subject: subjectEmail.value,
htmlBody: {
@@ -504,7 +526,8 @@ class ComposerController extends BaseController {
if (!_validateAttachmentsSize()) {
showConfirmDialogAction(
context,
AppLocalizations.of(context).message_dialog_send_email_exceeds_maximum_size(filesize(mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value ?? 0, 0)),
AppLocalizations.of(context).message_dialog_send_email_exceeds_maximum_size(
filesize(mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value ?? 0, 0)),
AppLocalizations.of(context).got_it,
() => {},
title: AppLocalizations.of(context).sending_failed,
@@ -521,7 +544,8 @@ class ComposerController extends BaseController {
final session = mailboxDashBoardController.sessionCurrent;
final mapDefaultMailboxId = mailboxDashBoardController.mapDefaultMailboxId;
final userProfile = mailboxDashBoardController.userProfile.value;
if (arguments != null && session != null && mapDefaultMailboxId.isNotEmpty && userProfile != null) {
if (arguments != null && session != null && mapDefaultMailboxId.isNotEmpty
&& userProfile != null) {
_saveEmailAddress();
final email = await _generateEmail(mapDefaultMailboxId, userProfile);
@@ -530,11 +554,11 @@ class ComposerController extends BaseController {
final submissionCreateId = Id(_uuid.v1());
mailboxDashBoardController.consumeState(_sendEmailInteractor.execute(
accountId,
EmailRequest(email, submissionCreateId, mailboxIdSaved: sentMailboxId,
emailIdDestroyed: arguments.emailActionType == EmailActionType.edit
? arguments.presentationEmail?.id
: null)));
accountId,
EmailRequest(email, submissionCreateId, mailboxIdSaved: sentMailboxId,
emailIdDestroyed: arguments.emailActionType == EmailActionType.edit
? arguments.presentationEmail?.id
: null)));
}
if (kIsWeb) {
@@ -564,7 +588,8 @@ class ComposerController extends BaseController {
Future<List<EmailAddress>> getAutoCompleteSuggestion({String? word, bool? isAll}) async {
if (_contactSuggestionSource == ContactSuggestionSource.all) {
return await _getAutoCompleteWithDeviceContactInteractor.execute(AutoCompletePattern(word: word, isAll: isAll))
return await _getAutoCompleteWithDeviceContactInteractor
.execute(AutoCompletePattern(word: word, isAll: isAll))
.then((value) => value.fold(
(failure) => <EmailAddress>[],
(success) => success is GetAutoCompleteSuccess ? success.listEmailAddress : <EmailAddress>[]));
@@ -610,7 +635,8 @@ class ComposerController extends BaseController {
if (currentContext != null) {
showConfirmDialogAction(
currentContext!,
AppLocalizations.of(currentContext!).message_dialog_upload_attachments_exceeds_maximum_size(filesize(mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value ?? 0, 0)),
AppLocalizations.of(currentContext!).message_dialog_upload_attachments_exceeds_maximum_size(
filesize(mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value ?? 0, 0)),
AppLocalizations.of(currentContext!).got_it,
() => {},
title: AppLocalizations.of(currentContext!).maximum_files_size,
@@ -686,7 +712,8 @@ class ComposerController extends BaseController {
}
Future<bool> _isEmailChanged(BuildContext context, ComposerArguments arguments) async {
final newEmailBody = await _getEmailBodyText(onlyText: true);
final newEmailBody = (await _getEmailBodyText(changedEmail: true))
.replaceAll('<p><br><br><br></p>', '');
log('ComposerController::_isEmailChanged(): newEmailBody: $newEmailBody');
var oldEmailBody = '';
final contentEmail = getContentEmail(context);
@@ -747,9 +774,11 @@ class ComposerController extends BaseController {
final oldEmail = arguments.presentationEmail;
if (arguments.emailActionType == EmailActionType.edit && oldEmail != null) {
mailboxDashBoardController.consumeState(_updateEmailDraftsInteractor.execute(accountId, newEmail, oldEmail.id));
mailboxDashBoardController.consumeState(
_updateEmailDraftsInteractor.execute(accountId, newEmail, oldEmail.id));
} else {
mailboxDashBoardController.consumeState(_saveEmailAsDraftsInteractor.execute(accountId, newEmail));
mailboxDashBoardController.consumeState(
_saveEmailAsDraftsInteractor.execute(accountId, newEmail));
}
}
}
@@ -821,7 +850,8 @@ class ComposerController extends BaseController {
}
void toggleDisplayAttachments() {
final newExpandMode = expandModeAttachments.value == ExpandMode.COLLAPSE ? ExpandMode.EXPAND : ExpandMode.COLLAPSE;
final newExpandMode = expandModeAttachments.value == ExpandMode.COLLAPSE
? ExpandMode.EXPAND : ExpandMode.COLLAPSE;
expandModeAttachments.value = newExpandMode;
}
@@ -912,7 +942,87 @@ class ComposerController extends BaseController {
}
void selectIdentity(Identity? newIdentity) {
final formerIdentity = identitySelected.value;
identitySelected.value = newIdentity;
if (newIdentity != null) {
_applyIdentityForAllFieldComposer(formerIdentity, newIdentity);
FocusManager.instance.primaryFocus?.unfocus();
}
}
bool get _isMobileApp {
return !BuildUtils.isWeb
&& currentContext != null
&& _responsiveUtils.isMobile(currentContext!);
}
void _applyIdentityForAllFieldComposer(Identity? formerIdentity, Identity newIdentity) {
if (formerIdentity != null) {
// Remove former identity
if (formerIdentity.bcc?.isNotEmpty == true) {
_removeBccEmailAddressFromFormerIdentity(formerIdentity.bcc!);
}
if (!_isMobileApp) {
_removeSignature();
}
}
// Add new identity
if (newIdentity.bcc?.isNotEmpty == true) {
_applyBccEmailAddressFromIdentity(newIdentity.bcc!);
}
if (!_isMobileApp) {
if (newIdentity.htmlSignature?.value.isNotEmpty == true) {
_applySignature(newIdentity.htmlSignature!);
} else if (newIdentity.textSignature?.value.isNotEmpty == true) {
_applySignature(newIdentity.textSignature!);
}
}
}
void _applyBccEmailAddressFromIdentity(Set<EmailAddress> listEmailAddress) {
if (!listEmailAddressType.contains(PrefixEmailAddress.bcc)) {
listEmailAddressType.add(PrefixEmailAddress.bcc);
}
listBccEmailAddress = listEmailAddress.toList();
toAddressExpandMode.value = ExpandMode.COLLAPSE;
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
_updateStatusEmailSendButton();
}
void _removeBccEmailAddressFromFormerIdentity(Set<EmailAddress> listEmailAddress) {
listBccEmailAddress = listBccEmailAddress
.where((address) => !listEmailAddress.contains(address))
.toList();
if (listBccEmailAddress.isEmpty) {
listEmailAddressType.remove(PrefixEmailAddress.bcc);
}
toAddressExpandMode.value = ExpandMode.COLLAPSE;
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
_updateStatusEmailSendButton();
}
void _applySignature(Signature signature) {
final signatureAsHtml = '--<br><br>${signature.value}';
log('ComposerController::_applySignature(): $signatureAsHtml');
if (BuildUtils.isWeb) {
htmlControllerBrowser.insertSignature(signatureAsHtml);
} else {
htmlEditorApi?.insertSignature(signatureAsHtml);
}
}
void _removeSignature() {
log('ComposerController::_removeSignature():');
if (BuildUtils.isWeb) {
htmlControllerBrowser.removeSignature();
} else {
htmlEditorApi?.removeSignature();
}
}
void closeComposer() {
@@ -89,7 +89,9 @@ class ComposerView extends GetWidget<ComposerController> {
borderRadius: const BorderRadius.all(Radius.circular(24)),
child: SafeArea(
child: Column(children: [
Padding(padding: const EdgeInsets.symmetric(horizontal: 16), child: _buildAppBar(context, controller.isEnableEmailSendButton.value)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: _buildAppBar(context, controller.isEnableEmailSendButton.value)),
const Padding(padding: EdgeInsets.only(top: 8), child: Divider(color: AppColor.colorDividerComposer, height: 1)),
Expanded(child: _buildBodyTablet(context)),
const Divider(color: AppColor.colorDividerComposer, height: 1),
@@ -119,12 +121,13 @@ class ComposerView extends GetWidget<ComposerController> {
controller.closeComposer();
}),
Expanded(child: _buildTitleComposer(context)),
buildIconWeb(
icon: SvgPicture.asset(
isEnableSendButton ? imagePaths.icSendMobile : imagePaths.icSendDisable,
fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).send,
onTap: () => controller.sendEmailAction(context)),
if (responsiveUtils.isMobile(context))
buildIconWeb(
icon: SvgPicture.asset(
isEnableSendButton ? imagePaths.icSendMobile : imagePaths.icSendDisable,
fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).send,
onTap: () => controller.sendEmailAction(context)),
]
),
);
@@ -481,6 +484,7 @@ class ComposerView extends GetWidget<ComposerController> {
return HtmlEditor(
key: const Key('composer_editor'),
minHeight: 550,
initialContent: '<p><br><br><br></p>',
onCreated: (editorApi) => controller.htmlEditorApi = editorApi);
} else {
final message = controller.getContentEmail(context);
@@ -331,29 +331,31 @@ class ComposerView extends GetWidget<ComposerController> {
customButton: SvgPicture.asset(imagePaths.icEditIdentity),
items: controller.listIdentities.map((item) => DropdownMenuItem<Identity>(
value: item,
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: item == controller.identitySelected.value ? AppColor.colorBgMenuItemDropDownSelected : Colors.transparent),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
item.name ?? '',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.normal, color: Colors.black),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
item.email ?? '',
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.normal, color: AppColor.colorHintSearchBar),
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
]
child: PointerInterceptor(
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: item == controller.identitySelected.value ? AppColor.colorBgMenuItemDropDownSelected : Colors.transparent),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
item.name ?? '',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.normal, color: Colors.black),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
item.email ?? '',
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.normal, color: AppColor.colorHintSearchBar),
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
]
),
),
),
)).toList(),
@@ -516,7 +518,7 @@ class ComposerView extends GetWidget<ComposerController> {
Obx(() {
if (controller.composerArguments.value != null) {
if (controller.composerArguments.value?.emailActionType == EmailActionType.compose) {
final initContent = controller.textEditorWeb ?? '';
final initContent = controller.textEditorWeb ?? '<p><br><br><br></p>';
return Expanded(child: Padding(
padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10),
child: _buildEditor(context, initContent)));
@@ -554,8 +556,8 @@ class ComposerView extends GetWidget<ComposerController> {
return html_editor_browser.HtmlEditor(
key: const Key('composer_editor_web'),
controller: controller.htmlControllerBrowser,
htmlEditorOptions: html_editor_browser.HtmlEditorOptions(
hint: AppLocalizations.of(context).hint_compose_email,
htmlEditorOptions: const html_editor_browser.HtmlEditorOptions(
hint: '',
darkMode: false,
),
blockQuotedContent: initContent,
@@ -150,14 +150,9 @@ class MailboxDashBoardController extends ReloadableController {
message: AppLocalizations.of(currentContext!).message_has_been_sent_successfully,
icon: _imagePaths.icSendToast);
}
clearState();
} else if (success is SaveEmailAsDraftsSuccess) {
log('MailboxDashBoardController::onDone(): SaveEmailAsDraftsSuccess');
_saveEmailAsDraftsSuccess(success);
clearState();
} else if (success is RemoveEmailDraftsSuccess
|| success is UpdateEmailDraftsSuccess) {
clearState();
} else if (success is MoveToMailboxSuccess) {
_moveToMailboxSuccess(success);
} else if (success is DeleteEmailPermanentlySuccess) {
+3 -2
View File
@@ -122,13 +122,14 @@ dependencies:
# enough_html_editor: Compose email for Mobile & Tablet
enough_html_editor:
git:
url: https://github.com/Enough-Software/enough_html_editor.git
url: https://github.com/dab246/enough_html_editor.git
ref: improvements_for_use_tmail
# html_editor_enhanced: Compose email for Web Browser
html_editor_enhanced:
git:
url: https://github.com/dab246/html-editor-enhanced.git
ref: fix_display_content
ref: improvements_for_use_tmail
# fk_user_agent
fk_user_agent: 2.1.0