diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index b10cb1586..c8bc6610d 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -225,4 +225,11 @@ abstract class EmailDataSource { EmailId emailId, KeyWordIdentifier labelKeyword, ); + + Future removeLabelFromThread( + Session session, + AccountId accountId, + List emailIds, + KeyWordIdentifier labelKeyword, + ); } \ 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 67748fb7b..568a46e8d 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -604,4 +604,21 @@ class EmailDataSourceImpl extends EmailDataSource { ); }).catchError(_exceptionThrower.throwException); } + + @override + Future removeLabelFromThread( + Session session, + AccountId accountId, + List emailIds, + KeyWordIdentifier labelKeyword, + ) { + return Future.sync(() async { + return await emailAPI.removeLabelFromThread( + session, + accountId, + emailIds, + labelKeyword, + ); + }).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 9d62611ed..7071d5a49 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 @@ -590,4 +590,9 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource { Future removeLabelFromEmail(Session session, AccountId accountId, EmailId emailId, KeyWordIdentifier labelKeyword) { throw UnimplementedError(); } + + @override + Future removeLabelFromThread(Session session, AccountId accountId, List emailIds, KeyWordIdentifier labelKeyword) { + throw UnimplementedError(); + } } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart index a2217cef7..6afa73b29 100644 --- a/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart @@ -366,4 +366,9 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource { Future removeLabelFromEmail(Session session, AccountId accountId, EmailId emailId, KeyWordIdentifier labelKeyword) { throw UnimplementedError(); } + + @override + Future removeLabelFromThread(Session session, AccountId accountId, List emailIds, KeyWordIdentifier labelKeyword) { + throw UnimplementedError(); + } } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart index 9393ca526..eabfeaf8c 100644 --- a/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart @@ -280,4 +280,9 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource { Future removeLabelFromEmail(Session session, AccountId accountId, EmailId emailId, KeyWordIdentifier labelKeyword) { throw UnimplementedError(); } + + @override + Future removeLabelFromThread(Session session, AccountId accountId, List emailIds, KeyWordIdentifier labelKeyword) { + 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 3323265af..a28c55852 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -1026,4 +1026,39 @@ class EmailAPI with HandleSetErrorMixin, MailAPIMixin { throw parseErrorForSetResponse(response, emailId.id); } } + + Future removeLabelFromThread( + Session session, + AccountId accountId, + List emailIds, + KeyWordIdentifier labelKeyword, + ) async { + final method = SetEmailMethod(accountId) + ..addUpdates(emailIds.generateMapUpdateObjectLabel( + labelKeyword, + remove: true, + )); + + final builder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); + final invocation = builder.invocation(method); + + final capabilities = method.requiredCapabilities + .toCapabilitiesSupportTeamMailboxes(session, accountId); + final result = await (builder..usings(capabilities)).build().execute(); + + final response = result.parse( + invocation.methodCallId, + SetEmailResponse.deserialize, + ); + + final emailIdsUpdated = response?.updated?.keys ?? []; + final ids = emailIds.map((emailId) => emailId.id); + final isUpdated = emailIdsUpdated.every(ids.contains); + + if (emailIdsUpdated.isEmpty || !isUpdated) { + for (var id in emailIds) { + throw parseErrorForSetResponse(response, id.id); + } + } + } } \ 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 85aa09324..9aae2bb61 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -525,4 +525,19 @@ class EmailRepositoryImpl extends EmailRepository { labelKeyword, ); } + + @override + Future removeLabelFromThread( + Session session, + AccountId accountId, + List emailIds, + KeyWordIdentifier labelKeyword, + ) { + return emailDataSource[DataSourceType.network]!.removeLabelFromThread( + session, + accountId, + emailIds, + labelKeyword, + ); + } } \ 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 ca2fc0aa4..f1dd0e5cf 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -182,4 +182,11 @@ abstract class EmailRepository { EmailId emailId, KeyWordIdentifier labelKeyword, ); + + Future removeLabelFromThread( + Session session, + AccountId accountId, + List emailIds, + KeyWordIdentifier labelKeyword, + ); } \ No newline at end of file diff --git a/lib/features/email/domain/state/labels/remove_a_label_from_an_thread_state.dart b/lib/features/email/domain/state/labels/remove_a_label_from_an_thread_state.dart new file mode 100644 index 000000000..8081f2abe --- /dev/null +++ b/lib/features/email/domain/state/labels/remove_a_label_from_an_thread_state.dart @@ -0,0 +1,33 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; + +class RemovingALabelFromAThread extends LoadingState {} + +class RemoveALabelFromAThreadSuccess extends UIState { + final List emailIds; + final KeyWordIdentifier labelKeyword; + final String labelDisplay; + + RemoveALabelFromAThreadSuccess( + this.emailIds, + this.labelKeyword, + this.labelDisplay, + ); + + @override + List get props => [emailIds, labelKeyword, labelDisplay]; +} + +class RemoveALabelFromAThreadFailure extends FeatureFailure { + final String labelDisplay; + + RemoveALabelFromAThreadFailure({ + dynamic exception, + required this.labelDisplay, + }) : super(exception: exception); + + @override + List get props => [...super.props, labelDisplay]; +} diff --git a/lib/features/email/domain/usecases/labels/remove_a_label_from_a_thread_interactor.dart b/lib/features/email/domain/usecases/labels/remove_a_label_from_a_thread_interactor.dart new file mode 100644 index 000000000..f84c227e6 --- /dev/null +++ b/lib/features/email/domain/usecases/labels/remove_a_label_from_a_thread_interactor.dart @@ -0,0 +1,43 @@ +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:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; +import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/email/domain/state/labels/remove_a_label_from_an_thread_state.dart'; + +class RemoveALabelFromAThreadInteractor { + final EmailRepository _emailRepository; + + RemoveALabelFromAThreadInteractor(this._emailRepository); + + Stream> execute( + Session session, + AccountId accountId, + List emailIds, + KeyWordIdentifier labelKeyword, + String labelDisplay, + ) async* { + try { + yield Right(RemovingALabelFromAThread()); + await _emailRepository.removeLabelFromThread( + session, + accountId, + emailIds, + labelKeyword, + ); + yield Right(RemoveALabelFromAThreadSuccess( + emailIds, + labelKeyword, + labelDisplay, + )); + } catch (e) { + yield Left(RemoveALabelFromAThreadFailure( + exception: e, + labelDisplay: labelDisplay, + )); + } + } +} diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index 5b8135e27..ef6f76415 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -270,11 +270,8 @@ class EmailView extends GetWidget { if (isLabelAvailable) { emailLabels = presentationEmail.getLabelList(listLabels); } - return EmailSubjectWidget( - presentationEmail: presentationEmail.copyWith( - subject: threadSubject, - ), + emailSubject: threadSubject ?? presentationEmail.getEmailTitle(), imagePaths: controller.imagePaths, isMobileResponsive: isMobileResponsive, labels: emailLabels, diff --git a/lib/features/email/presentation/widgets/email_subject_widget.dart b/lib/features/email/presentation/widgets/email_subject_widget.dart index 051947045..8b0fb5f9a 100644 --- a/lib/features/email/presentation/widgets/email_subject_widget.dart +++ b/lib/features/email/presentation/widgets/email_subject_widget.dart @@ -2,14 +2,13 @@ import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:flutter/material.dart'; import 'package:labels/model/label.dart'; -import 'package:model/email/presentation_email.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/email_subject_styles.dart'; import 'package:tmail_ui_user/features/labels/presentation/widgets/label_widget.dart'; typedef OnDeleteLabelAction = void Function(Label label); class EmailSubjectWidget extends StatefulWidget { - final PresentationEmail presentationEmail; + final String emailSubject; final ImagePaths imagePaths; final bool isMobileResponsive; final List