From 808191a806bfd4e26ca10fdf02e65d4202d667ea Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 21 Feb 2022 17:07:07 +0700 Subject: [PATCH] TF-82 Edit a email draft --- .../composer/domain/model/email_request.dart | 3 +- .../state/update_email_drafts_state.dart | 21 +++ .../update_email_drafts_interactor.dart | 25 +++ .../presentation/composer_bindings.dart | 8 +- .../presentation/composer_controller.dart | 144 +++++++++++++----- .../composer/presentation/composer_view.dart | 15 +- .../email_action_type_extension.dart | 2 + .../data/datasource/email_datasource.dart | 2 + .../email_datasource_impl.dart | 9 ++ .../email/data/network/email_api.dart | 42 ++++- .../repository/email_repository_impl.dart | 5 + .../domain/repository/email_repository.dart | 2 + .../email/presentation/email_controller.dart | 2 +- .../presentation/mailbox_controller.dart | 9 +- .../mailbox_dashboard_controller.dart | 8 +- .../presentation/thread_controller.dart | 22 ++- .../thread/presentation/thread_view.dart | 8 +- model/lib/email/email_action_type.dart | 1 + .../presentation_email_extension.dart | 2 + 19 files changed, 275 insertions(+), 55 deletions(-) create mode 100644 lib/features/composer/domain/state/update_email_drafts_state.dart create mode 100644 lib/features/composer/domain/usecases/update_email_drafts_interactor.dart diff --git a/lib/features/composer/domain/model/email_request.dart b/lib/features/composer/domain/model/email_request.dart index dece59e3d..aa6c53ce1 100644 --- a/lib/features/composer/domain/model/email_request.dart +++ b/lib/features/composer/domain/model/email_request.dart @@ -9,8 +9,9 @@ class EmailRequest with EquatableMixin { final Email email; final Id submissionCreateId; final MailboxId? mailboxIdSaved; + final EmailId? emailIdDestroyed; - EmailRequest(this.email, this.submissionCreateId, {this.mailboxIdSaved}); + EmailRequest(this.email, this.submissionCreateId, {this.mailboxIdSaved, this.emailIdDestroyed}); @override List get props => [email, submissionCreateId, mailboxIdSaved]; diff --git a/lib/features/composer/domain/state/update_email_drafts_state.dart b/lib/features/composer/domain/state/update_email_drafts_state.dart new file mode 100644 index 000000000..9dd953a9d --- /dev/null +++ b/lib/features/composer/domain/state/update_email_drafts_state.dart @@ -0,0 +1,21 @@ +import 'package:core/core.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; + +class UpdateEmailDraftsSuccess extends UIState { + + final Email emailAsDrafts; + + UpdateEmailDraftsSuccess(this.emailAsDrafts); + + @override + List get props => []; +} + +class UpdateEmailDraftsFailure extends FeatureFailure { + final exception; + + UpdateEmailDraftsFailure(this.exception); + + @override + List get props => [exception]; +} \ No newline at end of file diff --git a/lib/features/composer/domain/usecases/update_email_drafts_interactor.dart b/lib/features/composer/domain/usecases/update_email_drafts_interactor.dart new file mode 100644 index 000000000..c4878fc39 --- /dev/null +++ b/lib/features/composer/domain/usecases/update_email_drafts_interactor.dart @@ -0,0 +1,25 @@ +import 'package:core/core.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:tmail_ui_user/features/composer/domain/state/update_email_drafts_state.dart'; +import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; + +class UpdateEmailDraftsInteractor { + final EmailRepository emailRepository; + + UpdateEmailDraftsInteractor(this.emailRepository); + + Stream> execute(AccountId accountId, Email newEmail, EmailId oldEmailId) async* { + try { + final newEmailDrafts = await emailRepository.updateEmailDrafts(accountId, newEmail, oldEmailId); + if (newEmailDrafts != null) { + yield Right(UpdateEmailDraftsSuccess(newEmailDrafts)); + } else { + yield Left(UpdateEmailDraftsFailure(null)); + } + } catch (e) { + yield Left(UpdateEmailDraftsFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_bindings.dart b/lib/features/composer/presentation/composer_bindings.dart index f06e43b37..0dfc78afd 100644 --- a/lib/features/composer/presentation/composer_bindings.dart +++ b/lib/features/composer/presentation/composer_bindings.dart @@ -22,6 +22,7 @@ import 'package:tmail_ui_user/features/composer/domain/repository/contact_reposi import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_with_device_contact_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_device_contact_suggestions_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/save_email_as_drafts_interactor.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/update_email_drafts_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/upload_attachment_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/save_email_addresses_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart'; @@ -36,6 +37,7 @@ import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart'; import 'package:uuid/uuid.dart'; @@ -113,6 +115,8 @@ class ComposerBindings extends BaseBindings { Get.lazyPut(() => UploadMultipleAttachmentInteractor(Get.find())); Get.lazyPut(() => SendEmailInteractor(Get.find())); Get.lazyPut(() => SaveEmailAsDraftsInteractor(Get.find())); + Get.lazyPut(() => GetEmailContentInteractor(Get.find())); + Get.lazyPut(() => UpdateEmailDraftsInteractor(Get.find())); } @override @@ -130,7 +134,9 @@ class ComposerBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), - Get.find() + Get.find(), + Get.find(), + Get.find(), )); } } \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 67ef2e0bf..23c23d509 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -33,8 +33,11 @@ import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_s import 'package:tmail_ui_user/features/composer/domain/usecases/save_email_addresses_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/save_email_as_drafts_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_interactor.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/update_email_drafts_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.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/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/upload/domain/state/local_file_picker_state.dart'; @@ -52,6 +55,7 @@ class ComposerController extends BaseController { final composerArguments = Rxn(); final isEnableEmailSendButton = false.obs; final attachments = [].obs; + final emailContents = [].obs; final SendEmailInteractor _sendEmailInteractor; final SaveEmailAddressesInteractor _saveEmailAddressInteractor; @@ -65,6 +69,8 @@ class ComposerController extends BaseController { final UploadMultipleAttachmentInteractor _uploadMultipleAttachmentInteractor; final DeviceInfoPlugin _deviceInfoPlugin; final SaveEmailAsDraftsInteractor _saveEmailAsDraftsInteractor; + final GetEmailContentInteractor _getEmailContentInteractor; + final UpdateEmailDraftsInteractor _updateEmailDraftsInteractor; List listToEmailAddress = []; List listCcEmailAddress = []; @@ -100,6 +106,8 @@ class ComposerController extends BaseController { this._localFilePickerInteractor, this._uploadMultipleAttachmentInteractor, this._saveEmailAsDraftsInteractor, + this._getEmailContentInteractor, + this._updateEmailDraftsInteractor, ); @override @@ -113,7 +121,7 @@ class ComposerController extends BaseController { @override void onReady() async { super.onReady(); - _getSelectedEmail(); + _initEmail(); Future.delayed(Duration(milliseconds: 500), () => _checkContactPermission()); } @@ -136,6 +144,8 @@ class ComposerController extends BaseController { || success is UploadMultipleAttachmentAllSuccess || success is UploadMultipleAttachmentHasSomeFailure) { _uploadAttachmentsSuccess(success); + } else if (success is GetEmailContentSuccess) { + _getEmailContentSuccess(success); } }); } @@ -152,19 +162,22 @@ class ComposerController extends BaseController { popBack(); } - void _getSelectedEmail() { + void _initEmail() { final arguments = Get.arguments; if (arguments is ComposerArguments) { composerArguments.value = arguments; - _initToEmailAddress(); - _initSubjectEmail(); + if (arguments.emailActionType == EmailActionType.edit) { + _getEmailContentAction(arguments); + } + _initToEmailAddress(arguments); + _initSubjectEmail(arguments); } } - void _initSubjectEmail() { + void _initSubjectEmail(ComposerArguments arguments) { if (Get.context != null) { - final subjectEmail = composerArguments.value?.presentationEmail?.getEmailTitle().trim() ?? ''; - final newSubject = composerArguments.value?.emailActionType.getSubjectComposer(Get.context!, subjectEmail) ?? ''; + final subjectEmail = arguments.presentationEmail?.getEmailTitle().trim() ?? ''; + final newSubject = arguments.emailActionType.getSubjectComposer(Get.context!, subjectEmail); setSubjectEmail(newSubject); subjectEmailInputController.text = newSubject; } @@ -174,16 +187,19 @@ class ComposerController extends BaseController { if (composerArguments.value != null && composerArguments.value!.emailActionType != EmailActionType.compose && Get.context != null) { - final contentEmailQuoted = _getBodyEmailQuotedAsHtml(Get.context!); - return contentEmailQuoted; + if (composerArguments.value?.emailActionType == EmailActionType.edit) { + return _getOldEmailContentAsHtml(Get.context!); + } else { + return _getBodyEmailQuotedAsHtml(Get.context!, composerArguments.value!); + } } return ''; } - Tuple2? getHeaderEmailQuoted(String locale) { - if (composerArguments.value != null && composerArguments.value?.presentationEmail != null) { - final sentDate = composerArguments.value!.presentationEmail?.sentAt; - final emailAddress = composerArguments.value!.presentationEmail?.from.listEmailAddressToString(isFullEmailAddress: true) ?? ''; + Tuple2? _getHeaderEmailQuoted(String locale, ComposerArguments arguments) { + if (arguments.presentationEmail != null) { + final sentDate = arguments.presentationEmail?.sentAt; + final emailAddress = arguments.presentationEmail?.from.listEmailAddressToString(isFullEmailAddress: true) ?? ''; return Tuple2(sentDate.formatDate(pattern: 'MMM d, y h:mm a', locale: locale), emailAddress); } return null; @@ -194,15 +210,13 @@ class ComposerController extends BaseController { expandMode.value = newExpandMode; } - void _initToEmailAddress() { - if (composerArguments.value != null && composerArguments.value?.presentationEmail != null) { - final userEmailAddress = EmailAddress(null, composerArguments.value!.userProfile.email); + void _initToEmailAddress(ComposerArguments arguments) { + if (arguments.presentationEmail != null) { + final userEmailAddress = EmailAddress(null, arguments.userProfile.email); - final recipients = composerArguments.value!.presentationEmail!.generateRecipientsEmailAddressForComposer( - composerArguments.value?.emailActionType, - composerArguments.value?.mailboxRole); + final recipients = arguments.presentationEmail!.generateRecipientsEmailAddressForComposer(arguments.emailActionType, arguments.mailboxRole); - if (composerArguments.value?.mailboxRole == PresentationMailbox.roleSent) { + if (arguments.mailboxRole == PresentationMailbox.roleSent || arguments.emailActionType == EmailActionType.edit) { listToEmailAddress = recipients.value1; listCcEmailAddress = recipients.value2; listBccEmailAddress = recipients.value3; @@ -258,15 +272,15 @@ class ComposerController extends BaseController { } } - String _getBodyEmailQuotedAsHtml(BuildContext context) { - final headerEmailQuoted = getHeaderEmailQuoted(Localizations.localeOf(context).toLanguageTag()); + String _getBodyEmailQuotedAsHtml(BuildContext context, ComposerArguments arguments) { + final headerEmailQuoted = _getHeaderEmailQuoted(Localizations.localeOf(context).toLanguageTag(), arguments); final headerEmailQuotedAsHtml = headerEmailQuoted != null ? AppLocalizations.of(context).header_email_quoted(headerEmailQuoted.value1, headerEmailQuoted.value2) .addBlockTag('p', attribute: 'style=\"font-size:14px;font-style:italic;color:#182952;\"') : ''; - final trustAsHtml = composerArguments.value?.emailContents + final trustAsHtml = arguments.emailContents ?.map((emailContent) => emailContent.content) .toList() .join('
') ?? ''; @@ -276,12 +290,12 @@ class ComposerController extends BaseController { return emailQuotedHtml; } - Future generateEmail({bool asDrafts = false}) async { + Future _generateEmail(ComposerArguments arguments, {bool asDrafts = false}) async { final generateEmailId = EmailId(Id(_uuid.v1())); - final outboxMailboxId = composerArguments.value!.mapMailboxId[PresentationMailbox.roleOutbox]; - final draftMailboxId = composerArguments.value!.mapMailboxId[PresentationMailbox.roleDrafts]; + final outboxMailboxId = arguments.mapMailboxId[PresentationMailbox.roleOutbox]; + final draftMailboxId = arguments.mapMailboxId[PresentationMailbox.roleDrafts]; final listFromEmailAddress = { - EmailAddress(null, composerArguments.value!.userProfile.email) + EmailAddress(null, arguments.userProfile.email) }; final generatePartId = PartId(_uuid.v1()); final generateBlobId = Id(_uuid.v1()); @@ -335,16 +349,23 @@ class ComposerController extends BaseController { void sendEmailAction(BuildContext context) async { if (isEnableEmailSendButton.value) { - _saveEmailAddress(); + final arguments = composerArguments.value; + if (arguments != null) { + _saveEmailAddress(); - final email = await generateEmail(); - final accountId = composerArguments.value!.session.accounts.keys.first; - final sentMailboxId = composerArguments.value!.mapMailboxId[PresentationMailbox.roleSent]; - final submissionCreateId = Id(_uuid.v1()); + final email = await _generateEmail(arguments); + final accountId = arguments.session.accounts.keys.first; + final sentMailboxId = arguments.mapMailboxId[PresentationMailbox.roleSent]; + final submissionCreateId = Id(_uuid.v1()); - mailboxDashBoardController.consumeState(_sendEmailInteractor.execute( - accountId, - EmailRequest(email, submissionCreateId, mailboxIdSaved: sentMailboxId))); + mailboxDashBoardController.consumeState(_sendEmailInteractor.execute( + accountId, + EmailRequest( + email, + submissionCreateId, + mailboxIdSaved: sentMailboxId, + emailIdDestroyed: arguments.emailActionType == EmailActionType.edit ? arguments.presentationEmail?.id : null))); + } popBack(); } else { @@ -457,12 +478,57 @@ class ComposerController extends BaseController { } void saveEmailAsDrafts() async { - _saveEmailAddress(); + final arguments = composerArguments.value; + if (arguments != null) { + _saveEmailAddress(); - final newEmail = await generateEmail(asDrafts: true); - final accountId = composerArguments.value!.session.accounts.keys.first; + final newEmail = await _generateEmail(arguments, asDrafts: true); + final accountId = arguments.session.accounts.keys.first; + final oldEmail = arguments.presentationEmail; - mailboxDashBoardController.consumeState(_saveEmailAsDraftsInteractor.execute(accountId, newEmail)); + if (arguments.emailActionType == EmailActionType.edit && oldEmail != null) { + mailboxDashBoardController.consumeState(_updateEmailDraftsInteractor.execute(accountId, newEmail, oldEmail.id)); + } else { + mailboxDashBoardController.consumeState(_saveEmailAsDraftsInteractor.execute(accountId, newEmail)); + } + } + } + + void _getEmailContentAction(ComposerArguments arguments) async { + final baseDownloadUrl = arguments.session.getDownloadUrl(); + final accountId = arguments.session.accounts.keys.first; + final emailId = arguments.presentationEmail?.id; + if (emailId != null) { + consumeState(_getEmailContentInteractor.execute(accountId, emailId, baseDownloadUrl)); + } + } + + void _getEmailContentSuccess(GetEmailContentSuccess success) { + emailContents.value = success.emailContents; + attachments.value = success.attachments; + } + + String _getOldEmailContentAsHtml(BuildContext context) { + if (emailContents.isNotEmpty) { + final trustAsHtml = emailContents + .map((emailContent) => emailContent.content) + .toList() + .join('
'); + return trustAsHtml; + } + return ''; + } + + String getEmailAddressSender() { + final arguments = composerArguments.value; + if (arguments != null) { + if (arguments.emailActionType == EmailActionType.edit) { + return arguments.presentationEmail?.from?.first.emailAddress ?? ''; + } else { + return arguments.userProfile.email; + } + } + return ''; } void backToEmailViewAction() { diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index 9dcac9957..9391aec40 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -59,9 +59,16 @@ class ComposerView extends GetWidget { return Padding( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), child: Obx(() => (TopBarComposerWidgetBuilder(imagePaths, controller.isEnableEmailSendButton.value) - ..addSendEmailActionClick(() => controller.sendEmailAction(context)) - ..addAttachFileActionClick(() => controller.openPickAttachmentMenu(context, _pickAttachmentsActionTiles(context))) + ..addSendEmailActionClick(() { + controller.htmlEditorApi?.unfocus(context); + controller.sendEmailAction(context); + }) + ..addAttachFileActionClick(() { + controller.htmlEditorApi?.unfocus(context); + controller.openPickAttachmentMenu(context, _pickAttachmentsActionTiles(context)); + }) ..addBackActionClick(() { + controller.htmlEditorApi?.unfocus(context); controller.saveEmailAsDrafts(); controller.backToEmailViewAction(); })) @@ -137,8 +144,8 @@ class ComposerView extends GetWidget { children: [ Obx(() => controller.composerArguments.value != null ? Text( - '<${controller.composerArguments.value?.userProfile.email ?? ''}>', - style: TextStyle(fontSize: 14, color: AppColor.nameUserColor)) + '<${controller.getEmailAddressSender()}>', + style: TextStyle(fontSize: 14, color: AppColor.nameUserColor)) : SizedBox.shrink() ) ], diff --git a/lib/features/composer/presentation/extensions/email_action_type_extension.dart b/lib/features/composer/presentation/extensions/email_action_type_extension.dart index 7e38c92ae..b552e02f8 100644 --- a/lib/features/composer/presentation/extensions/email_action_type_extension.dart +++ b/lib/features/composer/presentation/extensions/email_action_type_extension.dart @@ -19,6 +19,8 @@ extension EmailActionTypeExtension on EmailActionType { } else { return '${AppLocalizations.of(context).prefix_forward_email} $subject'; } + case EmailActionType.edit: + return subject; default: return ''; } diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index 5ca62ccd2..5cb07df7b 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -38,4 +38,6 @@ abstract class EmailDataSource { Future saveEmailAsDrafts(AccountId accountId, Email email); Future removeEmailDrafts(AccountId accountId, EmailId emailId); + + Future updateEmailDrafts(AccountId accountId, Email newEmail, EmailId oldEmailId); } \ No newline at end of file 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 7daed804c..0f59b42fb 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -104,4 +104,13 @@ class EmailDataSourceImpl extends EmailDataSource { throw error; }); } + + @override + Future updateEmailDrafts(AccountId accountId, Email newEmail, EmailId oldEmailId) { + return Future.sync(() async { + return await emailAPI.updateEmailDrafts(accountId, newEmail, oldEmailId); + }).catchError((error) { + throw error; + }); + } } \ No newline at end of file diff --git a/lib/features/email/data/network/email_api.dart b/lib/features/email/data/network/email_api.dart index 7b8806dd7..e6274607e 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -68,6 +68,11 @@ class EmailAPI { final setEmailMethod = SetEmailMethod(accountId) ..addCreate(emailRequest.email.id.id, emailRequest.email); + if (emailRequest.emailIdDestroyed != null) { + setEmailMethod + ..addDestroy({emailRequest.emailIdDestroyed!.id}); + } + final setEmailSubmissionMethod = SetEmailSubmissionMethod(accountId) ..addCreate( emailRequest.submissionCreateId, @@ -107,8 +112,11 @@ class EmailAPI { methodName: setEmailInvocation.methodName); return Future.sync(() async { - final emailCreated = setEmailResponse!.created![emailRequest.email.id.id]; - return setEmailSubmissionResponse!.updated![emailCreated!.id.id] == null; + final emailCreated = setEmailResponse?.created?[emailRequest.email.id.id]; + if (emailCreated != null) { + return setEmailSubmissionResponse?.updated?[emailCreated.id.id] == null; + } + return false; }).catchError((error) { throw error; }); @@ -301,4 +309,34 @@ class EmailAPI { throw error; }); } + + Future updateEmailDrafts(AccountId accountId, Email newEmail, EmailId oldEmailId) async { + final setEmailMethod = SetEmailMethod(accountId) + ..addCreate(newEmail.id.id, newEmail) + ..addDestroy({oldEmailId.id}); + + final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); + + final setEmailInvocation = requestBuilder.invocation(setEmailMethod); + + final response = await (requestBuilder + ..usings(setEmailMethod.requiredCapabilities)) + .build() + .execute(); + + final setEmailResponse = response.parse( + setEmailInvocation.methodCallId, + SetEmailResponse.deserialize); + + return Future.sync(() async { + final emailUpdated = setEmailResponse?.created?[newEmail.id.id]; + final emailDestroyed = setEmailResponse?.destroyed?.contains(oldEmailId.id); + if (emailUpdated != null && emailDestroyed == true) { + return emailUpdated; + } + return null; + }).catchError((error) { + throw error; + }); + } } \ No newline at end of file diff --git a/lib/features/email/data/repository/email_repository_impl.dart b/lib/features/email/data/repository/email_repository_impl.dart index 45ecfc1e7..c25646c25 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -98,4 +98,9 @@ class EmailRepositoryImpl extends EmailRepository { Future removeEmailDrafts(AccountId accountId, EmailId emailId) { return emailDataSource.removeEmailDrafts(accountId, emailId); } + + @override + Future updateEmailDrafts(AccountId accountId, Email newEmail, EmailId oldEmailId) { + return emailDataSource.updateEmailDrafts(accountId, newEmail, oldEmailId); + } } \ No newline at end of file diff --git a/lib/features/email/domain/repository/email_repository.dart b/lib/features/email/domain/repository/email_repository.dart index 04cc13d53..7c7295396 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -45,4 +45,6 @@ abstract class EmailRepository { Future saveEmailAsDrafts(AccountId accountId, Email email); Future removeEmailDrafts(AccountId accountId, EmailId emailId); + + Future updateEmailDrafts(AccountId accountId, Email newEmail, EmailId oldEmailId); } \ No newline at end of file diff --git a/lib/features/email/presentation/email_controller.dart b/lib/features/email/presentation/email_controller.dart index 5326d8ead..394225e91 100644 --- a/lib/features/email/presentation/email_controller.dart +++ b/lib/features/email/presentation/email_controller.dart @@ -342,7 +342,7 @@ class EmailController extends BaseController { bool canComposeEmail() => mailboxDashBoardController.sessionCurrent != null && mailboxDashBoardController.userProfile.value != null - && mailboxDashBoardController.mapDefaultMailboxId.containsKey(PresentationMailbox.roleOutbox) + && mailboxDashBoardController.mapDefaultMailboxId.isNotEmpty && mailboxDashBoardController.selectedEmail.value != null; void backToThreadView() { diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index 33d243fd1..66ccbc7e3 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -10,6 +10,8 @@ import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/caching/caching_manager.dart'; import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_drafts_state.dart'; +import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart'; +import 'package:tmail_ui_user/features/composer/domain/state/update_email_drafts_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; @@ -86,9 +88,10 @@ class MailboxController extends BaseController { || success is MoveMultipleEmailToMailboxHasSomeEmailFailure) { mailboxDashBoardController.clearState(); refreshMailboxChanges(); - } else if (success is SaveEmailAsDraftsSuccess) { - refreshMailboxChanges(); - } else if (success is RemoveEmailDraftsSuccess) { + } else if (success is SaveEmailAsDraftsSuccess + || success is RemoveEmailDraftsSuccess + || success is SendEmailSuccess + || success is UpdateEmailDraftsSuccess) { refreshMailboxChanges(); } }); diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart index 8a36e46d7..c7c3a7c49 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart @@ -10,6 +10,7 @@ import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_drafts_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart'; +import 'package:tmail_ui_user/features/composer/domain/state/update_email_drafts_state.dart'; import 'package:tmail_ui_user/features/email/presentation/email_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_user_profile_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart'; @@ -85,6 +86,10 @@ class MailboxDashBoardController extends BaseController { icon: _imagePaths.icSendToast); } clearState(); + } else if (failure is SaveEmailAsDraftsFailure + || failure is RemoveEmailDraftsFailure + || failure is UpdateEmailDraftsFailure) { + clearState(); } }, (success) { @@ -102,7 +107,8 @@ class MailboxDashBoardController extends BaseController { } else if (success is SaveEmailAsDraftsSuccess) { _saveEmailAsDraftsSuccess(success); clearState(); - } else if (success is RemoveEmailDraftsSuccess) { + } else if (success is RemoveEmailDraftsSuccess + || success is UpdateEmailDraftsSuccess) { clearState(); } } diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index f6bf4b0be..7165f682a 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -15,6 +15,8 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_drafts_state.dart'; +import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart'; +import 'package:tmail_ui_user/features/composer/domain/state/update_email_drafts_state.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_request.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_state.dart'; @@ -135,7 +137,9 @@ class ThreadController extends BaseController { } else if (success is SearchEmailNewQuery){ _searchEmail(); } else if (success is SaveEmailAsDraftsSuccess - || success is RemoveEmailDraftsSuccess) { + || success is RemoveEmailDraftsSuccess + || success is SendEmailSuccess + || success is UpdateEmailDraftsSuccess) { cancelSelectEmail(); _refreshEmailChanges(); } @@ -673,7 +677,7 @@ class ThreadController extends BaseController { bool canComposeEmail() => mailboxDashBoardController.sessionCurrent != null && mailboxDashBoardController.userProfile.value != null - && mailboxDashBoardController.mapDefaultMailboxId.containsKey(PresentationMailbox.roleOutbox); + && mailboxDashBoardController.mapDefaultMailboxId.isNotEmpty; bool isSelectionEnabled() => currentSelectMode.value == SelectMode.ACTIVE; @@ -704,6 +708,20 @@ class ThreadController extends BaseController { push(AppRoutes.EMAIL); } + void editEmail(PresentationEmail presentationEmail) { + if (canComposeEmail()) { + push( + AppRoutes.COMPOSER, + arguments: ComposerArguments( + emailActionType: EmailActionType.edit, + presentationEmail: presentationEmail, + mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role, + session: mailboxDashBoardController.sessionCurrent!, + userProfile: mailboxDashBoardController.userProfile.value!, + mapMailboxId: mailboxDashBoardController.mapDefaultMailboxId)); + } + } + void composeEmailAction() { if (canComposeEmail()) { push( diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index f5ca1066e..81e065b4d 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -375,7 +375,13 @@ class ThreadView extends GetWidget { controller.currentSelectMode.value, controller.mailboxDashBoardController.searchState.value.searchStatus, controller.searchQuery) - ..onOpenEmailAction((selectedEmail) => controller.previewEmail(context, selectedEmail)) + ..onOpenEmailAction((selectedEmail) { + if (controller.mailboxDashBoardController.selectedMailbox.value?.role == PresentationMailbox.roleDrafts) { + controller.editEmail(selectedEmail); + } else { + controller.previewEmail(context, selectedEmail); + } + }) ..onSelectEmailAction((selectedEmail) => controller.selectEmail(context, selectedEmail))) .build()), ) diff --git a/model/lib/email/email_action_type.dart b/model/lib/email/email_action_type.dart index c6e042fb6..32a919426 100644 --- a/model/lib/email/email_action_type.dart +++ b/model/lib/email/email_action_type.dart @@ -10,4 +10,5 @@ enum EmailActionType { move, markAsSpam, delete, + edit, } \ No newline at end of file diff --git a/model/lib/extensions/presentation_email_extension.dart b/model/lib/extensions/presentation_email_extension.dart index 3264d6206..430186294 100644 --- a/model/lib/extensions/presentation_email_extension.dart +++ b/model/lib/extensions/presentation_email_extension.dart @@ -106,6 +106,8 @@ extension PresentationEmailExtension on PresentationEmail { } else { return Tuple3(to.asList() + from.asList(), cc.asList(), bcc.asList()); } + case EmailActionType.edit: + return Tuple3(to.asList(), cc.asList(), bcc.asList()); default: return Tuple3([], [], []); }