TF-2116 Persist new signature when open draft email
(cherry picked from commit 4bf72a6e31855061b232fe3c6bab483bcb3f264a)
This commit is contained in:
@@ -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: []),
|
||||
|
||||
Reference in New Issue
Block a user