TF-3643 Thread Detail get emails meta data method
TF-3643 Thread Detail cache email with thread id TF-3643 Thread Detail view thread detail TF-3643 Thread Detail [Part 2 fixup] remove ThreadDetailController dependency from MailboxDashboardController TF-3643 Thread Detail [Part 2 fixup] remove EmailSupervisorController dependency from ThreadDetailController TF-3643 Thread Detail [Part 2 fixup] move isSearchRunning check into ThreadDetailController TF-3643 Thread Detail [Part 2 fixup] ensure GetEmailsByIdsInteractor performance TF-3643 Thread Detail [Part 2 fixup] unit test loadMoreThreadDetailEmails TF-3643 Thread Detail [Part 2 fixup] unit test getEmailIdsToLoad TF-3643 Thread Detail [Part 2 fixup] optimize ThreadDetailBinding bindingsDataSource TF-3643 Thread Detail [Part 2 fixup] optimize getThreadDetailLoadingView TF-3643 Thread Detail [Part 2 fixup] readd unique ValueKey for HtmlContentViewerOnWebWidget TF-3643 Thread Detail [Part 2 fixup] unit test initializeThreadDetailEmails TF-3643 Thread Detail [Part 3 fixup] Update private action button methods TF-3643 Thread Detail [Part 4 fixup] Collapsed email preview 65 characters max TF-3643 Thread Detail [Part 4 fixup] Remove personal scripts TF-3643 Thread Detail move EmailInThreadStatus into PresentationEmail TF-3643 Thread Detail refactor toggle collapse expand TF-3643 Thread Detail EmailViewAppBarWidget required parameters TF-3643 Thread Detail tap avatar toggle expansion TF-3643 Thread Detail fix cannot open single email TF-3643 Thread Detail fix open thread first time blank view TF-3643 Thread Detail fix thread detail UI TF-3643 Thread Detail fix round bottom view TF-3643 Thread Detail fix round bottom view TF-3643 Thread Detail bottom actions depend on current expanded email TF-3643 Thread Detail hide next previous actions TF-3643 Thread Detail fix load more show initial loading view TF-3643 Thread Detail fix cannot view single email TF-3643 Thread Detail fix bottom place holder not aligned TF-3643 Thread Detail fix back to search view TF-3643 Thread Detail fix display 65 character preview collapsed email TF-3643 Thread Detail fix search not work TF-3643 Thread Detail fix bottom place holder UI TF-3643 Thread Detail fix expand view TF-3643 Thread Detail jump to top of last email view TF-3643 Thread Detail remove toMe condition of filtering thread TF-3643 Thread Detail fix sort thread detail TF-3643 Thread Detail fix assert not work in release TF-3643 Thread Detail add load more test case <20 TF-3643 Thread Detail remove the use of currentThreadId TF-3643 Thread Detail remove the use of selectedEmail in SingleEmailController TF-3643 Thread Detail fix unit test SingleEmailController calendar TF-3643 Thread Detail disable 2 way scroll control TF-3643 Thread Detail allow thread to load sent email incase thread only contains 1 email TF-3643 Thread Detail disable single email page view TF-3643 Thread Detail update emailsNotLoadedCount name TF-3643 Thread Detail remove collapsed email char limit TF-3643 Thread Detail fix CI TF-3643 Thread Detail unit test handleGetEmailIdsByThreadIdSuccess TF-3643 Thread Detail make _currentEmailId private TF-3643 Thread Detail fix search click users TF-3643 Thread Detail fix scroll jump TF-3643 Thread Detail fix scroll jump TF-3643 Thread Detail fix web back action TF-3643 Thread Detail round bottom actions TF-3643 Thread Detail fix tablet large empty email TF-3643 Thread Detail fix email subject selectable TF-3643 Thread Detail fix email single view with top divider TF-3643 Thread Detail fix email cache threadId missing TF-3643 Thread Detail fix filter sent email in thread details TF-3643 Thread Detail fix select text TF-3643 Thread Detail fix subject alignment TF-3643 Thread Detail fix subject alignment TF-3643 Thread Detail fix tablet large empty email TF-3643 Thread Detail add nullcheck for _jumpScrollViewToTopOfEmail TF-3643 Thread Detail decouple ThreadDetailController from SingleEmailController TF-3643 Thread Detail combine emailIds to emailIdsPresentation TF-3643 Thread Detail remove currentEmailLoaded obx listener of SingleEmailController TF-3643 Thread Detail replace threadAction with an email ui action TF-3643 Thread Detail fix top and bottom bar delay displaying TF-3643 Thread Detail update ReplyForwardBottomBarAction to PerformEmailActionInThreadDetailAction TF-3643 Thread Detail use getOwnEmailAddress in ThreadDetailAppBar TF-3643 Thread Detail add TextOverflow.ellipsis to EmailSubjectWidget TF-3643 Thread Detail fix round border threadDetailLoadingView
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
|
||||
|
||||
extension CloseThreadDetailAction on ThreadDetailController {
|
||||
void closeThreadDetailAction(BuildContext? context) {
|
||||
if (isSearchRunning) {
|
||||
mailboxDashBoardController.dispatchRoute(DashboardRoutes.searchEmail);
|
||||
} else {
|
||||
mailboxDashBoardController.dispatchRoute(DashboardRoutes.thread);
|
||||
}
|
||||
for (var emailId in emailIdsPresentation.keys) {
|
||||
mailboxDashBoardController.dispatchEmailUIAction(
|
||||
CloseEmailInThreadDetailAction(emailId),
|
||||
);
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
|
||||
|
||||
extension FocusThreadDetailExpandedEmail on ThreadDetailController {
|
||||
void focusExpandedEmail(EmailId emailId) {
|
||||
Future.delayed(const Duration(milliseconds: 200), () {
|
||||
final context = GlobalObjectKey(emailId.id.value).currentContext;
|
||||
if (context != null && context.mounted) {
|
||||
Scrollable.ensureVisible(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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,
|
||||
}) {
|
||||
final isLoading = viewState.value.fold(
|
||||
(failure) => false,
|
||||
(success) => success is GettingThreadById
|
||||
);
|
||||
if (!isLoading) return const SizedBox.shrink();
|
||||
|
||||
return Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
margin: isResponsiveDesktop
|
||||
? const EdgeInsetsDirectional.only(end: 16)
|
||||
: null,
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: const SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: CupertinoLoadingWidget(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/email_view.dart';
|
||||
import 'package:model/email/email_in_thread_status.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/load_more_thread_detail_emails.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/toggle_thread_detail_collape_expand.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/widgets/thread_detail_collapsed_email.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/widgets/thread_detail_load_more_circle.dart';
|
||||
|
||||
extension GetThreadDetailEmailViews on ThreadDetailController {
|
||||
List<Widget> getThreadDetailEmailViews() {
|
||||
int? firstEmailNotLoadedIndex;
|
||||
if (emailsToLoadMoreCount > 0) {
|
||||
final firstNotLoadedEmailId = emailIdsPresentation.entries.firstWhereOrNull(
|
||||
(entry) => entry.value == null
|
||||
)?.key;
|
||||
if (firstNotLoadedEmailId == null) {
|
||||
firstEmailNotLoadedIndex = -1;
|
||||
} else {
|
||||
firstEmailNotLoadedIndex = emailIdsPresentation
|
||||
.keys
|
||||
.toList()
|
||||
.indexOf(firstNotLoadedEmailId);
|
||||
}
|
||||
}
|
||||
|
||||
return emailIdsPresentation.entries.map((entry) {
|
||||
final emailId = entry.key;
|
||||
final presentationEmail = entry.value;
|
||||
final indexOfEmailId = emailIdsPresentation.keys.toList().indexOf(emailId);
|
||||
if (presentationEmail == null) {
|
||||
if (indexOfEmailId != firstEmailNotLoadedIndex) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return ThreadDetailLoadMoreCircle(
|
||||
count: emailsToLoadMoreCount,
|
||||
onTap: loadMoreThreadDetailEmails,
|
||||
imagePaths: imagePaths,
|
||||
isLoading: loadingThreadDetail,
|
||||
);
|
||||
}
|
||||
|
||||
if (presentationEmail.emailInThreadStatus == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final isFirstEmailInThreadDetail = indexOfEmailId == 0;
|
||||
|
||||
if (presentationEmail.emailInThreadStatus == EmailInThreadStatus.collapsed) {
|
||||
return ThreadDetailCollapsedEmail(
|
||||
presentationEmail: presentationEmail.copyWith(
|
||||
subject: isFirstEmailInThreadDetail
|
||||
? emailIdsPresentation.values.last?.subject
|
||||
: null
|
||||
),
|
||||
showSubject: isFirstEmailInThreadDetail,
|
||||
imagePaths: imagePaths,
|
||||
responsiveUtils: responsiveUtils,
|
||||
mailboxContain: presentationEmail.mailboxContain,
|
||||
emailLoaded: null,
|
||||
openEmailAddressDetailAction: (context, emailAddress) {
|
||||
// TODO: Next PR
|
||||
},
|
||||
onToggleThreadDetailCollapseExpand: () {
|
||||
toggleThreadDetailCollapeExpand(presentationEmail);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsetsDirectional.only(bottom: 16),
|
||||
child: EmailView(
|
||||
key: GlobalObjectKey(presentationEmail.id?.id.value ?? ''),
|
||||
isInsideThreadDetailView: true,
|
||||
emailId: presentationEmail.id,
|
||||
isFirstEmailInThreadDetail: isFirstEmailInThreadDetail,
|
||||
threadSubject: isFirstEmailInThreadDetail
|
||||
? emailIdsPresentation.values.last?.subject
|
||||
: null,
|
||||
onToggleThreadDetailCollapseExpand: () {
|
||||
toggleThreadDetailCollapeExpand(presentationEmail);
|
||||
},
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -5,6 +5,14 @@ extension HandleGetEmailIdsByThreadIdSuccess on ThreadDetailController {
|
||||
void handleGetEmailIdsByThreadIdSuccess(
|
||||
GetThreadByIdSuccess success,
|
||||
) {
|
||||
emailIds.value = success.emailIds;
|
||||
if (success.emailIds.isNotEmpty) {
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/bindings/email_bindings.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart';
|
||||
import 'package:model/email/email_in_thread_status.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/presentation/thread_detail_controller.dart';
|
||||
|
||||
extension HandleGetEmailsByIdsSuccess on ThreadDetailController {
|
||||
void handleGetEmailsByIdsSuccess(GetEmailsByIdsSuccess success) {
|
||||
final currentRoute = mailboxDashBoardController.dashboardRoute.value;
|
||||
if (currentRoute != DashboardRoutes.threadDetailed) {
|
||||
return;
|
||||
}
|
||||
|
||||
final isLoadMore = emailIdsPresentation.values.whereNotNull().isNotEmpty;
|
||||
final currentScrollPosition = scrollController?.position.pixels;
|
||||
final maxScrollExtent = scrollController?.position.maxScrollExtent;
|
||||
final currentBottomScrollPosition = currentScrollPosition != null
|
||||
&& maxScrollExtent != null
|
||||
? maxScrollExtent - currentScrollPosition
|
||||
: null;
|
||||
|
||||
for (var presentationEmail in success.presentationEmails) {
|
||||
if (presentationEmail.id == null) continue;
|
||||
if (presentationEmail.id == emailIdsPresentation.keys.last) {
|
||||
EmailBindings(currentEmailId: presentationEmail.id).dependencies();
|
||||
currentExpandedEmailId.value = presentationEmail.id;
|
||||
}
|
||||
emailIdsPresentation[presentationEmail.id!] = presentationEmail.copyWith(
|
||||
emailInThreadStatus: presentationEmail.id == emailIdsPresentation.keys.last
|
||||
? EmailInThreadStatus.expanded
|
||||
: EmailInThreadStatus.collapsed,
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadMore) return;
|
||||
|
||||
final currentExpandedEmailIndex = currentExpandedEmailId.value == null
|
||||
? -1
|
||||
: emailIdsPresentation.keys.toList().indexOf(currentExpandedEmailId.value!);
|
||||
final firstLoadedMoreEmailId = success.presentationEmails.firstOrNull?.id;
|
||||
final firstLoadedMoreEmailIndex = firstLoadedMoreEmailId == null
|
||||
? -1
|
||||
: emailIdsPresentation.keys.toList().indexOf(firstLoadedMoreEmailId);
|
||||
if (currentExpandedEmailIndex == -1 || firstLoadedMoreEmailIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentBottomScrollPosition != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final newMaxScrollExtent = scrollController?.position.maxScrollExtent;
|
||||
if (newMaxScrollExtent == null) return;
|
||||
|
||||
if (currentExpandedEmailIndex < firstLoadedMoreEmailIndex) {
|
||||
return;
|
||||
} else if (newMaxScrollExtent != maxScrollExtent!) {
|
||||
scrollController?.jumpTo(newMaxScrollExtent - currentBottomScrollPosition);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import 'package:dartz/dartz.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/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() {
|
||||
final emailIdToLoadContent = emailIdsPresentation.keys.last;
|
||||
final emailIdsToLoadMetaData = ThreadDetailPresentationUtils.getEmailIdsToLoad(
|
||||
Map.from(emailIdsPresentation)..remove(emailIdToLoadContent),
|
||||
);
|
||||
|
||||
if (accountId == null || session == null) {
|
||||
consumeState(Stream.value(Left(GetEmailsByIdsFailure(
|
||||
exception: NotFoundSessionException(),
|
||||
))));
|
||||
return;
|
||||
}
|
||||
consumeState(getEmailsByIdsInteractor.execute(
|
||||
session!,
|
||||
accountId!,
|
||||
[...emailIdsToLoadMetaData, emailIdToLoadContent],
|
||||
properties: EmailUtils.getPropertiesForEmailGetMethod(
|
||||
session!,
|
||||
accountId!,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:dartz/dartz.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/presentation/thread_detail_controller.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/utils/thread_detail_presentation_utils.dart';
|
||||
|
||||
extension LoadMoreThreadDetailEmails on ThreadDetailController {
|
||||
void loadMoreThreadDetailEmails() {
|
||||
if (accountId == null || session == null) {
|
||||
consumeState(Stream.value(Left(GetEmailsByIdsFailure(
|
||||
exception: NotFoundSessionException(),
|
||||
))));
|
||||
return;
|
||||
}
|
||||
|
||||
final emailIdsToLoadMetaData = ThreadDetailPresentationUtils
|
||||
.getEmailIdsToLoad(emailIdsPresentation);
|
||||
if (emailIdsToLoadMetaData.isEmpty) {
|
||||
return;
|
||||
}
|
||||
consumeState(getEmailsByIdsInteractor.execute(
|
||||
session!,
|
||||
accountId!,
|
||||
emailIdsToLoadMetaData,
|
||||
properties: EmailUtils.getPropertiesForEmailGetMethod(
|
||||
session!,
|
||||
accountId!,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:model/email/presentation_email.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/bindings/email_bindings.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart';
|
||||
import 'package:model/email/email_in_thread_status.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/focus_thread_detail_expanded_email.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
|
||||
|
||||
extension ToggleThreadDetailCollapeExpand on ThreadDetailController {
|
||||
void toggleThreadDetailCollapeExpand(PresentationEmail presentationEmail) {
|
||||
final emailId = presentationEmail.id;
|
||||
final expansionStatus = presentationEmail.emailInThreadStatus;
|
||||
if (emailId == null || expansionStatus == null) return;
|
||||
|
||||
if (expansionStatus == EmailInThreadStatus.expanded) {
|
||||
emailIdsPresentation[emailId] = presentationEmail.copyWith(
|
||||
emailInThreadStatus: EmailInThreadStatus.collapsed,
|
||||
);
|
||||
currentExpandedEmailId.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
final isInitialized = Get.isRegistered<SingleEmailController>(
|
||||
tag: emailId.id.value,
|
||||
);
|
||||
if (!isInitialized) {
|
||||
EmailBindings(currentEmailId: presentationEmail.id).dependencies();
|
||||
} else {
|
||||
focusExpandedEmail(emailId);
|
||||
}
|
||||
for (var key in emailIdsPresentation.keys) {
|
||||
if (emailIdsPresentation[key] == null) continue;
|
||||
|
||||
if (key == emailId) {
|
||||
emailIdsPresentation[key] = presentationEmail.copyWith(
|
||||
emailInThreadStatus: EmailInThreadStatus.expanded,
|
||||
);
|
||||
currentExpandedEmailId.value = key;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (emailIdsPresentation[key]?.emailInThreadStatus == EmailInThreadStatus.expanded) {
|
||||
emailIdsPresentation[key] = emailIdsPresentation[key]?.copyWith(
|
||||
emailInThreadStatus: EmailInThreadStatus.collapsed,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user