From 8864fbc03a6720c8a3ce32dd94220280c189464c Mon Sep 17 00:00:00 2001 From: DatDang Date: Mon, 16 Jun 2025 11:07:38 +0700 Subject: [PATCH] TF-3775 Thread Detail handle error getEmailLoaded TF-3775 Thread Detail readd EventBodyContentWidget maxHeight TF-3775 Thread Detail fix max lines subject mobile TF-3775 Thread Detail add email attachments top spacing TF-3775 Thread Detail fix email not updated on move/trash/spam TF-3775 Thread Detail Fix exception fast switch email in tablet large TF-3775 Thread Detail Adjust MailUnsubscribedBanner vertical spacing --- .../presentation/bindings/email_bindings.dart | 1 - .../controller/single_email_controller.dart | 72 +--------------- .../styles/email_attachments_styles.dart | 2 +- .../email_action_reactor.dart | 84 ++++++++++--------- .../event_body_content_widget.dart | 3 +- .../widgets/email_subject_widget.dart | 5 +- .../widgets/mail_unsubscribed_banner.dart | 2 +- .../mailbox_dashboard_controller.dart | 5 ++ .../action/thread_detail_ui_action.dart | 18 +++- ..._thread_detail_email_mailbox_contains.dart | 14 ++++ .../get_thread_detail_loading_view.dart | 37 ++++---- .../get_thread_details_email_views.dart | 4 +- .../extension/handle_email_moved_action.dart | 11 +++ .../handle_get_emails_by_ids_success.dart | 1 + .../thread_detail_on_email_action_click.dart | 16 +--- .../thread_detail_controller.dart | 21 ++++- .../presentation/thread_detail_view.dart | 8 +- .../single_email_controller_test.dart | 4 - 18 files changed, 154 insertions(+), 154 deletions(-) create mode 100644 lib/features/thread_detail/presentation/extension/get_thread_detail_email_mailbox_contains.dart create mode 100644 lib/features/thread_detail/presentation/extension/handle_email_moved_action.dart diff --git a/lib/features/email/presentation/bindings/email_bindings.dart b/lib/features/email/presentation/bindings/email_bindings.dart index 7851a978b..eb0007a67 100644 --- a/lib/features/email/presentation/bindings/email_bindings.dart +++ b/lib/features/email/presentation/bindings/email_bindings.dart @@ -76,7 +76,6 @@ class EmailBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), - Get.find(), Get.find(), Get.find(), Get.find(), diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 0c227df48..2d53fb675 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -15,7 +15,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_file_dialog/flutter_file_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:get/get_navigation/src/dialog/dialog_route.dart'; import 'package:http_parser/http_parser.dart'; @@ -42,14 +41,11 @@ import 'package:share_plus/share_plus.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/base/state/button_state.dart'; -import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart'; import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart'; import 'package:tmail_ui_user/features/email/domain/extensions/list_attachments_extension.dart'; import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart'; import 'package:tmail_ui_user/features/email/domain/model/event_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.dart'; -import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; -import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/send_receipt_to_sender_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/view_entire_message_request.dart'; @@ -68,7 +64,6 @@ import 'package:tmail_ui_user/features/email/domain/state/get_entire_message_as_ import 'package:tmail_ui_user/features/email/domain/state/get_html_content_from_attachment_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/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/parse_calendar_event_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/preview_email_from_eml_file_state.dart'; @@ -89,7 +84,6 @@ import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment 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_read_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/parse_calendar_event_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart'; @@ -119,7 +113,6 @@ import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.d import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/get_mailbox_contain_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; @@ -161,7 +154,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { final DownloadAttachmentsInteractor _downloadAttachmentsInteractor; final DeviceManager _deviceManager; final ExportAttachmentInteractor _exportAttachmentInteractor; - final MoveToMailboxInteractor _moveToMailboxInteractor; final MarkAsStarEmailInteractor _markAsStarEmailInteractor; final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor; final GetAllIdentitiesInteractor _getAllIdentitiesInteractor; @@ -230,7 +222,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { this._downloadAttachmentsInteractor, this._deviceManager, this._exportAttachmentInteractor, - this._moveToMailboxInteractor, this._markAsStarEmailInteractor, this._downloadAttachmentForWebInteractor, this._getAllIdentitiesInteractor, @@ -282,8 +273,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { _exportAttachmentSuccessAction(success); } else if (success is ExportAllAttachmentsSuccess) { _exportAllAttachmentsSuccessAction(success); - } else if (success is MoveToMailboxSuccess) { - _moveToMailboxSuccess(success); } else if (success is MarkAsStarEmailSuccess) { _markAsEmailStarSuccess(success); } else if (success is DownloadAttachmentForWebSuccess) { @@ -780,7 +769,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } PresentationMailbox? getMailboxContain(PresentationEmail email) { - return mailboxDashBoardController.getMailboxContain(email); + return email.findMailboxContain(mailboxDashBoardController.mapMailboxById); } void markAsEmailRead( @@ -1206,7 +1195,9 @@ class SingleEmailController extends BaseController with AppLoaderMixin { accountId!, email, mapMailbox: mailboxDashBoardController.mapMailboxById, - selectedMailbox: mailboxDashBoardController.selectedMailbox.value, + selectedMailbox: currentEmail?.findMailboxContain( + mailboxDashBoardController.mapMailboxById, + ), isSearchEmailRunning: mailboxDashBoardController.searchController.isSearchEmailRunning, ); if (!context.mounted || moveActionRequest == null) return; @@ -1222,61 +1213,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } } - void _moveToMailbox( - BuildContext context, - Session session, - AccountId accountId, - MoveToMailboxRequest moveRequest, - Map emailIdsWithReadStatus, - ) { - consumeState(_moveToMailboxInteractor.execute( - session, - accountId, - moveRequest, - emailIdsWithReadStatus, - )); - } - - void _moveToMailboxSuccess(MoveToMailboxSuccess success) { - mailboxDashBoardController.dispatchState(Right(success)); - if (success.moveAction == MoveAction.moving && currentContext != null && currentOverlayContext != null) { - appToast.showToastMessage( - currentOverlayContext!, - success.emailActionType.getToastMessageMoveToMailboxSuccess(currentContext!, destinationPath: success.destinationPath), - actionName: AppLocalizations.of(currentContext!).undo, - onActionClick: () { - _revertedToOriginalMailbox(MoveToMailboxRequest( - {success.destinationMailboxId: [success.emailId]}, - success.currentMailboxId, - MoveAction.undo, - success.emailActionType), - success.emailIdsWithReadStatus, - ); - }, - leadingSVGIcon: imagePaths.icFolderMailbox, - leadingSVGIconColor: Colors.white, - backgroundColor: AppColor.toastSuccessBackgroundColor, - textColor: Colors.white, - actionIcon: SvgPicture.asset(imagePaths.icUndo) - ); - } - } - - void _revertedToOriginalMailbox( - MoveToMailboxRequest newMoveRequest, - Map emailIdsWithReadStatus, - ) { - if (accountId != null && session != null) { - _moveToMailbox( - currentContext!, - session!, - accountId!, - newMoveRequest, - emailIdsWithReadStatus, - ); - } - } - void moveToTrash(BuildContext context, PresentationEmail email) { if (session != null && accountId != null) { final moveActionRequest = emailActionReactor.moveToTrash( diff --git a/lib/features/email/presentation/styles/email_attachments_styles.dart b/lib/features/email/presentation/styles/email_attachments_styles.dart index 6d063b938..a7a019a2f 100644 --- a/lib/features/email/presentation/styles/email_attachments_styles.dart +++ b/lib/features/email/presentation/styles/email_attachments_styles.dart @@ -25,7 +25,7 @@ class EmailAttachmentsStyles { static const FontWeight buttonFontWeight = FontWeight.w400; static const FontWeight buttonMoreAttachmentsFontWeight = FontWeight.w500; - static const EdgeInsetsGeometry padding = EdgeInsetsDirectional.only(start: 16, end: 16, bottom: 12); + static const EdgeInsetsGeometry padding = EdgeInsetsDirectional.only(start: 16, end: 16, bottom: 12, top: 16); static const EdgeInsetsGeometry buttonPadding = EdgeInsets.symmetric(vertical: 8, horizontal: 12); static const EdgeInsetsGeometry mobileButtonPadding = EdgeInsets.symmetric(vertical: 8, horizontal: 3); static const EdgeInsetsGeometry moreButtonMargin = EdgeInsetsDirectional.only(bottom: 2, start: 8); diff --git a/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart b/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart index 7ba95f653..7692ce51e 100644 --- a/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart +++ b/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart @@ -50,6 +50,7 @@ import 'package:tmail_ui_user/features/email/domain/model/move_action.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/email/domain/state/get_email_content_state.dart'; +import 'package:tmail_ui_user/features/email/domain/state/print_email_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_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_read_interactor.dart'; @@ -360,22 +361,26 @@ class EmailActionReactor with MessageDialogActionMixin, PopupContextMenuActionMi PresentationEmail presentationEmail, { required String ownEmailAddress, required EmailLoaded? emailLoaded, - VoidCallback? onGetEmailContentFailure, Session? session, AccountId? accountId, String? baseDownloadUrl, TransformConfiguration? transformConfiguration, }) async* { - emailLoaded ??= await _getEmailLoaded( - session, - accountId, - presentationEmail.id, - baseDownloadUrl, - transformConfiguration, - ); - - if (emailLoaded == null) { - onGetEmailContentFailure?.call(); + try { + emailLoaded ??= await _getEmailLoaded( + session, + accountId, + presentationEmail.id, + baseDownloadUrl, + transformConfiguration, + ); + + if (emailLoaded == null) { + yield Left(PrintEmailFailure()); + return; + } + } catch (e) { + yield Left(PrintEmailFailure(exception: e)); return; } @@ -848,7 +853,7 @@ class EmailActionReactor with MessageDialogActionMixin, PopupContextMenuActionMi EmailId? emailId, String? baseDownloadUrl, TransformConfiguration? transformConfiguration, - ) { + ) async { if (session == null || accountId == null || emailId == null || @@ -858,33 +863,34 @@ class EmailActionReactor with MessageDialogActionMixin, PopupContextMenuActionMi return Future.value(null); } - return _getEmailContentInteractor - .execute( - session, - accountId, - emailId, - baseDownloadUrl, - transformConfiguration, - ) - .last - .then( - (value) { - return value.fold( - (failure) => null, - (success) => success is GetEmailContentSuccess - ? EmailLoaded( - htmlContent: success.htmlEmailContent, - attachments: List.of(success.attachments ?? []), - inlineImages: List.of(success.inlineImages ?? []), - emailCurrent: success.emailCurrent, - ) - : null, - ); - }, - onError: (error) { - logError('EmailActionReactor::_getEmailLoaded(): error: $error'); + final result = await _getEmailContentInteractor.execute( + session, + accountId, + emailId, + baseDownloadUrl, + transformConfiguration, + ).last; + + return result.fold( + (failure) { + if (failure is FeatureFailure) { + throw failure.exception; + } + + return null; + }, + (success) { + if (success is! GetEmailContentSuccess) { return null; - }, - ); + } + + return EmailLoaded( + htmlContent: success.htmlEmailContent, + attachments: List.of(success.attachments ?? []), + inlineImages: List.of(success.inlineImages ?? []), + emailCurrent: success.emailCurrent, + ); + }, + ); } } \ No newline at end of file diff --git a/lib/features/email/presentation/widgets/calendar_event/event_body_content_widget.dart b/lib/features/email/presentation/widgets/calendar_event/event_body_content_widget.dart index b6ba007a9..49c1972eb 100644 --- a/lib/features/email/presentation/widgets/calendar_event/event_body_content_widget.dart +++ b/lib/features/email/presentation/widgets/calendar_event/event_body_content_widget.dart @@ -46,7 +46,8 @@ class EventBodyContentWidget extends StatelessWidget { child: Stack( children: [ if (PlatformInfo.isWeb) - Padding( + Container( + constraints: const BoxConstraints(maxHeight: EventDescriptionDetailWidgetStyles.maxHeight), padding: const EdgeInsetsDirectional.only(end: EventDescriptionDetailWidgetStyles.webContentPadding), child: LayoutBuilder(builder: (context, constraints) { return Stack( diff --git a/lib/features/email/presentation/widgets/email_subject_widget.dart b/lib/features/email/presentation/widgets/email_subject_widget.dart index 285ea10bd..52d123354 100644 --- a/lib/features/email/presentation/widgets/email_subject_widget.dart +++ b/lib/features/email/presentation/widgets/email_subject_widget.dart @@ -1,4 +1,5 @@ import 'package:core/presentation/utils/theme_utils.dart'; +import 'package:core/utils/platform_info.dart'; import 'package:flutter/material.dart'; import 'package:model/email/presentation_email.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/email_subject_styles.dart'; @@ -19,10 +20,10 @@ class EmailSubjectWidget extends StatelessWidget { padding: EmailSubjectStyles.padding, child: Text( presentationEmail.getEmailTitle(), - maxLines: EmailSubjectStyles.maxLines, style: ThemeUtils.textStyleHeadingH5(color: Colors.black).copyWith( - overflow: TextOverflow.ellipsis, + overflow: PlatformInfo.isWeb ? TextOverflow.ellipsis : null, ), + maxLines: EmailSubjectStyles.maxLines, ) ); } diff --git a/lib/features/email/presentation/widgets/mail_unsubscribed_banner.dart b/lib/features/email/presentation/widgets/mail_unsubscribed_banner.dart index 217377b05..0ddbd8251 100644 --- a/lib/features/email/presentation/widgets/mail_unsubscribed_banner.dart +++ b/lib/features/email/presentation/widgets/mail_unsubscribed_banner.dart @@ -20,7 +20,7 @@ class MailUnsubscribedBanner extends StatelessWidget { Widget build(BuildContext context) { if (presentationEmail?.isSubscribed == true && emailUnsubscribe == null) { return Padding( - padding: const EdgeInsetsDirectional.only(bottom: 16, start: 20, end: 20), + padding: const EdgeInsetsDirectional.symmetric(vertical: 16, horizontal: 20), child: Text( AppLocalizations.of(context).mailUnsubscribedMessage(presentationEmail?.firstEmailAddressInFrom ?? ''), style: ThemeUtils.defaultTextStyleInterFont.copyWith( diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index 612cd634b..59dd1bc04 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -1025,6 +1025,11 @@ class MailboxDashBoardController extends ReloadableController } void _moveToMailboxSuccess(MoveToMailboxSuccess success) { + dispatchThreadDetailUIAction(EmailMovedAction( + emailId: success.emailId, + originalMailboxId: success.currentMailboxId, + targetMailboxId: success.destinationMailboxId, + )); if (success.moveAction == MoveAction.moving && currentContext != null && currentOverlayContext != null) { appToast.showToastMessage( currentOverlayContext!, diff --git a/lib/features/thread_detail/presentation/action/thread_detail_ui_action.dart b/lib/features/thread_detail/presentation/action/thread_detail_ui_action.dart index 236cfdb4d..668a0f624 100644 --- a/lib/features/thread_detail/presentation/action/thread_detail_ui_action.dart +++ b/lib/features/thread_detail/presentation/action/thread_detail_ui_action.dart @@ -1,5 +1,6 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.dart'; class ThreadDetailUIAction extends UIAction { @@ -22,4 +23,19 @@ class UpdatedEmailKeywordsAction extends ThreadDetailUIAction { List get props => [emailId, updatedKeyword, value]; } -class UpdatedThreadDetailSettingAction extends ThreadDetailUIAction {} \ No newline at end of file +class UpdatedThreadDetailSettingAction extends ThreadDetailUIAction {} + +class EmailMovedAction extends ThreadDetailUIAction { + EmailMovedAction({ + required this.emailId, + required this.originalMailboxId, + required this.targetMailboxId, + }); + + final EmailId emailId; + final MailboxId originalMailboxId; + final MailboxId targetMailboxId; + + @override + List get props => [emailId, originalMailboxId, targetMailboxId]; +} \ No newline at end of file diff --git a/lib/features/thread_detail/presentation/extension/get_thread_detail_email_mailbox_contains.dart b/lib/features/thread_detail/presentation/extension/get_thread_detail_email_mailbox_contains.dart new file mode 100644 index 000000000..b90bdd7bb --- /dev/null +++ b/lib/features/thread_detail/presentation/extension/get_thread_detail_email_mailbox_contains.dart @@ -0,0 +1,14 @@ +import 'package:model/email/presentation_email.dart'; +import 'package:model/extensions/presentation_email_extension.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart'; + +extension GetThreadDetailEmailMailboxContains on ThreadDetailController { + PresentationMailbox? getThreadDetailEmailMailboxContains( + PresentationEmail presentationEmail, + ) { + return presentationEmail.findMailboxContain( + mailboxDashBoardController.mapMailboxById, + ); + } +} \ No newline at end of file diff --git a/lib/features/thread_detail/presentation/extension/get_thread_detail_loading_view.dart b/lib/features/thread_detail/presentation/extension/get_thread_detail_loading_view.dart index 6261063fc..9157ac036 100644 --- a/lib/features/thread_detail/presentation/extension/get_thread_detail_loading_view.dart +++ b/lib/features/thread_detail/presentation/extension/get_thread_detail_loading_view.dart @@ -6,26 +6,31 @@ extension GetThreadDetailLoadingView on ThreadDetailController { Widget getThreadDetailLoadingView({ required bool isResponsiveDesktop, required bool isLoading, + bool isExpanded = true, }) { if (!isLoading) return const SizedBox.shrink(); - return Expanded( - child: Container( - alignment: Alignment.center, - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - margin: isResponsiveDesktop - ? const EdgeInsetsDirectional.only(end: 16) - : null, - padding: const EdgeInsets.only(top: 16), - child: const SizedBox( - width: 24, - height: 24, - child: CupertinoLoadingWidget(), - ), + final child = Container( + alignment: Alignment.center, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + margin: isResponsiveDesktop + ? const EdgeInsetsDirectional.only(end: 16) + : null, + padding: const EdgeInsets.only(top: 16), + child: const SizedBox( + width: 24, + height: 24, + child: CupertinoLoadingWidget(), ), ); + + if (isExpanded) { + return Expanded(child: child); + } else { + return child; + } } } \ No newline at end of file diff --git a/lib/features/thread_detail/presentation/extension/get_thread_details_email_views.dart b/lib/features/thread_detail/presentation/extension/get_thread_details_email_views.dart index a97a55864..0d1bbf99b 100644 --- a/lib/features/thread_detail/presentation/extension/get_thread_details_email_views.dart +++ b/lib/features/thread_detail/presentation/extension/get_thread_details_email_views.dart @@ -3,7 +3,7 @@ import 'package:model/email/email_action_type.dart'; import 'package:model/extensions/presentation_email_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/email_view.dart'; import 'package:model/email/email_in_thread_status.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/get_mailbox_contain_extension.dart'; +import 'package:tmail_ui_user/features/thread_detail/presentation/extension/get_thread_detail_email_mailbox_contains.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/load_more_thread_detail_emails.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/thread_detail_on_email_action_click.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/thread_detail_open_email_address_detail_action.dart'; @@ -65,7 +65,7 @@ extension GetThreadDetailEmailViews on ThreadDetailController { emailLoaded: null, onEmailActionClick: threadDetailOnEmailActionClick, onMoreActionClick: (presentationEmail, position) => emailActionReactor.handleMoreEmailAction( - mailboxContain: mailboxDashBoardController.getMailboxContain(presentationEmail), + mailboxContain: getThreadDetailEmailMailboxContains(presentationEmail), presentationEmail: presentationEmail, position: position, responsiveUtils: responsiveUtils, diff --git a/lib/features/thread_detail/presentation/extension/handle_email_moved_action.dart b/lib/features/thread_detail/presentation/extension/handle_email_moved_action.dart new file mode 100644 index 000000000..0efee5fbd --- /dev/null +++ b/lib/features/thread_detail/presentation/extension/handle_email_moved_action.dart @@ -0,0 +1,11 @@ +import 'package:tmail_ui_user/features/thread_detail/presentation/action/thread_detail_ui_action.dart'; +import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart'; + +extension HandleEmailMovedAction on ThreadDetailController { + void handleEmailMovedAction(EmailMovedAction action) { + if (emailIdsPresentation[action.emailId] == null) return; + + emailIdsPresentation[action.emailId] = emailIdsPresentation[action.emailId] + ?.copyWith(mailboxIds: {action.targetMailboxId: true}); + } +} \ No newline at end of file diff --git a/lib/features/thread_detail/presentation/extension/handle_get_emails_by_ids_success.dart b/lib/features/thread_detail/presentation/extension/handle_get_emails_by_ids_success.dart index a46b6a752..29c33d6ad 100644 --- a/lib/features/thread_detail/presentation/extension/handle_get_emails_by_ids_success.dart +++ b/lib/features/thread_detail/presentation/extension/handle_get_emails_by_ids_success.dart @@ -51,6 +51,7 @@ extension HandleGetEmailsByIdsSuccess on ThreadDetailController { return; } + if (scrollController?.hasClients == false) return; final currentScrollPosition = scrollController?.position.pixels; final maxScrollExtent = scrollController?.position.maxScrollExtent; final currentBottomScrollPosition = currentScrollPosition != null diff --git a/lib/features/thread_detail/presentation/extension/thread_detail_on_email_action_click.dart b/lib/features/thread_detail/presentation/extension/thread_detail_on_email_action_click.dart index d77a93720..36256974d 100644 --- a/lib/features/thread_detail/presentation/extension/thread_detail_on_email_action_click.dart +++ b/lib/features/thread_detail/presentation/extension/thread_detail_on_email_action_click.dart @@ -6,12 +6,12 @@ import 'package:model/email/mark_star_action.dart'; import 'package:model/email/presentation_email.dart'; import 'package:model/email/read_actions.dart'; import 'package:model/extensions/email_extension.dart'; +import 'package:model/extensions/presentation_email_extension.dart'; import 'package:model/extensions/session_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; extension ThreadDetailOnEmailActionClick on ThreadDetailController { @@ -129,7 +129,9 @@ extension ThreadDetailOnEmailActionClick on ThreadDetailController { accountId!, presentationEmail, mapMailbox: mailboxDashBoardController.mapMailboxById, - selectedMailbox: mailboxDashBoardController.selectedMailbox.value, + selectedMailbox: presentationEmail.findMailboxContain( + mailboxDashBoardController.mapMailboxById, + ), isSearchEmailRunning: mailboxDashBoardController.searchController.isSearchEmailRunning, ); @@ -263,7 +265,6 @@ extension ThreadDetailOnEmailActionClick on ThreadDetailController { accountId: accountId, baseDownloadUrl: session?.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl), transformConfiguration: TransformConfiguration.forPreviewEmailOnWeb(), - onGetEmailContentFailure: _showUnknownErrorToast, )); } @@ -367,13 +368,4 @@ extension ThreadDetailOnEmailActionClick on ThreadDetailController { emailLoaded: null, ); } - - _showUnknownErrorToast() { - if (currentOverlayContext != null && currentContext != null) { - appToast.showToastErrorMessage( - currentOverlayContext!, - AppLocalizations.of(currentContext!).unknownError, - ); - } - } } \ No newline at end of file diff --git a/lib/features/thread_detail/presentation/thread_detail_controller.dart b/lib/features/thread_detail/presentation/thread_detail_controller.dart index db41e97d6..817eb2dc5 100644 --- a/lib/features/thread_detail/presentation/thread_detail_controller.dart +++ b/lib/features/thread_detail/presentation/thread_detail_controller.dart @@ -18,6 +18,7 @@ import 'package:model/extensions/keyword_identifier_extension.dart'; import 'package:model/extensions/session_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/email/domain/state/print_email_state.dart'; import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; import 'package:tmail_ui_user/features/email/presentation/model/email_loaded.dart'; import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; @@ -41,6 +42,7 @@ import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_emails_ import 'package:tmail_ui_user/features/thread_detail/presentation/action/thread_detail_ui_action.dart'; import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_detail_status_interactor.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/close_thread_detail_action.dart'; +import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_email_moved_action.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_emails_by_ids_success.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_thread_by_id_failure.dart'; @@ -48,6 +50,7 @@ import 'package:tmail_ui_user/features/thread_detail/presentation/extension/hand import 'package:tmail_ui_user/features/thread_detail/presentation/extension/initialize_thread_detail_emails.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/refresh_thread_detail_on_setting_changed.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/model/thread_detail_setting_status.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_collapsed_email_download_states.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/mark_collapsed_email_star_success.dart'; @@ -186,10 +189,13 @@ class ThreadDetailController extends BaseController { } } else if (action is UpdatedThreadDetailSettingAction) { consumeState(_getThreadDetailStatusInteractor.execute()); - mailboxDashBoardController.dispatchThreadDetailUIAction( - ThreadDetailUIAction(), - ); + } else if (action is EmailMovedAction) { + handleEmailMovedAction(action); } + // Reset [threadDetailUIAction] to original value + mailboxDashBoardController.dispatchThreadDetailUIAction( + ThreadDetailUIAction(), + ); }); ever(mailboxDashBoardController.emailUIAction, (action) { if (action is RefreshThreadDetailAction) { @@ -255,6 +261,15 @@ class ThreadDetailController extends BaseController { threadDetailSettingStatus = ThreadDetailSettingStatus.enabled; refreshThreadDetailOnSettingChanged(); } + if (failure is PrintEmailFailure) { + if (currentOverlayContext != null && currentContext != null) { + appToast.showToastErrorMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).unknownError, + ); + } + return; + } super.handleFailureViewState(failure); } } \ No newline at end of file diff --git a/lib/features/thread_detail/presentation/thread_detail_view.dart b/lib/features/thread_detail/presentation/thread_detail_view.dart index b96a5f0f4..6ccb317ec 100644 --- a/lib/features/thread_detail/presentation/thread_detail_view.dart +++ b/lib/features/thread_detail/presentation/thread_detail_view.dart @@ -5,15 +5,16 @@ import 'package:core/presentation/state/success.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:model/email/email_in_thread_status.dart'; import 'package:model/extensions/session_extension.dart'; import 'package:model/email/email_action_type.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; import 'package:tmail_ui_user/features/email/presentation/email_view.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_bottom_bar_widget.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/get_mailbox_contain_extension.dart'; import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/close_thread_detail_action.dart'; +import 'package:tmail_ui_user/features/thread_detail/presentation/extension/get_thread_detail_email_mailbox_contains.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/get_thread_detail_loading_view.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/get_thread_details_email_views.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/thread_detail_on_email_action_click.dart'; @@ -43,7 +44,7 @@ class ThreadDetailView extends GetWidget { mailboxContain: _getMailboxContain(), onEmailActionClick: controller.threadDetailOnEmailActionClick, onMoreActionClick: (presentationEmail, position) => controller.emailActionReactor.handleMoreEmailAction( - mailboxContain: controller.mailboxDashBoardController.getMailboxContain(presentationEmail), + mailboxContain: controller.getThreadDetailEmailMailboxContains(presentationEmail), presentationEmail: presentationEmail, position: position, responsiveUtils: controller.responsiveUtils, @@ -167,11 +168,12 @@ class ThreadDetailView extends GetWidget { return controller.getThreadDetailLoadingView( isResponsiveDesktop: false, isLoading: true, + isExpanded: false, ); } if (controller.emailIdsPresentation.length == 1 && - controller.emailIdsPresentation.values.firstOrNull != null) { + controller.emailIdsPresentation.values.firstOrNull?.emailInThreadStatus == EmailInThreadStatus.expanded) { final emailId = controller.emailIdsPresentation.values.firstOrNull!.id; return EmailView( 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 0c6678bf2..c929aa83b 100644 --- a/test/features/email/presentation/controller/single_email_controller_test.dart +++ b/test/features/email/presentation/controller/single_email_controller_test.dart @@ -39,7 +39,6 @@ import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_i 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_read_interactor.dart'; 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/parse_calendar_event_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart'; @@ -81,7 +80,6 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), - MockSpec(), MockSpec(), MockSpec(), MockSpec(), @@ -125,7 +123,6 @@ void main() { final downloadAttachmentsInteractor = MockDownloadAttachmentsInteractor(); final deviceManager = MockDeviceManager(); final exportAttachmentInteractor = MockExportAttachmentInteractor(); - final moveToMailboxInteractor = MockMoveToMailboxInteractor(); final markAsStarEmailInteractor = MockMarkAsStarEmailInteractor(); final downloadAttachmentForWebInteractor = MockDownloadAttachmentForWebInteractor(); @@ -204,7 +201,6 @@ void main() { downloadAttachmentsInteractor, deviceManager, exportAttachmentInteractor, - moveToMailboxInteractor, markAsStarEmailInteractor, downloadAttachmentForWebInteractor, getAllIdentitiesInteractor,