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
@@ -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: []),