diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index a5fa066a8..028365e88 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -29,4 +29,6 @@ abstract class EmailDataSource { ); Future> moveToMailbox(AccountId accountId, MoveRequest moveRequest); + + Future markAsImportant(AccountId accountId, EmailId emailId, ImportantAction importantAction); } \ 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 2731397da..7749b4676 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -77,4 +77,13 @@ class EmailDataSourceImpl extends EmailDataSource { throw error; }); } + + @override + Future markAsImportant(AccountId accountId, EmailId emailId, ImportantAction importantAction) { + return Future.sync(() async { + return await emailAPI.markAsImportant(accountId, emailId, importantAction); + }).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 2fac78fa6..8f4058735 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -219,4 +219,31 @@ class EmailAPI { throw error; }); } + + Future markAsImportant(AccountId accountId, EmailId emailId, ImportantAction importantAction) async { + final setEmailMethod = SetEmailMethod(accountId) + ..addUpdates({ + emailId.id: KeyWordIdentifier.emailFlagged.generateImportantActionPath(importantAction) + }); + + 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!.updated![emailId.id]; + return emailUpdated == 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 568944b06..466c8b124 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -59,4 +59,9 @@ class EmailRepositoryImpl extends EmailRepository { Future> moveToMailbox(AccountId accountId, MoveRequest moveRequest) { return emailDataSource.moveToMailbox(accountId, moveRequest); } + + @override + Future markAsImportant(AccountId accountId, EmailId emailId, ImportantAction importantAction) { + return emailDataSource.markAsImportant(accountId, emailId, importantAction); + } } \ 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 046e91362..cfd5c2e9f 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -28,4 +28,6 @@ abstract class EmailRepository { ); Future> moveToMailbox(AccountId accountId, MoveRequest moveRequest); + + Future markAsImportant(AccountId accountId, EmailId emailId, ImportantAction importantAction); } \ No newline at end of file diff --git a/lib/features/email/domain/state/mark_as_email_important_state.dart b/lib/features/email/domain/state/mark_as_email_important_state.dart new file mode 100644 index 000000000..efaae1b0a --- /dev/null +++ b/lib/features/email/domain/state/mark_as_email_important_state.dart @@ -0,0 +1,23 @@ +import 'package:core/core.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/model.dart'; + +class MarkAsEmailImportantSuccess extends UIState { + final EmailId emailId; + final ImportantAction importantAction; + + MarkAsEmailImportantSuccess(this.emailId, this.importantAction); + + @override + List get props => [emailId, importantAction]; +} + +class MarkAsEmailImportantFailure extends FeatureFailure { + final exception; + final ImportantAction importantAction; + + MarkAsEmailImportantFailure(this.exception, this.importantAction); + + @override + List get props => [exception, importantAction]; +} \ No newline at end of file diff --git a/lib/features/email/domain/usecases/mark_as_email_important_interactor.dart b/lib/features/email/domain/usecases/mark_as_email_important_interactor.dart new file mode 100644 index 000000000..8493f9cc9 --- /dev/null +++ b/lib/features/email/domain/usecases/mark_as_email_important_interactor.dart @@ -0,0 +1,22 @@ +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:model/model.dart'; +import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_important_state.dart'; + +class MarkAsEmailImportantInteractor { + final EmailRepository emailRepository; + + MarkAsEmailImportantInteractor(this.emailRepository); + + Stream> execute(AccountId accountId, EmailId emailId, ImportantAction importantAction) async* { + try { + final result = await emailRepository.markAsImportant(accountId, emailId, importantAction); + yield result ? Right(MarkAsEmailImportantSuccess(emailId, importantAction)) : Left(MarkAsEmailImportantFailure(null, importantAction)); + } catch (e) { + yield Left(MarkAsEmailImportantFailure(e, importantAction)); + } + } +} \ No newline at end of file diff --git a/lib/features/email/presentation/email_bindings.dart b/lib/features/email/presentation/email_bindings.dart index 5d2ef7a84..42691e3a8 100644 --- a/lib/features/email/presentation/email_bindings.dart +++ b/lib/features/email/presentation/email_bindings.dart @@ -10,6 +10,7 @@ import 'package:tmail_ui_user/features/email/domain/repository/email_repository. import 'package:tmail_ui_user/features/email/domain/usecases/download_attachments_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_important_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/email_controller.dart'; @@ -35,6 +36,7 @@ class EmailBindings extends Bindings { Get.find(), Get.find())); Get.lazyPut(() => MoveToMailboxInteractor(Get.find())); + Get.lazyPut(() => MarkAsEmailImportantInteractor(Get.find())); Get.put(EmailController( Get.find(), Get.find(), @@ -43,5 +45,7 @@ class EmailBindings extends Bindings { Get.find(), Get.find(), Get.find())); + Get.find(), + Get.find())); } } \ 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 2edeea9a7..148e51348 100644 --- a/lib/features/email/presentation/email_controller.dart +++ b/lib/features/email/presentation/email_controller.dart @@ -17,10 +17,12 @@ import 'package:tmail_ui_user/features/email/domain/state/download_attachments_s import 'package:tmail_ui_user/features/email/domain/state/export_attachment_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state.dart'; +import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_important_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/download_attachments_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_important_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; @@ -42,6 +44,7 @@ class EmailController extends BaseController { final AppToast _appToast; final ExportAttachmentInteractor _exportAttachmentInteractor; final MoveToMailboxInteractor _moveToMailboxInteractor; + final MarkAsEmailImportantInteractor _markAsEmailImportantInteractor; final emailAddressExpandMode = ExpandMode.COLLAPSE.obs; final attachmentsExpandMode = ExpandMode.COLLAPSE.obs; @@ -56,6 +59,7 @@ class EmailController extends BaseController { this._appToast, this._exportAttachmentInteractor, this._moveToMailboxInteractor, + this._markAsEmailImportantInteractor, ); @override @@ -112,6 +116,8 @@ class EmailController extends BaseController { _exportAttachmentSuccessAction(success); } else if (success is MoveToMailboxSuccess) { _moveToMailboxSuccess(success); + } else if (success is MarkAsEmailImportantSuccess) { + _markAsEmailImportantSuccess(success); } }); } @@ -302,6 +308,19 @@ class EmailController extends BaseController { } } + void markAsEmailImportant(PresentationEmail presentationEmail) async { + final accountId = mailboxDashBoardController.accountId.value; + final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value; + if (accountId != null && mailboxCurrent != null) { + final importantAction = presentationEmail.isFlaggedEmail() ? ImportantAction.unMarkStar : ImportantAction.markStar; + consumeState(_markAsEmailImportantInteractor.execute(accountId, presentationEmail.id, importantAction)); + } + } + + void _markAsEmailImportantSuccess(Success success) { + mailboxDashBoardController.dispatchState(Right(success)); + } + bool canComposeEmail() => mailboxDashBoardController.sessionCurrent != null && mailboxDashBoardController.userProfile.value != null && mailboxDashBoardController.mapMailboxId.containsKey(PresentationMailbox.roleOutbox) diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index cc2328031..dbf9a0845 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -59,6 +59,9 @@ class EmailView extends GetView { emailController.markAsEmailRead(presentationEmail, ReadActions.markAsUnread)) ..addOnMoveToMailboxActionClick((presentationEmail) => emailController.openDestinationPickerView(presentationEmail))) + emailController.markAsEmailRead(presentationEmail, ReadActions.markAsUnread)) + ..addOnMarkAsImportantActionClick((presentationEmail) => + emailController.markAsEmailImportant(presentationEmail))) .build())); } diff --git a/lib/features/email/presentation/widgets/app_bar_mail_widget_builder.dart b/lib/features/email/presentation/widgets/app_bar_mail_widget_builder.dart index 3ab6e9dbe..b4edbad0e 100644 --- a/lib/features/email/presentation/widgets/app_bar_mail_widget_builder.dart +++ b/lib/features/email/presentation/widgets/app_bar_mail_widget_builder.dart @@ -9,11 +9,13 @@ import 'package:tmail_ui_user/main/routes/app_routes.dart'; typedef OnBackActionClick = void Function(); typedef OnUnreadEmailActionClick = void Function(PresentationEmail presentationEmail); typedef OnMoveToMailboxActionClick = void Function(PresentationEmail presentationEmail); +typedef OnMarkAsImportantActionClick = void Function(PresentationEmail presentationEmail); class AppBarMailWidgetBuilder { OnBackActionClick? _onBackActionClick; OnUnreadEmailActionClick? _onUnreadEmailActionClick; OnMoveToMailboxActionClick? _onMoveToMailboxActionClick; + OnMarkAsImportantActionClick? _onMarkAsImportantActionClick; final BuildContext _context; final ImagePaths _imagePaths; @@ -39,6 +41,10 @@ class AppBarMailWidgetBuilder { _onMoveToMailboxActionClick = onMoveToMailboxActionClick; } + void addOnMarkAsImportantActionClick(OnMarkAsImportantActionClick onMarkAsImportantActionClick) { + _onMarkAsImportantActionClick = onMarkAsImportantActionClick; + } + Widget build() { return Container( key: Key('app_bar_messenger_widget'), @@ -91,7 +97,11 @@ class AppBarMailWidgetBuilder { ButtonBuilder((_presentationEmail != null && _presentationEmail!.isFlaggedEmail()) ? _imagePaths.icFlagged : _imagePaths.icFlag) - .key(Key('button_mark_as_flag_email')) + .key(Key('button_mark_as_email_important')) + .onPressActionClick(() { + if (_onMarkAsImportantActionClick != null && _presentationEmail != null) { + _onMarkAsImportantActionClick!(_presentationEmail!); + }}) .build(), SizedBox(width: 10), ButtonBuilder(_imagePaths.icFolder) diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index b77e3b392..eb0d2471c 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -17,6 +17,7 @@ import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.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_important_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; @@ -93,6 +94,23 @@ class ThreadController extends BaseController { mailboxDashBoardController.clearState(); } }); + } else { + mailboxDashBoardController.viewState.value.map((success) { + if (success is MarkAsEmailReadSuccess || + success is MarkAsMultipleEmailReadAllSuccess || + success is MarkAsMultipleEmailReadHasSomeEmailFailure) { + _refreshListEmail(); + } + }); + } + }); + + mailboxDashBoardController.viewState.listen((state) { + state.map((success) { + if (success is MarkAsEmailImportantSuccess) { + _refreshListEmail(); + } + }); }); } @@ -138,6 +156,24 @@ class ThreadController extends BaseController { emailList.addAll(success.emailList); lastGetTotal = emailList.length; loadMoreState.value = success.emailList.isEmpty ? LoadMoreState.COMPLETED : LoadMoreState.IDLE; + + mailboxDashBoardController.viewState.value.map((success) { + if (success is MarkAsEmailImportantSuccess) { + _updateValueCurrentEmail(); + mailboxDashBoardController.clearState(); + } + }); + } + } + + void _updateValueCurrentEmail() { + if (mailboxDashBoardController.selectedEmail.value != null) { + try { + final newSelectedEmail = emailList.firstWhere( + (email) => email.id == mailboxDashBoardController.selectedEmail.value!.id, + orElse: null); + mailboxDashBoardController.setSelectedEmail(newSelectedEmail); + } catch(e){} } } diff --git a/model/lib/email/important_action.dart b/model/lib/email/important_action.dart new file mode 100644 index 000000000..3bfd81227 --- /dev/null +++ b/model/lib/email/important_action.dart @@ -0,0 +1,5 @@ + +enum ImportantAction { + markStar, + unMarkStar +} \ No newline at end of file diff --git a/model/lib/extensions/keyword_identifier_extension.dart b/model/lib/extensions/keyword_identifier_extension.dart index 227b23396..df6226f3c 100644 --- a/model/lib/extensions/keyword_identifier_extension.dart +++ b/model/lib/extensions/keyword_identifier_extension.dart @@ -1,5 +1,6 @@ import 'package:jmap_dart_client/jmap/core/patch_object.dart'; import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; +import 'package:model/email/important_action.dart'; import 'package:model/email/read_actions.dart'; extension KeyWordIdentifierExtension on KeyWordIdentifier { @@ -12,4 +13,10 @@ extension KeyWordIdentifierExtension on KeyWordIdentifier { _generatePath(): readActions == ReadActions.markAsRead ? true : null }); } + + PatchObject generateImportantActionPath(ImportantAction importantAction) { + return PatchObject({ + _generatePath(): importantAction == ImportantAction.markStar ? true : null + }); + } } \ No newline at end of file diff --git a/model/lib/model.dart b/model/lib/model.dart index 785c1e623..558fd32f7 100644 --- a/model/lib/model.dart +++ b/model/lib/model.dart @@ -25,6 +25,7 @@ export 'email/email_address_cache.dart'; export 'email/read_actions.dart'; export 'email/attachment.dart'; export 'email/move_action.dart'; +export 'email/important_action.dart'; // Extensions export 'extensions/email_address_extension.dart';