diff --git a/lib/features/email/presentation/action/email_ui_action.dart b/lib/features/email/presentation/action/email_ui_action.dart index 79e4a0916..6904ef2fd 100644 --- a/lib/features/email/presentation/action/email_ui_action.dart +++ b/lib/features/email/presentation/action/email_ui_action.dart @@ -3,6 +3,7 @@ import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:model/email/email_action_type.dart'; import 'package:model/email/presentation_email.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.dart'; +import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; class EmailUIAction extends UIAction { static final idle = EmailUIAction(); @@ -48,4 +49,13 @@ class PerformEmailActionInThreadDetailAction extends EmailUIAction { emailActionType, presentationEmail, ]; +} + +class RefreshThreadDetailAction extends EmailUIAction { + RefreshThreadDetailAction(this.emailChangeResponse); + + final EmailChangeResponse emailChangeResponse; + + @override + List get props => [emailChangeResponse]; } \ No newline at end of file diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index ef157866f..c7bf1a771 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -437,9 +437,7 @@ class EmailView extends GetWidget { ), child: LayoutBuilder(builder: (context, constraints) { return HtmlContentViewer( - key: PlatformInfo.isIntegrationTesting - ? controller.htmlContentViewKey - : null, + key: controller.htmlContentViewKey, contentHtml: allEmailContents, initialWidth: constraints.maxWidth, direction: AppUtils.getCurrentDirection(context), @@ -478,9 +476,7 @@ class EmailView extends GetWidget { ), child: LayoutBuilder(builder: (context, constraints) { return HtmlContentViewer( - key: PlatformInfo.isIntegrationTesting - ? controller.htmlContentViewKey - : null, + key: controller.htmlContentViewKey, contentHtml: allEmailContents, initialWidth: constraints.maxWidth, direction: AppUtils.getCurrentDirection(context), diff --git a/lib/features/thread/data/repository/thread_repository_impl.dart b/lib/features/thread/data/repository/thread_repository_impl.dart index e515fb6ff..d142a798f 100644 --- a/lib/features/thread/data/repository/thread_repository_impl.dart +++ b/lib/features/thread/data/repository/thread_repository_impl.dart @@ -273,7 +273,7 @@ class ThreadRepositoryImpl extends ThreadRepository { } ) async* { log('ThreadRepositoryImpl::refreshChanges(): $currentState'); - await _synchronizeCacheWithChanges( + final emailChangeResponse = await _synchronizeCacheWithChanges( session, accountId, currentState, @@ -305,9 +305,9 @@ class ThreadRepositoryImpl extends ThreadRepository { propertiesCreated: propertiesCreated, ); - yield networkEmailResponse; + yield networkEmailResponse.copyWith(emailChangeResponse: emailChangeResponse); } else { - yield newEmailResponse; + yield newEmailResponse.copyWith(emailChangeResponse: emailChangeResponse); } } @@ -396,7 +396,7 @@ class ThreadRepositoryImpl extends ThreadRepository { return listEmailIdDeleted; } - Future _synchronizeCacheWithChanges( + Future _synchronizeCacheWithChanges( Session session, AccountId accountId, jmap.State currentState, @@ -452,6 +452,8 @@ class ThreadRepositoryImpl extends ThreadRepository { await _updateState(accountId, session.username, emailChangeResponse.newStateEmail!); } } + + return emailChangeResponse; } @override diff --git a/lib/features/thread/domain/model/email_response.dart b/lib/features/thread/domain/model/email_response.dart index 690c602d3..bcce85b43 100644 --- a/lib/features/thread/domain/model/email_response.dart +++ b/lib/features/thread/domain/model/email_response.dart @@ -2,16 +2,19 @@ import 'package:equatable/equatable.dart'; import 'package:jmap_dart_client/jmap/core/state.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; class EmailsResponse with EquatableMixin { final List? emailList; final List? notFoundEmailIds; final State? state; + final EmailChangeResponse? emailChangeResponse; const EmailsResponse({ this.emailList, this.notFoundEmailIds, - this.state + this.state, + this.emailChangeResponse, }); bool hasEmails() => emailList != null && emailList!.isNotEmpty; @@ -21,5 +24,24 @@ class EmailsResponse with EquatableMixin { bool get existNotFoundEmails => notFoundEmailIds?.isNotEmpty == true; @override - List get props => [emailList, notFoundEmailIds, state]; + List get props => [ + emailList, + notFoundEmailIds, + state, + emailChangeResponse, + ]; + + EmailsResponse copyWith({ + List? emailList, + List? notFoundEmailIds, + State? state, + EmailChangeResponse? emailChangeResponse, + }) { + return EmailsResponse( + emailList: emailList ?? this.emailList, + notFoundEmailIds: notFoundEmailIds ?? this.notFoundEmailIds, + state: state ?? this.state, + emailChangeResponse: emailChangeResponse ?? this.emailChangeResponse, + ); + } } \ No newline at end of file diff --git a/lib/features/thread/domain/state/refresh_changes_all_email_state.dart b/lib/features/thread/domain/state/refresh_changes_all_email_state.dart index f281d436e..9933291da 100644 --- a/lib/features/thread/domain/state/refresh_changes_all_email_state.dart +++ b/lib/features/thread/domain/state/refresh_changes_all_email_state.dart @@ -3,6 +3,7 @@ import 'package:core/presentation/state/success.dart'; import 'package:jmap_dart_client/jmap/core/state.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/presentation_email.dart'; +import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; class RefreshChangesAllEmailLoading extends LoadingState {} @@ -10,18 +11,21 @@ class RefreshChangesAllEmailSuccess extends UIState { final List emailList; final State? currentEmailState; final MailboxId? currentMailboxId; + final EmailChangeResponse? emailChangeResponse; RefreshChangesAllEmailSuccess({ required this.emailList, this.currentEmailState, - this.currentMailboxId + this.currentMailboxId, + this.emailChangeResponse, }); @override List get props => [ emailList, currentEmailState, - currentMailboxId + currentMailboxId, + emailChangeResponse, ]; } diff --git a/lib/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart b/lib/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart index 032c56280..afa4200f3 100644 --- a/lib/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart +++ b/lib/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart @@ -59,6 +59,8 @@ class RefreshChangesEmailsInMailboxInteractor { return Right(RefreshChangesAllEmailSuccess( emailList: presentationEmailList, currentEmailState: emailResponse.state, - currentMailboxId: currentMailboxId)); + currentMailboxId: currentMailboxId, + emailChangeResponse: emailResponse.emailChangeResponse, + )); } } \ No newline at end of file diff --git a/lib/features/thread/presentation/extensions/refresh_thread_detail_extension.dart b/lib/features/thread/presentation/extensions/refresh_thread_detail_extension.dart new file mode 100644 index 000000000..5511b095d --- /dev/null +++ b/lib/features/thread/presentation/extensions/refresh_thread_detail_extension.dart @@ -0,0 +1,12 @@ +import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; +import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; +import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; + +extension RefreshThreadDetailExtension on ThreadController { + void refreshThreadDetail(EmailChangeResponse? emailChangeResponse) { + if (emailChangeResponse == null) return; + + mailboxDashBoardController + .dispatchEmailUIAction(RefreshThreadDetailAction(emailChangeResponse)); + } +} \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 43530c534..139a159d1 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -61,6 +61,7 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_changes_em import 'package:tmail_ui_user/features/thread/domain/usecases/search_email_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/search_more_email_interactor.dart'; import 'package:tmail_ui_user/features/thread/presentation/extensions/list_presentation_email_extensions.dart'; +import 'package:tmail_ui_user/features/thread/presentation/extensions/refresh_thread_detail_extension.dart'; import 'package:tmail_ui_user/features/thread/presentation/mixin/email_action_controller.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/loading_more_status.dart'; @@ -697,7 +698,7 @@ class ThreadController extends BaseController with EmailActionController { } Future> _refreshChangeListEmailCache() async { - return _refreshChangesEmailsInMailboxInteractor.execute( + final refreshState = await _refreshChangesEmailsInMailboxInteractor.execute( _session!, _accountId!, mailboxDashBoardController.currentEmailState!, @@ -716,6 +717,17 @@ class ThreadController extends BaseController with EmailActionController { mailboxId: selectedMailboxId, ), ).last; + + refreshState.fold( + (failure) {}, + (success) { + if (success is RefreshChangesAllEmailSuccess) { + refreshThreadDetail(success.emailChangeResponse); + } + }, + ); + + return refreshState; } Future _refreshChangeListEmail() async { diff --git a/lib/features/thread_detail/data/repository/thread_detail_repository_impl.dart b/lib/features/thread_detail/data/repository/thread_detail_repository_impl.dart index 5f1c0b3e7..1ec6957f0 100644 --- a/lib/features/thread_detail/data/repository/thread_detail_repository_impl.dart +++ b/lib/features/thread_detail/data/repository/thread_detail_repository_impl.dart @@ -25,8 +25,9 @@ class ThreadDetailRepositoryImpl implements ThreadDetailRepository { Session session, AccountId accountId, MailboxId sentMailboxId, - String ownEmailAddress, - ) async { + String ownEmailAddress, { + required EmailId? selectedEmailId, + }) async { final originalEmailIds = await threadDetailDataSource[DataSourceType.network]! .getThreadById(threadId, accountId); @@ -43,6 +44,7 @@ class ThreadDetailRepositoryImpl implements ThreadDetailRepository { emailIds, sentMailboxId, ownEmailAddress, + selectedEmailId: selectedEmailId, )) ); @@ -60,8 +62,9 @@ class ThreadDetailRepositoryImpl implements ThreadDetailRepository { AccountId accountId, List emailIds, MailboxId sentMailboxId, - String ownEmailAddress, - ) async { + String ownEmailAddress, { + required EmailId? selectedEmailId, + }) async { int retry = 3; while (retry > 0) { try { @@ -85,6 +88,7 @@ class ThreadDetailRepositoryImpl implements ThreadDetailRepository { email, sentMailboxId, ownEmailAddress, + selectedEmailId: selectedEmailId, )) .toList(); } catch (e) { @@ -99,12 +103,14 @@ class ThreadDetailRepositoryImpl implements ThreadDetailRepository { bool checkEmailValidForThreadDetail( Email email, MailboxId sentMailboxId, - String ownEmailAddress, - ) { + String ownEmailAddress, { + required EmailId? selectedEmailId + }) { return email.id != null && ( !email.inSentMailbox(sentMailboxId) || !email.fromMe(ownEmailAddress) || !email.recipientsHasMe(ownEmailAddress) + || email.id == selectedEmailId ); } diff --git a/lib/features/thread_detail/domain/repository/thread_detail_repository.dart b/lib/features/thread_detail/domain/repository/thread_detail_repository.dart index 08afb21ec..33bc4bd6a 100644 --- a/lib/features/thread_detail/domain/repository/thread_detail_repository.dart +++ b/lib/features/thread_detail/domain/repository/thread_detail_repository.dart @@ -10,8 +10,9 @@ abstract class ThreadDetailRepository { Session session, AccountId accountId, MailboxId sentMailboxId, - String ownEmailAddress, - ); + String ownEmailAddress, { + required EmailId? selectedEmailId, + }); Future> getEmailsByIds( Session session, diff --git a/lib/features/thread_detail/domain/state/get_emails_by_ids_state.dart b/lib/features/thread_detail/domain/state/get_emails_by_ids_state.dart index 4a30d1152..6cbac2748 100644 --- a/lib/features/thread_detail/domain/state/get_emails_by_ids_state.dart +++ b/lib/features/thread_detail/domain/state/get_emails_by_ids_state.dart @@ -12,14 +12,27 @@ class GettingEmailsByIds extends LoadingState { } class GetEmailsByIdsSuccess extends UIState { - GetEmailsByIdsSuccess(this.presentationEmails); + GetEmailsByIdsSuccess( + this.presentationEmails, { + this.updateCurrentThreadDetail = false, + }); final List presentationEmails; + final bool updateCurrentThreadDetail; @override - List get props => [presentationEmails]; + List get props => [presentationEmails, updateCurrentThreadDetail]; } class GetEmailsByIdsFailure extends FeatureFailure { - GetEmailsByIdsFailure({super.exception, super.onRetry}); + GetEmailsByIdsFailure({ + super.exception, + super.onRetry, + required this.updateCurrentThreadDetail, + }); + + final bool updateCurrentThreadDetail; + + @override + List get props => [...super.props, updateCurrentThreadDetail]; } \ No newline at end of file diff --git a/lib/features/thread_detail/domain/state/get_thread_by_id_state.dart b/lib/features/thread_detail/domain/state/get_thread_by_id_state.dart index 8ea94e3df..857196f0a 100644 --- a/lib/features/thread_detail/domain/state/get_thread_by_id_state.dart +++ b/lib/features/thread_detail/domain/state/get_thread_by_id_state.dart @@ -2,17 +2,34 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -class GettingThreadById extends LoadingState {} +class GettingThreadById extends LoadingState { + final bool updateCurrentThreadDetail; + + GettingThreadById({this.updateCurrentThreadDetail = false}); + + @override + List get props => [updateCurrentThreadDetail]; +} class GetThreadByIdSuccess extends UIState { final List emailIds; + final bool updateCurrentThreadDetail; - GetThreadByIdSuccess(this.emailIds); + GetThreadByIdSuccess(this.emailIds, {this.updateCurrentThreadDetail = false}); @override - List get props => [emailIds]; + List get props => [emailIds, updateCurrentThreadDetail]; } class GetThreadByIdFailure extends FeatureFailure { - GetThreadByIdFailure({super.exception, super.onRetry}); + GetThreadByIdFailure({ + super.exception, + super.onRetry, + required this.updateCurrentThreadDetail, + }); + + final bool updateCurrentThreadDetail; + + @override + List get props => [...super.props, updateCurrentThreadDetail]; } \ No newline at end of file diff --git a/lib/features/thread_detail/domain/usecases/get_emails_by_ids_interactor.dart b/lib/features/thread_detail/domain/usecases/get_emails_by_ids_interactor.dart index 8a0200832..d2be0863c 100644 --- a/lib/features/thread_detail/domain/usecases/get_emails_by_ids_interactor.dart +++ b/lib/features/thread_detail/domain/usecases/get_emails_by_ids_interactor.dart @@ -23,6 +23,7 @@ class GetEmailsByIdsInteractor { List emailIds, { Properties? properties, int? loadMoreIndex, + bool updateCurrentThreadDetail = false, }) async* { try { if (emailIds.length > 1 && ( @@ -41,12 +42,14 @@ class GetEmailsByIdsInteractor { ); yield Right(GetEmailsByIdsSuccess( result.map((e) => e.toPresentationEmail()).toList(), + updateCurrentThreadDetail: updateCurrentThreadDetail, )); } catch (e) { logError('GetEmailsByIdsInteractor::execute(): Exception: $e'); yield Left(GetEmailsByIdsFailure( exception: e, onRetry: execute(session, accountId, emailIds, properties: properties), + updateCurrentThreadDetail: updateCurrentThreadDetail, )); } } diff --git a/lib/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart b/lib/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart index 480a748f5..61e69080a 100644 --- a/lib/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart +++ b/lib/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart @@ -19,19 +19,27 @@ class GetThreadByIdInteractor { Session session, AccountId accountId, MailboxId sentMailboxId, - String ownEmailAddress, - ) async* { + String ownEmailAddress, { + required EmailId? selectedEmailId, + bool updateCurrentThreadDetail = false, + }) async* { try { - yield Right(GettingThreadById()); + yield Right(GettingThreadById( + updateCurrentThreadDetail: updateCurrentThreadDetail, + )); final result = await _threadDetailRepository.getThreadById( threadId, session, accountId, sentMailboxId, ownEmailAddress, + selectedEmailId: selectedEmailId, ); - yield Right(GetThreadByIdSuccess(result)); + yield Right(GetThreadByIdSuccess( + result, + updateCurrentThreadDetail: updateCurrentThreadDetail, + )); } catch (e) { logError('GetEmailIdsByThreadIdInteractor::execute(): Exception: $e'); yield Left(GetThreadByIdFailure( @@ -42,7 +50,9 @@ class GetThreadByIdInteractor { accountId, sentMailboxId, ownEmailAddress, + selectedEmailId: selectedEmailId, ), + updateCurrentThreadDetail: updateCurrentThreadDetail, )); } } 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 bfa01bd23..6261063fc 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 @@ -1,16 +1,12 @@ import 'package:core/presentation/views/loading/cupertino_loading_widget.dart'; import 'package:flutter/material.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/thread_detail_controller.dart'; extension GetThreadDetailLoadingView on ThreadDetailController { Widget getThreadDetailLoadingView({ required bool isResponsiveDesktop, + required bool isLoading, }) { - final isLoading = viewState.value.fold( - (failure) => false, - (success) => success is GettingThreadById - ); if (!isLoading) return const SizedBox.shrink(); return Expanded( diff --git a/lib/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success.dart b/lib/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success.dart index 0a0c23dcb..fcbfc44ec 100644 --- a/lib/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success.dart +++ b/lib/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success.dart @@ -1,3 +1,4 @@ +import 'package:jmap_dart_client/jmap/mail/email/email.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/thread_detail_controller.dart'; @@ -5,10 +6,27 @@ extension HandleGetEmailIdsByThreadIdSuccess on ThreadDetailController { void handleGetEmailIdsByThreadIdSuccess( GetThreadByIdSuccess success, ) { + final newEmailsInThreadDetail = []; if (success.emailIds.isNotEmpty) { - emailIdsPresentation.value = Map.fromEntries(success.emailIds.map( - (emailId) => MapEntry(emailId, null), - )); + if (success.updateCurrentThreadDetail) { + newEmailsInThreadDetail.addAll(success + .emailIds + .where( + (emailId) => !emailIdsPresentation.keys.contains(emailId), + ) + ); + emailIdsPresentation + ..removeWhere( + (key, value) => !success.emailIds.contains(key), + ) + ..addAll(Map.fromEntries( + newEmailsInThreadDetail.map((emailId) => MapEntry(emailId, null)), + )); + } else { + emailIdsPresentation.value = Map.fromEntries(success.emailIds.map( + (emailId) => MapEntry(emailId, null), + )); + } } else if (mailboxDashBoardController.selectedEmail.value?.id != null) { emailIdsPresentation.value = { mailboxDashBoardController.selectedEmail.value!.id!: null, 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 5064119d4..a46b6a752 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 @@ -18,6 +18,15 @@ extension HandleGetEmailsByIdsSuccess on ThreadDetailController { for (var presentationEmail in success.presentationEmails) { if (presentationEmail.id == null) continue; + + if (success.updateCurrentThreadDetail) { + emailIdsPresentation[presentationEmail.id!] = presentationEmail.copyWith( + emailInThreadStatus: emailIdsPresentation[presentationEmail.id!] + ?.emailInThreadStatus ?? EmailInThreadStatus.collapsed, + ); + continue; + } + if (presentationEmail.id == selectedEmailId) { EmailBindings(currentEmailId: presentationEmail.id).dependencies(); currentExpandedEmailId.value = presentationEmail.id; diff --git a/lib/features/thread_detail/presentation/extension/handle_get_thread_by_id_failure.dart b/lib/features/thread_detail/presentation/extension/handle_get_thread_by_id_failure.dart index e1247b25d..4946036c8 100644 --- a/lib/features/thread_detail/presentation/extension/handle_get_thread_by_id_failure.dart +++ b/lib/features/thread_detail/presentation/extension/handle_get_thread_by_id_failure.dart @@ -5,6 +5,8 @@ import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_ extension HandleGetThreadByIdFailure on ThreadDetailController { void handleGetThreadByIdFailure(GetThreadByIdFailure failure) { + if (failure.updateCurrentThreadDetail) return; + final selectedEmail = mailboxDashBoardController.selectedEmail.value; if (selectedEmail != null) { consumeState(Stream.value(Right(GetEmailsByIdsSuccess([selectedEmail])))); diff --git a/lib/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action.dart b/lib/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action.dart new file mode 100644 index 000000000..ead2226db --- /dev/null +++ b/lib/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action.dart @@ -0,0 +1,62 @@ +import 'package:dartz/dartz.dart'; +import 'package:model/extensions/list_email_extension.dart'; +import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.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/domain/usecases/get_thread_by_id_interactor.dart'; +import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart'; + +extension HandleRefreshThreadDetailAction on ThreadDetailController { + void handleRefreshThreadDetailAction( + RefreshThreadDetailAction action, + GetThreadByIdInteractor getThreadByIdInteractor, + ) { + if (!isThreadDetailEnabled) { + final currentEmailId = mailboxDashBoardController.selectedEmail.value?.id; + if (currentEmailId == null) return; + + final updatedEmailIds = action + .emailChangeResponse + .updated + ?.listEmailIds ?? []; + + if (updatedEmailIds.contains(currentEmailId)) { + consumeState(Stream.value(Right(GetThreadByIdSuccess( + [currentEmailId], + updateCurrentThreadDetail: true, + )))); + } + + return; + } + + final currentThreadId = mailboxDashBoardController.selectedEmail.value?.threadId; + final threadIdsEmailCreated = action + .emailChangeResponse + .created + ?.listThreadIds ?? []; + final threadIdsEmailUpdated = action + .emailChangeResponse + .updated + ?.listThreadIds ?? []; + final updatedThreadIds = threadIdsEmailCreated + threadIdsEmailUpdated; + final destroyedEmailIds = action.emailChangeResponse.destroyed ?? []; + + if (session != null && + accountId != null && + sentMailboxId != null && + ownEmailAddress != null && + currentThreadId != null && ( + updatedThreadIds.contains(currentThreadId) || + emailIdsPresentation.keys.any(destroyedEmailIds.contains))) { + consumeState(getThreadByIdInteractor.execute( + currentThreadId, + session!, + accountId!, + sentMailboxId!, + ownEmailAddress!, + updateCurrentThreadDetail: true, + selectedEmailId: mailboxDashBoardController.selectedEmail.value?.id, + )); + } + } +} \ No newline at end of file diff --git a/lib/features/thread_detail/presentation/extension/initialize_thread_detail_emails.dart b/lib/features/thread_detail/presentation/extension/initialize_thread_detail_emails.dart index 6a338071b..7b2dda054 100644 --- a/lib/features/thread_detail/presentation/extension/initialize_thread_detail_emails.dart +++ b/lib/features/thread_detail/presentation/extension/initialize_thread_detail_emails.dart @@ -1,27 +1,47 @@ import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.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/thread_detail_controller.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/utils/thread_detail_presentation_utils.dart'; extension InitializeThreadDetailEmails on ThreadDetailController { - void initializeThreadDetailEmails() { + void initializeThreadDetailEmails(GetThreadByIdSuccess success) { final threadDetailEnabled = isThreadDetailEnabled; final selectedEmail = mailboxDashBoardController.selectedEmail.value; - if (!threadDetailEnabled && selectedEmail != null) { + if (!threadDetailEnabled && + selectedEmail != null && + !success.updateCurrentThreadDetail) { consumeState(Stream.value(Right(GetEmailsByIdsSuccess([selectedEmail])))); return; } - final emailIdsToLoadMetaData = ThreadDetailPresentationUtils.getFirstLoadEmailIds( - emailIdsPresentation.keys.toList(), - selectedEmailId: mailboxDashBoardController.selectedEmail.value?.id, - ); + final existingEmailIds = emailIdsPresentation.keys.toList(); + final selectedEmailId = mailboxDashBoardController.selectedEmail.value?.id; + + List emailIdsToLoadMetaData = []; + if (success.updateCurrentThreadDetail) { + final nonNullEmailIds = emailIdsPresentation.entries + .where((entry) => entry.value != null) + .map((entry) => entry.key) + .toList(); + final newEmailIds = success.emailIds.where( + (emailId) => !existingEmailIds.contains(emailId), + ); + emailIdsToLoadMetaData = [...nonNullEmailIds, ...newEmailIds]; + } else { + emailIdsToLoadMetaData = ThreadDetailPresentationUtils.getFirstLoadEmailIds( + existingEmailIds, + selectedEmailId: selectedEmailId, + ); + } if (accountId == null || session == null) { consumeState(Stream.value(Left(GetEmailsByIdsFailure( exception: NotFoundSessionException(), + updateCurrentThreadDetail: false, )))); return; } @@ -33,6 +53,7 @@ extension InitializeThreadDetailEmails on ThreadDetailController { session!, accountId!, ).union(additionalProperties), + updateCurrentThreadDetail: success.updateCurrentThreadDetail, )); } } \ No newline at end of file diff --git a/lib/features/thread_detail/presentation/extension/load_more_thread_detail_emails.dart b/lib/features/thread_detail/presentation/extension/load_more_thread_detail_emails.dart index 914790cfa..9f0d13d05 100644 --- a/lib/features/thread_detail/presentation/extension/load_more_thread_detail_emails.dart +++ b/lib/features/thread_detail/presentation/extension/load_more_thread_detail_emails.dart @@ -15,6 +15,7 @@ extension LoadMoreThreadDetailEmails on ThreadDetailController { if (accountId == null || session == null) { consumeState(Stream.value(Left(GetEmailsByIdsFailure( exception: NotFoundSessionException(), + updateCurrentThreadDetail: false, )))); return; } diff --git a/lib/features/thread_detail/presentation/thread_detail_controller.dart b/lib/features/thread_detail/presentation/thread_detail_controller.dart index 0ccfcedf2..db41e97d6 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/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'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; @@ -43,6 +44,7 @@ import 'package:tmail_ui_user/features/thread_detail/presentation/extension/clos 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'; +import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action.dart'; 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'; @@ -164,6 +166,7 @@ class ThreadDetailController extends BaseController { accountId!, sentMailboxId!, ownEmailAddress!, + selectedEmailId: presentationEmail.id, )); } }); @@ -188,6 +191,15 @@ class ThreadDetailController extends BaseController { ); } }); + ever(mailboxDashBoardController.emailUIAction, (action) { + if (action is RefreshThreadDetailAction) { + mailboxDashBoardController.dispatchEmailUIAction(EmailUIAction()); + handleRefreshThreadDetailAction( + action, + _getEmailIdsByThreadIdInteractor, + ); + } + }); } void reset() { @@ -202,7 +214,7 @@ class ThreadDetailController extends BaseController { void handleSuccessViewState(success) { if (success is GetThreadByIdSuccess) { handleGetEmailIdsByThreadIdSuccess(success); - initializeThreadDetailEmails(); + initializeThreadDetailEmails(success); } else if (success is GetEmailsByIdsSuccess) { handleGetEmailsByIdsSuccess(success); } else if (success is MarkAsEmailReadSuccess) { @@ -231,6 +243,7 @@ class ThreadDetailController extends BaseController { handleGetThreadByIdFailure(failure); } if (failure is GetEmailsByIdsFailure) { + if (failure.updateCurrentThreadDetail) return; showRetryToast(failure); return; } diff --git a/lib/features/thread_detail/presentation/thread_detail_view.dart b/lib/features/thread_detail/presentation/thread_detail_view.dart index 87f25e196..b96a5f0f4 100644 --- a/lib/features/thread_detail/presentation/thread_detail_view.dart +++ b/lib/features/thread_detail/presentation/thread_detail_view.dart @@ -1,5 +1,7 @@ import 'package:collection/collection.dart'; import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -27,8 +29,7 @@ class ThreadDetailView extends GetWidget { child: Column( children: [ Obx(() { - final currentViewState = controller.viewState.value.fold(id, id); - if (currentViewState is GettingThreadById) { + if (showLoadingView(controller.viewState.value)) { return const SizedBox.shrink(); } @@ -68,40 +69,35 @@ class ThreadDetailView extends GetWidget { }), Obx(() => controller.getThreadDetailLoadingView( isResponsiveDesktop: controller.responsiveUtils.isDesktop(context), + isLoading: showLoadingView(controller.viewState.value), )), Obx(() { - return controller.viewState.value.fold( - (failure) => const SizedBox.shrink(), - (success) { - if (success is GettingThreadById) { - return const SizedBox.shrink(); - } + if (showLoadingView(controller.viewState.value)) { + return const SizedBox.shrink(); + } - return Expanded( - child: Padding( - padding: _padding(context), - child: Stack( - children: [ - const Positioned.fill( - child: ColoredBox(color: Colors.white), - ), - SingleChildScrollView( - controller: controller.scrollController, - child: Column( - mainAxisSize: MainAxisSize.min, - children: controller.getThreadDetailEmailViews() - ), - ), - ], + return Expanded( + child: Padding( + padding: _padding(context), + child: Stack( + children: [ + const Positioned.fill( + child: ColoredBox(color: Colors.white), ), - ), - ); - }, + SingleChildScrollView( + controller: controller.scrollController, + child: Column( + mainAxisSize: MainAxisSize.min, + children: controller.getThreadDetailEmailViews() + ), + ), + ], + ), + ), ); }), Obx(() { - final currentViewState = controller.viewState.value.fold(id, id); - if (currentViewState is GettingThreadById) { + if (showLoadingView(controller.viewState.value)) { return const SizedBox.shrink(); } @@ -166,10 +162,12 @@ class ThreadDetailView extends GetWidget { )); return Obx(() { - final currentViewState = controller.viewState.value.fold(id, id); - if (currentViewState is GettingThreadById && + if (showLoadingView(controller.viewState.value) && controller.responsiveUtils.isTabletLarge(context)) { - return controller.getThreadDetailLoadingView(isResponsiveDesktop: false); + return controller.getThreadDetailLoadingView( + isResponsiveDesktop: false, + isLoading: true, + ); } if (controller.emailIdsPresentation.length == 1 && @@ -215,4 +213,10 @@ class ThreadDetailView extends GetWidget { ) ); } + + bool showLoadingView(Either viewState) { + final viewStateValue = viewState.fold(id, id); + return viewStateValue is GettingThreadById && + !viewStateValue.updateCurrentThreadDetail; + } } \ No newline at end of file diff --git a/model/lib/extensions/list_email_extension.dart b/model/lib/extensions/list_email_extension.dart index 68ffb00ea..2572b09d6 100644 --- a/model/lib/extensions/list_email_extension.dart +++ b/model/lib/extensions/list_email_extension.dart @@ -11,6 +11,8 @@ extension ListEmailExtension on List { List get listEmailIds => map((email) => email.id).whereNotNull().toList(); + List get listThreadIds => map((email) => email.threadId).whereNotNull().toList(); + Email? findEmailById(EmailId emailId) { try { return firstWhere((email) => email.id == emailId); diff --git a/test/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success_test.dart b/test/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success_test.dart index df4211e1d..703422d85 100644 --- a/test/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success_test.dart +++ b/test/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success_test.dart @@ -28,9 +28,14 @@ void main() { EmailId(Id('1')), EmailId(Id('2')), ]); + final mailboxDashBoardController = MockMailboxDashBoardController(); when(threadDetailController.emailIdsPresentation).thenReturn( {}.obs, ); + when(mailboxDashBoardController.selectedEmail).thenReturn( + Rxn(PresentationEmail(id: EmailId(Id('1')))), + ); + when(threadDetailController.mailboxDashBoardController).thenReturn(mailboxDashBoardController); // act threadDetailController.handleGetEmailIdsByThreadIdSuccess(success); diff --git a/test/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action_test.dart b/test/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action_test.dart new file mode 100644 index 000000000..23b2256a6 --- /dev/null +++ b/test/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action_test.dart @@ -0,0 +1,316 @@ +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get/get.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:model/extensions/session_extension.dart'; +import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/thread/data/model/email_change_response.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/domain/usecases/get_thread_by_id_interactor.dart'; +import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action.dart'; +import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart'; + +import '../../../../fixtures/account_fixtures.dart'; +import '../../../../fixtures/session_fixtures.dart'; +import 'handle_refresh_thread_detail_action_test.mocks.dart'; + +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), + MockSpec(), +]) +void main() { + late MockThreadDetailController threadDetailController; + late MockMailboxDashBoardController mailboxDashBoardController; + late MockGetThreadByIdInteractor getThreadByIdInteractor; + + final sentMailboxId = MailboxId(Id('sent')); + final ownEmailAddress = SessionFixtures.aliceSession.getOwnEmailAddress(); + + setUp(() { + threadDetailController = MockThreadDetailController(); + mailboxDashBoardController = MockMailboxDashBoardController(); + getThreadByIdInteractor = MockGetThreadByIdInteractor(); + }); + + group('handle refresh thread detail action test', () { + test( + 'should call getThreadByIdInteractor.execute ' + 'when refreshThreadDetailAction is called ' + 'and list email created contains currentThreadId ' + 'and isThreadDetailEnabled is true', + () { + // arrange + final emailId = EmailId(Id('1')); + final threadId = ThreadId(Id('some-id')); + final action = RefreshThreadDetailAction(EmailChangeResponse( + created: [Email(id: emailId, threadId: threadId)], + )); + when(threadDetailController.mailboxDashBoardController) + .thenReturn(mailboxDashBoardController); + when(mailboxDashBoardController.selectedEmail) + .thenReturn(Rxn(PresentationEmail(id: emailId, threadId: threadId))); + when(threadDetailController.session) + .thenReturn(SessionFixtures.aliceSession); + when(threadDetailController.accountId) + .thenReturn(AccountFixtures.aliceAccountId); + when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId); + when(threadDetailController.ownEmailAddress).thenReturn(ownEmailAddress); + when(threadDetailController.isThreadDetailEnabled).thenReturn(true); + + // act + threadDetailController.handleRefreshThreadDetailAction( + action, + getThreadByIdInteractor, + ); + + // assert + verify(getThreadByIdInteractor.execute( + threadId, + SessionFixtures.aliceSession, + AccountFixtures.aliceAccountId, + sentMailboxId, + ownEmailAddress, + updateCurrentThreadDetail: true, + selectedEmailId: emailId, + )).called(1); + }); + + test( + 'should call getThreadByIdInteractor.execute ' + 'when refreshThreadDetailAction is called ' + 'and list email updated contains currentThreadId ' + 'and isThreadDetailEnabled is true', + () { + // arrange + final emailId = EmailId(Id('1')); + final threadId = ThreadId(Id('some-id')); + final action = RefreshThreadDetailAction(EmailChangeResponse( + updated: [Email(id: emailId, threadId: threadId)], + )); + when(threadDetailController.mailboxDashBoardController) + .thenReturn(mailboxDashBoardController); + when(mailboxDashBoardController.selectedEmail) + .thenReturn(Rxn(PresentationEmail(id: emailId, threadId: threadId))); + when(threadDetailController.session) + .thenReturn(SessionFixtures.aliceSession); + when(threadDetailController.accountId) + .thenReturn(AccountFixtures.aliceAccountId); + when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId); + when(threadDetailController.ownEmailAddress).thenReturn(ownEmailAddress); + when(threadDetailController.isThreadDetailEnabled).thenReturn(true); + + // act + threadDetailController.handleRefreshThreadDetailAction( + action, + getThreadByIdInteractor, + ); + + // assert + verify(getThreadByIdInteractor.execute( + threadId, + SessionFixtures.aliceSession, + AccountFixtures.aliceAccountId, + sentMailboxId, + ownEmailAddress, + updateCurrentThreadDetail: true, + selectedEmailId: emailId, + )).called(1); + }); + + test( + 'should call getThreadByIdInteractor.execute ' + 'when refreshThreadDetailAction is called ' + 'and list email destroyed contains any of emailId in current thread ' + 'and isThreadDetailEnabled is true', + () { + // arrange + final emailId = EmailId(Id('1')); + final threadId = ThreadId(Id('some-id')); + final action = RefreshThreadDetailAction(EmailChangeResponse( + destroyed: [emailId], + )); + when(threadDetailController.mailboxDashBoardController) + .thenReturn(mailboxDashBoardController); + when(mailboxDashBoardController.selectedEmail) + .thenReturn(Rxn(PresentationEmail(id: emailId, threadId: threadId))); + when(threadDetailController.session) + .thenReturn(SessionFixtures.aliceSession); + when(threadDetailController.accountId) + .thenReturn(AccountFixtures.aliceAccountId); + when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId); + when(threadDetailController.ownEmailAddress).thenReturn(ownEmailAddress); + when(threadDetailController.emailIdsPresentation).thenReturn({ + emailId: null + }.obs); + when(threadDetailController.isThreadDetailEnabled).thenReturn(true); + + // act + threadDetailController.handleRefreshThreadDetailAction( + action, + getThreadByIdInteractor, + ); + + // assert + verify(getThreadByIdInteractor.execute( + threadId, + SessionFixtures.aliceSession, + AccountFixtures.aliceAccountId, + sentMailboxId, + ownEmailAddress, + updateCurrentThreadDetail: true, + selectedEmailId: emailId, + )).called(1); + }); + + test( + 'should consume GetThreadByIdSuccess with selected email id ' + 'when refreshThreadDetailAction is called ' + 'and isThreadDetailEnabled is false ' + 'and selected email id not null ' + 'and list email updated contains selected email id', + () async { + // arrange + final emailId = EmailId(Id('1')); + final action = RefreshThreadDetailAction(EmailChangeResponse( + updated: [Email(id: emailId)], + )); + when(threadDetailController.mailboxDashBoardController) + .thenReturn(mailboxDashBoardController); + when(mailboxDashBoardController.selectedEmail) + .thenReturn(Rxn(PresentationEmail(id: emailId))); + when(threadDetailController.isThreadDetailEnabled).thenReturn(false); + + // act + threadDetailController.handleRefreshThreadDetailAction( + action, + getThreadByIdInteractor, + ); + + // assert + verifyNever(getThreadByIdInteractor.execute( + any, + any, + any, + any, + any, + updateCurrentThreadDetail: anyNamed('updateCurrentThreadDetail'), + selectedEmailId: anyNamed('selectedEmailId'), + )); + final args = verify( + threadDetailController.consumeState(captureAny), + ).captured.first as Stream; + final state = await args.last; + expect( + state, + Right(GetThreadByIdSuccess( + [emailId], + updateCurrentThreadDetail: true, + )), + ); + }); + + test( + 'should not call getThreadByIdInteractor.execute ' + 'and not consume GetThreadByIdSuccess with selected email id' + 'when refreshThreadDetailAction is called ' + 'and list email created does not contain currentThreadId ' + 'and list email updated does not contain currentThreadId ' + 'and list email destroyed does not contain any of emailId in current thread ' + 'and isThreadDetailEnabled is true', + () async { + // arrange + final emailId = EmailId(Id('1')); + final action = RefreshThreadDetailAction(EmailChangeResponse( + created: [ + Email(id: EmailId(Id('2')), threadId: ThreadId(Id('some-id-2'))), + ], + updated: [ + Email(id: EmailId(Id('3')), threadId: ThreadId(Id('some-id-3'))), + ], + destroyed: [ + EmailId(Id('4')), + ], + )); + when(threadDetailController.mailboxDashBoardController) + .thenReturn(mailboxDashBoardController); + when(mailboxDashBoardController.selectedEmail) + .thenReturn(Rxn(PresentationEmail())); + when(threadDetailController.session) + .thenReturn(SessionFixtures.aliceSession); + when(threadDetailController.accountId) + .thenReturn(AccountFixtures.aliceAccountId); + when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId); + when(threadDetailController.ownEmailAddress).thenReturn(ownEmailAddress); + when(threadDetailController.emailIdsPresentation).thenReturn({ + emailId: null, + }.obs); + when(threadDetailController.viewState).thenReturn(Rx(Right(UIState.idle))); + when(threadDetailController.isThreadDetailEnabled).thenReturn(true); + + // act + threadDetailController.handleRefreshThreadDetailAction( + action, + getThreadByIdInteractor, + ); + + // assert + verifyNever(getThreadByIdInteractor.execute( + any, + any, + any, + any, + any, + updateCurrentThreadDetail: anyNamed('updateCurrentThreadDetail'), + selectedEmailId: anyNamed('selectedEmailId'), + )); + verifyNever(threadDetailController.consumeState(any)); + }); + + test( + 'should not call getThreadByIdInteractor.execute ' + 'and not consume GetThreadByIdSuccess with selected email id' + 'when refreshThreadDetailAction is called ' + 'and isThreadDetailEnabled is false ' + 'and updated email ids does not contain selected email id', + () async { + // arrange + final emailId = EmailId(Id('1')); + final action = RefreshThreadDetailAction(EmailChangeResponse( + updated: [ + Email(id: EmailId(Id('3')), threadId: ThreadId(Id('some-id-3'))), + ], + )); + when(threadDetailController.mailboxDashBoardController) + .thenReturn(mailboxDashBoardController); + when(mailboxDashBoardController.selectedEmail) + .thenReturn(Rxn(PresentationEmail(id: emailId))); + when(threadDetailController.isThreadDetailEnabled).thenReturn(false); + + // act + threadDetailController.handleRefreshThreadDetailAction( + action, + getThreadByIdInteractor, + ); + + // assert + verifyNever(getThreadByIdInteractor.execute( + any, + any, + any, + any, + any, + updateCurrentThreadDetail: anyNamed('updateCurrentThreadDetail'), + selectedEmailId: anyNamed('selectedEmailId'), + )); + verifyNever(threadDetailController.consumeState(any)); + }); + }); +} \ No newline at end of file diff --git a/test/features/thread_detail/presentation/extension/initialize_thread_detail_emails_test.dart b/test/features/thread_detail/presentation/extension/initialize_thread_detail_emails_test.dart index d56c4df54..da3f59de1 100644 --- a/test/features/thread_detail/presentation/extension/initialize_thread_detail_emails_test.dart +++ b/test/features/thread_detail/presentation/extension/initialize_thread_detail_emails_test.dart @@ -8,6 +8,7 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:model/email/presentation_email.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/domain/usecases/get_emails_by_ids_interactor.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/initialize_thread_detail_emails.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart'; @@ -65,7 +66,9 @@ void main() { .thenReturn(Rxn(PresentationEmail())); // Act - threadDetailController.initializeThreadDetailEmails(); + threadDetailController.initializeThreadDetailEmails( + GetThreadByIdSuccess(emailIds), + ); // Assert final captured = verify(getEmailsByIdsInteractor.execute( diff --git a/test/features/thread_detail/presentation/extension/load_more_thread_detail_emails_test.dart b/test/features/thread_detail/presentation/extension/load_more_thread_detail_emails_test.dart index 3ee6c8817..fd25d9663 100644 --- a/test/features/thread_detail/presentation/extension/load_more_thread_detail_emails_test.dart +++ b/test/features/thread_detail/presentation/extension/load_more_thread_detail_emails_test.dart @@ -144,6 +144,7 @@ void main() { when(controller.accountId).thenReturn(AccountFixtures.aliceAccountId); when(controller.additionalProperties).thenReturn(Properties.empty()); when(controller.getEmailsByIdsInteractor).thenReturn(getEmailsByIdsInteractor); + when(controller.additionalProperties).thenReturn(Properties.empty()); when(controller.mailboxDashBoardController).thenReturn(mailboxDashBoardController); when(mailboxDashBoardController.selectedEmail).thenReturn(Rxn()); @@ -189,6 +190,7 @@ void main() { when(controller.accountId).thenReturn(AccountFixtures.aliceAccountId); when(controller.additionalProperties).thenReturn(Properties.empty()); when(controller.getEmailsByIdsInteractor).thenReturn(getEmailsByIdsInteractor); + when(controller.additionalProperties).thenReturn(Properties.empty()); when(controller.mailboxDashBoardController).thenReturn(mailboxDashBoardController); when(mailboxDashBoardController.selectedEmail).thenReturn(Rxn());