diff --git a/core/lib/core.dart b/core/lib/core.dart index 94a42cf1c..e36944332 100644 --- a/core/lib/core.dart +++ b/core/lib/core.dart @@ -7,6 +7,8 @@ export 'presentation/extensions/capitalize_extension.dart'; export 'presentation/extensions/list_extensions.dart'; export 'domain/extensions/datetime_extension.dart'; export 'presentation/extensions/html_extension.dart'; +export 'presentation/extensions/compare_string_extension.dart'; +export 'presentation/extensions/compare_list_extensions.dart'; // Exceptions export 'domain/exceptions/download_file_exception.dart'; diff --git a/core/lib/presentation/extensions/compare_list_extensions.dart b/core/lib/presentation/extensions/compare_list_extensions.dart new file mode 100644 index 000000000..09584250d --- /dev/null +++ b/core/lib/presentation/extensions/compare_list_extensions.dart @@ -0,0 +1,9 @@ + +import 'package:collection/collection.dart'; + +extension CompareListExtension on List { + bool isSame(List value) { + Function unOrdDeepEq = const DeepCollectionEquality.unordered().equals; + return unOrdDeepEq(this, value); + } +} \ No newline at end of file diff --git a/core/lib/presentation/extensions/compare_string_extension.dart b/core/lib/presentation/extensions/compare_string_extension.dart new file mode 100644 index 000000000..8ccb5eab4 --- /dev/null +++ b/core/lib/presentation/extensions/compare_string_extension.dart @@ -0,0 +1,4 @@ + +extension CompareStringExtension on String? { + bool isSame(String? value) => this == value; +} \ No newline at end of file diff --git a/core/pubspec.yaml b/core/pubspec.yaml index 8cf69db04..9ab07a007 100644 --- a/core/pubspec.yaml +++ b/core/pubspec.yaml @@ -73,6 +73,9 @@ dependencies: # url_launcher url_launcher: 6.0.17 + # collection + collection: 1.15.0 + dev_dependencies: flutter_test: sdk: flutter diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 23c23d509..00c7c0956 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -72,9 +72,9 @@ class ComposerController extends BaseController { final GetEmailContentInteractor _getEmailContentInteractor; final UpdateEmailDraftsInteractor _updateEmailDraftsInteractor; - List listToEmailAddress = []; - List listCcEmailAddress = []; - List listBccEmailAddress = []; + List listToEmailAddress = []; + List listCcEmailAddress = []; + List listBccEmailAddress = []; String? _subjectEmail; ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.localContact; HtmlEditorApi? htmlEditorApi; @@ -83,13 +83,19 @@ class ComposerController extends BaseController { final keyCcEmailAddress = GlobalKey(); final keyBccEmailAddress = GlobalKey(); + List initialAttachments = []; + void setSubjectEmail(String subject) => _subjectEmail = subject; - Future _getEmailBodyText() async { + Future _getEmailBodyText({bool onlyText = false}) async { if (kIsWeb) { return await htmlControllerBrowser.getText(); } else { - return (await htmlEditorApi?.getFullHtml()) ?? ''; + if (onlyText) { + return (await htmlEditorApi?.getText()) ?? ''; + } else { + return (await htmlEditorApi?.getFullHtml()) ?? ''; + } } } @@ -188,7 +194,7 @@ class ComposerController extends BaseController { && composerArguments.value!.emailActionType != EmailActionType.compose && Get.context != null) { if (composerArguments.value?.emailActionType == EmailActionType.edit) { - return _getOldEmailContentAsHtml(Get.context!); + return _getOldEmailContentAsHtml(); } else { return _getBodyEmailQuotedAsHtml(Get.context!, composerArguments.value!); } @@ -283,9 +289,9 @@ class ComposerController extends BaseController { final trustAsHtml = arguments.emailContents ?.map((emailContent) => emailContent.content) .toList() - .join('
') ?? ''; + .join('
') ?? ''; - final emailQuotedHtml = '


