Hot fix after some reload minimized composer should be lost body
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -19,6 +19,8 @@ class HideDraftSignatureTransformer extends DomTransformer {
|
||||
final currentStyle = signature.attributes['style']?.trim();
|
||||
if (currentStyle == null) {
|
||||
signature.attributes['style'] = 'display: none;';
|
||||
} else if (currentStyle.contains('display: block;') == true) {
|
||||
signature.attributes['style'] = currentStyle.replaceFirst('display: block;', 'display: none;');
|
||||
} else if (currentStyle.endsWith(';')) {
|
||||
signature.attributes['style'] = '$currentStyle display: none;';
|
||||
} else {
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/domain/exceptions/string_exception.dart';
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
|
||||
class StringConvert {
|
||||
@@ -107,4 +108,16 @@ class StringConvert {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static String getContentOriginal(String content) {
|
||||
try {
|
||||
final emailDocument = parse(content);
|
||||
final contentOriginal = emailDocument.body?.innerHtml ?? content;
|
||||
log('StringConvert::getContentOriginal:contentOriginal = $contentOriginal');
|
||||
return contentOriginal;
|
||||
} catch (e) {
|
||||
logError('StringConvert::getContentOriginal:Exception = $e');
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class ComposerRepositoryImpl extends ComposerRepository {
|
||||
emailContent = tupleContentInlineAttachments.value1;
|
||||
emailAttachments.addAll(tupleContentInlineAttachments.value2);
|
||||
|
||||
emailContent = await _removeCollapsedExpandedSignatureEffect(emailContent: emailContent);
|
||||
emailContent = await removeCollapsedExpandedSignatureEffect(emailContent: emailContent);
|
||||
|
||||
final userAgent = await _applicationManager.generateApplicationUserAgent();
|
||||
final emailBodyPartId = PartId(_uuid.v1());
|
||||
@@ -104,7 +104,8 @@ class ComposerRepositoryImpl extends ComposerRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _removeCollapsedExpandedSignatureEffect({required String emailContent}) {
|
||||
@override
|
||||
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent}) {
|
||||
try {
|
||||
return _htmlDataSource.removeCollapsedExpandedSignatureEffect(emailContent: emailContent);
|
||||
} catch (e) {
|
||||
|
||||
@@ -15,4 +15,6 @@ abstract class ComposerRepository {
|
||||
Future<UploadAttachment> uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken});
|
||||
|
||||
Future<String?> downloadImageAsBase64(String url, String cid, FileInfo fileInfo, {double? maxWidth, bool? compress});
|
||||
|
||||
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent});
|
||||
}
|
||||
@@ -39,6 +39,9 @@ class SaveComposerCacheOnWebInteractor {
|
||||
displayMode: createEmailRequest.displayMode,
|
||||
composerIndex: createEmailRequest.composerIndex,
|
||||
composerId: createEmailRequest.composerId,
|
||||
draftHash: createEmailRequest.savedDraftHash,
|
||||
actionType: createEmailRequest.savedActionType,
|
||||
draftEmailId: createEmailRequest.draftsEmailId,
|
||||
));
|
||||
return Right(SaveComposerCacheSuccess());
|
||||
} catch (exception) {
|
||||
|
||||
@@ -329,6 +329,7 @@ class ComposerBindings extends BaseBindings {
|
||||
Get.find<CreateNewAndSendEmailInteractor>(tag: composerId),
|
||||
Get.find<CreateNewAndSaveEmailToDraftsInteractor>(tag: composerId),
|
||||
Get.find<PrintEmailInteractor>(tag: composerId),
|
||||
Get.find<ComposerRepository>(tag: composerId),
|
||||
composerId: composerId,
|
||||
composerArgs: composerArguments,
|
||||
), tag: composerId);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -127,9 +127,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listToEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
fromState: controller.fromRecipientState.value,
|
||||
ccState: controller.ccRecipientState.value,
|
||||
bccState: controller.bccRecipientState.value,
|
||||
@@ -157,9 +155,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listCcEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
expandMode: controller.ccAddressExpandMode.value,
|
||||
controller: controller.ccEmailAddressController,
|
||||
focusNode: controller.ccAddressFocusNode,
|
||||
@@ -186,9 +182,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listBccEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
expandMode: controller.bccAddressExpandMode.value,
|
||||
controller: controller.bccEmailAddressController,
|
||||
focusNode: controller.bccAddressFocusNode,
|
||||
@@ -320,9 +314,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listToEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
fromState: controller.fromRecipientState.value,
|
||||
ccState: controller.ccRecipientState.value,
|
||||
bccState: controller.bccRecipientState.value,
|
||||
@@ -349,9 +341,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listCcEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
expandMode: controller.ccAddressExpandMode.value,
|
||||
controller: controller.ccEmailAddressController,
|
||||
focusNode: controller.ccAddressFocusNode,
|
||||
@@ -373,9 +363,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listBccEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
expandMode: controller.bccAddressExpandMode.value,
|
||||
controller: controller.bccEmailAddressController,
|
||||
focusNode: controller.bccAddressFocusNode,
|
||||
|
||||
@@ -98,9 +98,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listToEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
fromState: controller.fromRecipientState.value,
|
||||
ccState: controller.ccRecipientState.value,
|
||||
bccState: controller.bccRecipientState.value,
|
||||
@@ -129,9 +127,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listCcEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
expandMode: controller.ccAddressExpandMode.value,
|
||||
controller: controller.ccEmailAddressController,
|
||||
focusNode: controller.ccAddressFocusNode,
|
||||
@@ -156,9 +152,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listBccEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
expandMode: controller.bccAddressExpandMode.value,
|
||||
controller: controller.bccEmailAddressController,
|
||||
focusNode: controller.bccAddressFocusNode,
|
||||
@@ -248,7 +242,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
base64: base64,
|
||||
uploadError: uploadError
|
||||
),
|
||||
onInitialContentLoadComplete: controller.restoreCollapsibleButton,
|
||||
onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb,
|
||||
)),
|
||||
),
|
||||
),
|
||||
@@ -375,9 +369,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listToEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
fromState: controller.fromRecipientState.value,
|
||||
ccState: controller.ccRecipientState.value,
|
||||
bccState: controller.bccRecipientState.value,
|
||||
@@ -406,9 +398,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listCcEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
expandMode: controller.ccAddressExpandMode.value,
|
||||
controller: controller.ccEmailAddressController,
|
||||
focusNode: controller.ccAddressFocusNode,
|
||||
@@ -433,9 +423,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listBccEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
expandMode: controller.bccAddressExpandMode.value,
|
||||
controller: controller.bccEmailAddressController,
|
||||
focusNode: controller.bccAddressFocusNode,
|
||||
@@ -539,7 +527,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
base64: base64,
|
||||
uploadError: uploadError
|
||||
),
|
||||
onInitialContentLoadComplete: controller.restoreCollapsibleButton,
|
||||
onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb,
|
||||
);
|
||||
}),
|
||||
),
|
||||
@@ -694,9 +682,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listToEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
fromState: controller.fromRecipientState.value,
|
||||
ccState: controller.ccRecipientState.value,
|
||||
bccState: controller.bccRecipientState.value,
|
||||
@@ -725,9 +711,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listCcEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
expandMode: controller.ccAddressExpandMode.value,
|
||||
controller: controller.ccEmailAddressController,
|
||||
focusNode: controller.ccAddressFocusNode,
|
||||
@@ -752,9 +736,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
listEmailAddress: controller.listBccEmailAddress,
|
||||
imagePaths: controller.imagePaths,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minInputLengthAutocomplete: controller
|
||||
.mailboxDashBoardController
|
||||
.minInputLengthAutocomplete,
|
||||
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||
expandMode: controller.bccAddressExpandMode.value,
|
||||
controller: controller.bccEmailAddressController,
|
||||
focusNode: controller.bccAddressFocusNode,
|
||||
@@ -857,7 +839,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
||||
base64: base64,
|
||||
uploadError: uploadError
|
||||
),
|
||||
onInitialContentLoadComplete: controller.restoreCollapsibleButton,
|
||||
onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb,
|
||||
)),
|
||||
),
|
||||
),
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:rich_text_composer/views/commons/logger.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
|
||||
extension SanitizeSignatureInEmailContentExtension on ComposerController {
|
||||
|
||||
Future<void> restoreCollapsibleSignatureButton(String? emailContent) async {
|
||||
try {
|
||||
if (emailContent == null) return;
|
||||
|
||||
final emailDocument = parse(emailContent);
|
||||
final existedSignatureButton = emailDocument.querySelector('button.tmail-signature-button');
|
||||
if (existedSignatureButton != null) return;
|
||||
|
||||
final signature = emailDocument.querySelector('div.tmail-signature');
|
||||
if (signature == null) return;
|
||||
|
||||
restoringSignatureButton = true;
|
||||
await applySignature(signature.innerHtml);
|
||||
} catch (e) {
|
||||
logError('SanitizeSignatureInEmailContentExtension::restoreCollapsibleSignatureButton:Exception = $e');
|
||||
}
|
||||
}
|
||||
|
||||
void synchronizeInitEmailDraftHash(String? emailContent) {
|
||||
try {
|
||||
final emailDocument = parse(emailContent);
|
||||
final signatureButton = emailDocument.querySelector('button.tmail-signature-button');
|
||||
if (signatureButton == null) return;
|
||||
|
||||
restoringSignatureButton = false;
|
||||
synchronizeInitDraftHash = true;
|
||||
initEmailDraftHash();
|
||||
} catch (e) {
|
||||
logError('SanitizeSignatureInEmailContentExtension::synchronizeInitEmailDraftHash:Exception = $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
|
||||
import 'package:core/utils/list_utils.dart';
|
||||
import 'package:model/email/attachment.dart';
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:model/extensions/email_extension.dart';
|
||||
import 'package:model/extensions/list_attachment_extension.dart';
|
||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/list_shared_media_file_extension.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/upload/domain/extensions/list_file_info_extension.dart';
|
||||
|
||||
extension SetupEmailAttachmentsExtension on ComposerController {
|
||||
|
||||
void setupEmailAttachments(ComposerArguments arguments) {
|
||||
List<Attachment>? attachments;
|
||||
List<Attachment>? inlineImages;
|
||||
|
||||
switch(currentEmailActionType) {
|
||||
case EmailActionType.editSendingEmail:
|
||||
final sendingEmail = arguments.sendingEmail;
|
||||
final allAttachments = sendingEmail?.email.allAttachments;
|
||||
attachments = allAttachments?.getListAttachmentsDisplayedOutside(
|
||||
sendingEmail?.email.htmlBodyAttachments ?? [],
|
||||
);
|
||||
inlineImages = allAttachments?.listAttachmentsDisplayedInContent;
|
||||
break;
|
||||
case EmailActionType.composeFromFileShared:
|
||||
_uploadAttachmentFromFileShare(arguments.listSharedMediaFile!);
|
||||
break;
|
||||
case EmailActionType.reply:
|
||||
case EmailActionType.replyToList:
|
||||
case EmailActionType.replyAll:
|
||||
case EmailActionType.forward:
|
||||
attachments = arguments.attachments;
|
||||
inlineImages = arguments.inlineImages;
|
||||
break;
|
||||
case EmailActionType.reopenComposerBrowser:
|
||||
attachments = arguments.attachments;
|
||||
inlineImages = arguments.inlineImages;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
initAttachmentsAndInlineImages(
|
||||
attachments: attachments,
|
||||
inlineImages: inlineImages,
|
||||
);
|
||||
}
|
||||
|
||||
void _uploadAttachmentFromFileShare(List<SharedMediaFile> listSharedMediaFile) {
|
||||
final listFileInfo = listSharedMediaFile.toListFileInfo(isShared: true);
|
||||
|
||||
final tupleListFileInfo = partition(
|
||||
listFileInfo,
|
||||
(fileInfo) => fileInfo.isInline == true,
|
||||
);
|
||||
final listAttachments = tupleListFileInfo.value2;
|
||||
|
||||
uploadController.validateTotalSizeAttachmentsBeforeUpload(
|
||||
totalSizePreparedFiles: listFileInfo.totalSize,
|
||||
totalSizePreparedFilesWithDispositionAttachment: listAttachments.totalSize,
|
||||
onValidationSuccess: () => uploadAttachmentsAction(pickedFiles: listFileInfo),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/individual_header_identifier.dart';
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:model/extensions/email_extension.dart';
|
||||
import 'package:model/extensions/list_attachment_extension.dart';
|
||||
import 'package:model/extensions/list_email_content_extension.dart';
|
||||
import 'package:tmail_ui_user/features/composer/domain/state/restore_email_inline_images_state.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_email_request_read_receipt_flag_for_edit_draft_extension.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/setup_selected_identity_extension.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/transform_html_email_content_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
extension SetupEmailContentExtension on ComposerController {
|
||||
|
||||
Future<void> setupEmailContent(ComposerArguments arguments) async {
|
||||
switch(currentEmailActionType) {
|
||||
case EmailActionType.editAsNewEmail:
|
||||
case EmailActionType.editDraft:
|
||||
final session = mailboxDashBoardController.sessionCurrent;
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
final emailId = arguments.presentationEmail?.id;
|
||||
|
||||
if (session == null) {
|
||||
emailContentsViewState.value = Left(GetEmailContentFailure(NotFoundSessionException()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (accountId == null) {
|
||||
emailContentsViewState.value = Left(GetEmailContentFailure(NotFoundAccountIdException()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (emailId == null) {
|
||||
emailContentsViewState.value = Left(GetEmailContentFailure(NotFoundEmailException()));
|
||||
return;
|
||||
}
|
||||
|
||||
final resultState = await getEmailContentInteractor.execute(
|
||||
session,
|
||||
accountId,
|
||||
emailId,
|
||||
mailboxDashBoardController.baseDownloadUrl,
|
||||
TransformConfiguration.forEditDraftsEmail(),
|
||||
additionalProperties: Properties({
|
||||
IndividualHeaderIdentifier.identityHeader.value,
|
||||
}),
|
||||
).last;
|
||||
|
||||
final uiState = resultState.fold((failure) => failure, (success) => success);
|
||||
|
||||
if (uiState is GetEmailContentSuccess) {
|
||||
initAttachmentsAndInlineImages(
|
||||
attachments: uiState.attachments,
|
||||
inlineImages: uiState.inlineImages,
|
||||
);
|
||||
|
||||
if (currentEmailActionType == EmailActionType.editDraft) {
|
||||
setupEmailRequestReadReceiptFlagForEditDraft(
|
||||
uiState.emailCurrent!.hasRequestReadReceipt,
|
||||
);
|
||||
setupSelectedIdentityForEditDraft(
|
||||
uiState.emailCurrent!.identityIdFromHeader,
|
||||
);
|
||||
}
|
||||
|
||||
emailContentsViewState.value = Right(uiState);
|
||||
} else if (uiState is GetEmailContentFromCacheSuccess) {
|
||||
initAttachmentsAndInlineImages(
|
||||
attachments: uiState.attachments,
|
||||
inlineImages: uiState.inlineImages,
|
||||
);
|
||||
|
||||
if (currentEmailActionType == EmailActionType.editDraft) {
|
||||
setupEmailRequestReadReceiptFlagForEditDraft(
|
||||
uiState.emailCurrent.hasRequestReadReceipt,
|
||||
);
|
||||
}
|
||||
|
||||
emailContentsViewState.value = Right(uiState);
|
||||
} else if (uiState is GetEmailContentFailure) {
|
||||
emailContentsViewState.value = Left(uiState);
|
||||
consumeState(Stream.value(Left(uiState)));
|
||||
} else {
|
||||
emailContentsViewState.value = Right(UIState.idle);
|
||||
}
|
||||
break;
|
||||
case EmailActionType.editSendingEmail:
|
||||
final sendingEmail = arguments.sendingEmail;
|
||||
final successState = GetEmailContentSuccess(
|
||||
htmlEmailContent: sendingEmail?.presentationEmail.emailContentList.asHtmlString ?? '',
|
||||
emailCurrent: arguments.sendingEmail?.email,
|
||||
);
|
||||
if (PlatformInfo.isWeb) {
|
||||
setTextEditorWeb(successState.htmlEmailContent);
|
||||
}
|
||||
emailContentsViewState.value = Right(successState);
|
||||
break;
|
||||
case EmailActionType.composeFromContentShared:
|
||||
final successState = GetEmailContentSuccess(htmlEmailContent: arguments.emailContents ?? '');
|
||||
if (PlatformInfo.isWeb) {
|
||||
setTextEditorWeb(successState.htmlEmailContent);
|
||||
}
|
||||
emailContentsViewState.value = Right(successState);
|
||||
break;
|
||||
case EmailActionType.composeFromMailtoUri:
|
||||
final successState = GetEmailContentSuccess(htmlEmailContent: arguments.body ?? '');
|
||||
if (PlatformInfo.isWeb) {
|
||||
setTextEditorWeb(successState.htmlEmailContent);
|
||||
}
|
||||
emailContentsViewState.value = Right(successState);
|
||||
break;
|
||||
case EmailActionType.reply:
|
||||
case EmailActionType.replyToList:
|
||||
case EmailActionType.replyAll:
|
||||
case EmailActionType.forward:
|
||||
if (arguments.emailContents?.trim().isNotEmpty != true) {
|
||||
emailContentsViewState.value = Left(GetEmailContentFailure(EmptyEmailContentException()));
|
||||
} else {
|
||||
final resultState = await transformHtmlEmailContentInteractor.execute(
|
||||
arguments.emailContents ?? '',
|
||||
TransformConfiguration.forReplyForwardEmail(),
|
||||
).last;
|
||||
|
||||
final uiState = resultState.fold((failure) => failure, (success) => success);
|
||||
|
||||
if (uiState is TransformHtmlEmailContentSuccess) {
|
||||
final emailContent = uiState.htmlContent;
|
||||
|
||||
if (PlatformInfo.isWeb &&
|
||||
currentContext != null &&
|
||||
arguments.presentationEmail != null) {
|
||||
final emailContentQuoted = getEmailContentQuotedAsHtml(
|
||||
locale: Localizations.localeOf(currentContext!),
|
||||
appLocalizations: AppLocalizations.of(currentContext!),
|
||||
emailContent: emailContent,
|
||||
emailActionType: currentEmailActionType!,
|
||||
presentationEmail: arguments.presentationEmail!,
|
||||
);
|
||||
|
||||
setTextEditorWeb(emailContentQuoted);
|
||||
}
|
||||
|
||||
final successState = GetEmailContentSuccess(htmlEmailContent: emailContent);
|
||||
emailContentsViewState.value = Right(successState);
|
||||
} else if (uiState is TransformHtmlEmailContentFailure) {
|
||||
emailContentsViewState.value = Left(GetEmailContentFailure(uiState.exception));
|
||||
consumeState(Stream.value(Left(GetEmailContentFailure(uiState.exception))));
|
||||
} else {
|
||||
emailContentsViewState.value = Right(UIState.idle);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EmailActionType.reopenComposerBrowser:
|
||||
final inlineImages = arguments.inlineImages ?? [];
|
||||
final content = arguments.emailContents ?? '';
|
||||
final displayMode = arguments.displayMode;
|
||||
|
||||
if (content.trim().isEmpty) {
|
||||
emailContentsViewState.value = Left(GetEmailContentFailure(EmptyEmailContentException()));
|
||||
} else if (displayMode.isNotContentVisible()) {
|
||||
final successState = GetEmailContentSuccess(htmlEmailContent: content);
|
||||
setTextEditorWeb(content);
|
||||
emailContentsViewState.value = Right(successState);
|
||||
} else {
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
final downloadUrl = mailboxDashBoardController.baseDownloadUrl;
|
||||
|
||||
if (restoreEmailInlineImagesInteractor == null ||
|
||||
accountId == null ||
|
||||
downloadUrl.isEmpty) {
|
||||
emailContentsViewState.value = Left(GetEmailContentFailure(NotFoundAccountIdException()));
|
||||
return;
|
||||
}
|
||||
|
||||
final resultState = await restoreEmailInlineImagesInteractor!.execute(
|
||||
htmlContent: content,
|
||||
transformConfiguration: TransformConfiguration.forRestoreEmail(),
|
||||
mapUrlDownloadCID: inlineImages.toMapCidImageDownloadUrl(
|
||||
accountId: accountId,
|
||||
downloadUrl: downloadUrl,
|
||||
),
|
||||
).last;
|
||||
|
||||
final uiState = resultState.fold((failure) => failure, (success) => success);
|
||||
|
||||
if (uiState is RestoreEmailInlineImagesSuccess) {
|
||||
final emailContent = uiState.emailContent;
|
||||
final successState = GetEmailContentSuccess(htmlEmailContent: emailContent);
|
||||
setTextEditorWeb(emailContent);
|
||||
emailContentsViewState.value = Right(successState);
|
||||
} else if (uiState is RestoreEmailInlineImagesFailure) {
|
||||
emailContentsViewState.value = Left(GetEmailContentFailure(uiState.exception));
|
||||
consumeState(Stream.value(Left(GetEmailContentFailure(uiState.exception))));
|
||||
} else {
|
||||
emailContentsViewState.value = Right(UIState.idle);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EmailActionType.composeFromUnsubscribeMailtoLink:
|
||||
final successState = GetEmailContentSuccess(htmlEmailContent: arguments.body ?? '');
|
||||
if (PlatformInfo.isWeb) {
|
||||
setTextEditorWeb(successState.htmlEmailContent);
|
||||
}
|
||||
emailContentsViewState.value = Right(successState);
|
||||
break;
|
||||
default:
|
||||
emailContentsViewState.value = Right(UIState.idle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
|
||||
extension SetupEmailImportantFlagExtension on ComposerController {
|
||||
|
||||
void setupEmailImportantFlag(ComposerArguments arguments) {
|
||||
switch(currentEmailActionType) {
|
||||
case EmailActionType.editAsNewEmail:
|
||||
case EmailActionType.editDraft:
|
||||
isMarkAsImportant.value = arguments.presentationEmail?.isMarkAsImportant ?? false;
|
||||
break;
|
||||
case EmailActionType.reopenComposerBrowser:
|
||||
isMarkAsImportant.value = arguments.isMarkAsImportant ?? false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
|
||||
extension SetupEmailOtherComponentsExtension on ComposerController {
|
||||
|
||||
void setupEmailOtherComponents(ComposerArguments arguments) {
|
||||
switch(currentEmailActionType) {
|
||||
case EmailActionType.editDraft:
|
||||
emailIdEditing = arguments.presentationEmail?.id;
|
||||
break;
|
||||
case EmailActionType.editSendingEmail:
|
||||
emailIdEditing = arguments.sendingEmail?.presentationEmail.id;
|
||||
break;
|
||||
case EmailActionType.reopenComposerBrowser:
|
||||
screenDisplayMode.value = arguments.displayMode;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
minInputLengthAutocomplete = mailboxDashBoardController.minInputLengthAutocomplete;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:model/mailbox/expand_mode.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
|
||||
extension SetupEmailRecipientsExtension on ComposerController {
|
||||
|
||||
void setupEmailRecipients(ComposerArguments arguments) {
|
||||
switch(currentEmailActionType) {
|
||||
case EmailActionType.editAsNewEmail:
|
||||
case EmailActionType.editDraft:
|
||||
case EmailActionType.reopenComposerBrowser:
|
||||
initEmailAddress(
|
||||
presentationEmail: arguments.presentationEmail!,
|
||||
actionType: currentEmailActionType!,
|
||||
);
|
||||
break;
|
||||
case EmailActionType.editSendingEmail:
|
||||
initEmailAddress(
|
||||
presentationEmail: arguments.sendingEmail!.presentationEmail,
|
||||
actionType: currentEmailActionType!,
|
||||
);
|
||||
break;
|
||||
case EmailActionType.composeFromEmailAddress:
|
||||
case EmailActionType.composeFromUnsubscribeMailtoLink:
|
||||
final emailAddressOfTo = arguments.listEmailAddress ?? [];
|
||||
if (emailAddressOfTo.isNotEmpty) {
|
||||
listToEmailAddress.addAll(emailAddressOfTo);
|
||||
isInitialRecipient.value = true;
|
||||
toAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||
}
|
||||
break;
|
||||
case EmailActionType.composeFromMailtoUri:
|
||||
final emailAddressOfTo = arguments.listEmailAddress ?? [];
|
||||
final emailAddressOfCc = arguments.cc ?? [];
|
||||
final emailAddressOfBc = arguments.bcc ?? [];
|
||||
|
||||
if (emailAddressOfTo.isNotEmpty) {
|
||||
listToEmailAddress.addAll(emailAddressOfTo);
|
||||
isInitialRecipient.value = true;
|
||||
toAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||
}
|
||||
|
||||
if (emailAddressOfCc.isNotEmpty) {
|
||||
listCcEmailAddress = emailAddressOfCc;
|
||||
ccRecipientState.value = PrefixRecipientState.enabled;
|
||||
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||
}
|
||||
|
||||
if (emailAddressOfBc.isNotEmpty) {
|
||||
listBccEmailAddress = emailAddressOfBc;
|
||||
bccRecipientState.value = PrefixRecipientState.enabled;
|
||||
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||
}
|
||||
break;
|
||||
case EmailActionType.reply:
|
||||
case EmailActionType.replyToList:
|
||||
case EmailActionType.replyAll:
|
||||
initEmailAddress(
|
||||
presentationEmail: arguments.presentationEmail!,
|
||||
actionType: currentEmailActionType!,
|
||||
listPost: arguments.listPost,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
updateStatusEmailSendButton();
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:server_settings/server_settings/tmail_server_settings_extension.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/server_settings/domain/state/get_server_setting_state.dart';
|
||||
|
||||
extension SetupEmailRequestReadReceiptFlagExtension on ComposerController {
|
||||
|
||||
void setupEmailRequestReadReceiptFlag(ComposerArguments arguments) {
|
||||
if (currentEmailActionType == EmailActionType.reopenComposerBrowser) {
|
||||
hasRequestReadReceipt.value = arguments.hasRequestReadReceipt ?? false;
|
||||
} else if (currentEmailActionType != EmailActionType.editDraft) {
|
||||
getServerSetting();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getServerSetting() async {
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
if (accountId == null) return;
|
||||
|
||||
final resultState = await getServerSettingInteractor.execute(accountId).last;
|
||||
|
||||
final uiState = resultState.fold((failure) => failure, (success) => success);
|
||||
|
||||
if (uiState is GetServerSettingSuccess) {
|
||||
hasRequestReadReceipt.value = uiState.settingOption.isAlwaysReadReceipts;
|
||||
initEmailDraftHash();
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
|
||||
extension SetupEmailRequestReadReceiptFlagExtension on ComposerController {
|
||||
|
||||
void setupEmailRequestReadReceiptFlagForEditDraft(bool isRequestReadReceipt) {
|
||||
hasRequestReadReceipt.value = isRequestReadReceipt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
extension SetupEmailSubjectExtension on ComposerController {
|
||||
|
||||
void setupEmailSubject(ComposerArguments arguments) {
|
||||
String subject = '';
|
||||
|
||||
switch(currentEmailActionType!) {
|
||||
case EmailActionType.editAsNewEmail:
|
||||
case EmailActionType.editDraft:
|
||||
case EmailActionType.reply:
|
||||
case EmailActionType.replyToList:
|
||||
case EmailActionType.replyAll:
|
||||
case EmailActionType.forward:
|
||||
case EmailActionType.reopenComposerBrowser:
|
||||
subject = arguments.presentationEmail!.getEmailTitle().trim();
|
||||
break;
|
||||
case EmailActionType.editSendingEmail:
|
||||
subject = arguments.sendingEmail!.presentationEmail.getEmailTitle().trim();
|
||||
break;
|
||||
case EmailActionType.composeFromMailtoUri:
|
||||
case EmailActionType.composeFromUnsubscribeMailtoLink:
|
||||
subject = arguments.subject ?? '';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
final newSubject = currentEmailActionType!.getSubjectComposer(
|
||||
currentContext,
|
||||
subject,
|
||||
);
|
||||
|
||||
if (newSubject.isNotEmpty) {
|
||||
setSubjectEmail(subject);
|
||||
subjectEmailInputController.text = subject;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/list_identities_extension.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart';
|
||||
|
||||
extension SetupListIdentitiesExtension on ComposerController {
|
||||
|
||||
Future<void> setupListIdentities(ComposerArguments arguments) async {
|
||||
final identities = arguments.identities ?? [];
|
||||
if (identities.isEmpty) {
|
||||
await getAllIdentitiesAsSynchronize();
|
||||
} else {
|
||||
listFromIdentities.value = identities;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getAllIdentitiesAsSynchronize() async {
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
final session = mailboxDashBoardController.sessionCurrent;
|
||||
|
||||
if (accountId == null || session == null) return;
|
||||
|
||||
final resultState = await getAllIdentitiesInteractor.execute(
|
||||
session,
|
||||
accountId,
|
||||
).last;
|
||||
|
||||
final uiState = resultState.fold((failure) => failure, (success) => success);
|
||||
|
||||
if (uiState is GetAllIdentitiesSuccess) {
|
||||
final identitiesMayDeleted = uiState.identities?.toListMayDeleted() ?? [];
|
||||
if (identitiesMayDeleted.isNotEmpty) {
|
||||
listFromIdentities.value = identitiesMayDeleted;
|
||||
}
|
||||
} else if (uiState is GetAllIdentitiesFailure) {
|
||||
consumeState(Stream.value(Left(uiState)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:core/core.dart';
|
||||
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
|
||||
extension SetupSelectedIdentityExtension on ComposerController {
|
||||
|
||||
Future<void> setupSelectedIdentity() async {
|
||||
if (identitySelected.value != null) {
|
||||
if (PlatformInfo.isMobile && currentEmailActionType == EmailActionType.editDraft) {
|
||||
await selectIdentity(identitySelected.value);
|
||||
onCompleteSetupComposer();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (listFromIdentities.isEmpty &&
|
||||
currentEmailActionType == EmailActionType.editSendingEmail) {
|
||||
final signatureContent = await htmlEditorApi?.getSignatureContent();
|
||||
if (signatureContent?.trim().isNotEmpty == true) {
|
||||
await applySignature(signatureContent!);
|
||||
}
|
||||
}
|
||||
|
||||
if (listFromIdentities.isNotEmpty) {
|
||||
final currentIdentity = _findIdentityById(
|
||||
composerArguments.value?.selectedIdentityId,
|
||||
) ?? listFromIdentities.first;
|
||||
|
||||
if (currentEmailActionType == EmailActionType.editDraft ||
|
||||
currentEmailActionType == EmailActionType.reopenComposerBrowser &&
|
||||
savedActionType == EmailActionType.editDraft) {
|
||||
identitySelected.value = currentIdentity;
|
||||
} else if (currentEmailActionType == EmailActionType.editAsNewEmail) {
|
||||
identitySelected.value = currentIdentity;
|
||||
await selectIdentity(currentIdentity);
|
||||
} else {
|
||||
await selectIdentity(currentIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
onCompleteSetupComposer();
|
||||
}
|
||||
|
||||
Future<void> setupSelectedIdentityWithoutApplySignature() async {
|
||||
if (identitySelected.value == null && listFromIdentities.isNotEmpty) {
|
||||
final currentIdentity = _findIdentityById(
|
||||
composerArguments.value?.selectedIdentityId,
|
||||
) ?? listFromIdentities.first;
|
||||
|
||||
identitySelected.value = currentIdentity;
|
||||
}
|
||||
|
||||
onCompleteSetupComposer();
|
||||
}
|
||||
|
||||
void setupSelectedIdentityForEditDraft(IdentityId? identityId) {
|
||||
if (identityId == null) return;
|
||||
|
||||
if (listFromIdentities.isNotEmpty) {
|
||||
final currentIdentity = _findIdentityById(identityId);
|
||||
identitySelected.value = currentIdentity;
|
||||
}
|
||||
}
|
||||
|
||||
Identity? _findIdentityById(IdentityId? identityId) {
|
||||
return listFromIdentities
|
||||
.firstWhereOrNull((identity) => identity.id == identityId);
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,9 @@ class CreateEmailRequest with EquatableMixin {
|
||||
final Uri? uploadUri;
|
||||
final int? composerIndex;
|
||||
final String? composerId;
|
||||
final int? savedDraftHash;
|
||||
final EmailActionType? savedActionType;
|
||||
final EmailId? savedEmailDraftId;
|
||||
|
||||
CreateEmailRequest({
|
||||
required this.session,
|
||||
@@ -71,6 +74,9 @@ class CreateEmailRequest with EquatableMixin {
|
||||
this.uploadUri,
|
||||
this.composerIndex,
|
||||
this.composerId,
|
||||
this.savedDraftHash,
|
||||
this.savedActionType,
|
||||
this.savedEmailDraftId,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -103,5 +109,8 @@ class CreateEmailRequest with EquatableMixin {
|
||||
uploadUri,
|
||||
composerIndex,
|
||||
composerId,
|
||||
savedDraftHash,
|
||||
savedActionType,
|
||||
savedEmailDraftId,
|
||||
];
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:model/email/attachment.dart';
|
||||
|
||||
part 'saved_email_draft.g.dart';
|
||||
|
||||
@JsonSerializable(includeIfNull: false, explicitToJson: true)
|
||||
class SavedEmailDraft with EquatableMixin {
|
||||
final String content;
|
||||
final String subject;
|
||||
@@ -28,29 +34,20 @@ class SavedEmailDraft with EquatableMixin {
|
||||
this.isMarkAsImportant = false,
|
||||
});
|
||||
|
||||
factory SavedEmailDraft.empty() {
|
||||
return SavedEmailDraft(
|
||||
subject: '',
|
||||
content: '',
|
||||
toRecipients: {},
|
||||
ccRecipients: {},
|
||||
bccRecipients: {},
|
||||
replyToRecipients: {},
|
||||
attachments: [],
|
||||
identity: null,
|
||||
hasReadReceipt: false,
|
||||
);
|
||||
}
|
||||
factory SavedEmailDraft.fromJson(Map<String, dynamic> json) => _$SavedEmailDraftFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SavedEmailDraftToJson(this);
|
||||
|
||||
String asString() => jsonEncode(toJson());
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
content,
|
||||
subject,
|
||||
// Prevent identical Set<EmailAddress>
|
||||
{0: toRecipients},
|
||||
{1: ccRecipients},
|
||||
{2: bccRecipients},
|
||||
{3: replyToRecipients},
|
||||
toRecipients,
|
||||
ccRecipients,
|
||||
bccRecipients,
|
||||
replyToRecipients,
|
||||
attachments,
|
||||
identity,
|
||||
hasReadReceipt,
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
enum SignatureStatus {
|
||||
editedAvailable,
|
||||
editedApplied,
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import 'package:model/email/email_action_type.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/view/editor_view_mixin.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/mobile_editor_widget.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/transform_html_email_content_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
||||
@@ -107,14 +106,14 @@ class MobileEditorView extends StatelessWidget with EditorViewMixin {
|
||||
);
|
||||
},
|
||||
(success) {
|
||||
if (success is TransformHtmlEmailContentLoading) {
|
||||
if (success is GetEmailContentLoading) {
|
||||
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
||||
} else {
|
||||
final emailContentQuoted = getEmailContentQuotedAsHtml(
|
||||
locale: Localizations.localeOf(context),
|
||||
appLocalizations: AppLocalizations.of(context),
|
||||
emailContent: success is TransformHtmlEmailContentSuccess
|
||||
? success.htmlContent
|
||||
emailContent: success is GetEmailContentSuccess
|
||||
? success.htmlEmailContent
|
||||
: '',
|
||||
emailActionType: arguments!.emailActionType,
|
||||
presentationEmail: arguments!.presentationEmail!
|
||||
|
||||
@@ -7,11 +7,9 @@ import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:html_editor_enhanced/html_editor.dart';
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:tmail_ui_user/features/composer/domain/state/restore_email_inline_images_state.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/view/editor_view_mixin.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/web/web_editor_widget.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/transform_html_email_content_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
||||
@@ -115,12 +113,15 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
||||
onInitialContentLoadComplete: onInitialContentLoadComplete,
|
||||
),
|
||||
(success) {
|
||||
if (success is GetEmailContentLoading || success is RestoringEmailInlineImages) {
|
||||
if (success is GetEmailContentLoading) {
|
||||
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
||||
} else {
|
||||
var newContent = success is GetEmailContentSuccess
|
||||
? success.htmlEmailContent
|
||||
: HtmlExtension.editorStartTags;
|
||||
var newContent = '';
|
||||
if (success is GetEmailContentSuccess) {
|
||||
newContent = success.htmlEmailContent;
|
||||
} else if (success is GetEmailContentFromCacheSuccess) {
|
||||
newContent = success.htmlEmailContent;
|
||||
}
|
||||
if (newContent.isEmpty) {
|
||||
newContent = HtmlExtension.editorStartTags;
|
||||
}
|
||||
@@ -181,14 +182,14 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
||||
);
|
||||
},
|
||||
(success) {
|
||||
if (success is TransformHtmlEmailContentLoading) {
|
||||
if (success is GetEmailContentLoading) {
|
||||
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
||||
} else {
|
||||
final emailContentQuoted = getEmailContentQuotedAsHtml(
|
||||
locale: Localizations.localeOf(context),
|
||||
appLocalizations: AppLocalizations.of(context),
|
||||
emailContent: success is TransformHtmlEmailContentSuccess
|
||||
? success.htmlContent
|
||||
emailContent: success is GetEmailContentSuccess
|
||||
? success.htmlEmailContent
|
||||
: '',
|
||||
emailActionType: arguments!.emailActionType,
|
||||
presentationEmail: arguments!.presentationEmail!
|
||||
|
||||
@@ -190,23 +190,24 @@ class HtmlAnalyzer {
|
||||
}
|
||||
|
||||
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent}) async {
|
||||
log('HtmlAnalyzer::removeCollapsedExpandedSignatureEffect: BEFORE = $emailContent');
|
||||
final document = parse(emailContent);
|
||||
final signatureElements = document.querySelectorAll('div.tmail-signature');
|
||||
await Future.wait(signatureElements.map((signatureTag) async {
|
||||
final signatureChildren = signatureTag.children;
|
||||
for (var child in signatureChildren) {
|
||||
log('HtmlAnalyzer::removeCollapsedExpandedSignatureEffect: CHILD = ${child.outerHtml}');
|
||||
if (child.attributes['class']?.contains('tmail-signature-button') == true) {
|
||||
child.remove();
|
||||
} else if (child.attributes['class']?.contains('tmail-signature-content') == true) {
|
||||
signatureTag.innerHtml = child.innerHtml;
|
||||
try {
|
||||
final document = parse(emailContent);
|
||||
final signatureElements = document.querySelectorAll('div.tmail-signature');
|
||||
await Future.wait(signatureElements.map((signatureTag) async {
|
||||
final signatureChildren = signatureTag.children;
|
||||
for (var child in signatureChildren) {
|
||||
if (child.attributes['class']?.contains('tmail-signature-button') == true) {
|
||||
child.remove();
|
||||
} else if (child.attributes['class']?.contains('tmail-signature-content') == true) {
|
||||
signatureTag.innerHtml = child.innerHtml;
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
final newContent = document.body?.innerHtml ?? emailContent;
|
||||
log('HtmlAnalyzer::removeCollapsedExpandedSignatureEffect: AFTER = $newContent');
|
||||
return newContent;
|
||||
}));
|
||||
return document.body?.innerHtml ?? emailContent;
|
||||
} catch (e) {
|
||||
logError('HtmlAnalyzer::removeCollapsedExpandedSignatureEffect:Exception = $e');
|
||||
return emailContent;
|
||||
}
|
||||
}
|
||||
|
||||
Future<(Attachment attachment, String taskId)?> _retrieveAttachmentFromUpload({
|
||||
|
||||
@@ -31,13 +31,13 @@ class GetEmailContentFromCacheSuccess extends UIState {
|
||||
final String htmlEmailContent;
|
||||
final List<Attachment>? attachments;
|
||||
final List<Attachment>? inlineImages;
|
||||
final Email? emailCurrent;
|
||||
final Email emailCurrent;
|
||||
|
||||
GetEmailContentFromCacheSuccess({
|
||||
required this.htmlEmailContent,
|
||||
required this.emailCurrent,
|
||||
this.attachments,
|
||||
this.inlineImages,
|
||||
this.emailCurrent
|
||||
});
|
||||
|
||||
@override
|
||||
|
||||
@@ -619,7 +619,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
if (emailSupervisorController.presentationEmailsLoaded.length > ThreadConstants.defaultLimit.value.toInt()) {
|
||||
emailSupervisorController.popFirstEmailQueue();
|
||||
}
|
||||
emailSupervisorController.popEmailQueue(success.emailCurrent?.id);
|
||||
emailSupervisorController.popEmailQueue(success.emailCurrent.id);
|
||||
|
||||
currentEmailLoaded.value = EmailLoaded(
|
||||
htmlContent: success.htmlEmailContent,
|
||||
@@ -629,7 +629,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
);
|
||||
emailSupervisorController.pushEmailQueue(currentEmailLoaded.value!);
|
||||
|
||||
if (success.emailCurrent?.id == currentEmail?.id) {
|
||||
if (success.emailCurrent.id == currentEmail?.id) {
|
||||
attachments.value = success.attachments ?? [];
|
||||
attachmentsViewState.value = {
|
||||
for (var attachment in attachments.where((item) => item.blobId != null))
|
||||
@@ -645,13 +645,13 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
emailContents.value = success.htmlEmailContent;
|
||||
}
|
||||
|
||||
final isShowMessageReadReceipt = success.emailCurrent?.hasReadReceipt(mailboxDashBoardController.mapMailboxById) == true;
|
||||
final isShowMessageReadReceipt = success.emailCurrent.hasReadReceipt(mailboxDashBoardController.mapMailboxById) == true;
|
||||
if (isShowMessageReadReceipt) {
|
||||
_handleReadReceipt();
|
||||
}
|
||||
|
||||
if (currentEmail?.isSubscribed == false && success.emailCurrent?.hasListUnsubscribe == true) {
|
||||
_handleUnsubscribe(success.emailCurrent!.listUnsubscribe);
|
||||
if (currentEmail?.isSubscribed == false && success.emailCurrent.hasListUnsubscribe == true) {
|
||||
_handleUnsubscribe(success.emailCurrent.listUnsubscribe);
|
||||
} else {
|
||||
emailUnsubscribe.value = null;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ class ComposerArguments extends RouterArguments {
|
||||
final List<EmailAddress>? bcc;
|
||||
final String? listPost;
|
||||
final String? composerId;
|
||||
final int? savedDraftHash;
|
||||
final EmailActionType? savedActionType;
|
||||
final EmailId? savedEmailDraftId;
|
||||
|
||||
ComposerArguments({
|
||||
this.emailActionType = EmailActionType.compose,
|
||||
@@ -60,6 +63,9 @@ class ComposerArguments extends RouterArguments {
|
||||
this.listPost,
|
||||
this.isMarkAsImportant,
|
||||
this.composerId,
|
||||
this.savedDraftHash,
|
||||
this.savedActionType,
|
||||
this.savedEmailDraftId,
|
||||
});
|
||||
|
||||
factory ComposerArguments.fromSendingEmail(SendingEmail sendingEmail) =>
|
||||
@@ -125,6 +131,9 @@ class ComposerArguments extends RouterArguments {
|
||||
displayMode: composerCache.displayMode,
|
||||
isMarkAsImportant: composerCache.isMarkAsImportant,
|
||||
composerId: composerCache.composerId,
|
||||
savedDraftHash: composerCache.draftHash,
|
||||
savedActionType: composerCache.actionType,
|
||||
savedEmailDraftId: composerCache.draftEmailId,
|
||||
);
|
||||
|
||||
factory ComposerArguments.replyEmail({
|
||||
@@ -246,6 +255,9 @@ class ComposerArguments extends RouterArguments {
|
||||
bcc,
|
||||
listPost,
|
||||
composerId,
|
||||
savedDraftHash,
|
||||
savedActionType,
|
||||
savedEmailDraftId,
|
||||
];
|
||||
|
||||
ComposerArguments copyWith({
|
||||
@@ -272,6 +284,9 @@ class ComposerArguments extends RouterArguments {
|
||||
List<EmailAddress>? bcc,
|
||||
String? listPost,
|
||||
String? composerId,
|
||||
int? savedDraftHash,
|
||||
EmailActionType? savedActionType,
|
||||
EmailId? savedEmailDraftId,
|
||||
}) {
|
||||
return ComposerArguments(
|
||||
emailActionType: emailActionType ?? this.emailActionType,
|
||||
@@ -297,6 +312,9 @@ class ComposerArguments extends RouterArguments {
|
||||
bcc: bcc ?? this.bcc,
|
||||
listPost: listPost ?? this.listPost,
|
||||
composerId: composerId ?? this.composerId,
|
||||
savedDraftHash: savedDraftHash ?? this.savedDraftHash,
|
||||
savedActionType: savedActionType ?? this.savedActionType,
|
||||
savedEmailDraftId: savedEmailDraftId ?? this.savedEmailDraftId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:jmap_dart_client/http/converter/email_id_nullable_converter.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
|
||||
|
||||
part 'composer_cache.g.dart';
|
||||
|
||||
@JsonSerializable(explicitToJson: true, includeIfNull: false)
|
||||
@JsonSerializable(
|
||||
explicitToJson: true,
|
||||
includeIfNull: false,
|
||||
converters: [
|
||||
EmailIdNullableConverter(),
|
||||
]
|
||||
)
|
||||
class ComposerCache with EquatableMixin {
|
||||
|
||||
final Email? email;
|
||||
@@ -14,6 +22,9 @@ class ComposerCache with EquatableMixin {
|
||||
final ScreenDisplayMode displayMode;
|
||||
final int? composerIndex;
|
||||
final String? composerId;
|
||||
final int? draftHash;
|
||||
final EmailActionType? actionType;
|
||||
final EmailId? draftEmailId;
|
||||
|
||||
ComposerCache({
|
||||
this.email,
|
||||
@@ -22,6 +33,9 @@ class ComposerCache with EquatableMixin {
|
||||
this.displayMode = ScreenDisplayMode.normal,
|
||||
this.composerIndex,
|
||||
this.composerId,
|
||||
this.draftHash,
|
||||
this.actionType,
|
||||
this.draftEmailId,
|
||||
});
|
||||
|
||||
factory ComposerCache.fromJson(Map<String, dynamic> json) => _$ComposerCacheFromJson(json);
|
||||
@@ -36,5 +50,8 @@ class ComposerCache with EquatableMixin {
|
||||
displayMode,
|
||||
composerIndex,
|
||||
composerId,
|
||||
draftHash,
|
||||
actionType,
|
||||
draftEmailId,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:jmap_dart_client/http/converter/id_nullable_converter.dart';
|
||||
import 'package:jmap_dart_client/http/converter/media_type_nullable_converter.dart';
|
||||
import 'package:jmap_dart_client/http/converter/part_id_nullable_converter.dart';
|
||||
import 'package:jmap_dart_client/http/converter/unsigned_int_nullable_converter.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:uri/uri.dart';
|
||||
|
||||
part 'attachment.g.dart';
|
||||
|
||||
@JsonSerializable(
|
||||
includeIfNull: false,
|
||||
explicitToJson: true,
|
||||
converters: [
|
||||
PartIdNullableConverter(),
|
||||
IdNullableConverter(),
|
||||
UnsignedIntNullableConverter(),
|
||||
MediaTypeNullableConverter(),
|
||||
],
|
||||
)
|
||||
class Attachment with EquatableMixin {
|
||||
|
||||
static const String eventICSSubtype = 'ics';
|
||||
@@ -62,6 +79,10 @@ class Attachment with EquatableMixin {
|
||||
}
|
||||
}
|
||||
|
||||
factory Attachment.fromJson(Map<String, dynamic> json) => _$AttachmentFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$AttachmentToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
partId,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import 'package:core/utils/string_convert.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:model/email/email_content_type.dart';
|
||||
|
||||
class EmailContent with EquatableMixin {
|
||||
|
||||
static const defaultHtmlTagsWrapContent = '<html><head></head><body></body></html>';
|
||||
|
||||
final String content;
|
||||
final EmailContentType type;
|
||||
|
||||
@@ -23,28 +21,6 @@ extension EmailContentExtension on EmailContent {
|
||||
.replaceAll('\n', '<br/>')
|
||||
.replaceAll('\n\n', '<br/>');
|
||||
}
|
||||
final newContent = _getContentOriginal(content);
|
||||
return newContent;
|
||||
}
|
||||
|
||||
String _getContentOriginal(String content) {
|
||||
if (content == EmailContent.defaultHtmlTagsWrapContent ||
|
||||
content.trim().isEmpty) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const firstTags = '<html><head></head><body>';
|
||||
const latestTags = '</body></html>';
|
||||
if (content.startsWith(firstTags) && content.endsWith(latestTags)) {
|
||||
const firstIndex = firstTags.length;
|
||||
final latestIndex = content.length - latestTags.length;
|
||||
|
||||
if (latestIndex > firstIndex) {
|
||||
final contentOriginal = content.substring(firstIndex, latestIndex);
|
||||
return contentOriginal;
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
return StringConvert.getContentOriginal(content);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,9 +26,9 @@ void main() {
|
||||
final props = savedEmailDraft.props;
|
||||
|
||||
// assert
|
||||
expect(props[2], equals({0: savedEmailDraft.toRecipients}));
|
||||
expect(props[3], equals({1: savedEmailDraft.ccRecipients}));
|
||||
expect(props[4], equals({2: savedEmailDraft.bccRecipients}));
|
||||
expect(props[2], equals(savedEmailDraft.toRecipients));
|
||||
expect(props[3], equals(savedEmailDraft.ccRecipients));
|
||||
expect(props[4], equals(savedEmailDraft.bccRecipients));
|
||||
});
|
||||
|
||||
test(
|
||||
@@ -123,11 +123,11 @@ void main() {
|
||||
attachments: [],
|
||||
hasReadReceipt: false
|
||||
);
|
||||
final hashCodeBeforeChange = savedEmailDraft.hashCode;
|
||||
final hashCodeBeforeChange = savedEmailDraft.asString().hashCode;
|
||||
|
||||
// act
|
||||
listToRecipients.add(EmailAddress('to name 2', 'to email 2'));
|
||||
final hashCodeAfterChange = savedEmailDraft.hashCode;
|
||||
final hashCodeAfterChange = savedEmailDraft.asString().hashCode;
|
||||
|
||||
// assert
|
||||
expect(hashCodeBeforeChange, isNot(hashCodeAfterChange));
|
||||
@@ -163,7 +163,10 @@ void main() {
|
||||
);
|
||||
|
||||
// assert
|
||||
expect(savedEmailDraft.hashCode, equals(savedEmailDraft2.hashCode));
|
||||
expect(
|
||||
savedEmailDraft.asString().hashCode,
|
||||
equals(savedEmailDraft2.asString().hashCode),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user