TF-3804 Thread Detail Load selected email in parallel with thread metadata
This commit is contained in:
+24
-26
@@ -1,4 +1,3 @@
|
||||
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';
|
||||
|
||||
@@ -6,31 +5,30 @@ extension HandleGetEmailIdsByThreadIdSuccess on ThreadDetailController {
|
||||
void handleGetEmailIdsByThreadIdSuccess(
|
||||
GetThreadByIdSuccess success,
|
||||
) {
|
||||
final newEmailsInThreadDetail = <EmailId>[];
|
||||
if (success.emailIds.isNotEmpty) {
|
||||
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,
|
||||
};
|
||||
if (success.emailIds.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final allEmailIds = success.emailIds;
|
||||
if (success.updateCurrentThreadDetail) {
|
||||
final newEmailIds = allEmailIds.where(
|
||||
(emailId) => !emailIdsPresentation.keys.contains(emailId),
|
||||
);
|
||||
emailIdsPresentation
|
||||
..removeWhere((key, _) => !allEmailIds.contains(key))
|
||||
..addEntries(
|
||||
newEmailIds.map((emailId) => MapEntry(emailId, null)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final selectedEmail = mailboxDashBoardController.selectedEmail.value;
|
||||
final selectedEmailId = selectedEmail?.id;
|
||||
emailIdsPresentation.value = {
|
||||
for (final id in allEmailIds)
|
||||
id: id == selectedEmailId
|
||||
? emailIdsPresentation[id] ?? selectedEmail
|
||||
: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -38,7 +38,7 @@ extension HandleGetEmailsByIdsSuccess on ThreadDetailController {
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadMore) return;
|
||||
if (_skipScrollJump(isLoadMore)) return;
|
||||
|
||||
final currentExpandedEmailIndex = currentExpandedEmailId.value == null
|
||||
? -1
|
||||
@@ -72,4 +72,7 @@ extension HandleGetEmailsByIdsSuccess on ThreadDetailController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool _skipScrollJump(bool isLoadMore) =>
|
||||
!isLoadMore || emailIdsPresentation.length == 1;
|
||||
}
|
||||
+60
-26
@@ -1,9 +1,18 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:model/email/email_in_thread_status.dart';
|
||||
import 'package:model/extensions/email_extension.dart';
|
||||
import 'package:model/extensions/keyword_identifier_extension.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_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/domain/usecases/get_thread_by_id_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/close_thread_detail_action.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/utils/thread_detail_presentation_utils.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
extension HandleRefreshThreadDetailAction on ThreadDetailController {
|
||||
void handleRefreshThreadDetailAction(
|
||||
@@ -30,33 +39,58 @@ extension HandleRefreshThreadDetailAction on ThreadDetailController {
|
||||
}
|
||||
|
||||
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 (currentThreadId == null) return;
|
||||
final emailIdsDestroyed = Set<EmailId>.from(
|
||||
action.emailChangeResponse.destroyed ?? const []);
|
||||
|
||||
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,
|
||||
));
|
||||
final emailsCreated = action.emailChangeResponse.created
|
||||
?.where((email) => email.threadId == currentThreadId)
|
||||
.map((email) => email.toPresentationEmail().copyWith(
|
||||
emailInThreadStatus: EmailInThreadStatus.collapsed,
|
||||
))
|
||||
.toList(growable: false) ?? const [];
|
||||
|
||||
final emailsUpdated = action.emailChangeResponse.updated
|
||||
?.where((email) => email.threadId == currentThreadId)
|
||||
.map((email) => email.toPresentationEmail())
|
||||
.toList(growable: false) ?? const [];
|
||||
|
||||
final afterRefreshedEmailIds = ThreadDetailPresentationUtils.refreshEmailIds(
|
||||
original: emailIdsPresentation.keys.toList(),
|
||||
created: emailsCreated.map((email) => email.id).whereNotNull().toList(),
|
||||
destroyed: emailIdsDestroyed.toList(),
|
||||
);
|
||||
final afterRefreshedEmails = ThreadDetailPresentationUtils.refreshPresentationEmails(
|
||||
original: emailIdsPresentation.values.whereNotNull().toList(),
|
||||
created: emailsCreated,
|
||||
updated: emailsUpdated,
|
||||
destroyed: emailIdsDestroyed.toList(),
|
||||
);
|
||||
|
||||
if (afterRefreshedEmailIds.isEmpty) {
|
||||
closeThreadDetailAction(currentContext);
|
||||
return;
|
||||
}
|
||||
|
||||
consumeState(Stream.value(Right(GetThreadByIdSuccess(
|
||||
afterRefreshedEmailIds,
|
||||
updateCurrentThreadDetail: true,
|
||||
))));
|
||||
consumeState(Stream.value(Right(GetEmailsByIdsSuccess(
|
||||
afterRefreshedEmails,
|
||||
updateCurrentThreadDetail: true,
|
||||
))));
|
||||
|
||||
final updatedExpandedEmail = afterRefreshedEmails.firstWhereOrNull(
|
||||
(email) => email.id == currentExpandedEmailId.value,
|
||||
);
|
||||
if (updatedExpandedEmail?.keywords?[KeyWordIdentifierExtension.unsubscribeMail] == true) {
|
||||
mailboxDashBoardController.dispatchEmailUIAction(
|
||||
UnsubscribeFromThreadAction(
|
||||
currentExpandedEmailId.value!,
|
||||
updatedExpandedEmail?.listUnsubscribe ?? '',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
-25
@@ -9,34 +9,18 @@ import 'package:tmail_ui_user/features/thread_detail/presentation/utils/thread_d
|
||||
|
||||
extension InitializeThreadDetailEmails on ThreadDetailController {
|
||||
void initializeThreadDetailEmails(GetThreadByIdSuccess success) {
|
||||
final threadDetailEnabled = isThreadDetailEnabled;
|
||||
final selectedEmail = mailboxDashBoardController.selectedEmail.value;
|
||||
if (!threadDetailEnabled &&
|
||||
selectedEmail != null &&
|
||||
!success.updateCurrentThreadDetail) {
|
||||
consumeState(Stream.value(Right(GetEmailsByIdsSuccess([selectedEmail]))));
|
||||
return;
|
||||
}
|
||||
|
||||
final existingEmailIds = emailIdsPresentation.keys.toList();
|
||||
final selectedEmailId = mailboxDashBoardController.selectedEmail.value?.id;
|
||||
if (skipLoadThreadMetaData(
|
||||
selectedEmailId: selectedEmailId,
|
||||
updateCurrentThreadDetail: success.updateCurrentThreadDetail,
|
||||
)) return;
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
emailIdsToLoadMetaData = ThreadDetailPresentationUtils.getFirstLoadEmailIds(
|
||||
emailIdsPresentation.keys.toList(),
|
||||
selectedEmailId: selectedEmailId,
|
||||
);
|
||||
emailIdsToLoadMetaData.remove(selectedEmailId);
|
||||
|
||||
if (accountId == null || session == null) {
|
||||
consumeState(Stream.value(Left(GetEmailsByIdsFailure(
|
||||
@@ -45,6 +29,8 @@ extension InitializeThreadDetailEmails on ThreadDetailController {
|
||||
))));
|
||||
return;
|
||||
}
|
||||
if (_currentThreadOnlyContainsSelectedEmail(selectedEmailId)) return;
|
||||
|
||||
consumeState(getEmailsByIdsInteractor.execute(
|
||||
session!,
|
||||
accountId!,
|
||||
@@ -56,4 +42,19 @@ extension InitializeThreadDetailEmails on ThreadDetailController {
|
||||
updateCurrentThreadDetail: success.updateCurrentThreadDetail,
|
||||
));
|
||||
}
|
||||
|
||||
bool _currentThreadOnlyContainsSelectedEmail(EmailId? selectedEmailId) {
|
||||
return selectedEmailId != null &&
|
||||
emailIdsPresentation.length == 1 &&
|
||||
emailIdsPresentation.keys.contains(selectedEmailId);
|
||||
}
|
||||
|
||||
bool skipLoadThreadMetaData({
|
||||
EmailId? selectedEmailId,
|
||||
bool updateCurrentThreadDetail = false,
|
||||
}) {
|
||||
return selectedEmailId == null ||
|
||||
!isThreadDetailEnabled ||
|
||||
updateCurrentThreadDetail;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:model/email/presentation_email.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_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/domain/usecases/get_thread_by_id_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/close_thread_detail_action.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
|
||||
|
||||
extension ThreadDetailOnSelectedEmailUpdated on ThreadDetailController {
|
||||
void onSelectedEmailUpdated(
|
||||
PresentationEmail? selectedEmail,
|
||||
GetThreadByIdInteractor getThreadByIdInteractor,
|
||||
BuildContext? context,
|
||||
) {
|
||||
if (selectedEmail?.id == null) {
|
||||
closeThreadDetailAction(context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentExpandedEmailId.value != null) {
|
||||
mailboxDashBoardController.dispatchEmailUIAction(
|
||||
DisposePreviousExpandedEmailAction(
|
||||
currentExpandedEmailId.value!,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_preloadSelectedEmail(selectedEmail!);
|
||||
}
|
||||
|
||||
void _preloadSelectedEmail(PresentationEmail selectedEmail) {
|
||||
consumeState(Stream.fromIterable([
|
||||
Right(PreloadEmailIdsInThreadSuccess([selectedEmail.id!])),
|
||||
Right(PreloadEmailsByIdsSuccess([selectedEmail])),
|
||||
]));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user