$headerEmailQuotedAsHtml${trustAsHtml.addBlockQuoteTag()}
'; + final emailQuotedHtml = '


$headerEmailQuotedAsHtml${trustAsHtml.addBlockQuoteTag()}
'; return emailQuotedHtml; } @@ -477,19 +483,59 @@ class ComposerController extends BaseController { attachments.removeWhere((attachment) => attachment == attachmentRemoved); } + Future _isEmailChanged(ComposerArguments arguments) async { + final newEmailBody = await _getEmailBodyText(onlyText: true); + final oldEmailBody = kIsWeb ? getContentEmail() : '\n${getContentEmail()}\n'; + final isEmailBodyChanged = !oldEmailBody.isSame(newEmailBody); + + final newEmailSubject = _subjectEmail; + final subjectEmail = arguments.presentationEmail?.getEmailTitle().trim() ?? ''; + final oldEmailSubject = arguments.emailActionType.getSubjectComposer(Get.context!, subjectEmail); + final isEmailSubjectChanged = !oldEmailSubject.isSame(newEmailSubject); + + final recipients = arguments.presentationEmail + ?.generateRecipientsEmailAddressForComposer(arguments.emailActionType, arguments.mailboxRole) + ?? Tuple3([], [], []); + + final newToEmailAddress = listToEmailAddress; + final oldToEmailAddress = recipients.value1; + final isToEmailAddressChanged = !oldToEmailAddress.isSame(newToEmailAddress); + + final newCcEmailAddress = listToEmailAddress; + final oldCcEmailAddress = recipients.value1; + final isCcEmailAddressChanged = !oldCcEmailAddress.isSame(newCcEmailAddress); + + final newBccEmailAddress = listToEmailAddress; + final oldBccEmailAddress = recipients.value1; + final isBccEmailAddressChanged = !oldBccEmailAddress.isSame(newBccEmailAddress); + + final isAttachmentsChanged = !initialAttachments.isSame(attachments.toList()); + + if (isEmailBodyChanged || isEmailSubjectChanged + || isToEmailAddressChanged || isCcEmailAddressChanged + || isBccEmailAddressChanged || isAttachmentsChanged) { + return true; + } + + return false; + } + void saveEmailAsDrafts() async { final arguments = composerArguments.value; - if (arguments != null) { - _saveEmailAddress(); + if (arguments != null && Get.context != null) { + final isChanged = await _isEmailChanged(arguments); + if (isChanged) { + _saveEmailAddress(); - final newEmail = await _generateEmail(arguments, asDrafts: true); - final accountId = arguments.session.accounts.keys.first; - final oldEmail = arguments.presentationEmail; + final newEmail = await _generateEmail(arguments, asDrafts: true); + final accountId = arguments.session.accounts.keys.first; + final oldEmail = arguments.presentationEmail; - if (arguments.emailActionType == EmailActionType.edit && oldEmail != null) { - mailboxDashBoardController.consumeState(_updateEmailDraftsInteractor.execute(accountId, newEmail, oldEmail.id)); - } else { - mailboxDashBoardController.consumeState(_saveEmailAsDraftsInteractor.execute(accountId, newEmail)); + if (arguments.emailActionType == EmailActionType.edit && oldEmail != null) { + mailboxDashBoardController.consumeState(_updateEmailDraftsInteractor.execute(accountId, newEmail, oldEmail.id)); + } else { + mailboxDashBoardController.consumeState(_saveEmailAsDraftsInteractor.execute(accountId, newEmail)); + } } } } @@ -506,9 +552,10 @@ class ComposerController extends BaseController { void _getEmailContentSuccess(GetEmailContentSuccess success) { emailContents.value = success.emailContents; attachments.value = success.attachments; + initialAttachments = success.attachments; } - String _getOldEmailContentAsHtml(BuildContext context) { + String _getOldEmailContentAsHtml() { if (emailContents.isNotEmpty) { final trustAsHtml = emailContents .map((emailContent) => emailContent.content)