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();
|
final currentStyle = signature.attributes['style']?.trim();
|
||||||
if (currentStyle == null) {
|
if (currentStyle == null) {
|
||||||
signature.attributes['style'] = 'display: none;';
|
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(';')) {
|
} else if (currentStyle.endsWith(';')) {
|
||||||
signature.attributes['style'] = '$currentStyle display: none;';
|
signature.attributes['style'] = '$currentStyle display: none;';
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:core/domain/exceptions/string_exception.dart';
|
import 'package:core/domain/exceptions/string_exception.dart';
|
||||||
|
import 'package:html/parser.dart';
|
||||||
import 'package:http_parser/http_parser.dart';
|
import 'package:http_parser/http_parser.dart';
|
||||||
|
|
||||||
class StringConvert {
|
class StringConvert {
|
||||||
@@ -107,4 +108,16 @@ class StringConvert {
|
|||||||
return null;
|
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;
|
emailContent = tupleContentInlineAttachments.value1;
|
||||||
emailAttachments.addAll(tupleContentInlineAttachments.value2);
|
emailAttachments.addAll(tupleContentInlineAttachments.value2);
|
||||||
|
|
||||||
emailContent = await _removeCollapsedExpandedSignatureEffect(emailContent: emailContent);
|
emailContent = await removeCollapsedExpandedSignatureEffect(emailContent: emailContent);
|
||||||
|
|
||||||
final userAgent = await _applicationManager.generateApplicationUserAgent();
|
final userAgent = await _applicationManager.generateApplicationUserAgent();
|
||||||
final emailBodyPartId = PartId(_uuid.v1());
|
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 {
|
try {
|
||||||
return _htmlDataSource.removeCollapsedExpandedSignatureEffect(emailContent: emailContent);
|
return _htmlDataSource.removeCollapsedExpandedSignatureEffect(emailContent: emailContent);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -15,4 +15,6 @@ abstract class ComposerRepository {
|
|||||||
Future<UploadAttachment> uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken});
|
Future<UploadAttachment> uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken});
|
||||||
|
|
||||||
Future<String?> downloadImageAsBase64(String url, String cid, FileInfo fileInfo, {double? maxWidth, bool? compress});
|
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,
|
displayMode: createEmailRequest.displayMode,
|
||||||
composerIndex: createEmailRequest.composerIndex,
|
composerIndex: createEmailRequest.composerIndex,
|
||||||
composerId: createEmailRequest.composerId,
|
composerId: createEmailRequest.composerId,
|
||||||
|
draftHash: createEmailRequest.savedDraftHash,
|
||||||
|
actionType: createEmailRequest.savedActionType,
|
||||||
|
draftEmailId: createEmailRequest.draftsEmailId,
|
||||||
));
|
));
|
||||||
return Right(SaveComposerCacheSuccess());
|
return Right(SaveComposerCacheSuccess());
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
|
|||||||
@@ -329,6 +329,7 @@ class ComposerBindings extends BaseBindings {
|
|||||||
Get.find<CreateNewAndSendEmailInteractor>(tag: composerId),
|
Get.find<CreateNewAndSendEmailInteractor>(tag: composerId),
|
||||||
Get.find<CreateNewAndSaveEmailToDraftsInteractor>(tag: composerId),
|
Get.find<CreateNewAndSaveEmailToDraftsInteractor>(tag: composerId),
|
||||||
Get.find<PrintEmailInteractor>(tag: composerId),
|
Get.find<PrintEmailInteractor>(tag: composerId),
|
||||||
|
Get.find<ComposerRepository>(tag: composerId),
|
||||||
composerId: composerId,
|
composerId: composerId,
|
||||||
composerArgs: composerArguments,
|
composerArgs: composerArguments,
|
||||||
), tag: composerId);
|
), tag: composerId);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -127,9 +127,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listToEmailAddress,
|
listEmailAddress: controller.listToEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
fromState: controller.fromRecipientState.value,
|
fromState: controller.fromRecipientState.value,
|
||||||
ccState: controller.ccRecipientState.value,
|
ccState: controller.ccRecipientState.value,
|
||||||
bccState: controller.bccRecipientState.value,
|
bccState: controller.bccRecipientState.value,
|
||||||
@@ -157,9 +155,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listCcEmailAddress,
|
listEmailAddress: controller.listCcEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
expandMode: controller.ccAddressExpandMode.value,
|
expandMode: controller.ccAddressExpandMode.value,
|
||||||
controller: controller.ccEmailAddressController,
|
controller: controller.ccEmailAddressController,
|
||||||
focusNode: controller.ccAddressFocusNode,
|
focusNode: controller.ccAddressFocusNode,
|
||||||
@@ -186,9 +182,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listBccEmailAddress,
|
listEmailAddress: controller.listBccEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
expandMode: controller.bccAddressExpandMode.value,
|
expandMode: controller.bccAddressExpandMode.value,
|
||||||
controller: controller.bccEmailAddressController,
|
controller: controller.bccEmailAddressController,
|
||||||
focusNode: controller.bccAddressFocusNode,
|
focusNode: controller.bccAddressFocusNode,
|
||||||
@@ -320,9 +314,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listToEmailAddress,
|
listEmailAddress: controller.listToEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
fromState: controller.fromRecipientState.value,
|
fromState: controller.fromRecipientState.value,
|
||||||
ccState: controller.ccRecipientState.value,
|
ccState: controller.ccRecipientState.value,
|
||||||
bccState: controller.bccRecipientState.value,
|
bccState: controller.bccRecipientState.value,
|
||||||
@@ -349,9 +341,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listCcEmailAddress,
|
listEmailAddress: controller.listCcEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
expandMode: controller.ccAddressExpandMode.value,
|
expandMode: controller.ccAddressExpandMode.value,
|
||||||
controller: controller.ccEmailAddressController,
|
controller: controller.ccEmailAddressController,
|
||||||
focusNode: controller.ccAddressFocusNode,
|
focusNode: controller.ccAddressFocusNode,
|
||||||
@@ -373,9 +363,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listBccEmailAddress,
|
listEmailAddress: controller.listBccEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
expandMode: controller.bccAddressExpandMode.value,
|
expandMode: controller.bccAddressExpandMode.value,
|
||||||
controller: controller.bccEmailAddressController,
|
controller: controller.bccEmailAddressController,
|
||||||
focusNode: controller.bccAddressFocusNode,
|
focusNode: controller.bccAddressFocusNode,
|
||||||
|
|||||||
@@ -98,9 +98,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listToEmailAddress,
|
listEmailAddress: controller.listToEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
fromState: controller.fromRecipientState.value,
|
fromState: controller.fromRecipientState.value,
|
||||||
ccState: controller.ccRecipientState.value,
|
ccState: controller.ccRecipientState.value,
|
||||||
bccState: controller.bccRecipientState.value,
|
bccState: controller.bccRecipientState.value,
|
||||||
@@ -129,9 +127,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listCcEmailAddress,
|
listEmailAddress: controller.listCcEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
expandMode: controller.ccAddressExpandMode.value,
|
expandMode: controller.ccAddressExpandMode.value,
|
||||||
controller: controller.ccEmailAddressController,
|
controller: controller.ccEmailAddressController,
|
||||||
focusNode: controller.ccAddressFocusNode,
|
focusNode: controller.ccAddressFocusNode,
|
||||||
@@ -156,9 +152,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listBccEmailAddress,
|
listEmailAddress: controller.listBccEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
expandMode: controller.bccAddressExpandMode.value,
|
expandMode: controller.bccAddressExpandMode.value,
|
||||||
controller: controller.bccEmailAddressController,
|
controller: controller.bccEmailAddressController,
|
||||||
focusNode: controller.bccAddressFocusNode,
|
focusNode: controller.bccAddressFocusNode,
|
||||||
@@ -248,7 +242,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
base64: base64,
|
base64: base64,
|
||||||
uploadError: uploadError
|
uploadError: uploadError
|
||||||
),
|
),
|
||||||
onInitialContentLoadComplete: controller.restoreCollapsibleButton,
|
onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb,
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -375,9 +369,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listToEmailAddress,
|
listEmailAddress: controller.listToEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
fromState: controller.fromRecipientState.value,
|
fromState: controller.fromRecipientState.value,
|
||||||
ccState: controller.ccRecipientState.value,
|
ccState: controller.ccRecipientState.value,
|
||||||
bccState: controller.bccRecipientState.value,
|
bccState: controller.bccRecipientState.value,
|
||||||
@@ -406,9 +398,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listCcEmailAddress,
|
listEmailAddress: controller.listCcEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
expandMode: controller.ccAddressExpandMode.value,
|
expandMode: controller.ccAddressExpandMode.value,
|
||||||
controller: controller.ccEmailAddressController,
|
controller: controller.ccEmailAddressController,
|
||||||
focusNode: controller.ccAddressFocusNode,
|
focusNode: controller.ccAddressFocusNode,
|
||||||
@@ -433,9 +423,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listBccEmailAddress,
|
listEmailAddress: controller.listBccEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
expandMode: controller.bccAddressExpandMode.value,
|
expandMode: controller.bccAddressExpandMode.value,
|
||||||
controller: controller.bccEmailAddressController,
|
controller: controller.bccEmailAddressController,
|
||||||
focusNode: controller.bccAddressFocusNode,
|
focusNode: controller.bccAddressFocusNode,
|
||||||
@@ -539,7 +527,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
base64: base64,
|
base64: base64,
|
||||||
uploadError: uploadError
|
uploadError: uploadError
|
||||||
),
|
),
|
||||||
onInitialContentLoadComplete: controller.restoreCollapsibleButton,
|
onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -694,9 +682,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listToEmailAddress,
|
listEmailAddress: controller.listToEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
fromState: controller.fromRecipientState.value,
|
fromState: controller.fromRecipientState.value,
|
||||||
ccState: controller.ccRecipientState.value,
|
ccState: controller.ccRecipientState.value,
|
||||||
bccState: controller.bccRecipientState.value,
|
bccState: controller.bccRecipientState.value,
|
||||||
@@ -725,9 +711,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listCcEmailAddress,
|
listEmailAddress: controller.listCcEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
expandMode: controller.ccAddressExpandMode.value,
|
expandMode: controller.ccAddressExpandMode.value,
|
||||||
controller: controller.ccEmailAddressController,
|
controller: controller.ccEmailAddressController,
|
||||||
focusNode: controller.ccAddressFocusNode,
|
focusNode: controller.ccAddressFocusNode,
|
||||||
@@ -752,9 +736,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
listEmailAddress: controller.listBccEmailAddress,
|
listEmailAddress: controller.listBccEmailAddress,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth,
|
||||||
minInputLengthAutocomplete: controller
|
minInputLengthAutocomplete: controller.minInputLengthAutocomplete,
|
||||||
.mailboxDashBoardController
|
|
||||||
.minInputLengthAutocomplete,
|
|
||||||
expandMode: controller.bccAddressExpandMode.value,
|
expandMode: controller.bccAddressExpandMode.value,
|
||||||
controller: controller.bccEmailAddressController,
|
controller: controller.bccEmailAddressController,
|
||||||
focusNode: controller.bccAddressFocusNode,
|
focusNode: controller.bccAddressFocusNode,
|
||||||
@@ -857,7 +839,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
base64: base64,
|
base64: base64,
|
||||||
uploadError: uploadError
|
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 Uri? uploadUri;
|
||||||
final int? composerIndex;
|
final int? composerIndex;
|
||||||
final String? composerId;
|
final String? composerId;
|
||||||
|
final int? savedDraftHash;
|
||||||
|
final EmailActionType? savedActionType;
|
||||||
|
final EmailId? savedEmailDraftId;
|
||||||
|
|
||||||
CreateEmailRequest({
|
CreateEmailRequest({
|
||||||
required this.session,
|
required this.session,
|
||||||
@@ -71,6 +74,9 @@ class CreateEmailRequest with EquatableMixin {
|
|||||||
this.uploadUri,
|
this.uploadUri,
|
||||||
this.composerIndex,
|
this.composerIndex,
|
||||||
this.composerId,
|
this.composerId,
|
||||||
|
this.savedDraftHash,
|
||||||
|
this.savedActionType,
|
||||||
|
this.savedEmailDraftId,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -103,5 +109,8 @@ class CreateEmailRequest with EquatableMixin {
|
|||||||
uploadUri,
|
uploadUri,
|
||||||
composerIndex,
|
composerIndex,
|
||||||
composerId,
|
composerId,
|
||||||
|
savedDraftHash,
|
||||||
|
savedActionType,
|
||||||
|
savedEmailDraftId,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.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';
|
import 'package:model/email/attachment.dart';
|
||||||
|
|
||||||
|
part 'saved_email_draft.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable(includeIfNull: false, explicitToJson: true)
|
||||||
class SavedEmailDraft with EquatableMixin {
|
class SavedEmailDraft with EquatableMixin {
|
||||||
final String content;
|
final String content;
|
||||||
final String subject;
|
final String subject;
|
||||||
@@ -28,29 +34,20 @@ class SavedEmailDraft with EquatableMixin {
|
|||||||
this.isMarkAsImportant = false,
|
this.isMarkAsImportant = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory SavedEmailDraft.empty() {
|
factory SavedEmailDraft.fromJson(Map<String, dynamic> json) => _$SavedEmailDraftFromJson(json);
|
||||||
return SavedEmailDraft(
|
|
||||||
subject: '',
|
Map<String, dynamic> toJson() => _$SavedEmailDraftToJson(this);
|
||||||
content: '',
|
|
||||||
toRecipients: {},
|
String asString() => jsonEncode(toJson());
|
||||||
ccRecipients: {},
|
|
||||||
bccRecipients: {},
|
|
||||||
replyToRecipients: {},
|
|
||||||
attachments: [],
|
|
||||||
identity: null,
|
|
||||||
hasReadReceipt: false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
content,
|
content,
|
||||||
subject,
|
subject,
|
||||||
// Prevent identical Set<EmailAddress>
|
toRecipients,
|
||||||
{0: toRecipients},
|
ccRecipients,
|
||||||
{1: ccRecipients},
|
bccRecipients,
|
||||||
{2: bccRecipients},
|
replyToRecipients,
|
||||||
{3: replyToRecipients},
|
|
||||||
attachments,
|
attachments,
|
||||||
identity,
|
identity,
|
||||||
hasReadReceipt,
|
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/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/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/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/email/presentation/model/composer_arguments.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
||||||
@@ -107,14 +106,14 @@ class MobileEditorView extends StatelessWidget with EditorViewMixin {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
(success) {
|
(success) {
|
||||||
if (success is TransformHtmlEmailContentLoading) {
|
if (success is GetEmailContentLoading) {
|
||||||
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
||||||
} else {
|
} else {
|
||||||
final emailContentQuoted = getEmailContentQuotedAsHtml(
|
final emailContentQuoted = getEmailContentQuotedAsHtml(
|
||||||
locale: Localizations.localeOf(context),
|
locale: Localizations.localeOf(context),
|
||||||
appLocalizations: AppLocalizations.of(context),
|
appLocalizations: AppLocalizations.of(context),
|
||||||
emailContent: success is TransformHtmlEmailContentSuccess
|
emailContent: success is GetEmailContentSuccess
|
||||||
? success.htmlContent
|
? success.htmlEmailContent
|
||||||
: '',
|
: '',
|
||||||
emailActionType: arguments!.emailActionType,
|
emailActionType: arguments!.emailActionType,
|
||||||
presentationEmail: arguments!.presentationEmail!
|
presentationEmail: arguments!.presentationEmail!
|
||||||
|
|||||||
@@ -7,11 +7,9 @@ import 'package:dartz/dartz.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:html_editor_enhanced/html_editor.dart';
|
import 'package:html_editor_enhanced/html_editor.dart';
|
||||||
import 'package:model/email/email_action_type.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/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/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/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/email/presentation/model/composer_arguments.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
||||||
@@ -115,12 +113,15 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
|||||||
onInitialContentLoadComplete: onInitialContentLoadComplete,
|
onInitialContentLoadComplete: onInitialContentLoadComplete,
|
||||||
),
|
),
|
||||||
(success) {
|
(success) {
|
||||||
if (success is GetEmailContentLoading || success is RestoringEmailInlineImages) {
|
if (success is GetEmailContentLoading) {
|
||||||
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
||||||
} else {
|
} else {
|
||||||
var newContent = success is GetEmailContentSuccess
|
var newContent = '';
|
||||||
? success.htmlEmailContent
|
if (success is GetEmailContentSuccess) {
|
||||||
: HtmlExtension.editorStartTags;
|
newContent = success.htmlEmailContent;
|
||||||
|
} else if (success is GetEmailContentFromCacheSuccess) {
|
||||||
|
newContent = success.htmlEmailContent;
|
||||||
|
}
|
||||||
if (newContent.isEmpty) {
|
if (newContent.isEmpty) {
|
||||||
newContent = HtmlExtension.editorStartTags;
|
newContent = HtmlExtension.editorStartTags;
|
||||||
}
|
}
|
||||||
@@ -181,14 +182,14 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
(success) {
|
(success) {
|
||||||
if (success is TransformHtmlEmailContentLoading) {
|
if (success is GetEmailContentLoading) {
|
||||||
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
||||||
} else {
|
} else {
|
||||||
final emailContentQuoted = getEmailContentQuotedAsHtml(
|
final emailContentQuoted = getEmailContentQuotedAsHtml(
|
||||||
locale: Localizations.localeOf(context),
|
locale: Localizations.localeOf(context),
|
||||||
appLocalizations: AppLocalizations.of(context),
|
appLocalizations: AppLocalizations.of(context),
|
||||||
emailContent: success is TransformHtmlEmailContentSuccess
|
emailContent: success is GetEmailContentSuccess
|
||||||
? success.htmlContent
|
? success.htmlEmailContent
|
||||||
: '',
|
: '',
|
||||||
emailActionType: arguments!.emailActionType,
|
emailActionType: arguments!.emailActionType,
|
||||||
presentationEmail: arguments!.presentationEmail!
|
presentationEmail: arguments!.presentationEmail!
|
||||||
|
|||||||
@@ -190,23 +190,24 @@ class HtmlAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent}) async {
|
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent}) async {
|
||||||
log('HtmlAnalyzer::removeCollapsedExpandedSignatureEffect: BEFORE = $emailContent');
|
try {
|
||||||
final document = parse(emailContent);
|
final document = parse(emailContent);
|
||||||
final signatureElements = document.querySelectorAll('div.tmail-signature');
|
final signatureElements = document.querySelectorAll('div.tmail-signature');
|
||||||
await Future.wait(signatureElements.map((signatureTag) async {
|
await Future.wait(signatureElements.map((signatureTag) async {
|
||||||
final signatureChildren = signatureTag.children;
|
final signatureChildren = signatureTag.children;
|
||||||
for (var child in signatureChildren) {
|
for (var child in signatureChildren) {
|
||||||
log('HtmlAnalyzer::removeCollapsedExpandedSignatureEffect: CHILD = ${child.outerHtml}');
|
if (child.attributes['class']?.contains('tmail-signature-button') == true) {
|
||||||
if (child.attributes['class']?.contains('tmail-signature-button') == true) {
|
child.remove();
|
||||||
child.remove();
|
} else if (child.attributes['class']?.contains('tmail-signature-content') == true) {
|
||||||
} else if (child.attributes['class']?.contains('tmail-signature-content') == true) {
|
signatureTag.innerHtml = child.innerHtml;
|
||||||
signatureTag.innerHtml = child.innerHtml;
|
}
|
||||||
}
|
}
|
||||||
}
|
}));
|
||||||
}));
|
return document.body?.innerHtml ?? emailContent;
|
||||||
final newContent = document.body?.innerHtml ?? emailContent;
|
} catch (e) {
|
||||||
log('HtmlAnalyzer::removeCollapsedExpandedSignatureEffect: AFTER = $newContent');
|
logError('HtmlAnalyzer::removeCollapsedExpandedSignatureEffect:Exception = $e');
|
||||||
return newContent;
|
return emailContent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<(Attachment attachment, String taskId)?> _retrieveAttachmentFromUpload({
|
Future<(Attachment attachment, String taskId)?> _retrieveAttachmentFromUpload({
|
||||||
|
|||||||
@@ -31,13 +31,13 @@ class GetEmailContentFromCacheSuccess extends UIState {
|
|||||||
final String htmlEmailContent;
|
final String htmlEmailContent;
|
||||||
final List<Attachment>? attachments;
|
final List<Attachment>? attachments;
|
||||||
final List<Attachment>? inlineImages;
|
final List<Attachment>? inlineImages;
|
||||||
final Email? emailCurrent;
|
final Email emailCurrent;
|
||||||
|
|
||||||
GetEmailContentFromCacheSuccess({
|
GetEmailContentFromCacheSuccess({
|
||||||
required this.htmlEmailContent,
|
required this.htmlEmailContent,
|
||||||
|
required this.emailCurrent,
|
||||||
this.attachments,
|
this.attachments,
|
||||||
this.inlineImages,
|
this.inlineImages,
|
||||||
this.emailCurrent
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -619,7 +619,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
if (emailSupervisorController.presentationEmailsLoaded.length > ThreadConstants.defaultLimit.value.toInt()) {
|
if (emailSupervisorController.presentationEmailsLoaded.length > ThreadConstants.defaultLimit.value.toInt()) {
|
||||||
emailSupervisorController.popFirstEmailQueue();
|
emailSupervisorController.popFirstEmailQueue();
|
||||||
}
|
}
|
||||||
emailSupervisorController.popEmailQueue(success.emailCurrent?.id);
|
emailSupervisorController.popEmailQueue(success.emailCurrent.id);
|
||||||
|
|
||||||
currentEmailLoaded.value = EmailLoaded(
|
currentEmailLoaded.value = EmailLoaded(
|
||||||
htmlContent: success.htmlEmailContent,
|
htmlContent: success.htmlEmailContent,
|
||||||
@@ -629,7 +629,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
);
|
);
|
||||||
emailSupervisorController.pushEmailQueue(currentEmailLoaded.value!);
|
emailSupervisorController.pushEmailQueue(currentEmailLoaded.value!);
|
||||||
|
|
||||||
if (success.emailCurrent?.id == currentEmail?.id) {
|
if (success.emailCurrent.id == currentEmail?.id) {
|
||||||
attachments.value = success.attachments ?? [];
|
attachments.value = success.attachments ?? [];
|
||||||
attachmentsViewState.value = {
|
attachmentsViewState.value = {
|
||||||
for (var attachment in attachments.where((item) => item.blobId != null))
|
for (var attachment in attachments.where((item) => item.blobId != null))
|
||||||
@@ -645,13 +645,13 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
emailContents.value = success.htmlEmailContent;
|
emailContents.value = success.htmlEmailContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
final isShowMessageReadReceipt = success.emailCurrent?.hasReadReceipt(mailboxDashBoardController.mapMailboxById) == true;
|
final isShowMessageReadReceipt = success.emailCurrent.hasReadReceipt(mailboxDashBoardController.mapMailboxById) == true;
|
||||||
if (isShowMessageReadReceipt) {
|
if (isShowMessageReadReceipt) {
|
||||||
_handleReadReceipt();
|
_handleReadReceipt();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentEmail?.isSubscribed == false && success.emailCurrent?.hasListUnsubscribe == true) {
|
if (currentEmail?.isSubscribed == false && success.emailCurrent.hasListUnsubscribe == true) {
|
||||||
_handleUnsubscribe(success.emailCurrent!.listUnsubscribe);
|
_handleUnsubscribe(success.emailCurrent.listUnsubscribe);
|
||||||
} else {
|
} else {
|
||||||
emailUnsubscribe.value = null;
|
emailUnsubscribe.value = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class ComposerArguments extends RouterArguments {
|
|||||||
final List<EmailAddress>? bcc;
|
final List<EmailAddress>? bcc;
|
||||||
final String? listPost;
|
final String? listPost;
|
||||||
final String? composerId;
|
final String? composerId;
|
||||||
|
final int? savedDraftHash;
|
||||||
|
final EmailActionType? savedActionType;
|
||||||
|
final EmailId? savedEmailDraftId;
|
||||||
|
|
||||||
ComposerArguments({
|
ComposerArguments({
|
||||||
this.emailActionType = EmailActionType.compose,
|
this.emailActionType = EmailActionType.compose,
|
||||||
@@ -60,6 +63,9 @@ class ComposerArguments extends RouterArguments {
|
|||||||
this.listPost,
|
this.listPost,
|
||||||
this.isMarkAsImportant,
|
this.isMarkAsImportant,
|
||||||
this.composerId,
|
this.composerId,
|
||||||
|
this.savedDraftHash,
|
||||||
|
this.savedActionType,
|
||||||
|
this.savedEmailDraftId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ComposerArguments.fromSendingEmail(SendingEmail sendingEmail) =>
|
factory ComposerArguments.fromSendingEmail(SendingEmail sendingEmail) =>
|
||||||
@@ -125,6 +131,9 @@ class ComposerArguments extends RouterArguments {
|
|||||||
displayMode: composerCache.displayMode,
|
displayMode: composerCache.displayMode,
|
||||||
isMarkAsImportant: composerCache.isMarkAsImportant,
|
isMarkAsImportant: composerCache.isMarkAsImportant,
|
||||||
composerId: composerCache.composerId,
|
composerId: composerCache.composerId,
|
||||||
|
savedDraftHash: composerCache.draftHash,
|
||||||
|
savedActionType: composerCache.actionType,
|
||||||
|
savedEmailDraftId: composerCache.draftEmailId,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory ComposerArguments.replyEmail({
|
factory ComposerArguments.replyEmail({
|
||||||
@@ -246,6 +255,9 @@ class ComposerArguments extends RouterArguments {
|
|||||||
bcc,
|
bcc,
|
||||||
listPost,
|
listPost,
|
||||||
composerId,
|
composerId,
|
||||||
|
savedDraftHash,
|
||||||
|
savedActionType,
|
||||||
|
savedEmailDraftId,
|
||||||
];
|
];
|
||||||
|
|
||||||
ComposerArguments copyWith({
|
ComposerArguments copyWith({
|
||||||
@@ -272,6 +284,9 @@ class ComposerArguments extends RouterArguments {
|
|||||||
List<EmailAddress>? bcc,
|
List<EmailAddress>? bcc,
|
||||||
String? listPost,
|
String? listPost,
|
||||||
String? composerId,
|
String? composerId,
|
||||||
|
int? savedDraftHash,
|
||||||
|
EmailActionType? savedActionType,
|
||||||
|
EmailId? savedEmailDraftId,
|
||||||
}) {
|
}) {
|
||||||
return ComposerArguments(
|
return ComposerArguments(
|
||||||
emailActionType: emailActionType ?? this.emailActionType,
|
emailActionType: emailActionType ?? this.emailActionType,
|
||||||
@@ -297,6 +312,9 @@ class ComposerArguments extends RouterArguments {
|
|||||||
bcc: bcc ?? this.bcc,
|
bcc: bcc ?? this.bcc,
|
||||||
listPost: listPost ?? this.listPost,
|
listPost: listPost ?? this.listPost,
|
||||||
composerId: composerId ?? this.composerId,
|
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: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:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:json_annotation/json_annotation.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';
|
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
|
||||||
|
|
||||||
part 'composer_cache.g.dart';
|
part 'composer_cache.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true, includeIfNull: false)
|
@JsonSerializable(
|
||||||
|
explicitToJson: true,
|
||||||
|
includeIfNull: false,
|
||||||
|
converters: [
|
||||||
|
EmailIdNullableConverter(),
|
||||||
|
]
|
||||||
|
)
|
||||||
class ComposerCache with EquatableMixin {
|
class ComposerCache with EquatableMixin {
|
||||||
|
|
||||||
final Email? email;
|
final Email? email;
|
||||||
@@ -14,6 +22,9 @@ class ComposerCache with EquatableMixin {
|
|||||||
final ScreenDisplayMode displayMode;
|
final ScreenDisplayMode displayMode;
|
||||||
final int? composerIndex;
|
final int? composerIndex;
|
||||||
final String? composerId;
|
final String? composerId;
|
||||||
|
final int? draftHash;
|
||||||
|
final EmailActionType? actionType;
|
||||||
|
final EmailId? draftEmailId;
|
||||||
|
|
||||||
ComposerCache({
|
ComposerCache({
|
||||||
this.email,
|
this.email,
|
||||||
@@ -22,6 +33,9 @@ class ComposerCache with EquatableMixin {
|
|||||||
this.displayMode = ScreenDisplayMode.normal,
|
this.displayMode = ScreenDisplayMode.normal,
|
||||||
this.composerIndex,
|
this.composerIndex,
|
||||||
this.composerId,
|
this.composerId,
|
||||||
|
this.draftHash,
|
||||||
|
this.actionType,
|
||||||
|
this.draftEmailId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ComposerCache.fromJson(Map<String, dynamic> json) => _$ComposerCacheFromJson(json);
|
factory ComposerCache.fromJson(Map<String, dynamic> json) => _$ComposerCacheFromJson(json);
|
||||||
@@ -36,5 +50,8 @@ class ComposerCache with EquatableMixin {
|
|||||||
displayMode,
|
displayMode,
|
||||||
composerIndex,
|
composerIndex,
|
||||||
composerId,
|
composerId,
|
||||||
|
draftHash,
|
||||||
|
actionType,
|
||||||
|
draftEmailId,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,29 @@
|
|||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:http_parser/http_parser.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/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/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/core/unsigned_int.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_body_part.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';
|
import 'package:uri/uri.dart';
|
||||||
|
|
||||||
|
part 'attachment.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable(
|
||||||
|
includeIfNull: false,
|
||||||
|
explicitToJson: true,
|
||||||
|
converters: [
|
||||||
|
PartIdNullableConverter(),
|
||||||
|
IdNullableConverter(),
|
||||||
|
UnsignedIntNullableConverter(),
|
||||||
|
MediaTypeNullableConverter(),
|
||||||
|
],
|
||||||
|
)
|
||||||
class Attachment with EquatableMixin {
|
class Attachment with EquatableMixin {
|
||||||
|
|
||||||
static const String eventICSSubtype = 'ics';
|
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
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
partId,
|
partId,
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
|
import 'package:core/utils/string_convert.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:model/email/email_content_type.dart';
|
import 'package:model/email/email_content_type.dart';
|
||||||
|
|
||||||
class EmailContent with EquatableMixin {
|
class EmailContent with EquatableMixin {
|
||||||
|
|
||||||
static const defaultHtmlTagsWrapContent = '<html><head></head><body></body></html>';
|
|
||||||
|
|
||||||
final String content;
|
final String content;
|
||||||
final EmailContentType type;
|
final EmailContentType type;
|
||||||
|
|
||||||
@@ -23,28 +21,6 @@ extension EmailContentExtension on EmailContent {
|
|||||||
.replaceAll('\n', '<br/>')
|
.replaceAll('\n', '<br/>')
|
||||||
.replaceAll('\n\n', '<br/>');
|
.replaceAll('\n\n', '<br/>');
|
||||||
}
|
}
|
||||||
final newContent = _getContentOriginal(content);
|
return StringConvert.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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -26,9 +26,9 @@ void main() {
|
|||||||
final props = savedEmailDraft.props;
|
final props = savedEmailDraft.props;
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(props[2], equals({0: savedEmailDraft.toRecipients}));
|
expect(props[2], equals(savedEmailDraft.toRecipients));
|
||||||
expect(props[3], equals({1: savedEmailDraft.ccRecipients}));
|
expect(props[3], equals(savedEmailDraft.ccRecipients));
|
||||||
expect(props[4], equals({2: savedEmailDraft.bccRecipients}));
|
expect(props[4], equals(savedEmailDraft.bccRecipients));
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
@@ -123,11 +123,11 @@ void main() {
|
|||||||
attachments: [],
|
attachments: [],
|
||||||
hasReadReceipt: false
|
hasReadReceipt: false
|
||||||
);
|
);
|
||||||
final hashCodeBeforeChange = savedEmailDraft.hashCode;
|
final hashCodeBeforeChange = savedEmailDraft.asString().hashCode;
|
||||||
|
|
||||||
// act
|
// act
|
||||||
listToRecipients.add(EmailAddress('to name 2', 'to email 2'));
|
listToRecipients.add(EmailAddress('to name 2', 'to email 2'));
|
||||||
final hashCodeAfterChange = savedEmailDraft.hashCode;
|
final hashCodeAfterChange = savedEmailDraft.asString().hashCode;
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(hashCodeBeforeChange, isNot(hashCodeAfterChange));
|
expect(hashCodeBeforeChange, isNot(hashCodeAfterChange));
|
||||||
@@ -163,7 +163,10 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(savedEmailDraft.hashCode, equals(savedEmailDraft2.hashCode));
|
expect(
|
||||||
|
savedEmailDraft.asString().hashCode,
|
||||||
|
equals(savedEmailDraft2.asString().hashCode),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user