diff --git a/lib/features/composer/domain/exceptions/send_email_exception.dart b/lib/features/composer/domain/exceptions/send_email_exception.dart new file mode 100644 index 0000000000..417539ee0e --- /dev/null +++ b/lib/features/composer/domain/exceptions/send_email_exception.dart @@ -0,0 +1,4 @@ + +class NotFoundOutboxMailboxException implements Exception { + NotFoundOutboxMailboxException(); +} \ No newline at end of file diff --git a/lib/features/composer/domain/model/email_request.dart b/lib/features/composer/domain/model/email_request.dart index aa6c53ce14..9cfeac9ac4 100644 --- a/lib/features/composer/domain/model/email_request.dart +++ b/lib/features/composer/domain/model/email_request.dart @@ -1,6 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; @@ -8,11 +9,35 @@ class EmailRequest with EquatableMixin { final Email email; final Id submissionCreateId; - final MailboxId? mailboxIdSaved; + final MailboxId? sentMailboxId; final EmailId? emailIdDestroyed; + final Identity? identity; - EmailRequest(this.email, this.submissionCreateId, {this.mailboxIdSaved, this.emailIdDestroyed}); + EmailRequest(this.email, this.submissionCreateId, { + this.sentMailboxId, + this.identity, + this.emailIdDestroyed + }); @override - List get props => [email, submissionCreateId, mailboxIdSaved]; + List get props => [ + email, + submissionCreateId, + sentMailboxId, + identity, + emailIdDestroyed + ]; +} + +extension EmailRequestExtension on EmailRequest { + + EmailRequest toEmailRequest({Email? newEmail}) { + return EmailRequest( + newEmail ?? email, + submissionCreateId, + sentMailboxId: sentMailboxId, + identity: identity, + emailIdDestroyed: emailIdDestroyed + ); + } } \ No newline at end of file diff --git a/lib/features/composer/domain/usecases/send_email_interactor.dart b/lib/features/composer/domain/usecases/send_email_interactor.dart index c142899cf4..bdfb81a0bb 100644 --- a/lib/features/composer/domain/usecases/send_email_interactor.dart +++ b/lib/features/composer/domain/usecases/send_email_interactor.dart @@ -4,15 +4,22 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; +import 'package:uuid/uuid.dart'; class SendEmailInteractor { final EmailRepository _emailRepository; final MailboxRepository _mailboxRepository; + final Uuid _uuid; - SendEmailInteractor(this._emailRepository, this._mailboxRepository); + SendEmailInteractor(this._emailRepository, this._mailboxRepository, this._uuid); - Stream> execute(AccountId accountId, EmailRequest emailRequest) async* { + Stream> execute( + AccountId accountId, + EmailRequest emailRequest, + {CreateNewMailboxRequest? mailboxRequest} + ) async* { try { yield Right(SendingEmailState()); @@ -24,7 +31,7 @@ class SendEmailInteractor { final currentMailboxState = listState.first; final currentEmailState = listState.last; - final result = await _emailRepository.sendEmail(accountId, emailRequest); + final result = await _emailRepository.sendEmail(accountId, emailRequest, mailboxRequest: mailboxRequest); if (result) { yield Right(SendEmailSuccess( currentEmailState: currentEmailState, diff --git a/lib/features/composer/presentation/composer_bindings.dart b/lib/features/composer/presentation/composer_bindings.dart index b37da2c8a6..e688e4e588 100644 --- a/lib/features/composer/presentation/composer_bindings.dart +++ b/lib/features/composer/presentation/composer_bindings.dart @@ -54,6 +54,7 @@ import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart'; import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart'; import 'package:tmail_ui_user/features/upload/presentation/controller/upload_controller.dart'; import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart'; +import 'package:uuid/uuid.dart'; import 'package:worker_manager/worker_manager.dart'; class ComposerBindings extends BaseBindings { @@ -134,7 +135,8 @@ class ComposerBindings extends BaseBindings { Get.lazyPut(() => UploadAttachmentInteractor(Get.find())); Get.lazyPut(() => SendEmailInteractor( Get.find(), - Get.find())); + Get.find(), + Get.find())); Get.lazyPut(() => SaveEmailAsDraftsInteractor( Get.find(), Get.find())); diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 5588a1d881..ac1210a419 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -48,6 +48,7 @@ import 'package:tmail_ui_user/features/composer/presentation/model/screen_displa import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_composer_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; @@ -302,11 +303,7 @@ class ComposerController extends BaseController { final userProfile = mailboxDashBoardController.userProfile.value; _removeComposerCacheOnWebInteractor.execute(); if (userProfile != null) { - final draftEmail = await _generateEmail( - currentContext!, - mailboxDashBoardController.mapDefaultMailboxIdByRole, - userProfile, - ); + final draftEmail = await _generateEmail(currentContext!, userProfile); _saveComposerCacheOnWebInteractor.execute(draftEmail); } }); @@ -524,13 +521,13 @@ class ComposerController extends BaseController { Future _generateEmail( BuildContext context, - Map mapDefaultMailboxId, UserProfile userProfile, - {bool asDrafts = false} + { + bool asDrafts = false, + MailboxId? draftMailboxId, + MailboxId? outboxMailboxId + } ) async { - final generateEmailId = EmailId(Id(_uuid.v1())); - final outboxMailboxId = mapDefaultMailboxId[PresentationMailbox.roleOutbox]; - final draftMailboxId = mapDefaultMailboxId[PresentationMailbox.roleDrafts]; Set listFromEmailAddress = {EmailAddress(null, userProfile.email)}; if (identitySelected.value?.email?.isNotEmpty == true) { listFromEmailAddress = {EmailAddress( @@ -541,37 +538,48 @@ class ComposerController extends BaseController { if (identitySelected.value?.replyTo?.isNotEmpty == true) { listReplyToEmailAddress = identitySelected.value!.replyTo!; } - final generatePartId = PartId(_uuid.v1()); final attachments = {}; attachments.addAll(uploadController.generateAttachments() ?? []); var emailBodyText = await _getEmailBodyText(context); - if (uploadController.mapInlineAttachments.isNotEmpty) { final mapContents = await _getMapContent(emailBodyText); - log('ComposerController::_generateEmail(): mapContents: $mapContents'); emailBodyText = mapContents.value1; final listInlineAttachment = mapContents.value2; final listInlineEmailBodyPart = listInlineAttachment .map((attachment) => attachment.toEmailBodyPart(charset: 'base64')) .toSet(); attachments.addAll(listInlineEmailBodyPart); - log('ComposerController::_generateEmail(): listInlineEmailBodyPart: $listInlineEmailBodyPart'); } final userAgent = await userAgentPlatform; - log('ComposerController::_generateEmail(): userAgent: $userAgent'); + + Map mailboxIds = {}; + if (asDrafts && draftMailboxId != null) { + mailboxIds[draftMailboxId] = true; + } + if (outboxMailboxId != null) { + mailboxIds[outboxMailboxId] = true; + } + + Map? mapKeywords = {}; + if (asDrafts) { + mapKeywords[KeyWordIdentifier.emailDraft] = true; + } + + final generateEmailId = EmailId(Id(_uuid.v1())); + final generatePartId = PartId(_uuid.v1()); return Email( generateEmailId, - mailboxIds: asDrafts ? {draftMailboxId!: true} : {outboxMailboxId!: true}, + mailboxIds: mailboxIds.isNotEmpty ? mailboxIds : null, from: listFromEmailAddress, to: listToEmailAddress.toSet(), cc: listCcEmailAddress.toSet(), bcc: listBccEmailAddress.toSet(), replyTo: listReplyToEmailAddress, - keywords: asDrafts ? {KeyWordIdentifier.emailDraft : true} : null, + keywords: mapKeywords.isNotEmpty ? mapKeywords : null, subject: subjectEmail.value, htmlBody: { EmailBodyPart( @@ -687,23 +695,33 @@ class ComposerController extends BaseController { void _handleSendMessages(BuildContext context) async { final arguments = composerArguments.value; - final session = mailboxDashBoardController.sessionCurrent; - final mapDefaultMailboxIdByRole = mailboxDashBoardController.mapDefaultMailboxIdByRole; + final accountId = mailboxDashBoardController.accountId.value; + final sentMailboxId = mailboxDashBoardController.mapDefaultMailboxIdByRole[PresentationMailbox.roleSent]; + final outboxMailboxId = mailboxDashBoardController.outboxMailbox?.id; + log('ComposerController::_handleSendMessages(): outboxMailboxId: $outboxMailboxId'); final userProfile = mailboxDashBoardController.userProfile.value; - if (arguments != null && session != null && mapDefaultMailboxIdByRole.isNotEmpty - && userProfile != null) { - - final email = await _generateEmail(context, mapDefaultMailboxIdByRole, userProfile); - final accountId = session.accounts.keys.first; - final sentMailboxId = mapDefaultMailboxIdByRole[PresentationMailbox.roleSent]; + if (arguments != null && accountId != null && userProfile != null) { + final email = await _generateEmail(context, userProfile, outboxMailboxId: outboxMailboxId); final submissionCreateId = Id(_uuid.v1()); - mailboxDashBoardController.consumeState(_sendEmailInteractor.execute( - accountId, - EmailRequest(email, submissionCreateId, mailboxIdSaved: sentMailboxId, - emailIdDestroyed: arguments.emailActionType == EmailActionType.edit - ? arguments.presentationEmail?.id - : null))); + mailboxDashBoardController.consumeState( + _sendEmailInteractor.execute( + accountId, + EmailRequest( + email, + submissionCreateId, + sentMailboxId: sentMailboxId, + identity: identitySelected.value, + emailIdDestroyed: arguments.emailActionType == EmailActionType.edit + ? arguments.presentationEmail?.id + : null), + mailboxRequest: outboxMailboxId == null + ? CreateNewMailboxRequest( + Id(_uuid.v1()), + PresentationMailbox.outboxMailboxName) + : null + ) + ); uploadController.clearInlineFileUploaded(); } @@ -892,25 +910,21 @@ class ComposerController extends BaseController { } void saveEmailAsDrafts(BuildContext context, {bool canPop = true}) async { - log('ComposerController::saveEmailAsDrafts():'); clearFocusEditor(context); final arguments = composerArguments.value; - final mapDefaultMailboxIdByRole = mailboxDashBoardController.mapDefaultMailboxIdByRole; + final draftMailboxId = mailboxDashBoardController.mapDefaultMailboxIdByRole[PresentationMailbox.roleDrafts]; final userProfile = mailboxDashBoardController.userProfile.value; - final session = mailboxDashBoardController.sessionCurrent; + final accountId = mailboxDashBoardController.accountId.value; - if (arguments != null && mapDefaultMailboxIdByRole.isNotEmpty && userProfile != null && session != null) { - log('ComposerController::saveEmailAsDrafts(): saveEmailAsDrafts START'); + if (arguments != null && userProfile != null && accountId != null) { final isChanged = await _isEmailChanged(context, arguments); - log('ComposerController::saveEmailAsDrafts(): saveEmailAsDrafts isChanged: $isChanged'); if (isChanged) { final newEmail = await _generateEmail( context, - mapDefaultMailboxIdByRole, userProfile, - asDrafts: true); - final accountId = session.accounts.keys.first; + asDrafts: true, + draftMailboxId: draftMailboxId); final oldEmail = arguments.presentationEmail; if (arguments.emailActionType == EmailActionType.edit && oldEmail != null) { diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index 67445e8569..12d3196a8d 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -9,11 +9,12 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; abstract class EmailDataSource { Future getEmailContent(AccountId accountId, EmailId emailId); - Future sendEmail(AccountId accountId, EmailRequest emailRequest); + Future sendEmail(AccountId accountId, EmailRequest emailRequest, {CreateNewMailboxRequest? mailboxRequest}); Future> markAsRead(AccountId accountId, List emails, ReadActions readActions); diff --git a/lib/features/email/data/datasource_impl/email_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_datasource_impl.dart index 6897e51b3e..e856c7a3f3 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -11,6 +11,7 @@ import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart' import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; class EmailDataSourceImpl extends EmailDataSource { @@ -28,9 +29,9 @@ class EmailDataSourceImpl extends EmailDataSource { } @override - Future sendEmail(AccountId accountId, EmailRequest emailRequest) { + Future sendEmail(AccountId accountId, EmailRequest emailRequest, {CreateNewMailboxRequest? mailboxRequest}) { return Future.sync(() async { - return await emailAPI.sendEmail(accountId, emailRequest); + return await emailAPI.sendEmail(accountId, emailRequest, mailboxRequest: mailboxRequest); }).catchError((error) { throw error; }); diff --git a/lib/features/email/data/network/email_api.dart b/lib/features/email/data/network/email_api.dart index aa35702419..4f60d1ff74 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -25,6 +25,8 @@ import 'package:jmap_dart_client/jmap/mail/email/submission/email_submission.dar import 'package:jmap_dart_client/jmap/mail/email/submission/email_submission_id.dart'; import 'package:jmap_dart_client/jmap/mail/email/submission/envelope.dart'; import 'package:jmap_dart_client/jmap/mail/email/submission/set/set_email_submission_method.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/set/set_mailbox_method.dart'; import 'package:model/account/account_request.dart'; import 'package:model/account/authentication_type.dart'; import 'package:model/download/download_task_id.dart'; @@ -34,12 +36,14 @@ import 'package:model/email/read_actions.dart'; import 'package:model/extensions/email_extension.dart'; import 'package:model/extensions/keyword_identifier_extension.dart'; import 'package:model/extensions/list_email_id_extension.dart'; +import 'package:model/extensions/mailbox_id_extension.dart'; import 'package:model/oidc/token.dart'; import 'package:path_provider/path_provider.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; class EmailAPI { @@ -76,39 +80,61 @@ class EmailAPI { }); } - Future sendEmail(AccountId accountId, EmailRequest emailRequest) async { - final mailboxIdSaved = emailRequest.mailboxIdSaved ?? emailRequest.email.mailboxIds?.keys.first; + Future sendEmail( + AccountId accountId, + EmailRequest emailRequest, + {CreateNewMailboxRequest? mailboxRequest} + ) async { + final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); + + Email? emailNeedsToBeCreated; + MailboxId? outboxMailboxId; + + if (mailboxRequest != null) { + final setMailboxMethod = SetMailboxMethod(accountId) + ..addCreate( + mailboxRequest.creationId, + Mailbox(name: mailboxRequest.newName, parentId: mailboxRequest.parentId)); + + requestBuilder.invocation(setMailboxMethod); + + outboxMailboxId = MailboxId(ReferenceId( + ReferencePrefix.defaultPrefix, + mailboxRequest.creationId)); + emailNeedsToBeCreated = emailRequest.email.updatedEmail(newMailboxIds: {outboxMailboxId: true}); + } else { + outboxMailboxId = emailRequest.email.mailboxIds?.keys.first; + emailNeedsToBeCreated = emailRequest.email; + } final setEmailMethod = SetEmailMethod(accountId) - ..addCreate(emailRequest.email.id.id, emailRequest.email); + ..addCreate(emailNeedsToBeCreated.id.id, emailNeedsToBeCreated); if (emailRequest.emailIdDestroyed != null) { - setEmailMethod - .addDestroy({emailRequest.emailIdDestroyed!.id}); + setEmailMethod.addDestroy({emailRequest.emailIdDestroyed!.id}); } + final setEmailInvocation = requestBuilder.invocation(setEmailMethod); final setEmailSubmissionMethod = SetEmailSubmissionMethod(accountId) ..addCreate( emailRequest.submissionCreateId, EmailSubmission( - emailId: EmailId(ReferenceId(ReferencePrefix.defaultPrefix, emailRequest.email.id.id)), + identityId: emailRequest.identity?.id?.id, + emailId: EmailId(ReferenceId(ReferencePrefix.defaultPrefix, emailNeedsToBeCreated.id.id)), envelope: Envelope( - Address(emailRequest.email.from?.first.email ?? ''), - emailRequest.email.getRecipientEmailAddressList().map((emailAddress) => Address(emailAddress)).toSet() + Address(emailNeedsToBeCreated.from?.first.email ?? ''), + emailNeedsToBeCreated.getRecipientEmailAddressList().map((emailAddress) => Address(emailAddress)).toSet() ) )) ..addOnSuccessUpdateEmail({ EmailSubmissionId(ReferenceId(ReferencePrefix.defaultPrefix, emailRequest.submissionCreateId)): PatchObject({ - PatchObject.mailboxIdsProperty: { - mailboxIdSaved?.id.value: true - }, - KeyWordIdentifier.emailSeen.generatePath(): true + emailRequest.sentMailboxId!.generatePath() : true, + outboxMailboxId!.generatePath() : null, + KeyWordIdentifier.emailSeen.generatePath(): true, + KeyWordIdentifier.emailDraft.generatePath(): null }) }); - final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); - - final setEmailInvocation = requestBuilder.invocation(setEmailMethod); final setEmailSubmissionInvocation = requestBuilder.invocation(setEmailSubmissionMethod); final response = await (requestBuilder @@ -125,15 +151,14 @@ class EmailAPI { SetEmailResponse.deserialize, methodName: setEmailInvocation.methodName); - return Future.sync(() async { - final emailCreated = setEmailResponse?.created?[emailRequest.email.id.id]; - if (emailCreated != null) { - return setEmailSubmissionResponse?.updated?[emailCreated.id.id] == null; - } + final emailCreated = setEmailResponse?.created?[emailNeedsToBeCreated.id.id]; + if (emailCreated != null) { + return setEmailSubmissionResponse?.notUpdated == null && + setEmailSubmissionResponse?.notCreated == null && + setEmailSubmissionResponse?.notDestroyed == null; + } else { return false; - }).catchError((error) { - throw error; - }); + } } Future> markAsRead(AccountId accountId, List emails, ReadActions readActions) async { diff --git a/lib/features/email/data/repository/email_repository_impl.dart b/lib/features/email/data/repository/email_repository_impl.dart index 7c09aa2476..5f69eb66af 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -16,6 +16,7 @@ import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_reques import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; class EmailRepositoryImpl extends EmailRepository { @@ -35,8 +36,8 @@ class EmailRepositoryImpl extends EmailRepository { } @override - Future sendEmail(AccountId accountId, EmailRequest emailRequest) { - return emailDataSource.sendEmail(accountId, emailRequest); + Future sendEmail(AccountId accountId, EmailRequest emailRequest, {CreateNewMailboxRequest? mailboxRequest}) { + return emailDataSource.sendEmail(accountId, emailRequest, mailboxRequest: mailboxRequest); } @override diff --git a/lib/features/email/domain/repository/email_repository.dart b/lib/features/email/domain/repository/email_repository.dart index ef64dd6672..4ddca04c78 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -10,11 +10,12 @@ import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; abstract class EmailRepository { Future getEmailContent(AccountId accountId, EmailId emailId); - Future sendEmail(AccountId accountId, EmailRequest emailRequest); + Future sendEmail(AccountId accountId, EmailRequest emailRequest, {CreateNewMailboxRequest? mailboxRequest}); Future> markAsRead(AccountId accountId, List emails, ReadActions readActions); diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index 05025b63bf..e4c4331211 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -146,11 +146,11 @@ class MailboxController extends BaseMailboxController { if (success is GetAllMailboxSuccess) { _currentMailboxState = success.currentMailboxState; await buildTree(success.mailboxList); - _setUpMapMailboxIdDefault(success.mailboxList, defaultMailboxTree.value, folderMailboxTree.value); + _setUpMapMailbox(success.mailboxList, defaultMailboxTree.value, folderMailboxTree.value); } else if (success is RefreshChangesAllMailboxSuccess) { _currentMailboxState = success.currentMailboxState; await refreshTree(success.mailboxList); - _setUpMapMailboxIdDefault(success.mailboxList, defaultMailboxTree.value, folderMailboxTree.value); + _setUpMapMailbox(success.mailboxList, defaultMailboxTree.value, folderMailboxTree.value); } }); } @@ -288,7 +288,7 @@ class MailboxController extends BaseMailboxController { } } - void _setUpMapMailboxIdDefault(List allMailbox, MailboxTree defaultTree, MailboxTree folderTree) { + void _setUpMapMailbox(List allMailbox, MailboxTree defaultTree, MailboxTree folderTree) { final mapDefaultMailboxIdByRole = { for (var mailboxNode in defaultTree.root.childrenItems ?? List.empty()) @@ -307,9 +307,22 @@ class MailboxController extends BaseMailboxController { }; mailboxDashBoardController.setMapDefaultMailboxIdByRole(mapDefaultMailboxIdByRole); - mailboxDashBoardController.setMapMailboxById(mapMailboxById); + try { + final outboxMailboxIdByRole = mapDefaultMailboxIdByRole[PresentationMailbox.roleOutbox]; + if (outboxMailboxIdByRole == null) { + final outboxMailboxByName = allMailbox + .firstWhere((mailbox) => mailbox.name?.toLowerCase() == PresentationMailbox.lowerCaseOutboxMailboxName); + mailboxDashBoardController.setOutboxMailbox(outboxMailboxByName); + } else { + mailboxDashBoardController.setOutboxMailbox(mapMailboxById[outboxMailboxIdByRole]!); + } + } catch (e) { + logError('MailboxController::_setUpMapMailboxIdDefault: Not found outbox mailbox'); + mailboxDashBoardController.setOutboxMailbox(null); + } + var mailboxCurrent = mailboxDashBoardController.selectedMailbox.value; if (mailboxCurrent != null) { diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index 5022ceb865..23558bfd1d 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -118,10 +118,13 @@ class MailboxDashBoardController extends ReloadableController { final composerOverlayState = ComposerOverlayState.inActive.obs; final viewStateMarkAsReadMailbox = Rx>(Right(UIState.idle)); final vacationResponse = Rxn(); + Session? sessionCurrent; Map mapDefaultMailboxIdByRole = {}; Map mapMailboxById = {}; + PresentationMailbox? outboxMailbox; RouterArguments? routerArguments; + late StreamSubscription _connectivityStreamSubscription; late StreamSubscription _emailReceiveManagerStreamSubscription; late StreamSubscription _fileReceiveManagerStreamSubscription; @@ -237,7 +240,7 @@ class MailboxDashBoardController extends ReloadableController { if (currentOverlayContext != null && currentContext != null) { _appToast.showToastWithIcon( currentOverlayContext!, - textColor: AppColor.toastSuccessBackgroundColor, + textColor: AppColor.primaryColor, message: AppLocalizations.of(currentContext!).message_has_been_sent_successfully, icon: _imagePaths.icSendToast); } @@ -381,6 +384,11 @@ class MailboxDashBoardController extends ReloadableController { mapMailboxById = newMapMailboxById; } + void setOutboxMailbox(PresentationMailbox? newOutbox) { + outboxMailbox = newOutbox; + log('MailboxDashBoardController::setOutboxMailbox(): $newOutbox'); + } + void setSelectedMailbox(PresentationMailbox? newPresentationMailbox) { selectedMailbox.value = newPresentationMailbox; } diff --git a/model/lib/extensions/email_extension.dart b/model/lib/extensions/email_extension.dart index 0551da954e..33b34640e0 100644 --- a/model/lib/extensions/email_extension.dart +++ b/model/lib/extensions/email_extension.dart @@ -4,6 +4,7 @@ import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart'; import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; extension EmailExtension on Email { @@ -23,7 +24,7 @@ extension EmailExtension on Email { return listEmailAddress; } - Email updatedEmail({Map? newKeywords}) { + Email updatedEmail({Map? newKeywords, Map? newMailboxIds}) { return Email( id, keywords: newKeywords ?? keywords, @@ -37,7 +38,12 @@ extension EmailExtension on Email { to: to, cc: cc, bcc: bcc, - replyTo: replyTo + replyTo: replyTo, + mailboxIds: newMailboxIds ?? mailboxIds, + htmlBody: htmlBody, + bodyValues: bodyValues, + headerUserAgent: headerUserAgent, + attachments: attachments ); } diff --git a/model/lib/extensions/mailbox_id_extension.dart b/model/lib/extensions/mailbox_id_extension.dart index 03a8fadfa9..89fee49136 100644 --- a/model/lib/extensions/mailbox_id_extension.dart +++ b/model/lib/extensions/mailbox_id_extension.dart @@ -1,9 +1,14 @@ import 'package:jmap_dart_client/jmap/core/patch_object.dart'; +import 'package:jmap_dart_client/jmap/core/reference_id.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; extension MailboxIdExtension on MailboxId { String generatePath() { - return '${PatchObject.mailboxIdsProperty}/${id.value}'; + if (id is ReferenceId) { + return '${PatchObject.mailboxIdsProperty}/${id.toString()}'; + } else { + return '${PatchObject.mailboxIdsProperty}/${id.value}'; + } } PatchObject generateMoveToMailboxActionPath(MailboxId destinationMailboxId) { diff --git a/model/lib/extensions/mailbox_name_extension.dart b/model/lib/extensions/mailbox_name_extension.dart index 1615885783..ddf015795c 100644 --- a/model/lib/extensions/mailbox_name_extension.dart +++ b/model/lib/extensions/mailbox_name_extension.dart @@ -7,4 +7,8 @@ extension MailboxNameExtension on MailboxName { } return name.toLowerCase().compareTo(other.name.toLowerCase()); } + + MailboxName toLowerCase() { + return MailboxName(name.toLowerCase()); + } } \ No newline at end of file diff --git a/model/lib/mailbox/presentation_mailbox.dart b/model/lib/mailbox/presentation_mailbox.dart index 68abf06e9f..29c1d8d22d 100644 --- a/model/lib/mailbox/presentation_mailbox.dart +++ b/model/lib/mailbox/presentation_mailbox.dart @@ -16,6 +16,9 @@ class PresentationMailbox with EquatableMixin { static final roleDrafts = Role('drafts'); static final roleSpam = Role('spam'); + static final outboxMailboxName = MailboxName('Outbox'); + static final lowerCaseOutboxMailboxName = MailboxName('outbox'); + final MailboxId id; final MailboxName? name; final MailboxId? parentId; @@ -70,7 +73,7 @@ class PresentationMailbox with EquatableMixin { bool get isSent => role == roleSent; - bool get isOutbox => name?.name == 'Outbox'; + bool get isOutbox => name == lowerCaseOutboxMailboxName || role == roleOutbox; bool matchCountingRules() { if (isSpam || isTrash || isDrafts || isTemplates || isSent) {