@@ -62,6 +62,7 @@ import 'package:tmail_ui_user/features/composer/presentation/extensions/list_sha
6262import 'package:tmail_ui_user/features/composer/presentation/mixin/drag_drog_file_mixin.dart' ;
6363import 'package:tmail_ui_user/features/composer/presentation/model/create_email_request.dart' ;
6464import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart' ;
65+ import 'package:tmail_ui_user/features/composer/presentation/model/saved_email_draft.dart' ;
6566import 'package:tmail_ui_user/features/composer/presentation/model/signature_status.dart' ;
6667import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart' ;
6768import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart' ;
@@ -109,7 +110,6 @@ class ComposerController extends BaseController with DragDropFileMixin implement
109110
110111 final mailboxDashBoardController = Get .find <MailboxDashBoardController >();
111112 final networkConnectionController = Get .find <NetworkConnectionController >();
112- final _dynamicUrlInterceptors = Get .find <DynamicUrlInterceptors >();
113113 final _beforeReconnectManager = Get .find <BeforeReconnectManager >();
114114
115115 final composerArguments = Rxn <ComposerArguments >();
@@ -199,6 +199,14 @@ class ComposerController extends BaseController with DragDropFileMixin implement
199199 ButtonState _saveToDraftButtonState = ButtonState .enabled;
200200 ButtonState _sendButtonState = ButtonState .enabled;
201201 SignatureStatus _identityContentOnOpenPolicy = SignatureStatus .editedAvailable;
202+ int ? _savedEmailDraftHash;
203+ bool _restoringSignatureButton = false ;
204+
205+ @visibleForTesting
206+ bool get restoringSignatureButton => _restoringSignatureButton;
207+
208+ @visibleForTesting
209+ int ? get savedEmailDraftHash => _savedEmailDraftHash;
202210
203211 late Worker uploadInlineImageWorker;
204212 late Worker dashboardViewStateWorker;
@@ -330,6 +338,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement
330338 maxWithEditor = null ;
331339 } else if (success is GetAlwaysReadReceiptSettingSuccess ) {
332340 hasRequestReadReceipt.value = success.alwaysReadReceiptEnabled;
341+ _initEmailDraftHash ();
333342 } else if (success is RestoreEmailInlineImagesSuccess ) {
334343 _updateEditorContent (success);
335344 }
@@ -358,6 +367,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement
358367 }
359368 } else if (failure is GetAlwaysReadReceiptSettingFailure ) {
360369 hasRequestReadReceipt.value = false ;
370+ _initEmailDraftHash ();
361371 }
362372 }
363373
@@ -732,16 +742,18 @@ class ComposerController extends BaseController with DragDropFileMixin implement
732742 (identity) => identity.id == identityId);
733743 }
734744
735- void _initIdentities (ComposerArguments composerArguments) {
745+ Future < void > _initIdentities (ComposerArguments composerArguments) async {
736746 listFromIdentities.value = composerArguments.identities ?? [];
737747 final selectedIdentityFromId = _selectIdentityFromId (
738748 composerArguments.selectedIdentityId);
739749 if (listFromIdentities.isEmpty) {
740750 _getAllIdentities ();
741751 } else if (selectedIdentityFromId != null ) {
742- _selectIdentity (selectedIdentityFromId);
752+ await _selectIdentity (selectedIdentityFromId);
753+ _initEmailDraftHash ();
743754 } else if (composerArguments.identities? .isNotEmpty == true ) {
744- _selectIdentity (composerArguments.identities! .first);
755+ await _selectIdentity (composerArguments.identities! .first);
756+ _initEmailDraftHash ();
745757 }
746758 }
747759
@@ -764,8 +776,10 @@ class ComposerController extends BaseController with DragDropFileMixin implement
764776 composerArguments.value? .selectedIdentityId);
765777 if (selectedIdentityFromId != null ) {
766778 await _selectIdentity (selectedIdentityFromId);
779+ _initEmailDraftHash ();
767780 } else {
768781 await _selectIdentity (listIdentitiesMayDeleted.firstOrNull);
782+ _initEmailDraftHash ();
769783 }
770784 }
771785 }
@@ -1216,7 +1230,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement
12161230 final session = mailboxDashBoardController.sessionCurrent;
12171231 final accountId = mailboxDashBoardController.accountId.value;
12181232 if (session != null && accountId != null ) {
1219- final uploadUri = session.getUploadUri (accountId, jmapUrl: _dynamicUrlInterceptors .jmapUrl);
1233+ final uploadUri = session.getUploadUri (accountId, jmapUrl: dynamicUrlInterceptors .jmapUrl);
12201234 uploadController.justUploadAttachmentsAction (
12211235 uploadFiles: pickedFiles,
12221236 uploadUri: uploadUri,
@@ -1230,50 +1244,41 @@ class ComposerController extends BaseController with DragDropFileMixin implement
12301244 uploadController.deleteFileUploaded (uploadId);
12311245 }
12321246
1233- Future <bool > _validateEmailChange ({
1234- required BuildContext context,
1235- required EmailActionType emailActionType,
1236- PresentationEmail ? presentationEmail,
1237- Role ? mailboxRole,
1238- }) async {
1239- final newEmailBody = await _getContentInEditor ();
1240- final oldEmailBody = _initTextEditor ?? '' ;
1241- log ('ComposerController::_validateEmailChange: newEmailBody = $newEmailBody | oldEmailBody = $oldEmailBody ' );
1242- final isEmailBodyChanged = ! oldEmailBody.trim ().isSame (newEmailBody.trim ());
1243-
1244- final newEmailSubject = subjectEmail.value ?? '' ;
1245- final oldEmailSubject = emailActionType == EmailActionType .editDraft
1246- ? presentationEmail? .getEmailTitle ().trim () ?? ''
1247- : '' ;
1248- final isEmailSubjectChanged = ! oldEmailSubject.trim ().isSame (newEmailSubject.trim ());
1249-
1250- final recipients = presentationEmail
1251- ? .generateRecipientsEmailAddressForComposer (
1252- emailActionType: emailActionType,
1253- mailboxRole: mailboxRole
1254- ) ?? const Tuple3 (< EmailAddress > [], < EmailAddress > [], < EmailAddress > []);
1255-
1256- final newToEmailAddress = listToEmailAddress;
1257- final oldToEmailAddress = emailActionType == EmailActionType .editDraft ? recipients.value1 : [];
1258- final isToEmailAddressChanged = ! oldToEmailAddress.isSame (newToEmailAddress);
1259-
1260- final newCcEmailAddress = listCcEmailAddress;
1261- final oldCcEmailAddress = emailActionType == EmailActionType .editDraft ? recipients.value2 : [];
1262- final isCcEmailAddressChanged = ! oldCcEmailAddress.isSame (newCcEmailAddress);
1263-
1264- final newBccEmailAddress = listBccEmailAddress;
1265- final oldBccEmailAddress = emailActionType == EmailActionType .editDraft ? recipients.value3 : [];
1266- final isBccEmailAddressChanged = ! oldBccEmailAddress.isSame (newBccEmailAddress);
1267-
1268- final isAttachmentsChanged = ! initialAttachments.isSame (uploadController.attachmentsUploaded.toList ());
1269- log ('ComposerController::_validateChangeEmail: isEmailBodyChanged = $isEmailBodyChanged | isEmailSubjectChanged = $isEmailSubjectChanged | isToEmailAddressChanged = $isToEmailAddressChanged | isCcEmailAddressChanged = $isCcEmailAddressChanged | isBccEmailAddressChanged = $isBccEmailAddressChanged | isAttachmentsChanged = $isAttachmentsChanged ' );
1270- if (isEmailBodyChanged || isEmailSubjectChanged
1271- || isToEmailAddressChanged || isCcEmailAddressChanged
1272- || isBccEmailAddressChanged || isAttachmentsChanged) {
1273- return true ;
1247+ Future <bool > _validateEmailChange () async {
1248+ final newDraftHash = await _hashDraftEmail ();
1249+
1250+ return _savedEmailDraftHash != newDraftHash;
1251+ }
1252+
1253+ Future <int > _hashDraftEmail () async {
1254+ final emailContent = await _getContentInEditor ();
1255+
1256+ final savedEmailDraft = SavedEmailDraft (
1257+ subject: subjectEmail.value ?? '' ,
1258+ content: emailContent,
1259+ toRecipients: listToEmailAddress.toSet (),
1260+ ccRecipients: listCcEmailAddress.toSet (),
1261+ bccRecipients: listBccEmailAddress.toSet (),
1262+ identity: identitySelected.value,
1263+ attachments: uploadController.attachmentsUploaded,
1264+ hasReadReceipt: hasRequestReadReceipt.value,
1265+ );
1266+
1267+ return savedEmailDraft.hashCode;
1268+ }
1269+
1270+ Future <void > _updateSavedEmailDraftHash () async {
1271+ _savedEmailDraftHash = await _hashDraftEmail ();
1272+ }
1273+
1274+ Future <void > _initEmailDraftHash () async {
1275+ if (composerArguments.value? .emailActionType != EmailActionType .compose
1276+ && composerArguments.value? .emailActionType != EmailActionType .editDraft
1277+ ) {
1278+ return ;
12741279 }
12751280
1276- return false ;
1281+ _savedEmailDraftHash = await _hashDraftEmail () ;
12771282 }
12781283
12791284 void handleClickSaveAsDraftsButton (BuildContext context) async {
@@ -1306,10 +1311,12 @@ class ComposerController extends BaseController with DragDropFileMixin implement
13061311 _saveToDraftButtonState = ButtonState .enabled;
13071312 _emailIdEditing = resultState.emailId;
13081313 mailboxDashBoardController.consumeState (Stream .value (Right <Failure , Success >(resultState)));
1314+ _updateSavedEmailDraftHash ();
13091315 } else if (resultState is UpdateEmailDraftsSuccess ) {
13101316 _saveToDraftButtonState = ButtonState .enabled;
13111317 _emailIdEditing = resultState.emailId;
13121318 mailboxDashBoardController.consumeState (Stream .value (Right <Failure , Success >(resultState)));
1319+ _updateSavedEmailDraftHash ();
13131320 } else if ((resultState is SaveEmailAsDraftsFailure && resultState.exception is SavingEmailToDraftsCanceledException ) ||
13141321 (resultState is UpdateEmailDraftsFailure && resultState.exception is SavingEmailToDraftsCanceledException )) {
13151322 _saveToDraftButtonState = ButtonState .enabled;
@@ -1437,6 +1444,8 @@ class ComposerController extends BaseController with DragDropFileMixin implement
14371444 final selectedIdentityFromHeader = _selectIdentityFromId (identityIdFromHeader);
14381445 if (selectedIdentityFromHeader == null ) return ;
14391446 identitySelected.value = selectedIdentityFromHeader;
1447+
1448+ _initEmailDraftHash ();
14401449 }
14411450
14421451 Future <void > restoreCollapsibleButton (String ? emailContent) async {
@@ -1445,6 +1454,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement
14451454 final emailDocument = parse (emailContent);
14461455 final signature = emailDocument.querySelector ('div.tmail-signature' );
14471456 if (signature == null ) return ;
1457+ _restoringSignatureButton = true ;
14481458 await _applySignature (signature.innerHtml);
14491459 } catch (e) {
14501460 logError ('ComposerController::_restoreCollapsibleButton: $e ' );
@@ -1793,7 +1803,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement
17931803 void _handleUploadInlineSuccess (SuccessAttachmentUploadState uploadState) {
17941804 uploadController.clearUploadInlineViewState ();
17951805
1796- final baseDownloadUrl = mailboxDashBoardController.sessionCurrent? .getDownloadUrl (jmapUrl: _dynamicUrlInterceptors .jmapUrl);
1806+ final baseDownloadUrl = mailboxDashBoardController.sessionCurrent? .getDownloadUrl (jmapUrl: dynamicUrlInterceptors .jmapUrl);
17971807 final accountId = mailboxDashBoardController.accountId.value;
17981808
17991809 if (baseDownloadUrl != null && accountId != null ) {
@@ -1988,6 +1998,18 @@ class ComposerController extends BaseController with DragDropFileMixin implement
19881998 initTextEditor (text);
19891999 }
19902000 _textEditorWeb = text;
2001+
2002+ _initEmailDraftHashAfterSignatureButtonRestored (text);
2003+ }
2004+
2005+ void _initEmailDraftHashAfterSignatureButtonRestored (String ? emailContent) {
2006+ if (! _restoringSignatureButton) return ;
2007+ final emailDocument = parse (emailContent);
2008+ final signatureButton = emailDocument.querySelector ('button.tmail-signature-button' );
2009+ if (signatureButton == null ) return ;
2010+
2011+ _restoringSignatureButton = false ;
2012+ _initEmailDraftHash ();
19912013 }
19922014
19932015 void initTextEditor (String ? text) {
@@ -2087,12 +2109,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement
20872109 return ;
20882110 }
20892111
2090- final isChanged = await _validateEmailChange (
2091- context: context,
2092- emailActionType: composerArguments.value! .emailActionType,
2093- presentationEmail: composerArguments.value! .presentationEmail,
2094- mailboxRole: composerArguments.value! .mailboxRole
2095- );
2112+ final isChanged = await _validateEmailChange ();
20962113
20972114 if (isChanged && context.mounted) {
20982115 clearFocus (context);
@@ -2368,6 +2385,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement
23682385 void _setUpRequestReadReceiptForDraftEmail (Email ? email) {
23692386 if (email? .hasRequestReadReceipt == true ) {
23702387 hasRequestReadReceipt.value = true ;
2388+ _initEmailDraftHash ();
23712389 } else {
23722390 _getAlwaysReadReceiptSetting ();
23732391 }
0 commit comments