From 41ed9a230df46e08df492d0657596fa00ff22ed9 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 10 May 2023 15:43:41 +0700 Subject: [PATCH] TF-1810 Add GetDetailedEmailByIdInteractor to get detailed email (cherry picked from commit 765cebec00ae26458fe7f5e17ec6026f82f1fd0f) --- .../data/datasource/email_datasource.dart | 2 + .../email_datasource_impl.dart | 7 +++ .../email_hive_cache_datasource_impl.dart | 5 ++ .../email/data/network/email_api.dart | 40 +++++++++--- .../repository/email_repository_impl.dart | 5 ++ .../domain/exceptions/email_exceptions.dart | 4 +- .../domain/extensions/email_extension.dart | 15 +++++ .../domain/repository/email_repository.dart | 2 + .../state/get_detailed_email_by_id_state.dart | 25 ++++++++ .../get_detailed_email_by_id_interator.dart | 63 +++++++++++++++++++ .../domain/constants/thread_constants.dart | 30 +++++++++ 11 files changed, 188 insertions(+), 10 deletions(-) create mode 100644 lib/features/email/domain/extensions/email_extension.dart create mode 100644 lib/features/email/domain/state/get_detailed_email_by_id_state.dart create mode 100644 lib/features/email/domain/usecases/get_detailed_email_by_id_interator.dart diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index 5863e2847..e7c7ad79b 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -75,4 +75,6 @@ abstract class EmailDataSource { Future deleteEmailPermanently(Session session, AccountId accountId, EmailId emailId); Future storeDetailedEmailToCache(Session session, AccountId accountId, DetailedEmail detailedEmail); + + Future getDetailedEmailById(Session session, AccountId accountId, EmailId emailId); } \ 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 cb5adaf62..ed2a44028 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -154,4 +154,11 @@ class EmailDataSourceImpl extends EmailDataSource { Future storeDetailedEmailToCache(Session session, AccountId accountId, DetailedEmail detailedEmail) { throw UnimplementedError(); } + + @override + Future getDetailedEmailById(Session session, AccountId accountId, EmailId emailId) { + return Future.sync(() async { + return await emailAPI.getDetailedEmailById(session, accountId, emailId); + }).catchError(_exceptionThrower.throwException); + } } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart index aba9fc70c..44022a05e 100644 --- a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart @@ -133,4 +133,9 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource { Future updateEmailDrafts(Session session, AccountId accountId, Email newEmail) { throw UnimplementedError(); } + + @override + Future getDetailedEmailById(Session session, AccountId accountId, EmailId emailId) { + throw UnimplementedError(); + } } \ 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 d1b727533..f3ac5a676 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -40,7 +40,6 @@ import 'package:model/account/account_request.dart'; import 'package:model/account/authentication_type.dart'; import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; -import 'package:model/email/email_property.dart'; import 'package:model/email/mark_star_action.dart'; import 'package:model/email/read_actions.dart'; import 'package:model/extensions/email_extension.dart'; @@ -54,10 +53,12 @@ import 'package:path_provider/path_provider.dart'; import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart'; import 'package:tmail_ui_user/features/composer/domain/exceptions/set_email_method_exception.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; +import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.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'; +import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:uuid/uuid.dart'; @@ -77,14 +78,7 @@ class EmailAPI with HandleSetErrorMixin { final getEmailMethod = GetEmailMethod(accountId) ..addIds({emailId.id}) - ..addProperties(Properties({ - EmailProperty.bodyValues, - EmailProperty.htmlBody, - EmailProperty.attachments, - EmailProperty.headers, - EmailProperty.keywords, - EmailProperty.mailboxIds, - })) + ..addProperties(ThreadConstants.propertiesGetEmailContent) ..addFetchHTMLBodyValues(true); final getEmailInvocation = jmapRequestBuilder.invocation(getEmailMethod); @@ -633,4 +627,32 @@ class EmailAPI with HandleSetErrorMixin { return setEmailResponse?.destroyed?.contains(emailId.id) == true; } + + Future getDetailedEmailById(Session session, AccountId accountId, EmailId emailId) async { + final jmapRequestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); + + final getEmailMethod = GetEmailMethod(accountId) + ..addIds({emailId.id}) + ..addProperties(ThreadConstants.propertiesGetDetailedEmail) + ..addFetchHTMLBodyValues(true); + + final getEmailInvocation = jmapRequestBuilder.invocation(getEmailMethod); + + final capabilities = getEmailMethod.requiredCapabilities.toCapabilitiesSupportTeamMailboxes(session, accountId); + + final result = await (jmapRequestBuilder + ..usings(capabilities)) + .build() + .execute(); + + final resultList = result.parse( + getEmailInvocation.methodCallId, + GetEmailResponse.deserialize); + + if (resultList?.list.isNotEmpty == true) { + return resultList!.list.first; + } else { + throw NotFoundEmailException(); + } + } } \ 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 5d69f73dd..6d95d8679 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -188,4 +188,9 @@ class EmailRepositoryImpl extends EmailRepository { Future storeDetailedEmailToCache(Session session, AccountId accountId, DetailedEmail detailedEmail) { return emailDataSource[DataSourceType.hiveCache]!.storeDetailedEmailToCache(session, accountId, detailedEmail); } + + @override + Future getDetailedEmailById(Session session, AccountId accountId, EmailId emailId) { + return emailDataSource[DataSourceType.network]!.getDetailedEmailById(session, accountId, emailId); + } } \ No newline at end of file diff --git a/lib/features/email/domain/exceptions/email_exceptions.dart b/lib/features/email/domain/exceptions/email_exceptions.dart index 092e886ab..c37c9dc4a 100644 --- a/lib/features/email/domain/exceptions/email_exceptions.dart +++ b/lib/features/email/domain/exceptions/email_exceptions.dart @@ -1,2 +1,4 @@ -class CannotDeleteOldEmailException implements Exception {} \ No newline at end of file +class CannotDeleteOldEmailException implements Exception {} + +class NotFoundEmailException implements Exception {} \ No newline at end of file diff --git a/lib/features/email/domain/extensions/email_extension.dart b/lib/features/email/domain/extensions/email_extension.dart new file mode 100644 index 000000000..e0da85bee --- /dev/null +++ b/lib/features/email/domain/extensions/email_extension.dart @@ -0,0 +1,15 @@ + +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/extensions/email_extension.dart'; +import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart'; + +extension EmailExtension on Email { + DetailedEmail toDetailedEmail({String? htmlEmailContent}) { + return DetailedEmail( + emailId: id!, + attachments: allAttachments, + headers: headers?.toList(), + htmlEmailContent: htmlEmailContent + ); + } +} \ 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 2d9bacab9..4fbe490da 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -89,4 +89,6 @@ abstract class EmailRepository { Future getEmailState(Session session, AccountId accountId); Future storeDetailedEmailToCache(Session session, AccountId accountId, DetailedEmail detailedEmail); + + Future getDetailedEmailById(Session session, AccountId accountId, EmailId emailId); } \ No newline at end of file diff --git a/lib/features/email/domain/state/get_detailed_email_by_id_state.dart b/lib/features/email/domain/state/get_detailed_email_by_id_state.dart new file mode 100644 index 000000000..a9a28d9cd --- /dev/null +++ b/lib/features/email/domain/state/get_detailed_email_by_id_state.dart @@ -0,0 +1,25 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart'; + +class GetDetailedEmailByIdLoading extends UIState {} + +class GetDetailedEmailByIdSuccess extends UIState { + + final PresentationEmail presentationEmail; + final DetailedEmail detailedEmail; + + GetDetailedEmailByIdSuccess(this.presentationEmail, this.detailedEmail); + + @override + List get props => [presentationEmail, detailedEmail]; +} + +class GetDetailedEmailByIdFailure extends FeatureFailure { + + GetDetailedEmailByIdFailure(dynamic exception) : super(exception: exception); + + @override + List get props => [exception]; +} \ No newline at end of file diff --git a/lib/features/email/domain/usecases/get_detailed_email_by_id_interator.dart b/lib/features/email/domain/usecases/get_detailed_email_by_id_interator.dart new file mode 100644 index 000000000..71f98fea8 --- /dev/null +++ b/lib/features/email/domain/usecases/get_detailed_email_by_id_interator.dart @@ -0,0 +1,63 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:model/extensions/email_extension.dart'; +import 'package:model/extensions/list_attachment_extension.dart'; +import 'package:model/extensions/list_email_content_extension.dart'; +import 'package:tmail_ui_user/features/email/domain/extensions/email_extension.dart'; +import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart'; +import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/email/domain/state/get_detailed_email_by_id_state.dart'; + +class GetDetailedEmailByIdInteractor { + final EmailRepository _emailRepository; + + GetDetailedEmailByIdInteractor(this._emailRepository); + + Stream> execute( + Session session, + AccountId accountId, + EmailId emailId, + String? baseDownloadUrl + ) async* { + try { + yield Right(GetDetailedEmailByIdLoading()); + + final email = await _emailRepository.getDetailedEmailById(session, accountId, emailId); + + final parsedEmail = await _parsingEmailToDetailedEmail(accountId, email, baseDownloadUrl); + + yield Right(GetDetailedEmailByIdSuccess(parsedEmail.value1, parsedEmail.value2)); + } catch (e) { + yield Left(GetDetailedEmailByIdFailure(e)); + } + } + + Future> _parsingEmailToDetailedEmail( + AccountId accountId, + Email email, + String? baseDownloadUrl + ) async { + String? htmlEmailContent; + + final listEmailContent = email.emailContentList; + if (listEmailContent.isNotEmpty) { + final newEmailContents = await _emailRepository.transformEmailContent( + listEmailContent, + email.allAttachments.listAttachmentsDisplayedInContent, + baseDownloadUrl, + accountId); + + htmlEmailContent = newEmailContents.asHtmlString; + } + + final detailedEmail = email.toDetailedEmail(htmlEmailContent: htmlEmailContent); + final presentationEmail = email.toPresentationEmail(); + + return Tuple2(presentationEmail, detailedEmail); + } +} \ No newline at end of file diff --git a/lib/features/thread/domain/constants/thread_constants.dart b/lib/features/thread/domain/constants/thread_constants.dart index b7032044f..0bcc4f406 100644 --- a/lib/features/thread/domain/constants/thread_constants.dart +++ b/lib/features/thread/domain/constants/thread_constants.dart @@ -37,4 +37,34 @@ class ThreadConstants { EmailProperty.hasAttachment, EmailProperty.mailboxIds, }); + + static final propertiesGetEmailContent = Properties({ + EmailProperty.bodyValues, + EmailProperty.htmlBody, + EmailProperty.attachments, + EmailProperty.headers, + EmailProperty.keywords, + EmailProperty.mailboxIds + }); + + static final propertiesGetDetailedEmail = Properties({ + EmailProperty.id, + EmailProperty.subject, + EmailProperty.from, + EmailProperty.to, + EmailProperty.cc, + EmailProperty.bcc, + EmailProperty.keywords, + EmailProperty.size, + EmailProperty.receivedAt, + EmailProperty.sentAt, + EmailProperty.preview, + EmailProperty.hasAttachment, + EmailProperty.replyTo, + EmailProperty.mailboxIds, + EmailProperty.bodyValues, + EmailProperty.htmlBody, + EmailProperty.attachments, + EmailProperty.headers + }); } \ No newline at end of file