TF-3698 Thread Detail Real time update

TF-3698 Thread Detail update reset RefreshThreadDetailAction position

TF-3698 Thread Detail refactor initializeThreadDetailEmails

TF-3698 Thread Detail refactor handleRefreshThreadDetailAction

TF-3698 Thread Detail create listThreadIds in ListEmailExtension
This commit is contained in:
DatDang
2025-05-27 11:03:28 +07:00
committed by Dat H. Pham
parent 1bc45e35f3
commit d210a892f2
28 changed files with 647 additions and 83 deletions
@@ -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(
@@ -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 = <EmailId>[];
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,
@@ -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;
@@ -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]))));
@@ -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,
));
}
}
}
@@ -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<EmailId> 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,
));
}
}
@@ -15,6 +15,7 @@ extension LoadMoreThreadDetailEmails on ThreadDetailController {
if (accountId == null || session == null) {
consumeState(Stream.value(Left(GetEmailsByIdsFailure(
exception: NotFoundSessionException(),
updateCurrentThreadDetail: false,
))));
return;
}