TF-2116 Persist new signature when open draft email

(cherry picked from commit 4bf72a6e31855061b232fe3c6bab483bcb3f264a)
This commit is contained in:
dab246
2023-09-12 11:48:19 +07:00
committed by Dat H. Pham
parent e63a8f7d7f
commit 56fac29e55
11 changed files with 102 additions and 184 deletions
@@ -108,69 +108,6 @@ class HtmlUtils {
.note-editable {
direction: ${direction.name};
}
blockquote {
margin-left: 8px;
margin-right: 8px;
padding-left: 12px;
padding-right: 12px;
border-left: 5px solid #eee;
}
pre {
display: block;
padding: 10px;
margin: 0 0 10px;
font-size: 13px;
line-height: 1.5;
color: #333;
word-break: break-all;
word-wrap: break-word;
background-color: #f5f5f5;
border: 1px solid #ccc;
border-radius: 4px;
overflow: auto;
}
div.tmail-signature {
text-align: left;
margin: 16px 0px 16px 0px;
}
.tmail-signature-button,
.tmail-signature-button * {
box-sizing: border-box;
}
.tmail-signature-button {
padding: 6px 40px 6px 16px;
border-radius: 4px;
color: #fff;
background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.0003 11.8319L5.53383 8.1098C5.18027 7.81516 4.6548 7.86293 4.36016 8.21649C4.06553 8.57006 4.1133 9.09553 4.46686 9.39016L9.46686 13.5568C9.7759 13.8144 10.2248 13.8144 10.5338 13.5568L15.5338 9.39016C15.8874 9.09553 15.9352 8.57006 15.6405 8.21649C15.3459 7.86293 14.8204 7.81516 14.4669 8.1098L10.0003 11.8319Z' fill='%23AEAEC0'/%3E%3C/svg%3E%0A");
background-repeat: no-repeat;
background-position: right 16px center;
background-color: #FFFFFF;
border-radius: 36px;
border-style: solid;
border-color: var(--m-3-syslight-outline-shadow-outline-variant, #cac4d0);
border-width: 0.5px;
flex-direction: row;
gap: 8px;
align-items: center;
justify-content: flex-start;
flex-shrink: 0;
position: relative;
cursor: pointer;
color: var(--m-3-syslight-tetirary-tertiary, #8c9caf);
text-align: left;
font: var(--m-3-body-large-2, 400 17px/24px "Inter", sans-serif);
}
.tmail-signature-content {
padding: 12px;
display: none;
overflow: hidden;
}
</style>
''';
} else if (PlatformInfo.isMobile) {
@@ -178,46 +115,6 @@ class HtmlUtils {
#editor {
direction: ${direction.name};
}
div.tmail-signature {
text-align: left;
margin: 16px 0px 16px 0px;
}
.tmail-signature-button,
.tmail-signature-button * {
box-sizing: border-box;
}
.tmail-signature-button {
padding: 6px 40px 6px 16px;
border-radius: 4px;
color: #fff;
background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.0003 11.8319L5.53383 8.1098C5.18027 7.81516 4.6548 7.86293 4.36016 8.21649C4.06553 8.57006 4.1133 9.09553 4.46686 9.39016L9.46686 13.5568C9.7759 13.8144 10.2248 13.8144 10.5338 13.5568L15.5338 9.39016C15.8874 9.09553 15.9352 8.57006 15.6405 8.21649C15.3459 7.86293 14.8204 7.81516 14.4669 8.1098L10.0003 11.8319Z' fill='%23AEAEC0'/%3E%3C/svg%3E%0A");
background-repeat: no-repeat;
background-position: right 16px center;
background-color: #FFFFFF;
border-radius: 36px;
border-style: solid;
border-color: var(--m-3-syslight-outline-shadow-outline-variant, #cac4d0);
border-width: 0.5px;
flex-direction: row;
gap: 8px;
align-items: center;
justify-content: flex-start;
flex-shrink: 0;
position: relative;
cursor: pointer;
color: var(--m-3-syslight-tetirary-tertiary, #8c9caf);
text-align: left;
font: var(--m-3-body-large-2, 400 17px/24px "Inter", sans-serif);
}
.tmail-signature-content {
padding: 12px;
display: none;
overflow: hidden;
}
''';
} else {
return '';
@@ -48,6 +48,7 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/update_email_dra
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/list_identities_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/image_source.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
@@ -102,8 +103,6 @@ class ComposerController extends BaseController {
final toAddressExpandMode = ExpandMode.EXPAND.obs;
final ccAddressExpandMode = ExpandMode.EXPAND.obs;
final bccAddressExpandMode = ExpandMode.EXPAND.obs;
final identitySelected = Rxn<Identity>();
final listIdentities = <Identity>[].obs;
final emailContentsViewState = Rxn<Either<Failure, Success>>();
final hasRequestReadReceipt = false.obs;
final ccRecipientState = PrefixRecipientState.disabled.obs;
@@ -162,50 +161,11 @@ class ComposerController extends BaseController {
double? maxWithEditor;
EmailId? _emailIdEditing;
bool isAttachmentCollapsed = false;
Identity? identitySelected;
late Worker uploadInlineImageWorker;
late Worker dashboardViewStateWorker;
void onChangeTextEditorWeb(String? text) {
initTextEditor(text);
_textEditorWeb = text;
}
void initTextEditor(String? text) {
if (_initTextEditor == null) {
_initTextEditor = text;
log('ComposerController::initTextEditor():$_initTextEditor');
}
}
String? get textEditorWeb => _textEditorWeb;
HtmlEditorApi? get htmlEditorApi => richTextMobileTabletController.htmlEditorApi;
void setSubjectEmail(String subject) => subjectEmail.value = subject;
Future<String> _getEmailBodyText(BuildContext context, {
bool changedEmail = false
}) async {
if (PlatformInfo.isWeb) {
var contentHtml = '';
if (_responsiveUtils.isWebDesktop(context) &&
screenDisplayMode.value == ScreenDisplayMode.minimize) {
contentHtml = textEditorWeb ?? '';
} else {
contentHtml = await richTextWebController.editorController.getText();
}
final newContentHtml = contentHtml.removeEditorStartTag();
log('ComposerController::_getEmailBodyText()::WEB:contentHtml: $contentHtml | newContentHtml: $newContentHtml');
return newContentHtml;
} else {
String contentHtml = await htmlEditorApi?.getText() ?? '';
final newContentHtml = contentHtml.removeEditorStartTag();
log('ComposerController::_getEmailBodyText()::Mobile:contentHtml: $contentHtml | newContentHtml: $newContentHtml');
return newContentHtml;
}
}
ComposerController(
this._deviceInfoPlugin,
this._localFilePickerInteractor,
@@ -419,7 +379,10 @@ class ComposerController extends BaseController {
_onChangeCursorOnMobile(coordinates, context);
},
);
if (identitySelected.value == null) {
}
void onLoadCompletedMobileEditorAction(HtmlEditorApi editorApi, WebUri? url) {
if (identitySelected == null) {
_getAllIdentities();
}
}
@@ -553,14 +516,9 @@ class ComposerController extends BaseController {
}
void _handleGetAllIdentitiesSuccess(GetAllIdentitiesSuccess success) async {
if (success.identities?.isNotEmpty == true) {
listIdentities.value = success.identities!
.where((identity) => identity.mayDelete == true)
.toList();
if (listIdentities.isNotEmpty) {
await selectIdentity(listIdentities.first);
}
final listIdentitiesMayDeleted = success.identities?.toListMayDeleted() ?? [];
if (listIdentitiesMayDeleted.isNotEmpty) {
await _selectIdentity(listIdentitiesMayDeleted.first);
}
_autoFocusFieldWhenLauncher();
@@ -671,20 +629,23 @@ class ComposerController extends BaseController {
}
) async {
Set<EmailAddress> listFromEmailAddress = {EmailAddress(null, userProfile.email)};
if (identitySelected.value?.email?.isNotEmpty == true) {
listFromEmailAddress = {EmailAddress(
identitySelected.value?.name,
identitySelected.value?.email)};
if (identitySelected?.email?.isNotEmpty == true) {
listFromEmailAddress = {
EmailAddress(
identitySelected?.name,
identitySelected?.email
)
};
}
Set<EmailAddress> listReplyToEmailAddress = {EmailAddress(null, userProfile.email)};
if (identitySelected.value?.replyTo?.isNotEmpty == true) {
listReplyToEmailAddress = identitySelected.value!.replyTo!;
if (identitySelected?.replyTo?.isNotEmpty == true) {
listReplyToEmailAddress = identitySelected!.replyTo!;
}
final attachments = <EmailBodyPart>{};
attachments.addAll(uploadController.generateAttachments() ?? []);
var emailBodyText = await _getEmailBodyText(context);
var emailBodyText = await _getEmailBodyText(context, asDrafts: asDrafts);
if (uploadController.mapInlineAttachments.isNotEmpty) {
final mapContents = await _getMapContent(emailBodyText);
emailBodyText = mapContents.value1;
@@ -858,7 +819,7 @@ class ComposerController extends BaseController {
: EmailRequest(
email: createdEmail,
sentMailboxId: sentMailboxId,
identityId: identitySelected.value?.id,
identityId: identitySelected?.id,
emailIdDestroyed: arguments.emailActionType == EmailActionType.editDraft
? arguments.presentationEmail?.id
: null,
@@ -1022,7 +983,7 @@ class ComposerController extends BaseController {
PresentationEmail? presentationEmail,
Role? mailboxRole,
}) async {
final newEmailBody = await _getEmailBodyText(context, changedEmail: true);
final newEmailBody = await _getEmailBodyText(context, asDrafts: true);
log('ComposerController::_isEmailChanged(): newEmailBody: $newEmailBody');
final oldEmailBody = _initTextEditor ?? '';
log('ComposerController::_isEmailChanged(): oldEmailBody: $oldEmailBody');
@@ -1064,6 +1025,7 @@ class ComposerController extends BaseController {
}
void saveToDraftAndClose(BuildContext context, {bool canPop = true}) async {
log('ComposerController::saveToDraftAndClose:');
clearFocusEditor(context);
final arguments = composerArguments.value;
@@ -1555,9 +1517,9 @@ class ComposerController extends BaseController {
}
}
Future<void> selectIdentity(Identity? newIdentity) async {
final formerIdentity = identitySelected.value;
identitySelected.value = newIdentity;
Future<void> _selectIdentity(Identity? newIdentity) async {
final formerIdentity = identitySelected;
identitySelected = newIdentity;
if (newIdentity != null) {
await _applyIdentityForAllFieldComposer(formerIdentity, newIdentity);
}
@@ -1568,14 +1530,12 @@ class ComposerController extends BaseController {
Identity newIdentity
) async {
if (formerIdentity != null) {
// Remove former identity
if (formerIdentity.bcc?.isNotEmpty == true) {
_removeBccEmailAddressFromFormerIdentity(formerIdentity.bcc!);
}
await _removeSignature();
}
// Add new identity
if (newIdentity.bcc?.isNotEmpty == true) {
_applyBccEmailAddressFromIdentity(newIdentity.bcc!);
}
@@ -1824,7 +1784,7 @@ class ComposerController extends BaseController {
richTextWebController.editorController.setFullScreen();
onChangeTextEditorWeb(initContent);
richTextWebController.setEnableCodeView();
if (identitySelected.value == null) {
if (identitySelected == null) {
_getAllIdentities();
}
}
@@ -1872,7 +1832,47 @@ class ComposerController extends BaseController {
UserProfile? get userProfile => mailboxDashBoardController.userProfile.value;
void openContextMenuOption(BuildContext context) {
String? get textEditorWeb => _textEditorWeb;
HtmlEditorApi? get htmlEditorApi => richTextMobileTabletController.htmlEditorApi;
void onChangeTextEditorWeb(String? text) {
initTextEditor(text);
_textEditorWeb = text;
}
void initTextEditor(String? text) {
if (_initTextEditor == null) {
_initTextEditor = text;
log('ComposerController::initTextEditor():$_initTextEditor');
}
}
void setSubjectEmail(String subject) => subjectEmail.value = subject;
Future<String> _getEmailBodyText(BuildContext context, {bool asDrafts = false}) async {
var contentHtml = '';
if (PlatformInfo.isWeb) {
if (_responsiveUtils.isDesktop(context) &&
screenDisplayMode.value == ScreenDisplayMode.minimize) {
contentHtml = _textEditorWeb ?? '';
} else {
if (asDrafts) {
contentHtml = await richTextWebController.editorController.getText();
} else {
contentHtml = await richTextWebController.editorController.getTextWithSignatureContent();
}
}
} else {
if (asDrafts) {
contentHtml = (await htmlEditorApi?.getText()) ?? '';
} else {
contentHtml = (await htmlEditorApi?.getTextWithSignatureContent()) ?? '';
}
}
final newContentHtml = contentHtml.removeEditorStartTag();
return newContentHtml;
}
}
@@ -185,6 +185,7 @@ class ComposerView extends GetWidget<ComposerController> {
arguments: controller.composerArguments.value,
contentViewState: controller.emailContentsViewState.value,
onCreatedEditorAction: controller.onCreatedMobileEditorAction,
onLoadCompletedEditorAction: controller.onLoadCompletedMobileEditorAction,
),
),
)),
@@ -325,6 +326,7 @@ class ComposerView extends GetWidget<ComposerController> {
arguments: controller.composerArguments.value,
contentViewState: controller.emailContentsViewState.value,
onCreatedEditorAction: controller.onCreatedMobileEditorAction,
onLoadCompletedEditorAction: controller.onLoadCompletedMobileEditorAction,
),
),
)),
@@ -23,7 +23,7 @@ import 'package:tmail_ui_user/main/routes/route_navigation.dart';
class RichTextWebController extends BaseRichTextController {
final editorController = HtmlEditorController(processNewLineAsBr: true);
final editorController = HtmlEditorController();
final listTextStyleApply = RxList<RichTextStyleType>();
final selectedTextColor = Colors.black.obs;
@@ -0,0 +1,7 @@
import 'package:jmap_dart_client/jmap/identities/identity.dart';
extension ListIdentitiesExtension on List<Identity> {
List<Identity> toListMayDeleted() => where((identity) => identity.mayDelete == true).toList();
}
@@ -17,10 +17,12 @@ class MobileEditorView extends StatelessWidget with EditorViewMixin {
final ComposerArguments? arguments;
final Either<Failure, Success>? contentViewState;
final OnCreatedEditorAction onCreatedEditorAction;
final OnLoadCompletedEditorAction onLoadCompletedEditorAction;
const MobileEditorView({
super.key,
required this.onCreatedEditorAction,
required this.onLoadCompletedEditorAction,
this.arguments,
this.contentViewState,
});
@@ -38,7 +40,8 @@ class MobileEditorView extends StatelessWidget with EditorViewMixin {
return MobileEditorWidget(
content: HtmlExtension.editorStartTags,
direction: AppUtils.getCurrentDirection(context),
onCreatedEditorAction: onCreatedEditorAction
onCreatedEditorAction: onCreatedEditorAction,
onLoadCompletedEditorAction: onLoadCompletedEditorAction
);
case EmailActionType.editDraft:
case EmailActionType.editSendingEmail:
@@ -51,7 +54,8 @@ class MobileEditorView extends StatelessWidget with EditorViewMixin {
(failure) => MobileEditorWidget(
content: HtmlExtension.editorStartTags,
direction: AppUtils.getCurrentDirection(context),
onCreatedEditorAction: onCreatedEditorAction
onCreatedEditorAction: onCreatedEditorAction,
onLoadCompletedEditorAction: onLoadCompletedEditorAction
),
(success) {
if (success is GetEmailContentLoading) {
@@ -66,7 +70,8 @@ class MobileEditorView extends StatelessWidget with EditorViewMixin {
return MobileEditorWidget(
content: newContent,
direction: AppUtils.getCurrentDirection(context),
onCreatedEditorAction: onCreatedEditorAction
onCreatedEditorAction: onCreatedEditorAction,
onLoadCompletedEditorAction: onLoadCompletedEditorAction
);
}
}
@@ -88,7 +93,8 @@ class MobileEditorView extends StatelessWidget with EditorViewMixin {
return MobileEditorWidget(
content: emailContentQuoted,
direction: AppUtils.getCurrentDirection(context),
onCreatedEditorAction: onCreatedEditorAction
onCreatedEditorAction: onCreatedEditorAction,
onLoadCompletedEditorAction: onLoadCompletedEditorAction
);
},
(success) {
@@ -106,7 +112,8 @@ class MobileEditorView extends StatelessWidget with EditorViewMixin {
return MobileEditorWidget(
content: emailContentQuoted,
direction: AppUtils.getCurrentDirection(context),
onCreatedEditorAction: onCreatedEditorAction
onCreatedEditorAction: onCreatedEditorAction,
onLoadCompletedEditorAction: onLoadCompletedEditorAction
);
}
}
@@ -115,7 +122,8 @@ class MobileEditorView extends StatelessWidget with EditorViewMixin {
return MobileEditorWidget(
content: HtmlExtension.editorStartTags,
direction: AppUtils.getCurrentDirection(context),
onCreatedEditorAction: onCreatedEditorAction
onCreatedEditorAction: onCreatedEditorAction,
onLoadCompletedEditorAction: onLoadCompletedEditorAction
);
}
}
@@ -4,18 +4,21 @@ import 'package:flutter/material.dart';
import 'package:rich_text_composer/rich_text_composer.dart';
typedef OnCreatedEditorAction = Function(BuildContext context, HtmlEditorApi editorApi, String content);
typedef OnLoadCompletedEditorAction = Function(HtmlEditorApi editorApi, WebUri? url);
class MobileEditorWidget extends StatelessWidget {
final String content;
final TextDirection direction;
final OnCreatedEditorAction onCreatedEditorAction;
final OnLoadCompletedEditorAction onLoadCompletedEditorAction;
const MobileEditorWidget({
super.key,
required this.content,
required this.direction,
required this.onCreatedEditorAction,
required this.onLoadCompletedEditorAction,
});
@override
@@ -26,7 +29,8 @@ class MobileEditorWidget extends StatelessWidget {
addDefaultSelectionMenuItems: false,
initialContent: content,
customStyleCss: HtmlUtils.customCssStyleHtmlEditor(direction: direction),
onCreated: (editorApi) => onCreatedEditorAction.call(context, editorApi, content)
onCreated: (editorApi) => onCreatedEditorAction.call(context, editorApi, content),
onCompleted: onLoadCompletedEditorAction,
);
}
}
@@ -66,9 +66,9 @@ class _WebEditorState extends State<WebEditorWidget> {
shouldEnsureVisible: true,
hint: '',
darkMode: false,
initialText: widget.content,
customBodyCssStyle: HtmlUtils.customCssStyleHtmlEditor(direction: widget.direction),
),
blockQuotedContent: widget.content,
htmlToolbarOptions: const HtmlToolbarOptions(
toolbarType: ToolbarType.hide,
defaultToolbarButtons: []
@@ -339,9 +339,9 @@ class IdentityCreatorView extends GetWidget<IdentityCreatorController>
shouldEnsureVisible: true,
hint: '',
darkMode: false,
initialText: initContent,
customBodyCssStyle: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context)),
),
blockQuotedContent: initContent,
htmlToolbarOptions: const html_editor_browser.HtmlToolbarOptions(
toolbarType: html_editor_browser.ToolbarType.hide,
defaultToolbarButtons: []
@@ -415,9 +415,9 @@ class VacationView extends GetWidget<VacationController> with RichTextButtonMixi
htmlEditorOptions: html_editor_browser.HtmlEditorOptions(
hint: '',
darkMode: false,
initialText: controller.vacationMessageHtmlText,
customBodyCssStyle: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context))
),
blockQuotedContent: controller.vacationMessageHtmlText ?? '',
htmlToolbarOptions: const html_editor_browser.HtmlToolbarOptions(
toolbarType: html_editor_browser.ToolbarType.hide,
defaultToolbarButtons: []),
+2 -2
View File
@@ -373,7 +373,7 @@ packages:
description:
path: "."
ref: "improvement/support-expanded-collapsed-signature"
resolved-ref: f69ea3c0d84c32fe291b1b6092a30af1544a3038
resolved-ref: d1ce315373e83eedc55b3bd8ec97ae6fad0e48ce
url: "https://github.com/linagora/enough_html_editor.git"
source: git
version: "0.0.5"
@@ -914,7 +914,7 @@ packages:
description:
path: "."
ref: "improvement/support-expanded-collapsed-signature"
resolved-ref: "96ae5c4ce7c2f5b84bd481df3aa02b38ac527a1d"
resolved-ref: b36f0ddfa291a9c3c291feda2834587d9df4f9a5
url: "https://github.com/linagora/html-editor-enhanced.git"
source: git
version: "2.5.1"