TF-4392 [Team Mailbox] Auto save draft email team mailbox when edit

This commit is contained in:
dab246
2026-03-30 16:16:29 +07:00
committed by Dat H. Pham
parent 9feef8da99
commit beb323874b
7 changed files with 53 additions and 31 deletions
@@ -1,20 +1,30 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/email_action_type.dart';
import 'package:model/extensions/presentation_mailbox_extension.dart'; import 'package:model/extensions/presentation_mailbox_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
extension GetDraftMailboxIdForComposerExtension on ComposerController { extension GetDraftMailboxIdForComposerExtension on ComposerController {
MailboxId? getDraftMailboxIdForComposer() { MailboxId? getDraftMailboxIdForComposer() {
final savedDraftMailboxId = composerArguments.value?.savedDraftMailboxId;
if (currentEmailActionType == EmailActionType.editDraft &&
savedDraftMailboxId != null) {
return savedDraftMailboxId;
}
final defaultDraftsMailbox = mailboxDashBoardController.mapDefaultMailboxIdByRole[ final defaultDraftsMailbox = mailboxDashBoardController.mapDefaultMailboxIdByRole[
PresentationMailbox.roleDrafts PresentationMailbox.roleDrafts
]; ];
final lowercaseDraftsRole = PresentationMailbox.roleDrafts.value.toLowerCase(); final lowercaseDraftsRole = PresentationMailbox.roleDrafts.value.toLowerCase();
final identityEmail = identitySelected.value?.email;
return mailboxDashBoardController.mapMailboxById.entries return mailboxDashBoardController.mapMailboxById.entries
.firstWhereOrNull((entry) { .firstWhereOrNull((entry) {
if (identityEmail == null) return false;
final mailbox = entry.value; final mailbox = entry.value;
return mailbox.emailTeamMailBoxes == identitySelected.value?.email && return mailbox.emailTeamMailBoxes == identityEmail &&
mailbox.name?.name.toLowerCase() == lowercaseDraftsRole; mailbox.name?.name.toLowerCase() == lowercaseDraftsRole;
}) })
?.key ?? defaultDraftsMailbox; ?.key ?? defaultDraftsMailbox;
@@ -39,6 +39,7 @@ class ComposerArguments extends RouterArguments {
final EmailActionType? savedActionType; final EmailActionType? savedActionType;
final EmailId? savedEmailDraftId; final EmailId? savedEmailDraftId;
final EmailId? savedEmailTemplateId; final EmailId? savedEmailTemplateId;
final MailboxId? savedDraftMailboxId;
ComposerArguments({ ComposerArguments({
this.emailActionType = EmailActionType.compose, this.emailActionType = EmailActionType.compose,
@@ -68,6 +69,7 @@ class ComposerArguments extends RouterArguments {
this.savedActionType, this.savedActionType,
this.savedEmailDraftId, this.savedEmailDraftId,
this.savedEmailTemplateId, this.savedEmailTemplateId,
this.savedDraftMailboxId,
}); });
factory ComposerArguments.fromSendingEmail(SendingEmail sendingEmail) => factory ComposerArguments.fromSendingEmail(SendingEmail sendingEmail) =>
@@ -109,10 +111,13 @@ class ComposerArguments extends RouterArguments {
bcc: bcc, bcc: bcc,
); );
factory ComposerArguments.editDraftEmail(PresentationEmail presentationEmail) => factory ComposerArguments.editDraftEmail({
ComposerArguments( required PresentationEmail presentationEmail,
required MailboxId savedDraftMailboxId,
}) => ComposerArguments(
emailActionType: EmailActionType.editDraft, emailActionType: EmailActionType.editDraft,
presentationEmail: presentationEmail, presentationEmail: presentationEmail,
savedDraftMailboxId: savedDraftMailboxId,
); );
factory ComposerArguments.editAsNewEmail( factory ComposerArguments.editAsNewEmail(
@@ -265,6 +270,7 @@ class ComposerArguments extends RouterArguments {
savedActionType, savedActionType,
savedEmailDraftId, savedEmailDraftId,
sendingEmailActionType, sendingEmailActionType,
savedDraftMailboxId,
]; ];
ComposerArguments copyWith({ ComposerArguments copyWith({
@@ -295,6 +301,7 @@ class ComposerArguments extends RouterArguments {
EmailActionType? savedActionType, EmailActionType? savedActionType,
EmailId? savedEmailDraftId, EmailId? savedEmailDraftId,
EmailId? savedEmailTemplateId, EmailId? savedEmailTemplateId,
MailboxId? savedDraftMailboxId,
}) { }) {
return ComposerArguments( return ComposerArguments(
emailActionType: emailActionType ?? this.emailActionType, emailActionType: emailActionType ?? this.emailActionType,
@@ -324,6 +331,7 @@ class ComposerArguments extends RouterArguments {
savedActionType: savedActionType ?? this.savedActionType, savedActionType: savedActionType ?? this.savedActionType,
savedEmailDraftId: savedEmailDraftId ?? this.savedEmailDraftId, savedEmailDraftId: savedEmailDraftId ?? this.savedEmailDraftId,
savedEmailTemplateId: savedEmailTemplateId ?? this.savedEmailTemplateId, savedEmailTemplateId: savedEmailTemplateId ?? this.savedEmailTemplateId,
savedDraftMailboxId: savedDraftMailboxId ?? this.savedDraftMailboxId,
); );
} }
} }
@@ -14,11 +14,9 @@ extension GetTrashMailboxIdAndPathExtension on MailboxDashBoardController {
trashPath: null as String?, trashPath: null as String?,
); );
final mailbox = selectedMailbox.value ?? emailMailbox; if (emailMailbox.isPersonal) return defaultResult;
if (mailbox.isPersonal) return defaultResult; final namespace = emailMailbox.namespace;
final namespace = mailbox.namespace;
if (namespace == null) return defaultResult; if (namespace == null) return defaultResult;
final trashId = findDefaultMailboxIdInTeamMailbox( final trashId = findDefaultMailboxIdInTeamMailbox(
@@ -924,7 +924,10 @@ class SearchEmailController extends BaseController
switch(actionType) { switch(actionType) {
case EmailActionType.preview: case EmailActionType.preview:
if (mailboxContain?.isDrafts == true) { if (mailboxContain?.isDrafts == true) {
editDraftEmail(selectedEmail); editDraftEmail(
presentationEmail: selectedEmail,
draftMailboxId: mailboxContain!.id,
);
} else { } else {
previewEmail(selectedEmail); previewEmail(selectedEmail);
} }
@@ -9,6 +9,7 @@ import 'package:get/get.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/session/session.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/email_action_type.dart'; import 'package:model/email/email_action_type.dart';
import 'package:model/email/mark_star_action.dart'; import 'package:model/email/mark_star_action.dart';
import 'package:model/email/presentation_email.dart'; import 'package:model/email/presentation_email.dart';
@@ -41,8 +42,16 @@ mixin EmailActionController {
final responsiveUtils = Get.find<ResponsiveUtils>(); final responsiveUtils = Get.find<ResponsiveUtils>();
final imagePaths = Get.find<ImagePaths>(); final imagePaths = Get.find<ImagePaths>();
void editDraftEmail(PresentationEmail presentationEmail) { void editDraftEmail({
mailboxDashBoardController.openComposer(ComposerArguments.editDraftEmail(presentationEmail)); required PresentationEmail presentationEmail,
required MailboxId draftMailboxId,
}) {
mailboxDashBoardController.openComposer(
ComposerArguments.editDraftEmail(
presentationEmail: presentationEmail,
savedDraftMailboxId: draftMailboxId,
),
);
} }
void editAsNewEmail( void editAsNewEmail(
@@ -1337,7 +1337,10 @@ class ThreadController extends BaseController with EmailActionController {
switch(actionType) { switch(actionType) {
case EmailActionType.preview: case EmailActionType.preview:
if (mailboxContain?.isDrafts == true) { if (mailboxContain?.isDrafts == true) {
editDraftEmail(selectedEmail); editDraftEmail(
presentationEmail: selectedEmail,
draftMailboxId: mailboxContain!.id,
);
} else if (mailboxContain?.isTemplates == true) { } else if (mailboxContain?.isTemplates == true) {
editAsNewEmail(selectedEmail, savedEmailTemplateId: selectedEmail.id); editAsNewEmail(selectedEmail, savedEmailTemplateId: selectedEmail.id);
} else { } else {
@@ -66,34 +66,25 @@ void main() {
); );
test( test(
'SHOULD return default trash id with null path ' 'SHOULD return team trash id '
'WHEN selectedMailbox is personal', 'WHEN emailMailbox is team mailbox even if selectedMailbox is personal',
() { () {
when(mockDashBoardController.selectedMailbox) when(mockDashBoardController.selectedMailbox)
.thenReturn(Rxn(personalMailbox)); .thenReturn(Rxn(personalMailbox));
when(mockDashBoardController.findDefaultMailboxIdInTeamMailbox(
namespace: teamNamespace,
mailboxName: PresentationMailbox.trashRole,
)).thenReturn(teamTrashId);
when(mockDashBoardController.getTeamMailboxNodePathWithSeparator(
mailboxId: teamTrashId,
)).thenReturn('Team/Trash');
final result = mockDashBoardController.getTrashMailboxIdAndPath( final result = mockDashBoardController.getTrashMailboxIdAndPath(
teamMailbox, teamMailbox,
); );
expect(result.trashId, equals(defaultTrashId)); expect(result.trashId, equals(teamTrashId));
expect(result.trashPath, isNull); expect(result.trashPath, equals('Team/Trash'));
},
);
test(
'SHOULD use selectedMailbox over emailMailbox '
'WHEN selectedMailbox is not null',
() {
when(mockDashBoardController.selectedMailbox)
.thenReturn(Rxn(personalMailbox));
final result = mockDashBoardController.getTrashMailboxIdAndPath(
teamMailbox,
);
expect(result.trashId, equals(defaultTrashId));
expect(result.trashPath, isNull);
}, },
); );