-
Notifications
You must be signed in to change notification settings - Fork 118
TF-4392 [Team Mailbox] Move email to Trash #4418
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; | ||
| import 'package:jmap_dart_client/jmap/mail/mailbox/namespace.dart'; | ||
| import 'package:model/mailbox/presentation_mailbox.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; | ||
| import 'package:tmail_ui_user/main/routes/route_navigation.dart'; | ||
|
|
||
| mixin HandleTeamMailboxMixin { | ||
| MailboxTree? get _teamMailboxesTree => | ||
| getBinding<MailboxController>()?.teamMailboxesTree.value; | ||
|
|
||
| MailboxNode? _findTeamMailboxNodeByNamespaceOnFirstLevel( | ||
| Namespace namespace, | ||
| ) { | ||
| return _teamMailboxesTree?.findNodeOnFirstLevel( | ||
| (node) => node.item.namespace == namespace, | ||
| ); | ||
| } | ||
|
|
||
| PresentationMailbox? _findDefaultMailboxInTeamMailbox({ | ||
| required Namespace namespace, | ||
| required String mailboxName, | ||
| }) { | ||
| final teamMailboxNode = | ||
| _findTeamMailboxNodeByNamespaceOnFirstLevel(namespace); | ||
| if (teamMailboxNode == null) return null; | ||
|
|
||
| final mailboxNode = teamMailboxNode.findNodeOnFirstLevel( | ||
| (node) => | ||
| node.mailboxNameAsString.toLowerCase() == mailboxName.toLowerCase(), | ||
| ); | ||
| if (mailboxNode == null) return null; | ||
|
|
||
| return mailboxNode.item; | ||
| } | ||
|
|
||
| MailboxId? findDefaultMailboxIdInTeamMailbox({ | ||
| required Namespace namespace, | ||
| required String mailboxName, | ||
| }) { | ||
| final teamMailbox = _findDefaultMailboxInTeamMailbox( | ||
| namespace: namespace, | ||
| mailboxName: mailboxName, | ||
| ); | ||
| return teamMailbox?.id; | ||
| } | ||
|
|
||
| String? getTeamMailboxNodePathWithSeparator({ | ||
| required MailboxId mailboxId, | ||
| String pathSeparator = '/', | ||
| }) { | ||
| return _teamMailboxesTree?.getNodePath(mailboxId, pathSeparator); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| 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/mailbox/presentation_mailbox.dart'; | ||
| import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; | ||
|
|
||
| extension GetTrashMailboxIdAndPathExtension on MailboxDashBoardController { | ||
| ({MailboxId? trashId, String? trashPath}) getTrashMailboxIdAndPath( | ||
| PresentationMailbox emailMailbox, | ||
| ) { | ||
| final defaultResult = ( | ||
| trashId: mapDefaultMailboxIdByRole[PresentationMailbox.roleTrash], | ||
| trashPath: null as String?, | ||
| ); | ||
|
|
||
| final mailbox = selectedMailbox.value ?? emailMailbox; | ||
|
|
||
| if (mailbox.isPersonal) return defaultResult; | ||
|
|
||
| final namespace = mailbox.namespace; | ||
| if (namespace == null) return defaultResult; | ||
|
|
||
| final trashId = findDefaultMailboxIdInTeamMailbox( | ||
| namespace: namespace, | ||
| mailboxName: PresentationMailbox.trashRole, | ||
| ); | ||
| if (trashId == null) return defaultResult; | ||
|
|
||
| final trashPath = getTeamMailboxNodePathWithSeparator( | ||
| mailboxId: trashId, | ||
| ); | ||
| return (trashId: trashId, trashPath: trashPath); | ||
| } | ||
|
|
||
| void emitMoveToTrashFailure(Exception exception) { | ||
| emitFailure( | ||
| controller: this, | ||
| failure: MoveToMailboxFailure( | ||
| EmailActionType.moveToTrash, | ||
| exception: exception, | ||
| ), | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_reques | |
| import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/get_trash_mailbox_id_and_path_extension.dart'; | ||
| import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_mailbox_state.dart'; | ||
| import 'package:tmail_ui_user/main/routes/route_navigation.dart'; | ||
|
|
||
|
|
@@ -29,7 +30,15 @@ extension HandleActionTypeForEmailSelection on MailboxDashBoardController { | |
| } else if (actionType == EmailActionType.moveToSpam) { | ||
| destinationMailboxId = spamMailboxId; | ||
| } else if (actionType == EmailActionType.moveToTrash) { | ||
| destinationMailboxId = getMailboxIdByRole(PresentationMailbox.roleTrash); | ||
| if (selectedMailbox.value?.isChildOfTeamMailboxes == true) { | ||
| final (:trashId, :trashPath) = | ||
| getTrashMailboxIdAndPath(selectedMailbox.value!); | ||
| destinationMailboxId = trashId; | ||
| destinationFolderPath = trashPath; | ||
| } else { | ||
| destinationMailboxId = | ||
| getMailboxIdByRole(PresentationMailbox.roleTrash); | ||
|
Comment on lines
32
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't resolve one trash mailbox from This branch ignores 🤖 Prompt for AI Agents |
||
| } | ||
| } else if (actionType == EmailActionType.archiveMessage) { | ||
| destinationMailboxId = getMailboxIdByRole(PresentationMailbox.roleArchive); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,8 +21,11 @@ import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.dart' | |
| import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; | ||
| import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.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/features/mailbox/domain/exceptions/mailbox_exception.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/get_trash_mailbox_id_and_path_extension.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_action_type_for_email_selection.dart'; | ||
| import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart'; | ||
| import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart'; | ||
|
|
@@ -59,23 +62,54 @@ mixin EmailActionController { | |
| mailboxDashBoardController.openEmailDetailedView(presentationEmail); | ||
| } | ||
|
|
||
| void moveToTrash(PresentationEmail email, {PresentationMailbox? mailboxContain}) async { | ||
| void moveToTrash( | ||
| PresentationEmail email, { | ||
| PresentationMailbox? mailboxContain, | ||
| }) { | ||
| if (mailboxContain == null) { | ||
| mailboxDashBoardController.emitMoveToTrashFailure( | ||
| NotFoundMailboxOfEmailException(), | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| final session = mailboxDashBoardController.sessionCurrent; | ||
| if (session == null) { | ||
| mailboxDashBoardController.emitMoveToTrashFailure( | ||
| NotFoundSessionException(), | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| final accountId = mailboxDashBoardController.accountId.value; | ||
| final trashMailboxId = mailboxDashBoardController.mapDefaultMailboxIdByRole[PresentationMailbox.roleTrash]; | ||
| if (accountId == null) { | ||
| mailboxDashBoardController.emitMoveToTrashFailure( | ||
| NotFoundAccountIdException(), | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| if (session != null && mailboxContain != null && accountId != null && trashMailboxId != null) { | ||
| _moveToTrashAction( | ||
| session, | ||
| accountId, | ||
| MoveToMailboxRequest( | ||
| {mailboxContain.id: email.id != null ? [email.id!] : []}, | ||
| trashMailboxId, | ||
| MoveAction.moving, | ||
| EmailActionType.moveToTrash), | ||
| email.id != null ? {email.id! : email.hasRead} : {}, | ||
| final (:trashId, :trashPath) = | ||
| mailboxDashBoardController.getTrashMailboxIdAndPath(mailboxContain); | ||
|
Comment on lines
+92
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make the trash lookup honor
🤖 Prompt for AI Agents |
||
| if (trashId == null) { | ||
| mailboxDashBoardController.emitMoveToTrashFailure( | ||
| NotFoundTrashMailboxException(), | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| _moveToTrashAction( | ||
| session, | ||
| accountId, | ||
| MoveToMailboxRequest( | ||
| {mailboxContain.id: email.id != null ? [email.id!] : []}, | ||
| trashId, | ||
| MoveAction.moving, | ||
| EmailActionType.moveToTrash, | ||
| destinationPath: trashPath, | ||
| ), | ||
| email.id != null ? {email.id!: email.hasRead} : {}, | ||
| ); | ||
| } | ||
|
|
||
| void _moveToTrashAction( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Virtual folders still mask the real mailbox here.
mailboxDashBoardController.getMailboxContain(email)returnsselectedMailboxwhenever search isn't running (lib/features/mailbox_dashboard/presentation/extensions/get_mailbox_contain_extension.dart:6-15). If the message is opened from Favorites or another virtual folder,currentMailboxbecomes that presentation-only mailbox instead of the email’s actual team mailbox, so the trash lookup below falls back to personal Trash. Mirror the bulk-action virtual-folder guard here and resolve fromemail.findMailboxContain(...)in that case.🤖 Prompt for AI Agents