From 54f1fd3a04a111f5c2a980caafa180762e9c5305 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 7 Dec 2022 01:13:13 +0700 Subject: [PATCH] TF-1270 Fix click on an email then another email open --- .../email_supervisor_controller.dart | 133 ++++++++++++----- .../controller/single_email_controller.dart | 140 +++++++++--------- .../email/presentation/email_view.dart | 26 ++-- .../model/page_view_navigator_state.dart | 6 + .../mailbox_dashboard_controller.dart | 12 ++ .../list_presentation_email_extension.dart | 2 + 6 files changed, 202 insertions(+), 117 deletions(-) create mode 100644 lib/features/email/presentation/model/page_view_navigator_state.dart diff --git a/lib/features/email/presentation/controller/email_supervisor_controller.dart b/lib/features/email/presentation/controller/email_supervisor_controller.dart index 5c2aa6d69..226daab65 100644 --- a/lib/features/email/presentation/controller/email_supervisor_controller.dart +++ b/lib/features/email/presentation/controller/email_supervisor_controller.dart @@ -1,39 +1,35 @@ import 'dart:collection'; +import 'package:collection/collection.dart'; import 'package:core/utils/build_utils.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.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:model/model.dart'; -import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:model/extensions/list_presentation_email_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/model/email_loaded.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/page_view_navigator_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; -class EmailSupervisorController extends BaseController { +class EmailSupervisorController extends GetxController { final mailboxDashBoardController = Get.find(); final Queue presentationEmailsLoaded = Queue(); PageController? pageController; - int currentIndexPageView = -1; - final canGetNewerEmail = true.obs; - final canGetOlderEmail = true.obs; - final supportedPageView = RxBool(true); + + int _currentEmailIndex = -1; + final List _currentListEmail = []; + + final pageViewNavigatorState = PageViewNavigatorState.none.obs; + final supportedPageView = RxBool(false); final scrollPhysicsPageView = Rxn(); - Rxn get selectedEmail => mailboxDashBoardController.selectedEmail; - Session? get sessionCurrent => mailboxDashBoardController.sessionCurrent; - AccountId? get accountId => mailboxDashBoardController.accountId.value; + List get currentListEmail => _currentListEmail; - RxList get listEmail { - if (mailboxDashBoardController.searchController.isSearchEmailRunning && !BuildUtils.isWeb) { - return mailboxDashBoardController.listResultSearch; - } else { - return mailboxDashBoardController.emailsInCurrentMailbox; - } - } + int get currentEmailIndex => _currentEmailIndex; + + void setCurrentEmailIndex(int index) => _currentEmailIndex = index; @override void onInit() { @@ -44,45 +40,85 @@ class EmailSupervisorController extends BaseController { @override void onClose() { pageController?.dispose(); + pageController = null; super.onClose(); } - void setCurrentPositionEmailInListEmail(EmailId? currentEmailId) { - currentIndexPageView = listEmail.indexWhere((e) => e.id == currentEmailId); - if(pageController != null && pageController!.hasClients && pageController!.page?.toInt() != currentIndexPageView) { - pageController!.jumpToPage(currentIndexPageView); + void updateNewCurrentListEmail() { + _currentListEmail.clear(); + if (isSearchActivatedOnMobile) { + _currentListEmail.addAll(mailboxDashBoardController.listResultSearch); } else { - pageController = PageController(initialPage: currentIndexPageView); + _currentListEmail.addAll(mailboxDashBoardController.emailsInCurrentMailbox); + } + } + + bool get isSearchActivatedOnMobile { + return mailboxDashBoardController.searchController.isSearchEmailRunning + && !BuildUtils.isWeb; + } + + void createPageControllerAndJumpToEmailById(EmailId currentEmailId) { + _currentEmailIndex = _currentListEmail.matchedIndex(currentEmailId); + if (pageController != null && pageController?.hasClients == true) { + _jumpToPage(_currentEmailIndex); + } else { + pageController = PageController(initialPage: _currentEmailIndex); + } + if (BuildUtils.isWeb) { + _updateStatePageViewNavigator(); } - _checkEnableNavigatorPageView(); } void onPageChanged(int index) { updateScrollPhysicPageView(); - mailboxDashBoardController.selectedEmail.value = listEmail[index]; + mailboxDashBoardController.openEmailDetailedView(currentListEmail[index]); } - void _checkEnableNavigatorPageView() { - canGetNewerEmail.value = listEmail.length > 1 && currentIndexPageView > 0; - canGetOlderEmail.value = listEmail.length > 1 && currentIndexPageView < listEmail.length - 1; + void _updateStatePageViewNavigator() { + if (_currentListEmail.length <= 1) { + pageViewNavigatorState.value = PageViewNavigatorState.none; + } else { + if (_currentEmailIndex > 0 && _currentEmailIndex < _currentListEmail.length - 1) { + pageViewNavigatorState.value = PageViewNavigatorState.all; + } else if (_currentEmailIndex <= 0) { + pageViewNavigatorState.value = PageViewNavigatorState.previous; + } else if (_currentEmailIndex >= _currentListEmail.length - 1) { + pageViewNavigatorState.value = PageViewNavigatorState.next; + } + } } - void getNewerEmail() { - currentIndexPageView = currentIndexPageView - 1; - _jumpToPage(); + bool get nextEmailActivated { + return pageViewNavigatorState.value == PageViewNavigatorState.next || + pageViewNavigatorState.value == PageViewNavigatorState.all; } - void getOlderEmail() { - currentIndexPageView = currentIndexPageView + 1; - _jumpToPage(); + bool get previousEmailActivated { + return pageViewNavigatorState.value == PageViewNavigatorState.previous || + pageViewNavigatorState.value == PageViewNavigatorState.all; } - void _jumpToPage() { + void moveToNextEmail() { + if (nextEmailActivated) { + _currentEmailIndex--; + _jumpToPage(_currentEmailIndex); + } + } + + void backToPreviousEmail() { + if (previousEmailActivated) { + _currentEmailIndex++; + _jumpToPage(_currentEmailIndex); + } + } + + void _jumpToPage(int page) { if (BuildUtils.isWeb) { - pageController?.jumpToPage(currentIndexPageView); + pageController?.jumpToPage(page); } else { pageController?.animateToPage( - currentIndexPageView, + page, duration: const Duration(milliseconds: 500), curve: Curves.easeInToLinear); } @@ -96,7 +132,24 @@ class EmailSupervisorController extends BaseController { } } - @override - void onDone() { + EmailLoaded? getEmailInQueueByEmailId(EmailId emailId) { + return presentationEmailsLoaded.firstWhereOrNull((e) => e.emailCurrent!.id == emailId); + } + + void popFirstEmailQueue() { + presentationEmailsLoaded.removeFirst(); + } + + void popEmailQueue(EmailId? emailId) { + presentationEmailsLoaded.removeWhere((e) => e.emailCurrent!.id == emailId); + } + + void pushEmailQueue(EmailLoaded emailLoaded) { + presentationEmailsLoaded.add(emailLoaded); + } + + void disposePageViewController() { + pageController?.dispose(); + pageController = null; } } \ No newline at end of file diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index fef55dff1..64029c01a 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -9,7 +9,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:debounce_throttle/debounce_throttle.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; @@ -49,6 +48,7 @@ import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_ import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/send_receipt_to_sender_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/page_view_navigator_state.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_dialog_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; @@ -102,16 +102,13 @@ class SingleEmailController extends BaseController with AppLoaderMixin { EmailId? _currentEmailId; Identity? _identitySelected; List? initialEmailContents; - late Worker emailWorker, accountIdWorker; - final Debouncer _getEmailDeBouncer = Debouncer( - const Duration(milliseconds: 800), - initialValue: null, - ); + late Worker selectedEmailWorker, accountIdWorker; + final StreamController> _downloadProgressStateController = StreamController>.broadcast(); Stream> get downloadProgressState => _downloadProgressStateController.stream; - PresentationEmail? get currentEmail => emailSupervisorController.selectedEmail.value; + PresentationEmail? get currentEmail => mailboxDashBoardController.selectedEmail.value; bool get isDisplayFullEmailAddress => emailAddressExpandMode.value == ExpandMode.EXPAND; @@ -131,7 +128,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { @override void onInit() { - _initializeDebounceTimeIndexPageViewChange(); _registerListenerWorker(); _listenDownloadAttachmentProgressState(); super.onInit(); @@ -140,19 +136,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin { @override void onClose() { _downloadProgressStateController.close(); - _getEmailDeBouncer.cancel(); _unregisterListenerWorker(); super.onClose(); } - void _initializeDebounceTimeIndexPageViewChange() { - _getEmailDeBouncer.values.listen((value) async { - if(value !=null) { - _getEmailContentAction(emailSupervisorController.listEmail[value].id); - } - }); - } - void _registerListenerWorker() { accountIdWorker = ever(mailboxDashBoardController.accountId, (accountId) { if (accountId is AccountId) { @@ -163,33 +150,57 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } }); - emailWorker = ever(emailSupervisorController.selectedEmail, (presentationEmail) { - log('SingleEmailController::_initWorker(): $presentationEmail'); - if (presentationEmail is PresentationEmail) { - if (_currentEmailId != presentationEmail.id) { - _currentEmailId = presentationEmail.id; - if (emailSupervisorController.listEmail.isNotEmpty) { - emailSupervisorController.supportedPageView.value = true; - emailSupervisorController.setCurrentPositionEmailInListEmail(_currentEmailId); - _getEmailDeBouncer.value = emailSupervisorController.currentIndexPageView; - } else { - emailSupervisorController.supportedPageView.value = false; - _getEmailContentAction(presentationEmail.id); - } - _resetToOriginalValue(); - if (!presentationEmail.hasRead) { - markAsEmailRead(presentationEmail, ReadActions.markAsRead); - } - if(_identitySelected == null) { - _getAllIdentities(); - } - } - } - }); + selectedEmailWorker = ever( + mailboxDashBoardController.selectedEmail, + _handleOpenEmailDetailedView + ); } void _unregisterListenerWorker() { - emailWorker.dispose(); + accountIdWorker.dispose(); + selectedEmailWorker.dispose(); + } + + void _handleOpenEmailDetailedView(PresentationEmail? selectedEmail) { + if (selectedEmail == null || _currentEmailId == selectedEmail.id) { + log('SingleEmailController::_handleOpenEmailDetailedView(): email unselected'); + return; + } + + emailSupervisorController.updateNewCurrentListEmail(); + _updateCurrentEmailId(selectedEmail.id); + _resetToOriginalValue(); + + if (emailSupervisorController.currentListEmail.isNotEmpty) { + _createMultipleEmailViewAsPageView(selectedEmail.id); + } else { + _createSingleEmailView(selectedEmail.id); + } + + if (!selectedEmail.hasRead) { + markAsEmailRead(selectedEmail, ReadActions.markAsRead); + } + + if (_identitySelected == null) { + _getAllIdentities(); + } + } + + void _updateCurrentEmailId(EmailId? emailId) { + _currentEmailId = emailId; + } + + void _createMultipleEmailViewAsPageView(EmailId emailId) { + log('SingleEmailController::_createMultipleEmailViewAsPageView():'); + emailSupervisorController.supportedPageView.value = true; + emailSupervisorController.createPageControllerAndJumpToEmailById(emailId); + _getEmailContentAction(emailId); + } + + void _createSingleEmailView(EmailId emailId) { + log('SingleEmailController::_createSingleEmailView():'); + emailSupervisorController.supportedPageView.value = false; + _getEmailContentAction(emailId); } void _listenDownloadAttachmentProgressState() { @@ -242,7 +253,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } void _getAllIdentities() { - final accountId = emailSupervisorController.accountId; + final accountId = mailboxDashBoardController.accountId.value; if (accountId != null) { consumeState(_getAllIdentitiesInteractor.execute(accountId)); } @@ -262,12 +273,11 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } void _getEmailContentAction(EmailId emailId) async { - final accountId = emailSupervisorController.accountId; - final baseDownloadUrl = emailSupervisorController.sessionCurrent?.getDownloadUrl(); - final currentEmail = emailSupervisorController.presentationEmailsLoaded.where((e) => e.emailCurrent!.id == emailId); - EmailLoaded? emailLoaded; - if(currentEmail.isNotEmpty) { - emailLoaded = currentEmail.first; + final accountId = mailboxDashBoardController.accountId.value; + final baseDownloadUrl = mailboxDashBoardController.sessionCurrent?.getDownloadUrl(); + final emailLoaded = emailSupervisorController.getEmailInQueueByEmailId(emailId); + + if (emailLoaded != null) { dispatchState(Right(GetEmailContentLoading())); await Future.delayed(const Duration(milliseconds: 300)); consumeState(Stream.value(Right(GetEmailContentSuccess( @@ -319,17 +329,18 @@ class SingleEmailController extends BaseController with AppLoaderMixin { void _getEmailContentSuccess(GetEmailContentSuccess success) { if(emailSupervisorController.presentationEmailsLoaded.length > ThreadConstants.defaultLimit.value.toInt()) { - emailSupervisorController.presentationEmailsLoaded.removeFirst(); + emailSupervisorController.popFirstEmailQueue(); } - final emailLoaded = EmailLoaded( + emailSupervisorController.popEmailQueue(success.emailCurrent?.id); + + emailSupervisorController.pushEmailQueue(EmailLoaded( success.emailContents.toList(), success.emailContentsDisplayed.toList(), success.attachments.toList(), success.emailCurrent, - ); - emailSupervisorController.presentationEmailsLoaded.removeWhere((e) => e.emailCurrent!.id == emailLoaded.emailCurrent!.id); - emailSupervisorController.presentationEmailsLoaded.add(emailLoaded); - if(success.emailCurrent?.id == mailboxDashBoardController.selectedEmail.value?.id) { + )); + + if (success.emailCurrent?.id == currentEmail?.id) { emailContents.value = success.emailContentsDisplayed; initialEmailContents = success.emailContents; attachments.value = success.attachments; @@ -999,14 +1010,13 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } void closeEmailView(BuildContext context) { - log('SingleEmailController::closeEmailView(): '); if (emailSupervisorController.supportedPageView.isTrue) { - emailSupervisorController.presentationEmailsLoaded.removeWhere((e) => e.emailCurrent?.id == currentEmail?.id); - emailSupervisorController.currentIndexPageView = -1; - _getEmailDeBouncer.value = null; + emailSupervisorController.popEmailQueue(_currentEmailId); + emailSupervisorController.setCurrentEmailIndex(-1); + emailSupervisorController.disposePageViewController(); } mailboxDashBoardController.clearSelectedEmail(); - _currentEmailId = null; + _updateCurrentEmailId(null); _resetToOriginalValue(); _updateRouteOnBrowser(); if (mailboxDashBoardController.searchController.isSearchEmailRunning) { @@ -1118,17 +1128,15 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } void toggleScrollPhysicsPagerView(bool leftDirection) { - log('SingleEmailController::toggleScrollPhysicsPagerView():leftDirection: $leftDirection'); - log('SingleEmailController::toggleScrollPhysicsPagerView():canGetOlderEmail: ${emailSupervisorController.canGetOlderEmail.isTrue}'); - log('SingleEmailController::toggleScrollPhysicsPagerView():canGetNewerEmail: ${emailSupervisorController.canGetNewerEmail.isTrue}'); + final navigatorState = emailSupervisorController.pageViewNavigatorState.value; if (leftDirection) { - if (emailSupervisorController.canGetNewerEmail.isTrue) { - emailSupervisorController.getNewerEmail(); + if (navigatorState == PageViewNavigatorState.next) { + emailSupervisorController.moveToNextEmail(); } } else { - if (emailSupervisorController.canGetOlderEmail.isTrue) { - emailSupervisorController.getOlderEmail(); + if (navigatorState == PageViewNavigatorState.previous) { + emailSupervisorController.backToPreviousEmail(); } } } diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index 1bd9bb77f..b84b04f8d 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -105,7 +105,7 @@ class EmailView extends GetWidget const Divider(color: AppColor.colorDividerHorizontal, height: 1), Expanded(child: Obx(() { return controller.emailSupervisorController.supportedPageView.isTrue - ? _buildMultipleEmailView(controller.emailSupervisorController.listEmail) + ? _buildMultipleEmailView(controller.emailSupervisorController.currentListEmail) : _buildSingleEmailView(context, email); }), ), @@ -196,21 +196,25 @@ class EmailView extends GetWidget buildIconWeb( icon: SvgPicture.asset( imagePaths.icNewer, - color: controller.emailSupervisorController.canGetNewerEmail.value ? AppColor.primaryColor : AppColor.colorAttachmentIcon, + color: controller.emailSupervisorController.nextEmailActivated + ? AppColor.primaryColor + : AppColor.colorAttachmentIcon, width: IconUtils.defaultIconSize, height: IconUtils.defaultIconSize, fit: BoxFit.fill), tooltip: AppLocalizations.of(context).newer, - onTap: controller.emailSupervisorController.canGetNewerEmail.value ? controller.emailSupervisorController.getNewerEmail : null), + onTap: controller.emailSupervisorController.moveToNextEmail), buildIconWeb( icon: SvgPicture.asset( imagePaths.icOlder, width: IconUtils.defaultIconSize, height: IconUtils.defaultIconSize, - color: controller.emailSupervisorController.canGetOlderEmail.value ? AppColor.primaryColor : AppColor.colorAttachmentIcon, + color: controller.emailSupervisorController.previousEmailActivated + ? AppColor.primaryColor + : AppColor.colorAttachmentIcon, fit: BoxFit.fill), tooltip: AppLocalizations.of(context).older, - onTap: controller.emailSupervisorController.canGetOlderEmail.value ? controller.emailSupervisorController.getOlderEmail : null), + onTap: controller.emailSupervisorController.backToPreviousEmail), ]; } @@ -324,7 +328,7 @@ class EmailView extends GetWidget constraints.maxWidth), ), )), - _buildEmailReceivedTime(context), + _buildEmailReceivedTime(context, presentationEmail), ]), if (presentationEmail.numberOfAllEmailAddress() > 0) Obx(() => Row( @@ -369,14 +373,14 @@ class EmailView extends GetWidget }); } - Widget _buildEmailReceivedTime(BuildContext context) { + Widget _buildEmailReceivedTime(BuildContext context, PresentationEmail presentationEmail) { return Container( constraints: const BoxConstraints(maxWidth: 100), color: Colors.transparent, child: Text( - '${controller.currentEmail?.getReceivedAt( - Localizations.localeOf(context).toLanguageTag(), - pattern: controller.currentEmail?.receivedAt?.value.toLocal().toPatternForEmailView())}', + presentationEmail.getReceivedAt( + Localizations.localeOf(context).toLanguageTag(), + pattern: presentationEmail.receivedAt?.value.toLocal().toPatternForEmailView()), maxLines: 1, overflow: CommonTextStyle.defaultTextOverFlow, softWrap: CommonTextStyle.defaultSoftWrap, @@ -747,7 +751,7 @@ class EmailView extends GetWidget } Widget _buildEmailContent(BuildContext context, BoxConstraints constraints, PresentationEmail email) { - if(email.id != controller.mailboxDashBoardController.selectedEmail.value?.id) { + if(email.id != controller.currentEmail?.id) { return const SizedBox.shrink(); } return Obx(() { diff --git a/lib/features/email/presentation/model/page_view_navigator_state.dart b/lib/features/email/presentation/model/page_view_navigator_state.dart new file mode 100644 index 000000000..69668bf1b --- /dev/null +++ b/lib/features/email/presentation/model/page_view_navigator_state.dart @@ -0,0 +1,6 @@ +enum PageViewNavigatorState { + all, + previous, + next, + none, +} \ No newline at end of file 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 fefb6ad57..51badac3a 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -89,6 +89,7 @@ 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/navigation_router.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; +import 'package:tmail_ui_user/main/routes/route_utils.dart'; import 'package:tmail_ui_user/main/routes/router_arguments.dart'; import 'package:tmail_ui_user/main/utils/email_receive_manager.dart'; @@ -432,6 +433,17 @@ class MailboxDashBoardController extends ReloadableController { selectedEmail.value = null; } + void openEmailDetailedView(PresentationEmail presentationEmail) { + setSelectedEmail(presentationEmail); + dispatchRoute(DashboardRoutes.emailDetailed); + if (BuildUtils.isWeb && presentationEmail.routeWeb != null) { + RouteUtils.updateRouteOnBrowser( + 'Email-${presentationEmail.id.id.value}', + presentationEmail.routeWeb! + ); + } + } + void openMailboxMenuDrawer() { scaffoldKey.currentState?.openDrawer(); } diff --git a/model/lib/extensions/list_presentation_email_extension.dart b/model/lib/extensions/list_presentation_email_extension.dart index 309609413..4cac4d68b 100644 --- a/model/lib/extensions/list_presentation_email_extension.dart +++ b/model/lib/extensions/list_presentation_email_extension.dart @@ -96,4 +96,6 @@ extension ListPresentationEmailExtension on List { } }).toList(); } + + int matchedIndex(EmailId emailId) => indexWhere((email) => email.id == emailId); } \ No newline at end of file