diff --git a/lib/features/base/base_mailbox_controller.dart b/lib/features/base/base_mailbox_controller.dart index aa62a5145a..1e3ac764d4 100644 --- a/lib/features/base/base_mailbox_controller.dart +++ b/lib/features/base/base_mailbox_controller.dart @@ -553,4 +553,106 @@ abstract class BaseMailboxController extends BaseController { mailboxNode = personalMailboxTree.value.findNodeOnFirstLevel((node) => node.item.name?.name.toLowerCase() == name); return mailboxNode; } + + void updateMailboxNameById(MailboxId mailboxId, MailboxName mailboxName) { + MailboxNode? selectedNode; + + selectedNode = defaultMailboxTree.value.findNode((node) => node.item.id == mailboxId); + if (selectedNode != null) { + defaultMailboxTree.value.updateMailboxNameById(mailboxId, mailboxName); + defaultMailboxTree.refresh(); + return; + } + + selectedNode = personalMailboxTree.value.findNode((node) => node.item.id == mailboxId); + if (selectedNode != null) { + personalMailboxTree.value.updateMailboxNameById(mailboxId, mailboxName); + personalMailboxTree.refresh(); + return; + } + + selectedNode = teamMailboxesTree.value.findNode((node) => node.item.id == mailboxId); + if (selectedNode != null) { + teamMailboxesTree.value.updateMailboxNameById(mailboxId, mailboxName); + teamMailboxesTree.refresh(); + return; + } + } + + void updateUnreadCountOfMailboxById( + MailboxId mailboxId, { + int? readCount, + int? unreadCount, + }) { + int unreadChanges = 0; + if (readCount != null) { + unreadChanges -= readCount; + } + if (unreadCount != null) { + unreadChanges += unreadCount; + } + + MailboxNode? selectedNode; + + selectedNode = defaultMailboxTree.value.findNode((node) => node.item.id == mailboxId); + if (selectedNode != null) { + defaultMailboxTree.value.updateMailboxUnreadCountById( + mailboxId, + unreadChanges); + defaultMailboxTree.refresh(); + return; + } + + selectedNode = personalMailboxTree.value.findNode((node) => node.item.id == mailboxId); + if (selectedNode != null) { + personalMailboxTree.value.updateMailboxUnreadCountById( + mailboxId, + unreadChanges); + personalMailboxTree.refresh(); + return; + } + + selectedNode = teamMailboxesTree.value.findNode((node) => node.item.id == mailboxId); + if (selectedNode != null) { + teamMailboxesTree.value.updateMailboxUnreadCountById( + mailboxId, + unreadChanges); + teamMailboxesTree.refresh(); + return; + } + } + + void clearUnreadCount(MailboxId mailboxId) { + MailboxNode? selectedNode; + + selectedNode = defaultMailboxTree.value.findNode((node) => node.item.id == mailboxId); + if (selectedNode != null) { + final currentUnreadCount = selectedNode.item.unreadEmails?.value.value.toInt(); + defaultMailboxTree.value.updateMailboxUnreadCountById( + mailboxId, + -(currentUnreadCount ?? 0)); + defaultMailboxTree.refresh(); + return; + } + + selectedNode = personalMailboxTree.value.findNode((node) => node.item.id == mailboxId); + if (selectedNode != null) { + final currentUnreadCount = selectedNode.item.unreadEmails?.value.value.toInt(); + personalMailboxTree.value.updateMailboxUnreadCountById( + mailboxId, + -(currentUnreadCount ?? 0)); + personalMailboxTree.refresh(); + return; + } + + selectedNode = teamMailboxesTree.value.findNode((node) => node.item.id == mailboxId); + if (selectedNode != null) { + final currentUnreadCount = selectedNode.item.unreadEmails?.value.value.toInt(); + teamMailboxesTree.value.updateMailboxUnreadCountById( + mailboxId, + -(currentUnreadCount ?? 0)); + teamMailboxesTree.refresh(); + return; + } + } } \ No newline at end of file diff --git a/lib/features/email/domain/state/mark_as_email_read_state.dart b/lib/features/email/domain/state/mark_as_email_read_state.dart index 1c327f1da0..1bfb2e2052 100644 --- a/lib/features/email/domain/state/mark_as_email_read_state.dart +++ b/lib/features/email/domain/state/mark_as_email_read_state.dart @@ -1,6 +1,7 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/read_actions.dart'; import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.dart'; @@ -8,15 +9,17 @@ class MarkAsEmailReadSuccess extends UIState { final EmailId emailId; final ReadActions readActions; final MarkReadAction markReadAction; + final MailboxId? mailboxId; MarkAsEmailReadSuccess( this.emailId, this.readActions, this.markReadAction, + this.mailboxId, ); @override - List get props => [emailId, readActions, markReadAction]; + List get props => [emailId, readActions, markReadAction, mailboxId]; } class MarkAsEmailReadFailure extends FeatureFailure { diff --git a/lib/features/email/domain/state/mark_as_email_star_state.dart b/lib/features/email/domain/state/mark_as_email_star_state.dart index 3313c95253..91e87c19f0 100644 --- a/lib/features/email/domain/state/mark_as_email_star_state.dart +++ b/lib/features/email/domain/state/mark_as_email_star_state.dart @@ -1,14 +1,16 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/email/mark_star_action.dart'; class MarkAsStarEmailSuccess extends UIState { final MarkStarAction markStarAction; + final EmailId emailId; - MarkAsStarEmailSuccess(this.markStarAction); + MarkAsStarEmailSuccess(this.markStarAction, this.emailId); @override - List get props => [markStarAction]; + List get props => [markStarAction, emailId]; } class MarkAsStarEmailFailure extends FeatureFailure { diff --git a/lib/features/email/domain/usecases/mark_as_email_read_interactor.dart b/lib/features/email/domain/usecases/mark_as_email_read_interactor.dart index 7db473e033..d3505ae95b 100644 --- a/lib/features/email/domain/usecases/mark_as_email_read_interactor.dart +++ b/lib/features/email/domain/usecases/mark_as_email_read_interactor.dart @@ -4,6 +4,7 @@ import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/read_actions.dart'; import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; @@ -20,6 +21,7 @@ class MarkAsEmailReadInteractor { EmailId emailId, ReadActions readAction, MarkReadAction markReadAction, + MailboxId? mailboxId, ) async* { try { final result = await _emailRepository.markAsRead( @@ -33,6 +35,7 @@ class MarkAsEmailReadInteractor { result.first, readAction, markReadAction, + mailboxId, )); } catch (e) { yield Left(MarkAsEmailReadFailure(readAction, exception: e)); diff --git a/lib/features/email/domain/usecases/mark_as_star_email_interactor.dart b/lib/features/email/domain/usecases/mark_as_star_email_interactor.dart index aa8cc68c7e..db2b9704b8 100644 --- a/lib/features/email/domain/usecases/mark_as_star_email_interactor.dart +++ b/lib/features/email/domain/usecases/mark_as_star_email_interactor.dart @@ -26,7 +26,7 @@ class MarkAsStarEmailInteractor { [emailId], markStarAction, ); - yield Right(MarkAsStarEmailSuccess(markStarAction)); + yield Right(MarkAsStarEmailSuccess(markStarAction, emailId)); } catch (e) { yield Left(MarkAsStarEmailFailure(markStarAction, exception: e)); } diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 6918ee5256..1e795bbf2e 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -660,6 +660,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { presentationEmail.id!, readActions, markReadAction, + presentationEmail.mailboxContain?.mailboxId, )); } } diff --git a/lib/features/mailbox/domain/state/mark_as_mailbox_read_state.dart b/lib/features/mailbox/domain/state/mark_as_mailbox_read_state.dart index 60a2df0c15..6be792a793 100644 --- a/lib/features/mailbox/domain/state/mark_as_mailbox_read_state.dart +++ b/lib/features/mailbox/domain/state/mark_as_mailbox_read_state.dart @@ -1,6 +1,7 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:tmail_ui_user/features/base/state/ui_action_state.dart'; @@ -24,8 +25,10 @@ class UpdatingMarkAsMailboxReadState extends UIState { class MarkAsMailboxReadAllSuccess extends UIActionState { final String mailboxDisplayName; + final MailboxId mailboxId; MarkAsMailboxReadAllSuccess(this.mailboxDisplayName, + this.mailboxId, { jmap.State? currentEmailState, jmap.State? currentMailboxState, @@ -35,6 +38,7 @@ class MarkAsMailboxReadAllSuccess extends UIActionState { @override List get props => [ mailboxDisplayName, + mailboxId, ...super.props ]; } @@ -43,16 +47,22 @@ class MarkAsMailboxReadHasSomeEmailFailure extends UIState { final String mailboxDisplayName; final int countEmailsRead; + final MailboxId mailboxId; + final List successEmailIds; MarkAsMailboxReadHasSomeEmailFailure( this.mailboxDisplayName, this.countEmailsRead, + this.mailboxId, + this.successEmailIds, ); @override List get props => [ mailboxDisplayName, countEmailsRead, + mailboxId, + successEmailIds, ]; } diff --git a/lib/features/mailbox/domain/state/rename_mailbox_state.dart b/lib/features/mailbox/domain/state/rename_mailbox_state.dart index 1861185bc9..f3300267a9 100644 --- a/lib/features/mailbox/domain/state/rename_mailbox_state.dart +++ b/lib/features/mailbox/domain/state/rename_mailbox_state.dart @@ -1,9 +1,17 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/rename_mailbox_request.dart'; class LoadingRenameMailbox extends UIState {} -class RenameMailboxSuccess extends UIState {} +class RenameMailboxSuccess extends UIState { + RenameMailboxSuccess({required this.request}); + + final RenameMailboxRequest request; + + @override + List get props => [request]; +} class RenameMailboxFailure extends FeatureFailure { diff --git a/lib/features/mailbox/domain/usecases/mark_as_mailbox_read_interactor.dart b/lib/features/mailbox/domain/usecases/mark_as_mailbox_read_interactor.dart index 074efdd514..547ec1e57e 100644 --- a/lib/features/mailbox/domain/usecases/mark_as_mailbox_read_interactor.dart +++ b/lib/features/mailbox/domain/usecases/mark_as_mailbox_read_interactor.dart @@ -34,11 +34,13 @@ class MarkAsMailboxReadInteractor { onProgressController); if (totalEmailUnread == listEmails.length) { - yield Right(MarkAsMailboxReadAllSuccess(mailboxDisplayName)); + yield Right(MarkAsMailboxReadAllSuccess(mailboxDisplayName, mailboxId)); } else if (listEmails.isNotEmpty) { yield Right(MarkAsMailboxReadHasSomeEmailFailure( mailboxDisplayName, listEmails.length, + mailboxId, + listEmails, )); } else { yield Left(MarkAsMailboxReadAllFailure(mailboxDisplayName: mailboxDisplayName)); diff --git a/lib/features/mailbox/domain/usecases/rename_mailbox_interactor.dart b/lib/features/mailbox/domain/usecases/rename_mailbox_interactor.dart index 8e2ef950da..267be45198 100644 --- a/lib/features/mailbox/domain/usecases/rename_mailbox_interactor.dart +++ b/lib/features/mailbox/domain/usecases/rename_mailbox_interactor.dart @@ -18,7 +18,7 @@ class RenameMailboxInteractor { yield Right(LoadingRenameMailbox()); final result = await _mailboxRepository.renameMailbox(session, accountId, request); if (result) { - yield Right(RenameMailboxSuccess()); + yield Right(RenameMailboxSuccess(request: request)); } else { yield Left(RenameMailboxFailure(null)); } diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index 524f7d1a04..93bc6f72bb 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -20,6 +20,7 @@ import 'package:tmail_ui_user/features/base/base_mailbox_controller.dart'; import 'package:tmail_ui_user/features/base/mixin/contact_support_mixin.dart'; import 'package:tmail_ui_user/features/base/mixin/mailbox_action_handler_mixin.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; +import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; @@ -36,6 +37,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/model/subscribe_request.da import 'package:tmail_ui_user/features/mailbox/domain/state/create_new_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/delete_multiple_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/move_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/refresh_all_mailboxes_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/refresh_changes_all_mailboxes_state.dart'; @@ -70,6 +72,7 @@ import 'package:tmail_ui_user/features/push_notification/presentation/websocket/ import 'package:tmail_ui_user/features/push_notification/presentation/websocket/web_socket_queue_handler.dart'; import 'package:tmail_ui_user/features/search/mailbox/presentation/search_mailbox_bindings.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/dialog_router.dart'; @@ -167,6 +170,8 @@ class MailboxController extends BaseMailboxController _deleteMultipleMailboxSuccess(success.listMailboxIdDeleted, success.currentMailboxState); } else if (success is DeleteMultipleMailboxHasSomeSuccess) { _deleteMultipleMailboxSuccess(success.listMailboxIdDeleted, success.currentMailboxState); + } else if (success is RenameMailboxSuccess) { + _renameMailboxSuccess(success); } else if (success is MoveMailboxSuccess) { _moveMailboxSuccess(success); } else if (success is SubscribeMailboxSuccess) { @@ -253,6 +258,72 @@ class MailboxController extends BaseMailboxController mailboxDashBoardController.clearMailboxUIAction(); } }); + + ever(mailboxDashBoardController.viewState, (viewState) { + final reactionState = viewState.getOrElse(() => UIState.idle); + if (reactionState is MarkAsEmailReadSuccess) { + _handleMarkEmailsAsReadOrUnread( + affectedMailboxId: reactionState.mailboxId, + readCount: reactionState.readActions == ReadActions.markAsRead + ? 1 + : null, + unreadCount: reactionState.readActions == ReadActions.markAsUnread + ? 1 + : null, + ); + } else if (reactionState is MarkAsMultipleEmailReadAllSuccess) { + _handleMarkEmailsAsReadOrUnread( + affectedMailboxId: reactionState.mailboxId, + readCount: reactionState.readActions == ReadActions.markAsRead + ? reactionState.emailIds.length + : null, + unreadCount: reactionState.readActions == ReadActions.markAsUnread + ? reactionState.emailIds.length + : null, + ); + } else if (reactionState is MarkAsMultipleEmailReadHasSomeEmailFailure) { + _handleMarkEmailsAsReadOrUnread( + affectedMailboxId: reactionState.mailboxId, + readCount: reactionState.readActions == ReadActions.markAsRead + ? reactionState.successEmailIds.length + : null, + unreadCount: reactionState.readActions == ReadActions.markAsUnread + ? reactionState.successEmailIds.length + : null, + ); + } else if (reactionState is MarkAsMailboxReadAllSuccess) { + _handleMarkMailboxAsRead( + affectedMailboxId: reactionState.mailboxId, + ); + } else if (reactionState is MarkAsMailboxReadHasSomeEmailFailure) { + _handleMarkEmailsAsReadOrUnread( + affectedMailboxId: reactionState.mailboxId, + readCount: reactionState.successEmailIds.length, + ); + } + }); + } + + void _handleMarkEmailsAsReadOrUnread({ + required MailboxId? affectedMailboxId, + int? readCount, + int? unreadCount, + }) { + if (affectedMailboxId == null) return; + + updateUnreadCountOfMailboxById( + affectedMailboxId, + readCount: readCount, + unreadCount: unreadCount, + ); + } + + void _handleMarkMailboxAsRead({ + required MailboxId? affectedMailboxId, + }) { + if (affectedMailboxId == null) return; + + clearUnreadCount(affectedMailboxId); } void _initWebSocketQueueHandler() { @@ -636,6 +707,10 @@ class MailboxController extends BaseMailboxController } } + void _renameMailboxSuccess(RenameMailboxSuccess success) { + updateMailboxNameById(success.request.mailboxId, success.request.newName); + } + void _renameMailboxFailure(RenameMailboxFailure failure) { if (currentOverlayContext != null && currentContext != null) { final exception = failure.exception; diff --git a/lib/features/mailbox/presentation/model/mailbox_tree.dart b/lib/features/mailbox/presentation/model/mailbox_tree.dart index 125e6e3c63..dd7d8fa16c 100644 --- a/lib/features/mailbox/presentation/model/mailbox_tree.dart +++ b/lib/features/mailbox/presentation/model/mailbox_tree.dart @@ -3,6 +3,7 @@ import 'dart:collection'; import 'package:collection/collection.dart'; import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/mailbox/expand_mode.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; @@ -83,6 +84,25 @@ class MailboxTree with EquatableMixin { } } + void updateMailboxNameById(MailboxId mailboxId, MailboxName mailboxName) { + final matchedNode = findNode((node) => node.item.id == mailboxId); + if (matchedNode != null) { + matchedNode.item = matchedNode.item.copyWith(name: mailboxName); + } + } + + void updateMailboxUnreadCountById(MailboxId mailboxId, int unreadCount) { + final matchedNode = findNode((node) => node.item.id == mailboxId); + if (matchedNode != null) { + final currentUnreadCount = matchedNode.item.unreadEmails?.value.value ?? 0; + final updatedUnreadCount = currentUnreadCount + unreadCount; + if (updatedUnreadCount < 0) return; + matchedNode.item = matchedNode.item.copyWith( + unreadEmails: UnreadEmails(UnsignedInt(updatedUnreadCount)), + ); + } + } + String? getNodePath(MailboxId mailboxId) { final matchedNode = findNode((node) => node.item.id == mailboxId); if (matchedNode == null) { 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 7966608a71..3c7aff65a1 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -21,6 +21,7 @@ import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; +import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart'; import 'package:model/model.dart'; @@ -881,6 +882,7 @@ class MailboxDashBoardController extends ReloadableController EmailId emailId, ReadActions readActions, MarkReadAction markReadAction, + MailboxId? mailboxId, ) { if (accountId.value != null && sessionCurrent != null) { consumeState(_markAsEmailReadInteractor.execute( @@ -889,6 +891,7 @@ class MailboxDashBoardController extends ReloadableController emailId, readActions, markReadAction, + mailboxId, )); } } @@ -920,6 +923,7 @@ class MailboxDashBoardController extends ReloadableController accountId.value!, listEmailNeedMarkAsRead.listEmailIds, readActions, + listPresentationEmail.firstOrNull?.mailboxContain?.mailboxId, )); } } @@ -957,7 +961,7 @@ class MailboxDashBoardController extends ReloadableController message, actionName: AppLocalizations.of(currentContext!).undo, onActionClick: () { - markAsEmailRead(success.emailId, undoAction, MarkReadAction.undo); + markAsEmailRead(success.emailId, undoAction, MarkReadAction.undo, success.mailboxId); }, leadingSVGIcon: imagePaths.icToastSuccessMessage, backgroundColor: AppColor.toastSuccessBackgroundColor, @@ -2944,6 +2948,34 @@ class MailboxDashBoardController extends ReloadableController jmap.State? get currentEmailState => _currentEmailState; + void handleMarkEmailsAsReadOrUnreadByEmailIds({ + required List readEmailIds, + required List unreadEmailIds, + }) { + for (var presentationEmail in emailsInCurrentMailbox) { + if (readEmailIds.contains(presentationEmail.id)) { + presentationEmail.keywords?[KeyWordIdentifier.emailSeen] = true; + } else if (unreadEmailIds.contains(presentationEmail.id)) { + presentationEmail.keywords?.remove(KeyWordIdentifier.emailSeen); + } + } + emailsInCurrentMailbox.refresh(); + } + + void handleMarkEmailsAsStarById({ + required List starredEmailIds, + required List unstarredEmailIds, + }) { + for (var presentationEmail in emailsInCurrentMailbox) { + if (starredEmailIds.contains(presentationEmail.id)) { + presentationEmail.keywords?[KeyWordIdentifier.emailFlagged] = true; + } else if (unstarredEmailIds.contains(presentationEmail.id)) { + presentationEmail.keywords?.remove(KeyWordIdentifier.emailFlagged); + } + } + emailsInCurrentMailbox.refresh(); + } + @override void onClose() { if (PlatformInfo.isWeb) { diff --git a/lib/features/search/email/presentation/search_email_controller.dart b/lib/features/search/email/presentation/search_email_controller.dart index ccd7b79f7b..93525aa247 100644 --- a/lib/features/search/email/presentation/search_email_controller.dart +++ b/lib/features/search/email/presentation/search_email_controller.dart @@ -33,6 +33,8 @@ import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_e import 'package:tmail_ui_user/features/contact/presentation/model/contact_arguments.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.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/presentation/action/email_ui_action.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'; @@ -60,6 +62,8 @@ import 'package:tmail_ui_user/features/search/email/presentation/model/search_mo import 'package:tmail_ui_user/features/search/email/presentation/search_email_bindings.dart'; import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_as_star_multiple_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_more_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/search_email_interactor.dart'; @@ -260,6 +264,65 @@ class SearchEmailController extends BaseController } }, ); + + ever(mailboxDashBoardController.viewState, (viewState) { + final reactionState = viewState.getOrElse(() => UIState.idle); + if (reactionState is MarkAsEmailReadSuccess) { + mailboxDashBoardController.handleMarkEmailsAsReadOrUnreadByEmailIds( + readEmailIds: reactionState.readActions == ReadActions.markAsRead + ? [reactionState.emailId] + : [], + unreadEmailIds: reactionState.readActions == ReadActions.markAsUnread + ? [reactionState.emailId] + : [], + ); + } else if (reactionState is MarkAsMultipleEmailReadAllSuccess) { + mailboxDashBoardController.handleMarkEmailsAsReadOrUnreadByEmailIds( + readEmailIds: reactionState.readActions == ReadActions.markAsRead + ? reactionState.emailIds + : [], + unreadEmailIds: reactionState.readActions == ReadActions.markAsUnread + ? reactionState.emailIds + : [], + ); + } else if (reactionState is MarkAsMultipleEmailReadHasSomeEmailFailure) { + mailboxDashBoardController.handleMarkEmailsAsReadOrUnreadByEmailIds( + readEmailIds: reactionState.readActions == ReadActions.markAsRead + ? reactionState.successEmailIds + : [], + unreadEmailIds: reactionState.readActions == ReadActions.markAsUnread + ? reactionState.successEmailIds + : [], + ); + } else if (reactionState is MarkAsStarEmailSuccess) { + mailboxDashBoardController.handleMarkEmailsAsStarById( + starredEmailIds: reactionState.markStarAction == MarkStarAction.markStar + ? [reactionState.emailId] + : [], + unstarredEmailIds: reactionState.markStarAction == MarkStarAction.unMarkStar + ? [reactionState.emailId] + : [], + ); + } else if (reactionState is MarkAsStarMultipleEmailAllSuccess) { + mailboxDashBoardController.handleMarkEmailsAsStarById( + starredEmailIds: reactionState.markStarAction == MarkStarAction.markStar + ? reactionState.emailIds + : [], + unstarredEmailIds: reactionState.markStarAction == MarkStarAction.unMarkStar + ? reactionState.emailIds + : [], + ); + } else if (reactionState is MarkAsStarMultipleEmailHasSomeEmailFailure) { + mailboxDashBoardController.handleMarkEmailsAsStarById( + starredEmailIds: reactionState.markStarAction == MarkStarAction.markStar + ? reactionState.successEmailIds + : [], + unstarredEmailIds: reactionState.markStarAction == MarkStarAction.unMarkStar + ? reactionState.successEmailIds + : [], + ); + } + }); } void _refreshEmailChanges({jmap.State? newState}) { diff --git a/lib/features/search/mailbox/presentation/search_mailbox_controller.dart b/lib/features/search/mailbox/presentation/search_mailbox_controller.dart index 2ae4bc4455..cde0ca8c00 100644 --- a/lib/features/search/mailbox/presentation/search_mailbox_controller.dart +++ b/lib/features/search/mailbox/presentation/search_mailbox_controller.dart @@ -33,6 +33,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/model/subscribe_request.da import 'package:tmail_ui_user/features/mailbox/domain/state/create_new_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/delete_multiple_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/move_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/refresh_changes_all_mailboxes_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/rename_mailbox_state.dart'; @@ -147,6 +148,8 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa searchMailboxAction(); } else if (success is SearchMailboxSuccess) { _handleSearchMailboxSuccess(success); + } else if (success is RenameMailboxSuccess) { + updateMailboxNameById(success.request.mailboxId, success.request.newName); } else if (success is MoveMailboxSuccess) { _moveMailboxSuccess(success); } else if (success is DeleteMultipleMailboxAllSuccess) { @@ -183,6 +186,18 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa _refreshMailboxChanges(newState: action.newState); } }); + + ever(dashboardController.viewState, (viewState) { + final reactionState = viewState.getOrElse(() => UIState.idle); + if (reactionState is MarkAsMailboxReadAllSuccess) { + clearUnreadCount(reactionState.mailboxId); + } else if (reactionState is MarkAsMailboxReadHasSomeEmailFailure) { + updateUnreadCountOfMailboxById( + reactionState.mailboxId, + readCount: reactionState.countEmailsRead, + ); + } + }); } void _getAllMailboxAction() { diff --git a/lib/features/thread/data/repository/thread_repository_impl.dart b/lib/features/thread/data/repository/thread_repository_impl.dart index 7334f919c6..e8415e0e24 100644 --- a/lib/features/thread/data/repository/thread_repository_impl.dart +++ b/lib/features/thread/data/repository/thread_repository_impl.dart @@ -46,6 +46,7 @@ class ThreadRepositoryImpl extends ThreadRepository { Properties? propertiesCreated, Properties? propertiesUpdated, bool getLatestChanges = true, + bool skipCache = false, } ) async* { log('ThreadRepositoryImpl::getAllEmail(): filter = ${emailFilter?.mailboxId}'); @@ -85,7 +86,7 @@ class ThreadRepositoryImpl extends ThreadRepository { ); } yield networkEmailResponse; - } else { + } else if (!skipCache) { yield localEmailResponse; } diff --git a/lib/features/thread/domain/repository/thread_repository.dart b/lib/features/thread/domain/repository/thread_repository.dart index e794b893c3..1953645c06 100644 --- a/lib/features/thread/domain/repository/thread_repository.dart +++ b/lib/features/thread/domain/repository/thread_repository.dart @@ -26,6 +26,7 @@ abstract class ThreadRepository { Properties? propertiesCreated, Properties? propertiesUpdated, bool getLatestChanges = true, + bool skipCache = false, } ); diff --git a/lib/features/thread/domain/state/mark_as_multiple_email_read_state.dart b/lib/features/thread/domain/state/mark_as_multiple_email_read_state.dart index 9134f49b2b..0eb6ab7fc8 100644 --- a/lib/features/thread/domain/state/mark_as_multiple_email_read_state.dart +++ b/lib/features/thread/domain/state/mark_as_multiple_email_read_state.dart @@ -1,20 +1,24 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/read_actions.dart'; class LoadingMarkAsMultipleEmailReadAll extends UIState {} class MarkAsMultipleEmailReadAllSuccess extends UIState { - final int countMarkAsReadSuccess; + final List emailIds; final ReadActions readActions; + final MailboxId? mailboxId; MarkAsMultipleEmailReadAllSuccess( - this.countMarkAsReadSuccess, - this.readActions, + this.emailIds, + this.readActions, + this.mailboxId, ); @override - List get props => [countMarkAsReadSuccess, readActions]; + List get props => [emailIds, readActions, mailboxId]; } class MarkAsMultipleEmailReadAllFailure extends FeatureFailure { @@ -27,16 +31,18 @@ class MarkAsMultipleEmailReadAllFailure extends FeatureFailure { } class MarkAsMultipleEmailReadHasSomeEmailFailure extends UIState { - final int countMarkAsReadSuccess; + final List successEmailIds; final ReadActions readActions; + final MailboxId? mailboxId; MarkAsMultipleEmailReadHasSomeEmailFailure( - this.countMarkAsReadSuccess, + this.successEmailIds, this.readActions, + this.mailboxId, ); @override - List get props => [countMarkAsReadSuccess, readActions]; + List get props => [successEmailIds, readActions, mailboxId]; } class MarkAsMultipleEmailReadFailure extends FeatureFailure { diff --git a/lib/features/thread/domain/state/mark_as_star_multiple_email_state.dart b/lib/features/thread/domain/state/mark_as_star_multiple_email_state.dart index b41a891546..c7c5cecabf 100644 --- a/lib/features/thread/domain/state/mark_as_star_multiple_email_state.dart +++ b/lib/features/thread/domain/state/mark_as_star_multiple_email_state.dart @@ -1,5 +1,6 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/email/mark_star_action.dart'; class LoadingMarkAsStarMultipleEmailAll extends UIState {} @@ -7,14 +8,16 @@ class LoadingMarkAsStarMultipleEmailAll extends UIState {} class MarkAsStarMultipleEmailAllSuccess extends UIState { final int countMarkStarSuccess; final MarkStarAction markStarAction; + final List emailIds; MarkAsStarMultipleEmailAllSuccess( this.countMarkStarSuccess, this.markStarAction, + this.emailIds, ); @override - List get props => [countMarkStarSuccess, markStarAction]; + List get props => [countMarkStarSuccess, markStarAction, emailIds]; } class MarkAsStarMultipleEmailAllFailure extends FeatureFailure { @@ -29,14 +32,16 @@ class MarkAsStarMultipleEmailAllFailure extends FeatureFailure { class MarkAsStarMultipleEmailHasSomeEmailFailure extends UIState { final int countMarkStarSuccess; final MarkStarAction markStarAction; + final List successEmailIds; MarkAsStarMultipleEmailHasSomeEmailFailure( this.countMarkStarSuccess, this.markStarAction, + this.successEmailIds, ); @override - List get props => [countMarkStarSuccess, markStarAction]; + List get props => [countMarkStarSuccess, markStarAction, successEmailIds]; } class MarkAsStarMultipleEmailFailure extends FeatureFailure { diff --git a/lib/features/thread/domain/usecases/get_emails_in_mailbox_interactor.dart b/lib/features/thread/domain/usecases/get_emails_in_mailbox_interactor.dart index 0eccaae504..8e8a98bf55 100644 --- a/lib/features/thread/domain/usecases/get_emails_in_mailbox_interactor.dart +++ b/lib/features/thread/domain/usecases/get_emails_in_mailbox_interactor.dart @@ -27,6 +27,7 @@ class GetEmailsInMailboxInteractor { Properties? propertiesCreated, Properties? propertiesUpdated, bool getLatestChanges = true, + bool skipCache = false, } ) async* { try { @@ -41,7 +42,8 @@ class GetEmailsInMailboxInteractor { emailFilter: emailFilter, propertiesCreated: propertiesCreated, propertiesUpdated: propertiesUpdated, - getLatestChanges: getLatestChanges) + getLatestChanges: getLatestChanges, + skipCache: skipCache) .map((emailResponse) => _toGetEmailState( emailResponse: emailResponse, currentMailboxId: emailFilter?.mailboxId diff --git a/lib/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart b/lib/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart index 08779f9641..515a739414 100644 --- a/lib/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart +++ b/lib/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart @@ -4,6 +4,7 @@ import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart'; @@ -17,7 +18,8 @@ class MarkAsMultipleEmailReadInteractor { Session session, AccountId accountId, List emailIds, - ReadActions readAction + ReadActions readAction, + MailboxId? mailboxId, ) async* { try { yield Right(LoadingMarkAsMultipleEmailReadAll()); @@ -31,15 +33,17 @@ class MarkAsMultipleEmailReadInteractor { if (emailIds.length == result.length) { yield Right(MarkAsMultipleEmailReadAllSuccess( - result.length, - readAction, + result, + readAction, + mailboxId, )); } else if (result.isEmpty) { yield Left(MarkAsMultipleEmailReadAllFailure(readAction)); } else { yield Right(MarkAsMultipleEmailReadHasSomeEmailFailure( - result.length, - readAction, + result, + readAction, + mailboxId, )); } } catch (e) { diff --git a/lib/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart b/lib/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart index 6f828426b6..4d5d425040 100644 --- a/lib/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart +++ b/lib/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart @@ -26,15 +26,17 @@ class MarkAsStarMultipleEmailInteractor { if (emailIds.length == result.length) { yield Right(MarkAsStarMultipleEmailAllSuccess( - emailIds.length, - markStarAction, + emailIds.length, + markStarAction, + result, )); } else if (result.isEmpty) { yield Left(MarkAsStarMultipleEmailAllFailure(markStarAction)); } else { yield Right(MarkAsStarMultipleEmailHasSomeEmailFailure( - result.length, - markStarAction, + result.length, + markStarAction, + result, )); } } catch (e) { diff --git a/lib/features/thread/presentation/mixin/email_action_controller.dart b/lib/features/thread/presentation/mixin/email_action_controller.dart index baefee994b..04e2f12dd4 100644 --- a/lib/features/thread/presentation/mixin/email_action_controller.dart +++ b/lib/features/thread/presentation/mixin/email_action_controller.dart @@ -233,6 +233,7 @@ mixin EmailActionController { presentationEmail.id!, readActions, markReadAction, + presentationEmail.mailboxContain?.mailboxId, ); } diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 9b87815818..3cec4c8023 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -20,9 +20,12 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.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/presentation/action/email_ui_action.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/mailbox/domain/state/mark_as_mailbox_read_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart' as search; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; @@ -45,6 +48,8 @@ import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/domain/state/get_all_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/get_email_by_id_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/load_more_emails_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_as_star_multiple_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/refresh_all_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/refresh_changes_all_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart'; @@ -331,6 +336,83 @@ class ThreadController extends BaseController with EmailActionController { mailboxDashBoardController.clearEmailUIAction(); } }); + + ever(mailboxDashBoardController.viewState, (viewState) { + final reactionState = viewState.getOrElse(() => UIState.idle); + if (reactionState is MarkAsEmailReadSuccess) { + mailboxDashBoardController.handleMarkEmailsAsReadOrUnreadByEmailIds( + readEmailIds: reactionState.readActions == ReadActions.markAsRead + ? [reactionState.emailId] + : [], + unreadEmailIds: reactionState.readActions == ReadActions.markAsUnread + ? [reactionState.emailId] + : [], + ); + } else if (reactionState is MarkAsMultipleEmailReadAllSuccess) { + mailboxDashBoardController.handleMarkEmailsAsReadOrUnreadByEmailIds( + readEmailIds: reactionState.readActions == ReadActions.markAsRead + ? reactionState.emailIds + : [], + unreadEmailIds: reactionState.readActions == ReadActions.markAsUnread + ? reactionState.emailIds + : [], + ); + } else if (reactionState is MarkAsMultipleEmailReadHasSomeEmailFailure) { + mailboxDashBoardController.handleMarkEmailsAsReadOrUnreadByEmailIds( + readEmailIds: reactionState.readActions == ReadActions.markAsRead + ? reactionState.successEmailIds + : [], + unreadEmailIds: reactionState.readActions == ReadActions.markAsUnread + ? reactionState.successEmailIds + : [], + ); + } else if (reactionState is MarkAsMailboxReadAllSuccess) { + _handleMarkEmailsAsReadByMailboxId(reactionState.mailboxId); + } else if (reactionState is MarkAsMailboxReadHasSomeEmailFailure) { + mailboxDashBoardController.handleMarkEmailsAsReadOrUnreadByEmailIds( + readEmailIds: reactionState.successEmailIds, + unreadEmailIds: [], + ); + } else if (reactionState is MarkAsStarEmailSuccess) { + mailboxDashBoardController.handleMarkEmailsAsStarById( + starredEmailIds: reactionState.markStarAction == MarkStarAction.markStar + ? [reactionState.emailId] + : [], + unstarredEmailIds: reactionState.markStarAction == MarkStarAction.unMarkStar + ? [reactionState.emailId] + : [], + ); + } else if (reactionState is MarkAsStarMultipleEmailAllSuccess) { + mailboxDashBoardController.handleMarkEmailsAsStarById( + starredEmailIds: reactionState.markStarAction == MarkStarAction.markStar + ? reactionState.emailIds + : [], + unstarredEmailIds: reactionState.markStarAction == MarkStarAction.unMarkStar + ? reactionState.emailIds + : [], + ); + } else if (reactionState is MarkAsStarMultipleEmailHasSomeEmailFailure) { + mailboxDashBoardController.handleMarkEmailsAsStarById( + starredEmailIds: reactionState.markStarAction == MarkStarAction.markStar + ? reactionState.successEmailIds + : [], + unstarredEmailIds: reactionState.markStarAction == MarkStarAction.unMarkStar + ? reactionState.successEmailIds + : [], + ); + } + }); + } + + void _handleMarkEmailsAsReadByMailboxId(MailboxId mailboxId) { + if (mailboxDashBoardController.selectedMailbox.value?.id != mailboxId) return; + + for (var presentationEmail in mailboxDashBoardController.emailsInCurrentMailbox) { + if (presentationEmail.mailboxContain?.id != mailboxId) continue; + + presentationEmail.keywords?[KeyWordIdentifier.emailSeen] = true; + } + mailboxDashBoardController.emailsInCurrentMailbox.refresh(); } void _registerBrowserResizeListener() { @@ -444,7 +526,10 @@ class ThreadController extends BaseController with EmailActionController { } } - void _getAllEmailAction({bool getLatestChanges = true}) { + void _getAllEmailAction({ + bool getLatestChanges = true, + bool skipCache = false, + }) { log('ThreadController::_getAllEmailAction:'); if (_session != null &&_accountId != null) { consumeState(_getEmailsInMailboxInteractor.execute( @@ -460,6 +545,7 @@ class ThreadController extends BaseController with EmailActionController { propertiesCreated: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!), propertiesUpdated: ThreadConstants.propertiesUpdatedDefault, getLatestChanges: getLatestChanges, + skipCache: skipCache, )); } else { consumeState(Stream.value(Left(GetAllEmailFailure(NotFoundSessionException())))); @@ -508,7 +594,7 @@ class ThreadController extends BaseController with EmailActionController { if (searchController.isSearchEmailRunning) { _searchEmail(limit: limitEmailFetched); } else { - _getAllEmailAction(); + _getAllEmailAction(skipCache: true); } } @@ -792,6 +878,9 @@ class ThreadController extends BaseController with EmailActionController { } void cancelSelectEmail() { + if (mailboxDashBoardController.currentSelectMode.value == SelectMode.INACTIVE) { + return; + } final newEmailList = mailboxDashBoardController.emailsInCurrentMailbox .map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)) .toList(); diff --git a/model/lib/mailbox/presentation_mailbox.dart b/model/lib/mailbox/presentation_mailbox.dart index a810e1037c..5765975131 100644 --- a/model/lib/mailbox/presentation_mailbox.dart +++ b/model/lib/mailbox/presentation_mailbox.dart @@ -89,4 +89,42 @@ class PresentationMailbox with EquatableMixin { namespace, displayName, ]; + + PresentationMailbox copyWith({ + MailboxId? id, + MailboxName? name, + MailboxId? parentId, + Role? role, + SortOrder? sortOrder, + TotalEmails? totalEmails, + UnreadEmails? unreadEmails, + TotalThreads? totalThreads, + UnreadThreads? unreadThreads, + MailboxRights? myRights, + IsSubscribed? isSubscribed, + SelectMode? selectMode, + String? mailboxPath, + MailboxState? state, + Namespace? namespace, + String? displayName, + }) { + return PresentationMailbox( + id ?? this.id, + name: name ?? this.name, + parentId: parentId ?? this.parentId, + role: role ?? this.role, + sortOrder: sortOrder ?? this.sortOrder, + totalEmails: totalEmails ?? this.totalEmails, + unreadEmails: unreadEmails ?? this.unreadEmails, + totalThreads: totalThreads ?? this.totalThreads, + unreadThreads: unreadThreads ?? this.unreadThreads, + myRights: myRights ?? this.myRights, + isSubscribed: isSubscribed ?? this.isSubscribed, + selectMode: selectMode ?? this.selectMode, + mailboxPath: mailboxPath ?? this.mailboxPath, + state: state ?? this.state, + namespace: namespace ?? this.namespace, + displayName: displayName ?? this.displayName, + ); + } } \ No newline at end of file