diff --git a/lib/features/email/domain/state/calendar_event_accept_state.dart b/lib/features/email/domain/state/calendar_event_accept_state.dart index 142cd4be1..508f66614 100644 --- a/lib/features/email/domain/state/calendar_event_accept_state.dart +++ b/lib/features/email/domain/state/calendar_event_accept_state.dart @@ -1,10 +1,14 @@ +import 'package:tmail_ui_user/features/email/domain/model/event_action.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reply_state.dart'; class CalendarEventAccepting extends CalendarEventReplying {} class CalendarEventAccepted extends CalendarEventReplySuccess { - - CalendarEventAccepted(super.calendarEventAcceptResponse); + + CalendarEventAccepted(super.calendarEventAcceptResponse, super.emailId); + + @override + EventActionType getEventActionType() => EventActionType.yes; } class CalendarEventAcceptFailure extends CalendarEventReplyFailure { diff --git a/lib/features/email/domain/state/calendar_event_maybe_state.dart b/lib/features/email/domain/state/calendar_event_maybe_state.dart index c6a0ff793..aa98f8084 100644 --- a/lib/features/email/domain/state/calendar_event_maybe_state.dart +++ b/lib/features/email/domain/state/calendar_event_maybe_state.dart @@ -1,13 +1,14 @@ -import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_maybe_response.dart'; +import 'package:tmail_ui_user/features/email/domain/model/event_action.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reply_state.dart'; class CalendarEventMaybeReplying extends CalendarEventReplying {} class CalendarEventMaybeSuccess extends CalendarEventReplySuccess { - final CalendarEventMaybeResponse calendarEventMaybeResponse; - - CalendarEventMaybeSuccess(this.calendarEventMaybeResponse) - : super(calendarEventMaybeResponse); + + CalendarEventMaybeSuccess(super.calendarEventMaybeResponse, super.emailId); + + @override + EventActionType getEventActionType() => EventActionType.maybe; } class CalendarEventMaybeFailure extends CalendarEventReplyFailure { diff --git a/lib/features/email/domain/state/calendar_event_reject_state.dart b/lib/features/email/domain/state/calendar_event_reject_state.dart index 08929aec4..3e24b586b 100644 --- a/lib/features/email/domain/state/calendar_event_reject_state.dart +++ b/lib/features/email/domain/state/calendar_event_reject_state.dart @@ -1,13 +1,14 @@ -import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_reject_response.dart'; +import 'package:tmail_ui_user/features/email/domain/model/event_action.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reply_state.dart'; class CalendarEventRejecting extends CalendarEventReplying {} class CalendarEventRejected extends CalendarEventReplySuccess { - final CalendarEventRejectResponse calendarEventRejectResponse; - - CalendarEventRejected(this.calendarEventRejectResponse) - : super(calendarEventRejectResponse); + + CalendarEventRejected(super.calendarEventRejectResponse, super.emailId); + + @override + EventActionType getEventActionType() => EventActionType.no; } class CalendarEventRejectFailure extends CalendarEventReplyFailure { diff --git a/lib/features/email/domain/state/calendar_event_reply_state.dart b/lib/features/email/domain/state/calendar_event_reply_state.dart index c2acde6d8..8f55173fd 100644 --- a/lib/features/email/domain/state/calendar_event_reply_state.dart +++ b/lib/features/email/domain/state/calendar_event_reply_state.dart @@ -1,16 +1,21 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:jmap_dart_client/jmap/core/method/response/calendar_event_reply_response.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:tmail_ui_user/features/email/domain/model/event_action.dart'; class CalendarEventReplying extends LoadingState {} -class CalendarEventReplySuccess extends UIState { +abstract class CalendarEventReplySuccess extends UIState { + final EmailId emailId; final CalendarEventReplyResponse calendarEventReplyResponse; - CalendarEventReplySuccess(this.calendarEventReplyResponse); + CalendarEventReplySuccess(this.calendarEventReplyResponse, this.emailId); @override - List get props => [calendarEventReplyResponse]; + List get props => [calendarEventReplyResponse, emailId]; + + EventActionType getEventActionType(); } class CalendarEventReplyFailure extends FeatureFailure { diff --git a/lib/features/email/domain/usecases/calendar_event_accept_interactor.dart b/lib/features/email/domain/usecases/calendar_event_accept_interactor.dart index a6d9b41c7..75abe8236 100644 --- a/lib/features/email/domain/usecases/calendar_event_accept_interactor.dart +++ b/lib/features/email/domain/usecases/calendar_event_accept_interactor.dart @@ -3,6 +3,7 @@ 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/id.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:tmail_ui_user/features/email/domain/repository/calendar_event_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_accept_state.dart'; @@ -13,12 +14,13 @@ class AcceptCalendarEventInteractor { Stream> execute( AccountId accountId, - Set blobIds + Set blobIds, + EmailId emailId, ) async* { try { yield Right(CalendarEventAccepting()); final result = await _calendarEventRepository.acceptEventInvitation(accountId, blobIds); - yield Right(CalendarEventAccepted(result)); + yield Right(CalendarEventAccepted(result, emailId)); } catch (e) { yield Left(CalendarEventAcceptFailure(exception: e)); } diff --git a/lib/features/email/domain/usecases/calendar_event_reject_interactor.dart b/lib/features/email/domain/usecases/calendar_event_reject_interactor.dart index 433c2a75f..a86c90823 100644 --- a/lib/features/email/domain/usecases/calendar_event_reject_interactor.dart +++ b/lib/features/email/domain/usecases/calendar_event_reject_interactor.dart @@ -3,6 +3,7 @@ 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/id.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:tmail_ui_user/features/email/domain/repository/calendar_event_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reject_state.dart'; @@ -13,12 +14,13 @@ class RejectCalendarEventInteractor { Stream> execute( AccountId accountId, - Set blobIds + Set blobIds, + EmailId emailId, ) async* { try { yield Right(CalendarEventRejecting()); final result = await _calendarEventRepository.rejectEventInvitation(accountId, blobIds); - yield Right(CalendarEventRejected(result)); + yield Right(CalendarEventRejected(result, emailId)); } catch (e) { yield Left(CalendarEventRejectFailure(exception: e)); } diff --git a/lib/features/email/domain/usecases/maybe_calendar_event_interactor.dart b/lib/features/email/domain/usecases/maybe_calendar_event_interactor.dart index e5f593907..aa96df53a 100644 --- a/lib/features/email/domain/usecases/maybe_calendar_event_interactor.dart +++ b/lib/features/email/domain/usecases/maybe_calendar_event_interactor.dart @@ -3,6 +3,7 @@ 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/id.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:tmail_ui_user/features/email/domain/repository/calendar_event_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_maybe_state.dart'; @@ -13,12 +14,13 @@ class MaybeCalendarEventInteractor { Stream> execute( AccountId accountId, - Set blobIds + Set blobIds, + EmailId emailId, ) async* { try { yield Right(CalendarEventMaybeReplying()); final result = await _calendarEventRepository.maybeEventInvitation(accountId, blobIds); - yield Right(CalendarEventMaybeSuccess(result)); + yield Right(CalendarEventMaybeSuccess(result, emailId)); } catch (e) { yield Left(CalendarEventMaybeFailure(exception: e)); } diff --git a/lib/features/email/presentation/bindings/email_bindings.dart b/lib/features/email/presentation/bindings/email_bindings.dart index 19f0b8aeb..0cab2afcc 100644 --- a/lib/features/email/presentation/bindings/email_bindings.dart +++ b/lib/features/email/presentation/bindings/email_bindings.dart @@ -21,6 +21,7 @@ import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_ import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/store_event_attendance_status_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/view_attachment_for_web_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/controller/email_supervisor_controller.dart'; @@ -69,7 +70,8 @@ class EmailBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), - Get.find() + Get.find(), + Get.find(), )); } @@ -152,6 +154,7 @@ class EmailBindings extends BaseBindings { Get.find())); Get.lazyPut(() => StoreOpenedEmailInteractor(Get.find())); Get.lazyPut(() => PrintEmailInteractor(Get.find())); + Get.lazyPut(() => StoreEventAttendanceStatusInteractor(Get.find())); IdentityInteractorsBindings().dependencies(); } diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 4c81c87ee..f7bffd1cc 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -52,6 +52,7 @@ import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state. import 'package:tmail_ui_user/features/email/domain/state/parse_calendar_event_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/print_email_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/send_receipt_to_sender_state.dart'; +import 'package:tmail_ui_user/features/email/domain/state/store_event_attendance_status_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/unsubscribe_email_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/view_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_accept_interactor.dart'; @@ -67,6 +68,7 @@ import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_int import 'package:tmail_ui_user/features/email/domain/usecases/parse_calendar_event_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/send_receipt_to_sender_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/store_event_attendance_status_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/view_attachment_for_web_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; @@ -125,6 +127,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { final StoreOpenedEmailInteractor _storeOpenedEmailInteractor; final ViewAttachmentForWebInteractor _viewAttachmentForWebInteractor; final PrintEmailInteractor _printEmailInteractor; + final StoreEventAttendanceStatusInteractor _storeEventAttendanceStatusInteractor; CreateNewEmailRuleFilterInteractor? _createNewEmailRuleFilterInteractor; SendReceiptToSenderInteractor? _sendReceiptToSenderInteractor; @@ -155,7 +158,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { bool get calendarEventProcessing => viewState.value.fold( (failure) => false, - (success) => success is CalendarEventReplying); + (success) => success is CalendarEventReplying || success is StoreEventAttendanceStatusLoading); CalendarEvent? get calendarEvent => blobCalendarEvent.value?.calendarEventList.firstOrNull; Id? get _displayingEventBlobId => blobCalendarEvent.value?.blobId; @@ -173,6 +176,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { this._storeOpenedEmailInteractor, this._viewAttachmentForWebInteractor, this._printEmailInteractor, + this._storeEventAttendanceStatusInteractor, ); @override @@ -233,6 +237,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin { _handlePrintEmailSuccess(success); } else if (success is CalendarEventReplySuccess) { _calendarEventSuccess(success); + } else if (success is StoreEventAttendanceStatusSuccess) { + _showToastMessageEventAttendanceSuccess(success); } } @@ -255,7 +261,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin { emailLoadedViewState.value = Left(failure); } else if (failure is PrintEmailFailure) { _showMessageWhenEmailPrintingFailed(failure); - } else if (failure is CalendarEventReplyFailure) { + } else if (failure is CalendarEventReplyFailure + || failure is StoreEventAttendanceStatusFailure) { _calendarEventFailure(); } } @@ -1703,23 +1710,23 @@ class SingleEmailController extends BaseController with AppLoaderMixin { ); } - void onCalendarEventReplyAction(EventActionType eventActionType) { + void onCalendarEventReplyAction(EventActionType eventActionType, EmailId emailId) { switch (eventActionType) { case EventActionType.yes: - _acceptCalendarEventAction(); + _acceptCalendarEventAction(emailId); break; case EventActionType.maybe: - _maybeCalendarEventAction(); + _maybeCalendarEventAction(emailId); break; case EventActionType.no: - _rejectCalendarEventAction(); + _rejectCalendarEventAction(emailId); break; default: break; } } - void _acceptCalendarEventAction() { + void _acceptCalendarEventAction(EmailId emailId) { if (_acceptCalendarEventInteractor == null || _displayingEventBlobId == null || mailboxDashBoardController.accountId.value == null) { @@ -1727,11 +1734,13 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } else { consumeState(_acceptCalendarEventInteractor!.execute( mailboxDashBoardController.accountId.value!, - {_displayingEventBlobId!})); + {_displayingEventBlobId!}, + emailId + )); } } - void _rejectCalendarEventAction() { + void _rejectCalendarEventAction(EmailId emailId) { if (_rejectCalendarEventInteractor == null || _displayingEventBlobId == null || mailboxDashBoardController.accountId.value == null) { @@ -1739,11 +1748,13 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } else { consumeState(_rejectCalendarEventInteractor!.execute( mailboxDashBoardController.accountId.value!, - {_displayingEventBlobId!})); + {_displayingEventBlobId!}, + emailId + )); } } - void _maybeCalendarEventAction() { + void _maybeCalendarEventAction(EmailId emailId) { if (_maybeCalendarEventInteractor == null || _displayingEventBlobId == null || mailboxDashBoardController.accountId.value == null) { @@ -1751,27 +1762,42 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } else { consumeState(_maybeCalendarEventInteractor!.execute( mailboxDashBoardController.accountId.value!, - {_displayingEventBlobId!})); + {_displayingEventBlobId!}, + emailId + )); } } void _calendarEventSuccess(CalendarEventReplySuccess success) { - if (currentOverlayContext != null && currentContext != null) { - final appLocalization = AppLocalizations.of(currentContext!); - if (success is CalendarEventAccepted) { - appToast.showToastSuccessMessage( - currentOverlayContext!, - appLocalization.youWillAttendThisMeeting); - } else if (success is CalendarEventMaybeSuccess) { - appToast.showToastSuccessMessage( - currentOverlayContext!, - appLocalization.youMayAttendThisMeeting); - } else if (success is CalendarEventRejected) { - appToast.showToastSuccessMessage( - currentOverlayContext!, - appLocalization.youWillNotAttendThisMeeting); - } + final session = mailboxDashBoardController.sessionCurrent; + final accountId = mailboxDashBoardController.accountId.value; + + if (session == null || accountId == null) { + consumeState(Stream.value(Left(StoreEventAttendanceStatusFailure(exception: NotFoundSessionException())))); + return; } + + consumeState(_storeEventAttendanceStatusInteractor.execute( + session, + accountId, + success.emailId, + success.getEventActionType() + )); + } + + void _showToastMessageEventAttendanceSuccess(StoreEventAttendanceStatusSuccess success) { + final selectedEmail = mailboxDashBoardController.selectedEmail.value; + final newEmail = selectedEmail?.updateKeywords(success.updatedEmail.keywords); + mailboxDashBoardController.setSelectedEmail(newEmail); + mailboxDashBoardController.dispatchState(Right(success)); + + if (currentOverlayContext == null || currentContext == null) { + return; + } + + appToast.showToastSuccessMessage( + currentOverlayContext!, + success.eventActionType.getToastMessageSuccess(currentContext!)); } void _calendarEventFailure() { diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index 9555e6f48..f1f4a795e 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -371,28 +371,32 @@ class EmailView extends GetWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - CalendarEventInformationWidget( + Obx(() => CalendarEventInformationWidget( calendarEvent: calendarEvent, onOpenComposerAction: controller.openNewComposerAction, onOpenNewTabAction: controller.openNewTabAction, - onCalendarEventReplyActionClick: controller.onCalendarEventReplyAction, + onCalendarEventReplyActionClick: (eventActionType) => + controller.onCalendarEventReplyAction(eventActionType, presentationEmail.id!), calendarEventReplying: controller.calendarEventProcessing, - ), + presentationEmail: controller.currentEmail, + )), if (calendarEvent.getTitleEventAction(context, emailAddressSender ?? []).isNotEmpty) CalendarEventActionBannerWidget( calendarEvent: calendarEvent, listEmailAddressSender: emailAddressSender ?? [] ), - CalendarEventDetailWidget( + Obx(() => CalendarEventDetailWidget( calendarEvent: calendarEvent, emailContent: controller.currentEmailLoaded?.htmlContent ?? '', isDraggableAppActive: controller.mailboxDashBoardController.isDraggableAppActive, onOpenComposerAction: controller.openNewComposerAction, onOpenNewTabAction: controller.openNewTabAction, onMailtoDelegateAction: controller.openMailToLink, - onCalendarEventReplyActionClick: controller.onCalendarEventReplyAction, + onCalendarEventReplyActionClick: (eventActionType) => + controller.onCalendarEventReplyAction(eventActionType, presentationEmail.id!), calendarEventReplying: controller.calendarEventProcessing, - ), + presentationEmail: controller.currentEmail, + )), ], ) else if (presentationEmail.id == controller.currentEmail?.id) diff --git a/lib/features/email/presentation/styles/calendar_event_action_button_widget_styles.dart b/lib/features/email/presentation/styles/calendar_event_action_button_widget_styles.dart index 8c98ed4da..53c1eac76 100644 --- a/lib/features/email/presentation/styles/calendar_event_action_button_widget_styles.dart +++ b/lib/features/email/presentation/styles/calendar_event_action_button_widget_styles.dart @@ -11,6 +11,8 @@ class CalendarEventActionButtonWidgetStyles { static const Color backgroundColor = Colors.transparent; static Color loadingBackgroundColor = Colors.grey.shade300; + static const Color selectedBackgroundColor = AppColor.disableSendEmailButtonColor; + static const Color selectedTextColor = Colors.white; static const Color textColor = AppColor.primaryColor; static const FontWeight fontWeight = FontWeight.w500; diff --git a/lib/features/email/presentation/widgets/calendar_event/calendar_event_action_button_widget.dart b/lib/features/email/presentation/widgets/calendar_event/calendar_event_action_button_widget.dart index c75664e59..ffe7bbe5d 100644 --- a/lib/features/email/presentation/widgets/calendar_event/calendar_event_action_button_widget.dart +++ b/lib/features/email/presentation/widgets/calendar_event/calendar_event_action_button_widget.dart @@ -1,7 +1,8 @@ - -import 'package:core/core.dart'; +import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:model/email/presentation_email.dart'; import 'package:tmail_ui_user/features/email/domain/model/event_action.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/calendar_event_action_button_widget_styles.dart'; @@ -12,52 +13,155 @@ class CalendarEventActionButtonWidget extends StatelessWidget { final EdgeInsetsGeometry? margin; final OnCalendarEventReplyActionClick onCalendarEventReplyActionClick; final bool calendarEventReplying; + final PresentationEmail? presentationEmail; - const CalendarEventActionButtonWidget({ + final _responsiveUtils = Get.find(); + + CalendarEventActionButtonWidget({ super.key, required this.onCalendarEventReplyActionClick, required this.calendarEventReplying, this.margin, + this.presentationEmail, }); @override Widget build(BuildContext context) { - final responsiveUtils = Get.find(); return Container( width: double.infinity, margin: margin ?? CalendarEventActionButtonWidgetStyles.margin, - padding: responsiveUtils.isPortraitMobile(context) + padding: _responsiveUtils.isPortraitMobile(context) ? CalendarEventActionButtonWidgetStyles.paddingMobile : CalendarEventActionButtonWidgetStyles.paddingWeb, child: Wrap( spacing: CalendarEventActionButtonWidgetStyles.space, runSpacing: CalendarEventActionButtonWidgetStyles.space, children: EventActionType.values - .map((action) => TMailButtonWidget( - text: action.getLabelButton(context), - backgroundColor: calendarEventReplying - ? CalendarEventActionButtonWidgetStyles.loadingBackgroundColor - : CalendarEventActionButtonWidgetStyles.backgroundColor, - borderRadius: CalendarEventActionButtonWidgetStyles.borderRadius, - padding: CalendarEventActionButtonWidgetStyles.buttonPadding, - textStyle: const TextStyle( - fontWeight: CalendarEventActionButtonWidgetStyles.fontWeight, - fontSize: CalendarEventActionButtonWidgetStyles.textSize, - color: CalendarEventActionButtonWidgetStyles.textColor, + .map((action) => AbsorbPointer( + absorbing: _getCallbackFunction(action) == null, + child: TMailButtonWidget( + text: action.getLabelButton(context), + backgroundColor: _getButtonBackgroundColor(action), + borderRadius: CalendarEventActionButtonWidgetStyles.borderRadius, + padding: CalendarEventActionButtonWidgetStyles.buttonPadding, + textStyle: TextStyle( + fontWeight: CalendarEventActionButtonWidgetStyles.fontWeight, + fontSize: CalendarEventActionButtonWidgetStyles.textSize, + color: _getButtonTextColor(action), + ), + textAlign: TextAlign.center, + minWidth: CalendarEventActionButtonWidgetStyles.minWidth, + width: _responsiveUtils.isPortraitMobile(context) ? double.infinity : null, + border: Border.all( + width: CalendarEventActionButtonWidgetStyles.borderWidth, + color: _getButtonBorderColor(action) + ), + onTapActionCallback: _getCallbackFunction(action), ), - textAlign: TextAlign.center, - minWidth: CalendarEventActionButtonWidgetStyles.minWidth, - width: responsiveUtils.isPortraitMobile(context) ? double.infinity : null, - border: Border.all( - width: CalendarEventActionButtonWidgetStyles.borderWidth, - color: CalendarEventActionButtonWidgetStyles.textColor - ), - onTapActionCallback: calendarEventReplying - ? null - : () => onCalendarEventReplyActionClick(action), )) .toList(), ), ); } + + Color _getButtonBackgroundColor(EventActionType eventActionType) { + switch (eventActionType) { + case EventActionType.yes: + if (presentationEmail?.isAcceptedEventAttendance == true) { + return CalendarEventActionButtonWidgetStyles.selectedBackgroundColor; + } + + if (calendarEventReplying) { + return CalendarEventActionButtonWidgetStyles.loadingBackgroundColor; + } + + return CalendarEventActionButtonWidgetStyles.backgroundColor; + case EventActionType.maybe: + if (presentationEmail!.isTentativelyAcceptedEventAttendance == true) { + return CalendarEventActionButtonWidgetStyles.selectedBackgroundColor; + } + + if (calendarEventReplying) { + return CalendarEventActionButtonWidgetStyles.loadingBackgroundColor; + } + + return CalendarEventActionButtonWidgetStyles.backgroundColor; + case EventActionType.no: + if (presentationEmail?.isRejectedEventAttendance == true) { + return CalendarEventActionButtonWidgetStyles.selectedBackgroundColor; + } + + if (calendarEventReplying) { + return CalendarEventActionButtonWidgetStyles.loadingBackgroundColor; + } + + return CalendarEventActionButtonWidgetStyles.backgroundColor; + } + } + + Color _getButtonTextColor(EventActionType eventActionType) { + switch (eventActionType) { + case EventActionType.yes: + if (presentationEmail?.isAcceptedEventAttendance == true) { + return CalendarEventActionButtonWidgetStyles.selectedTextColor; + } + + return CalendarEventActionButtonWidgetStyles.textColor; + case EventActionType.maybe: + if (presentationEmail?.isTentativelyAcceptedEventAttendance == true) { + return CalendarEventActionButtonWidgetStyles.selectedTextColor; + } + + return CalendarEventActionButtonWidgetStyles.textColor; + case EventActionType.no: + if (presentationEmail?.isRejectedEventAttendance == true) { + return CalendarEventActionButtonWidgetStyles.selectedTextColor; + } + + return CalendarEventActionButtonWidgetStyles.textColor; + } + } + + Color _getButtonBorderColor(EventActionType eventActionType) { + switch (eventActionType) { + case EventActionType.yes: + if (presentationEmail?.isAcceptedEventAttendance == true) { + return CalendarEventActionButtonWidgetStyles.selectedBackgroundColor; + } + + return CalendarEventActionButtonWidgetStyles.textColor; + case EventActionType.maybe: + if (presentationEmail?.isTentativelyAcceptedEventAttendance == true) { + return CalendarEventActionButtonWidgetStyles.selectedBackgroundColor; + } + + return CalendarEventActionButtonWidgetStyles.textColor; + case EventActionType.no: + if (presentationEmail?.isRejectedEventAttendance == true) { + return CalendarEventActionButtonWidgetStyles.selectedBackgroundColor; + } + + return CalendarEventActionButtonWidgetStyles.textColor; + } + } + + Function()? _getCallbackFunction(EventActionType eventActionType) { + switch (eventActionType) { + case EventActionType.yes: + if (presentationEmail?.isAcceptedEventAttendance == true || calendarEventReplying) { + return null; + } + return () => onCalendarEventReplyActionClick(eventActionType); + case EventActionType.maybe: + if (presentationEmail?.isTentativelyAcceptedEventAttendance == true || calendarEventReplying) { + return null; + } + return () => onCalendarEventReplyActionClick(eventActionType); + case EventActionType.no: + if (presentationEmail?.isRejectedEventAttendance == true || calendarEventReplying) { + return null; + } + return () => onCalendarEventReplyActionClick(eventActionType); + } + } } \ No newline at end of file diff --git a/lib/features/email/presentation/widgets/calendar_event/calendar_event_detail_widget.dart b/lib/features/email/presentation/widgets/calendar_event/calendar_event_detail_widget.dart index 0b6abd201..c571072b5 100644 --- a/lib/features/email/presentation/widgets/calendar_event/calendar_event_detail_widget.dart +++ b/lib/features/email/presentation/widgets/calendar_event/calendar_event_detail_widget.dart @@ -1,7 +1,7 @@ - -import 'package:core/core.dart'; +import 'package:core/presentation/views/html_viewer/html_content_viewer_widget.dart'; import 'package:flutter/material.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart'; +import 'package:model/email/presentation_email.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_event_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/calendar_event_detail_widget_styles.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/calendar_event/calendar_event_action_button_widget.dart'; @@ -23,6 +23,7 @@ class CalendarEventDetailWidget extends StatelessWidget { final OnMailtoDelegateAction? onMailtoDelegateAction; final OnCalendarEventReplyActionClick onCalendarEventReplyActionClick; final bool calendarEventReplying; + final PresentationEmail? presentationEmail; const CalendarEventDetailWidget({ super.key, @@ -34,6 +35,7 @@ class CalendarEventDetailWidget extends StatelessWidget { this.onOpenNewTabAction, this.onOpenComposerAction, this.onMailtoDelegateAction, + this.presentationEmail, }); @override @@ -95,7 +97,9 @@ class CalendarEventDetailWidget extends StatelessWidget { ), CalendarEventActionButtonWidget( onCalendarEventReplyActionClick: onCalendarEventReplyActionClick, - calendarEventReplying: calendarEventReplying), + calendarEventReplying: calendarEventReplying, + presentationEmail: presentationEmail, + ), ], ), ); diff --git a/lib/features/email/presentation/widgets/calendar_event/calendar_event_information_widget.dart b/lib/features/email/presentation/widgets/calendar_event/calendar_event_information_widget.dart index 95edf1e32..852d6566d 100644 --- a/lib/features/email/presentation/widgets/calendar_event/calendar_event_information_widget.dart +++ b/lib/features/email/presentation/widgets/calendar_event/calendar_event_information_widget.dart @@ -1,9 +1,9 @@ - import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart'; +import 'package:model/email/presentation_email.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_event_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/calendar_event_information_widget_styles.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/calendar_event/calendar_date_icon_widget.dart'; @@ -23,19 +23,22 @@ class CalendarEventInformationWidget extends StatelessWidget { final OnOpenComposerAction? onOpenComposerAction; final OnCalendarEventReplyActionClick onCalendarEventReplyActionClick; final bool calendarEventReplying; + final PresentationEmail? presentationEmail; - const CalendarEventInformationWidget({ + final _responsiveUtils = Get.find(); + + CalendarEventInformationWidget({ super.key, required this.calendarEvent, required this.onCalendarEventReplyActionClick, required this.calendarEventReplying, this.onOpenNewTabAction, this.onOpenComposerAction, + this.presentationEmail, }); @override Widget build(BuildContext context) { - final responsiveUtils = Get.find(); return Container( clipBehavior: Clip.antiAlias, decoration: const ShapeDecoration( @@ -51,7 +54,7 @@ class CalendarEventInformationWidget extends StatelessWidget { margin: const EdgeInsetsDirectional.symmetric( vertical: CalendarEventInformationWidgetStyles.verticalMargin, horizontal: CalendarEventInformationWidgetStyles.horizontalMargin), - child: responsiveUtils.isPortraitMobile(context) + child: _responsiveUtils.isPortraitMobile(context) ? Column( children: [ CalendarDateIconWidget( @@ -118,6 +121,7 @@ class CalendarEventInformationWidget extends StatelessWidget { margin: EdgeInsetsDirectional.zero, onCalendarEventReplyActionClick: onCalendarEventReplyActionClick, calendarEventReplying: calendarEventReplying, + presentationEmail: presentationEmail, ), ], ), @@ -188,6 +192,7 @@ class CalendarEventInformationWidget extends StatelessWidget { margin: EdgeInsetsDirectional.zero, onCalendarEventReplyActionClick: onCalendarEventReplyActionClick, calendarEventReplying: calendarEventReplying, + presentationEmail: presentationEmail, ), ], ), diff --git a/lib/features/search/email/presentation/search_email_controller.dart b/lib/features/search/email/presentation/search_email_controller.dart index f0ae9252f..03f58018e 100644 --- a/lib/features/search/email/presentation/search_email_controller.dart +++ b/lib/features/search/email/presentation/search_email_controller.dart @@ -35,6 +35,7 @@ import 'package:tmail_ui_user/features/email/domain/state/delete_multiple_emails import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_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/store_event_attendance_status_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/unsubscribe_email_state.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; @@ -215,7 +216,8 @@ class SearchEmailController extends BaseController success is EmptySpamFolderSuccess || success is DeleteMultipleEmailsPermanentlyAllSuccess || success is DeleteMultipleEmailsPermanentlyHasSomeEmailFailure || - success is UnsubscribeEmailSuccess + success is UnsubscribeEmailSuccess || + success is StoreEventAttendanceStatusSuccess ) { _refreshEmailChanges(); } diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 04efe239d..32edf5aef 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -27,6 +27,7 @@ import 'package:tmail_ui_user/features/email/domain/state/delete_multiple_emails import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_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/store_event_attendance_status_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/unsubscribe_email_state.dart'; import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; @@ -368,6 +369,8 @@ class ThreadController extends BaseController with EmailActionController { refreshAllEmail(); } else if (success is UnsubscribeEmailSuccess) { _refreshEmailChanges(currentEmailState: success.currentEmailState); + } else if (success is StoreEventAttendanceStatusSuccess) { + _refreshEmailChanges(currentEmailState: success.currentEmailState); } }); }); diff --git a/model/lib/email/presentation_email.dart b/model/lib/email/presentation_email.dart index 999605dd7..5622a6de6 100644 --- a/model/lib/email/presentation_email.dart +++ b/model/lib/email/presentation_email.dart @@ -116,6 +116,12 @@ class PresentationEmail with EquatableMixin { bool get isSubscribed => keywords?.containsKey(KeyWordIdentifierExtension.unsubscribeMail) == true; + bool get isAcceptedEventAttendance => keywords?.containsKey(KeyWordIdentifierExtension.acceptedEventAttendance) == true; + + bool get isTentativelyAcceptedEventAttendance => keywords?.containsKey(KeyWordIdentifierExtension.tentativelyAcceptedEventAttendance) == true; + + bool get isRejectedEventAttendance => keywords?.containsKey(KeyWordIdentifierExtension.rejectedEventAttendance) == true; + bool get isAnsweredAndForwarded => isAnswered && isForwarded; bool get withAttachments => hasAttachment == true; diff --git a/test/features/email/domain/usecases/calendar_event_accept_interactor_test.dart b/test/features/email/domain/usecases/calendar_event_accept_interactor_test.dart index fbf7a6760..9e7be6396 100644 --- a/test/features/email/domain/usecases/calendar_event_accept_interactor_test.dart +++ b/test/features/email/domain/usecases/calendar_event_accept_interactor_test.dart @@ -4,6 +4,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/properties/event_id.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_accept_response.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:tmail_ui_user/features/email/domain/exceptions/calendar_event_exceptions.dart'; @@ -19,6 +20,7 @@ void main() { final acceptCalendarEventInteractor = AcceptCalendarEventInteractor(calendarEventRepository); final accountId = AccountId(Id('123')); final blobId = Id('123321'); + final emailId = EmailId(Id('abcde')); group('calendar event accept interactor test:', () { test('should emit CalendarEventAccepted when repo return data', () { @@ -32,10 +34,10 @@ void main() { // assert expect( - acceptCalendarEventInteractor.execute(accountId, {blobId}), + acceptCalendarEventInteractor.execute(accountId, {blobId}, emailId), emitsInOrder([ Right(CalendarEventAccepting()), - Right(CalendarEventAccepted(calendarEventAcceptResponse))])); + Right(CalendarEventAccepted(calendarEventAcceptResponse, emailId))])); }); test('should emit CalendarEventAcceptFailure when repo throw exception', () { @@ -45,7 +47,7 @@ void main() { // assert expect( - acceptCalendarEventInteractor.execute(accountId, {blobId}), + acceptCalendarEventInteractor.execute(accountId, {blobId}, emailId), emitsInOrder([ Right(CalendarEventAccepting()), Left(CalendarEventAcceptFailure(exception: exception))])); diff --git a/test/features/email/domain/usecases/calendar_event_reject_interactor_test.dart b/test/features/email/domain/usecases/calendar_event_reject_interactor_test.dart index 462d11412..e12d321d2 100644 --- a/test/features/email/domain/usecases/calendar_event_reject_interactor_test.dart +++ b/test/features/email/domain/usecases/calendar_event_reject_interactor_test.dart @@ -4,6 +4,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/properties/event_id.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_reject_response.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:mockito/mockito.dart'; import 'package:tmail_ui_user/features/email/domain/exceptions/calendar_event_exceptions.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reject_state.dart'; @@ -16,6 +17,7 @@ void main() { final rejectCalendarEventInteractor = RejectCalendarEventInteractor(calendarEventRepository); final accountId = AccountId(Id('123')); final blobId = Id('123321'); + final emailId = EmailId(Id('abcde')); group('calendar event reject interactor test:', () { test('should emit CalendarEventRejected when repo return data', () { @@ -29,10 +31,10 @@ void main() { // assert expect( - rejectCalendarEventInteractor.execute(accountId, {blobId}), + rejectCalendarEventInteractor.execute(accountId, {blobId}, emailId), emitsInOrder([ Right(CalendarEventRejecting()), - Right(CalendarEventRejected(calendarEventRejectResponse))])); + Right(CalendarEventRejected(calendarEventRejectResponse, emailId))])); }); test('should emit CalendarEventRejectFailure when repo throw exception', () { @@ -42,7 +44,7 @@ void main() { // assert expect( - rejectCalendarEventInteractor.execute(accountId, {blobId}), + rejectCalendarEventInteractor.execute(accountId, {blobId}, emailId), emitsInOrder([ Right(CalendarEventRejecting()), Left(CalendarEventRejectFailure(exception: exception))])); diff --git a/test/features/email/domain/usecases/maybe_calendar_event_interactor_test.dart b/test/features/email/domain/usecases/maybe_calendar_event_interactor_test.dart index 90cd57798..81f22cc8a 100644 --- a/test/features/email/domain/usecases/maybe_calendar_event_interactor_test.dart +++ b/test/features/email/domain/usecases/maybe_calendar_event_interactor_test.dart @@ -4,6 +4,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/properties/event_id.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/reply/calendar_event_maybe_response.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:mockito/mockito.dart'; import 'package:tmail_ui_user/features/email/domain/exceptions/calendar_event_exceptions.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_maybe_state.dart'; @@ -16,6 +17,7 @@ void main() { final maybeCalendarEventInteractor = MaybeCalendarEventInteractor(calendarEventRepository); final accountId = AccountId(Id('123')); final blobId = Id('123321'); + final emailId = EmailId(Id('abcde')); group('calendar event maybe interactor should emit expected states', () { test('when repo return data', () { @@ -29,10 +31,10 @@ void main() { // assert expect( - maybeCalendarEventInteractor.execute(accountId, {blobId}), + maybeCalendarEventInteractor.execute(accountId, {blobId}, emailId), emitsInOrder([ Right(CalendarEventMaybeReplying()), - Right(CalendarEventMaybeSuccess(calendarEventMaybeResponse))])); + Right(CalendarEventMaybeSuccess(calendarEventMaybeResponse, emailId))])); }); test('when repo throw exception', () { @@ -42,7 +44,7 @@ void main() { // assert expect( - maybeCalendarEventInteractor.execute(accountId, {blobId}), + maybeCalendarEventInteractor.execute(accountId, {blobId}, emailId), emitsInOrder([ Right(CalendarEventMaybeReplying()), Left(CalendarEventMaybeFailure(exception: exception))])); diff --git a/test/features/email/presentation/controller/single_email_controller_test.dart b/test/features/email/presentation/controller/single_email_controller_test.dart index 9b0bfc980..0af277ffe 100644 --- a/test/features/email/presentation/controller/single_email_controller_test.dart +++ b/test/features/email/presentation/controller/single_email_controller_test.dart @@ -10,6 +10,7 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/state.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:model/model.dart'; @@ -28,6 +29,7 @@ import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_ import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/store_event_attendance_status_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/view_attachment_for_web_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/controller/email_supervisor_controller.dart'; @@ -81,6 +83,7 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), + MockSpec(), ]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -112,6 +115,7 @@ void main() { final responsiveUtils = MockResponsiveUtils(); final uuid = MockUuid(); final printEmailInteractor = MockPrintEmailInteractor(); + final storeEventAttendanceStatusInteractor = MockStoreEventAttendanceStatusInteractor(); late SingleEmailController singleEmailController; @@ -161,6 +165,7 @@ void main() { storeOpenedEmailInteractor, viewAttachmentForWebInteractor, printEmailInteractor, + storeEventAttendanceStatusInteractor, ); }); @@ -269,6 +274,7 @@ void main() { group('calendar event reply test:', () { final blobId = Id('abc123'); + final emailId = EmailId(Id('xyz123')); final calendarEvent = CalendarEvent(); group('accept test:', () { @@ -290,11 +296,11 @@ void main() { calendarEventList: [calendarEvent])])); // act - singleEmailController.onCalendarEventReplyAction(EventActionType.yes); - await untilCalled(acceptCalendarEventInteractor.execute(any, any)); + singleEmailController.onCalendarEventReplyAction(EventActionType.yes, emailId); + await untilCalled(acceptCalendarEventInteractor.execute(any, any, any)); // assert - verify(acceptCalendarEventInteractor.execute(testAccountId, {blobId})).called(1); + verify(acceptCalendarEventInteractor.execute(testAccountId, {blobId}, emailId)).called(1); }); }); @@ -317,11 +323,11 @@ void main() { calendarEventList: [calendarEvent])])); // act - singleEmailController.onCalendarEventReplyAction(EventActionType.maybe); - await untilCalled(maybeCalendarEventInteractor.execute(any, any)); + singleEmailController.onCalendarEventReplyAction(EventActionType.maybe, emailId); + await untilCalled(maybeCalendarEventInteractor.execute(any, any, any)); // assert - verify(maybeCalendarEventInteractor.execute(testAccountId, {blobId})).called(1); + verify(maybeCalendarEventInteractor.execute(testAccountId, {blobId}, emailId)).called(1); }); }); @@ -344,11 +350,11 @@ void main() { calendarEventList: [calendarEvent])])); // act - singleEmailController.onCalendarEventReplyAction(EventActionType.no); - await untilCalled(rejectCalendarEventInteractor.execute(any, any)); + singleEmailController.onCalendarEventReplyAction(EventActionType.no, emailId); + await untilCalled(rejectCalendarEventInteractor.execute(any, any, any)); // assert - verify(rejectCalendarEventInteractor.execute(testAccountId, {blobId})).called(1); + verify(rejectCalendarEventInteractor.execute(testAccountId, {blobId}, emailId)).called(1); }); }); });