TF-942 Auto generate missing Outbox mailbox before send email

This commit is contained in:
dab246
2022-09-23 12:55:38 +07:00
committed by Dat H. Pham
parent 1722543808
commit 106a0c4a37
16 changed files with 205 additions and 85 deletions
@@ -0,0 +1,4 @@
class NotFoundOutboxMailboxException implements Exception {
NotFoundOutboxMailboxException();
}
@@ -1,6 +1,7 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/core/id.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/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
@@ -8,11 +9,35 @@ class EmailRequest with EquatableMixin {
final Email email; final Email email;
final Id submissionCreateId; final Id submissionCreateId;
final MailboxId? mailboxIdSaved; final MailboxId? sentMailboxId;
final EmailId? emailIdDestroyed; 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 @override
List<Object?> get props => [email, submissionCreateId, mailboxIdSaved]; List<Object?> 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
);
}
} }
@@ -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/model/email_request.dart';
import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.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/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:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart';
import 'package:uuid/uuid.dart';
class SendEmailInteractor { class SendEmailInteractor {
final EmailRepository _emailRepository; final EmailRepository _emailRepository;
final MailboxRepository _mailboxRepository; final MailboxRepository _mailboxRepository;
final Uuid _uuid;
SendEmailInteractor(this._emailRepository, this._mailboxRepository); SendEmailInteractor(this._emailRepository, this._mailboxRepository, this._uuid);
Stream<Either<Failure, Success>> execute(AccountId accountId, EmailRequest emailRequest) async* { Stream<Either<Failure, Success>> execute(
AccountId accountId,
EmailRequest emailRequest,
{CreateNewMailboxRequest? mailboxRequest}
) async* {
try { try {
yield Right<Failure, Success>(SendingEmailState()); yield Right<Failure, Success>(SendingEmailState());
@@ -24,7 +31,7 @@ class SendEmailInteractor {
final currentMailboxState = listState.first; final currentMailboxState = listState.first;
final currentEmailState = listState.last; final currentEmailState = listState.last;
final result = await _emailRepository.sendEmail(accountId, emailRequest); final result = await _emailRepository.sendEmail(accountId, emailRequest, mailboxRequest: mailboxRequest);
if (result) { if (result) {
yield Right<Failure, Success>(SendEmailSuccess( yield Right<Failure, Success>(SendEmailSuccess(
currentEmailState: currentEmailState, currentEmailState: currentEmailState,
@@ -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/domain/usecases/local_file_picker_interactor.dart';
import 'package:tmail_ui_user/features/upload/presentation/controller/upload_controller.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:tmail_ui_user/main/bindings/network/binding_tag.dart';
import 'package:uuid/uuid.dart';
import 'package:worker_manager/worker_manager.dart'; import 'package:worker_manager/worker_manager.dart';
class ComposerBindings extends BaseBindings { class ComposerBindings extends BaseBindings {
@@ -134,7 +135,8 @@ class ComposerBindings extends BaseBindings {
Get.lazyPut(() => UploadAttachmentInteractor(Get.find<ComposerRepository>())); Get.lazyPut(() => UploadAttachmentInteractor(Get.find<ComposerRepository>()));
Get.lazyPut(() => SendEmailInteractor( Get.lazyPut(() => SendEmailInteractor(
Get.find<EmailRepository>(), Get.find<EmailRepository>(),
Get.find<MailboxRepository>())); Get.find<MailboxRepository>(),
Get.find<Uuid>()));
Get.lazyPut(() => SaveEmailAsDraftsInteractor( Get.lazyPut(() => SaveEmailAsDraftsInteractor(
Get.find<EmailRepository>(), Get.find<EmailRepository>(),
Get.find<MailboxRepository>())); Get.find<MailboxRepository>()));
@@ -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/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/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/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/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/domain/usecases/save_composer_cache_on_web_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.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; final userProfile = mailboxDashBoardController.userProfile.value;
_removeComposerCacheOnWebInteractor.execute(); _removeComposerCacheOnWebInteractor.execute();
if (userProfile != null) { if (userProfile != null) {
final draftEmail = await _generateEmail( final draftEmail = await _generateEmail(currentContext!, userProfile);
currentContext!,
mailboxDashBoardController.mapDefaultMailboxIdByRole,
userProfile,
);
_saveComposerCacheOnWebInteractor.execute(draftEmail); _saveComposerCacheOnWebInteractor.execute(draftEmail);
} }
}); });
@@ -524,13 +521,13 @@ class ComposerController extends BaseController {
Future<Email> _generateEmail( Future<Email> _generateEmail(
BuildContext context, BuildContext context,
Map<Role, MailboxId> mapDefaultMailboxId,
UserProfile userProfile, UserProfile userProfile,
{bool asDrafts = false} {
bool asDrafts = false,
MailboxId? draftMailboxId,
MailboxId? outboxMailboxId
}
) async { ) async {
final generateEmailId = EmailId(Id(_uuid.v1()));
final outboxMailboxId = mapDefaultMailboxId[PresentationMailbox.roleOutbox];
final draftMailboxId = mapDefaultMailboxId[PresentationMailbox.roleDrafts];
Set<EmailAddress> listFromEmailAddress = {EmailAddress(null, userProfile.email)}; Set<EmailAddress> listFromEmailAddress = {EmailAddress(null, userProfile.email)};
if (identitySelected.value?.email?.isNotEmpty == true) { if (identitySelected.value?.email?.isNotEmpty == true) {
listFromEmailAddress = {EmailAddress( listFromEmailAddress = {EmailAddress(
@@ -541,37 +538,48 @@ class ComposerController extends BaseController {
if (identitySelected.value?.replyTo?.isNotEmpty == true) { if (identitySelected.value?.replyTo?.isNotEmpty == true) {
listReplyToEmailAddress = identitySelected.value!.replyTo!; listReplyToEmailAddress = identitySelected.value!.replyTo!;
} }
final generatePartId = PartId(_uuid.v1());
final attachments = <EmailBodyPart>{}; final attachments = <EmailBodyPart>{};
attachments.addAll(uploadController.generateAttachments() ?? []); attachments.addAll(uploadController.generateAttachments() ?? []);
var emailBodyText = await _getEmailBodyText(context); var emailBodyText = await _getEmailBodyText(context);
if (uploadController.mapInlineAttachments.isNotEmpty) { if (uploadController.mapInlineAttachments.isNotEmpty) {
final mapContents = await _getMapContent(emailBodyText); final mapContents = await _getMapContent(emailBodyText);
log('ComposerController::_generateEmail(): mapContents: $mapContents');
emailBodyText = mapContents.value1; emailBodyText = mapContents.value1;
final listInlineAttachment = mapContents.value2; final listInlineAttachment = mapContents.value2;
final listInlineEmailBodyPart = listInlineAttachment final listInlineEmailBodyPart = listInlineAttachment
.map((attachment) => attachment.toEmailBodyPart(charset: 'base64')) .map((attachment) => attachment.toEmailBodyPart(charset: 'base64'))
.toSet(); .toSet();
attachments.addAll(listInlineEmailBodyPart); attachments.addAll(listInlineEmailBodyPart);
log('ComposerController::_generateEmail(): listInlineEmailBodyPart: $listInlineEmailBodyPart');
} }
final userAgent = await userAgentPlatform; final userAgent = await userAgentPlatform;
log('ComposerController::_generateEmail(): userAgent: $userAgent');
Map<MailboxId, bool> mailboxIds = {};
if (asDrafts && draftMailboxId != null) {
mailboxIds[draftMailboxId] = true;
}
if (outboxMailboxId != null) {
mailboxIds[outboxMailboxId] = true;
}
Map<KeyWordIdentifier, bool>? mapKeywords = {};
if (asDrafts) {
mapKeywords[KeyWordIdentifier.emailDraft] = true;
}
final generateEmailId = EmailId(Id(_uuid.v1()));
final generatePartId = PartId(_uuid.v1());
return Email( return Email(
generateEmailId, generateEmailId,
mailboxIds: asDrafts ? {draftMailboxId!: true} : {outboxMailboxId!: true}, mailboxIds: mailboxIds.isNotEmpty ? mailboxIds : null,
from: listFromEmailAddress, from: listFromEmailAddress,
to: listToEmailAddress.toSet(), to: listToEmailAddress.toSet(),
cc: listCcEmailAddress.toSet(), cc: listCcEmailAddress.toSet(),
bcc: listBccEmailAddress.toSet(), bcc: listBccEmailAddress.toSet(),
replyTo: listReplyToEmailAddress, replyTo: listReplyToEmailAddress,
keywords: asDrafts ? {KeyWordIdentifier.emailDraft : true} : null, keywords: mapKeywords.isNotEmpty ? mapKeywords : null,
subject: subjectEmail.value, subject: subjectEmail.value,
htmlBody: { htmlBody: {
EmailBodyPart( EmailBodyPart(
@@ -687,23 +695,33 @@ class ComposerController extends BaseController {
void _handleSendMessages(BuildContext context) async { void _handleSendMessages(BuildContext context) async {
final arguments = composerArguments.value; final arguments = composerArguments.value;
final session = mailboxDashBoardController.sessionCurrent; final accountId = mailboxDashBoardController.accountId.value;
final mapDefaultMailboxIdByRole = mailboxDashBoardController.mapDefaultMailboxIdByRole; final sentMailboxId = mailboxDashBoardController.mapDefaultMailboxIdByRole[PresentationMailbox.roleSent];
final outboxMailboxId = mailboxDashBoardController.outboxMailbox?.id;
log('ComposerController::_handleSendMessages(): outboxMailboxId: $outboxMailboxId');
final userProfile = mailboxDashBoardController.userProfile.value; final userProfile = mailboxDashBoardController.userProfile.value;
if (arguments != null && session != null && mapDefaultMailboxIdByRole.isNotEmpty if (arguments != null && accountId != null && userProfile != null) {
&& userProfile != null) { final email = await _generateEmail(context, userProfile, outboxMailboxId: outboxMailboxId);
final email = await _generateEmail(context, mapDefaultMailboxIdByRole, userProfile);
final accountId = session.accounts.keys.first;
final sentMailboxId = mapDefaultMailboxIdByRole[PresentationMailbox.roleSent];
final submissionCreateId = Id(_uuid.v1()); final submissionCreateId = Id(_uuid.v1());
mailboxDashBoardController.consumeState(_sendEmailInteractor.execute( mailboxDashBoardController.consumeState(
accountId, _sendEmailInteractor.execute(
EmailRequest(email, submissionCreateId, mailboxIdSaved: sentMailboxId, accountId,
emailIdDestroyed: arguments.emailActionType == EmailActionType.edit EmailRequest(
? arguments.presentationEmail?.id email,
: null))); 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(); uploadController.clearInlineFileUploaded();
} }
@@ -892,25 +910,21 @@ class ComposerController extends BaseController {
} }
void saveEmailAsDrafts(BuildContext context, {bool canPop = true}) async { void saveEmailAsDrafts(BuildContext context, {bool canPop = true}) async {
log('ComposerController::saveEmailAsDrafts():');
clearFocusEditor(context); clearFocusEditor(context);
final arguments = composerArguments.value; final arguments = composerArguments.value;
final mapDefaultMailboxIdByRole = mailboxDashBoardController.mapDefaultMailboxIdByRole; final draftMailboxId = mailboxDashBoardController.mapDefaultMailboxIdByRole[PresentationMailbox.roleDrafts];
final userProfile = mailboxDashBoardController.userProfile.value; final userProfile = mailboxDashBoardController.userProfile.value;
final session = mailboxDashBoardController.sessionCurrent; final accountId = mailboxDashBoardController.accountId.value;
if (arguments != null && mapDefaultMailboxIdByRole.isNotEmpty && userProfile != null && session != null) { if (arguments != null && userProfile != null && accountId != null) {
log('ComposerController::saveEmailAsDrafts(): saveEmailAsDrafts START');
final isChanged = await _isEmailChanged(context, arguments); final isChanged = await _isEmailChanged(context, arguments);
log('ComposerController::saveEmailAsDrafts(): saveEmailAsDrafts isChanged: $isChanged');
if (isChanged) { if (isChanged) {
final newEmail = await _generateEmail( final newEmail = await _generateEmail(
context, context,
mapDefaultMailboxIdByRole,
userProfile, userProfile,
asDrafts: true); asDrafts: true,
final accountId = session.accounts.keys.first; draftMailboxId: draftMailboxId);
final oldEmail = arguments.presentationEmail; final oldEmail = arguments.presentationEmail;
if (arguments.emailActionType == EmailActionType.edit && oldEmail != null) { if (arguments.emailActionType == EmailActionType.edit && oldEmail != null) {
@@ -9,11 +9,12 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
import 'package:tmail_ui_user/features/composer/domain/model/email_request.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/model/move_to_mailbox_request.dart';
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
abstract class EmailDataSource { abstract class EmailDataSource {
Future<Email> getEmailContent(AccountId accountId, EmailId emailId); Future<Email> getEmailContent(AccountId accountId, EmailId emailId);
Future<bool> sendEmail(AccountId accountId, EmailRequest emailRequest); Future<bool> sendEmail(AccountId accountId, EmailRequest emailRequest, {CreateNewMailboxRequest? mailboxRequest});
Future<List<Email>> markAsRead(AccountId accountId, List<Email> emails, ReadActions readActions); Future<List<Email>> markAsRead(AccountId accountId, List<Email> emails, ReadActions readActions);
@@ -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/datasource/email_datasource.dart';
import 'package:tmail_ui_user/features/email/data/network/email_api.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/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 { class EmailDataSourceImpl extends EmailDataSource {
@@ -28,9 +29,9 @@ class EmailDataSourceImpl extends EmailDataSource {
} }
@override @override
Future<bool> sendEmail(AccountId accountId, EmailRequest emailRequest) { Future<bool> sendEmail(AccountId accountId, EmailRequest emailRequest, {CreateNewMailboxRequest? mailboxRequest}) {
return Future.sync(() async { return Future.sync(() async {
return await emailAPI.sendEmail(accountId, emailRequest); return await emailAPI.sendEmail(accountId, emailRequest, mailboxRequest: mailboxRequest);
}).catchError((error) { }).catchError((error) {
throw error; throw error;
}); });
+48 -23
View File
@@ -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/email_submission_id.dart';
import 'package:jmap_dart_client/jmap/mail/email/submission/envelope.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/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/account_request.dart';
import 'package:model/account/authentication_type.dart'; import 'package:model/account/authentication_type.dart';
import 'package:model/download/download_task_id.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/email_extension.dart';
import 'package:model/extensions/keyword_identifier_extension.dart'; import 'package:model/extensions/keyword_identifier_extension.dart';
import 'package:model/extensions/list_email_id_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:model/oidc/token.dart';
import 'package:path_provider/path_provider.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/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/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/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/login/domain/exceptions/authentication_exception.dart';
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
class EmailAPI { class EmailAPI {
@@ -76,39 +80,61 @@ class EmailAPI {
}); });
} }
Future<bool> sendEmail(AccountId accountId, EmailRequest emailRequest) async { Future<bool> sendEmail(
final mailboxIdSaved = emailRequest.mailboxIdSaved ?? emailRequest.email.mailboxIds?.keys.first; 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) final setEmailMethod = SetEmailMethod(accountId)
..addCreate(emailRequest.email.id.id, emailRequest.email); ..addCreate(emailNeedsToBeCreated.id.id, emailNeedsToBeCreated);
if (emailRequest.emailIdDestroyed != null) { if (emailRequest.emailIdDestroyed != null) {
setEmailMethod setEmailMethod.addDestroy({emailRequest.emailIdDestroyed!.id});
.addDestroy({emailRequest.emailIdDestroyed!.id});
} }
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
final setEmailSubmissionMethod = SetEmailSubmissionMethod(accountId) final setEmailSubmissionMethod = SetEmailSubmissionMethod(accountId)
..addCreate( ..addCreate(
emailRequest.submissionCreateId, emailRequest.submissionCreateId,
EmailSubmission( 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( envelope: Envelope(
Address(emailRequest.email.from?.first.email ?? ''), Address(emailNeedsToBeCreated.from?.first.email ?? ''),
emailRequest.email.getRecipientEmailAddressList().map((emailAddress) => Address(emailAddress)).toSet() emailNeedsToBeCreated.getRecipientEmailAddressList().map((emailAddress) => Address(emailAddress)).toSet()
) )
)) ))
..addOnSuccessUpdateEmail({ ..addOnSuccessUpdateEmail({
EmailSubmissionId(ReferenceId(ReferencePrefix.defaultPrefix, emailRequest.submissionCreateId)): PatchObject({ EmailSubmissionId(ReferenceId(ReferencePrefix.defaultPrefix, emailRequest.submissionCreateId)): PatchObject({
PatchObject.mailboxIdsProperty: { emailRequest.sentMailboxId!.generatePath() : true,
mailboxIdSaved?.id.value: true outboxMailboxId!.generatePath() : null,
}, KeyWordIdentifier.emailSeen.generatePath(): true,
KeyWordIdentifier.emailSeen.generatePath(): true KeyWordIdentifier.emailDraft.generatePath(): null
}) })
}); });
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
final setEmailSubmissionInvocation = requestBuilder.invocation(setEmailSubmissionMethod); final setEmailSubmissionInvocation = requestBuilder.invocation(setEmailSubmissionMethod);
final response = await (requestBuilder final response = await (requestBuilder
@@ -125,15 +151,14 @@ class EmailAPI {
SetEmailResponse.deserialize, SetEmailResponse.deserialize,
methodName: setEmailInvocation.methodName); methodName: setEmailInvocation.methodName);
return Future.sync(() async { final emailCreated = setEmailResponse?.created?[emailNeedsToBeCreated.id.id];
final emailCreated = setEmailResponse?.created?[emailRequest.email.id.id]; if (emailCreated != null) {
if (emailCreated != null) { return setEmailSubmissionResponse?.notUpdated == null &&
return setEmailSubmissionResponse?.updated?[emailCreated.id.id] == null; setEmailSubmissionResponse?.notCreated == null &&
} setEmailSubmissionResponse?.notDestroyed == null;
} else {
return false; return false;
}).catchError((error) { }
throw error;
});
} }
Future<List<Email>> markAsRead(AccountId accountId, List<Email> emails, ReadActions readActions) async { Future<List<Email>> markAsRead(AccountId accountId, List<Email> emails, ReadActions readActions) async {
@@ -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/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/datasource/state_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_type.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 { class EmailRepositoryImpl extends EmailRepository {
@@ -35,8 +36,8 @@ class EmailRepositoryImpl extends EmailRepository {
} }
@override @override
Future<bool> sendEmail(AccountId accountId, EmailRequest emailRequest) { Future<bool> sendEmail(AccountId accountId, EmailRequest emailRequest, {CreateNewMailboxRequest? mailboxRequest}) {
return emailDataSource.sendEmail(accountId, emailRequest); return emailDataSource.sendEmail(accountId, emailRequest, mailboxRequest: mailboxRequest);
} }
@override @override
@@ -10,11 +10,12 @@ import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
import 'package:model/model.dart'; import 'package:model/model.dart';
import 'package:tmail_ui_user/features/composer/domain/model/email_request.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/model/move_to_mailbox_request.dart';
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
abstract class EmailRepository { abstract class EmailRepository {
Future<Email> getEmailContent(AccountId accountId, EmailId emailId); Future<Email> getEmailContent(AccountId accountId, EmailId emailId);
Future<bool> sendEmail(AccountId accountId, EmailRequest emailRequest); Future<bool> sendEmail(AccountId accountId, EmailRequest emailRequest, {CreateNewMailboxRequest? mailboxRequest});
Future<List<Email>> markAsRead(AccountId accountId, List<Email> emails, ReadActions readActions); Future<List<Email>> markAsRead(AccountId accountId, List<Email> emails, ReadActions readActions);
@@ -146,11 +146,11 @@ class MailboxController extends BaseMailboxController {
if (success is GetAllMailboxSuccess) { if (success is GetAllMailboxSuccess) {
_currentMailboxState = success.currentMailboxState; _currentMailboxState = success.currentMailboxState;
await buildTree(success.mailboxList); await buildTree(success.mailboxList);
_setUpMapMailboxIdDefault(success.mailboxList, defaultMailboxTree.value, folderMailboxTree.value); _setUpMapMailbox(success.mailboxList, defaultMailboxTree.value, folderMailboxTree.value);
} else if (success is RefreshChangesAllMailboxSuccess) { } else if (success is RefreshChangesAllMailboxSuccess) {
_currentMailboxState = success.currentMailboxState; _currentMailboxState = success.currentMailboxState;
await refreshTree(success.mailboxList); 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<PresentationMailbox> allMailbox, MailboxTree defaultTree, MailboxTree folderTree) { void _setUpMapMailbox(List<PresentationMailbox> allMailbox, MailboxTree defaultTree, MailboxTree folderTree) {
final mapDefaultMailboxIdByRole = { final mapDefaultMailboxIdByRole = {
for (var mailboxNode for (var mailboxNode
in defaultTree.root.childrenItems ?? List<MailboxNode>.empty()) in defaultTree.root.childrenItems ?? List<MailboxNode>.empty())
@@ -307,9 +307,22 @@ class MailboxController extends BaseMailboxController {
}; };
mailboxDashBoardController.setMapDefaultMailboxIdByRole(mapDefaultMailboxIdByRole); mailboxDashBoardController.setMapDefaultMailboxIdByRole(mapDefaultMailboxIdByRole);
mailboxDashBoardController.setMapMailboxById(mapMailboxById); 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; var mailboxCurrent = mailboxDashBoardController.selectedMailbox.value;
if (mailboxCurrent != null) { if (mailboxCurrent != null) {
@@ -118,10 +118,13 @@ class MailboxDashBoardController extends ReloadableController {
final composerOverlayState = ComposerOverlayState.inActive.obs; final composerOverlayState = ComposerOverlayState.inActive.obs;
final viewStateMarkAsReadMailbox = Rx<Either<Failure, Success>>(Right(UIState.idle)); final viewStateMarkAsReadMailbox = Rx<Either<Failure, Success>>(Right(UIState.idle));
final vacationResponse = Rxn<VacationResponse>(); final vacationResponse = Rxn<VacationResponse>();
Session? sessionCurrent; Session? sessionCurrent;
Map<Role, MailboxId> mapDefaultMailboxIdByRole = {}; Map<Role, MailboxId> mapDefaultMailboxIdByRole = {};
Map<MailboxId, PresentationMailbox> mapMailboxById = {}; Map<MailboxId, PresentationMailbox> mapMailboxById = {};
PresentationMailbox? outboxMailbox;
RouterArguments? routerArguments; RouterArguments? routerArguments;
late StreamSubscription _connectivityStreamSubscription; late StreamSubscription _connectivityStreamSubscription;
late StreamSubscription _emailReceiveManagerStreamSubscription; late StreamSubscription _emailReceiveManagerStreamSubscription;
late StreamSubscription _fileReceiveManagerStreamSubscription; late StreamSubscription _fileReceiveManagerStreamSubscription;
@@ -237,7 +240,7 @@ class MailboxDashBoardController extends ReloadableController {
if (currentOverlayContext != null && currentContext != null) { if (currentOverlayContext != null && currentContext != null) {
_appToast.showToastWithIcon( _appToast.showToastWithIcon(
currentOverlayContext!, currentOverlayContext!,
textColor: AppColor.toastSuccessBackgroundColor, textColor: AppColor.primaryColor,
message: AppLocalizations.of(currentContext!).message_has_been_sent_successfully, message: AppLocalizations.of(currentContext!).message_has_been_sent_successfully,
icon: _imagePaths.icSendToast); icon: _imagePaths.icSendToast);
} }
@@ -381,6 +384,11 @@ class MailboxDashBoardController extends ReloadableController {
mapMailboxById = newMapMailboxById; mapMailboxById = newMapMailboxById;
} }
void setOutboxMailbox(PresentationMailbox? newOutbox) {
outboxMailbox = newOutbox;
log('MailboxDashBoardController::setOutboxMailbox(): $newOutbox');
}
void setSelectedMailbox(PresentationMailbox? newPresentationMailbox) { void setSelectedMailbox(PresentationMailbox? newPresentationMailbox) {
selectedMailbox.value = newPresentationMailbox; selectedMailbox.value = newPresentationMailbox;
} }
+8 -2
View File
@@ -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.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_body_part.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/email/keyword_identifier.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
extension EmailExtension on Email { extension EmailExtension on Email {
@@ -23,7 +24,7 @@ extension EmailExtension on Email {
return listEmailAddress; return listEmailAddress;
} }
Email updatedEmail({Map<KeyWordIdentifier, bool>? newKeywords}) { Email updatedEmail({Map<KeyWordIdentifier, bool>? newKeywords, Map<MailboxId, bool>? newMailboxIds}) {
return Email( return Email(
id, id,
keywords: newKeywords ?? keywords, keywords: newKeywords ?? keywords,
@@ -37,7 +38,12 @@ extension EmailExtension on Email {
to: to, to: to,
cc: cc, cc: cc,
bcc: bcc, bcc: bcc,
replyTo: replyTo replyTo: replyTo,
mailboxIds: newMailboxIds ?? mailboxIds,
htmlBody: htmlBody,
bodyValues: bodyValues,
headerUserAgent: headerUserAgent,
attachments: attachments
); );
} }
@@ -1,9 +1,14 @@
import 'package:jmap_dart_client/jmap/core/patch_object.dart'; 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'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
extension MailboxIdExtension on MailboxId { extension MailboxIdExtension on MailboxId {
String generatePath() { 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) { PatchObject generateMoveToMailboxActionPath(MailboxId destinationMailboxId) {
@@ -7,4 +7,8 @@ extension MailboxNameExtension on MailboxName {
} }
return name.toLowerCase().compareTo(other.name.toLowerCase()); return name.toLowerCase().compareTo(other.name.toLowerCase());
} }
MailboxName toLowerCase() {
return MailboxName(name.toLowerCase());
}
} }
+4 -1
View File
@@ -16,6 +16,9 @@ class PresentationMailbox with EquatableMixin {
static final roleDrafts = Role('drafts'); static final roleDrafts = Role('drafts');
static final roleSpam = Role('spam'); static final roleSpam = Role('spam');
static final outboxMailboxName = MailboxName('Outbox');
static final lowerCaseOutboxMailboxName = MailboxName('outbox');
final MailboxId id; final MailboxId id;
final MailboxName? name; final MailboxName? name;
final MailboxId? parentId; final MailboxId? parentId;
@@ -70,7 +73,7 @@ class PresentationMailbox with EquatableMixin {
bool get isSent => role == roleSent; bool get isSent => role == roleSent;
bool get isOutbox => name?.name == 'Outbox'; bool get isOutbox => name == lowerCaseOutboxMailboxName || role == roleOutbox;
bool matchCountingRules() { bool matchCountingRules() {
if (isSpam || isTrash || isDrafts || isTemplates || isSent) { if (isSpam || isTrash || isDrafts || isTemplates || isSent) {