diff --git a/lib/features/email/presentation/controller/email_supervisor_controller.dart b/lib/features/email/presentation/controller/email_supervisor_controller.dart index eb90ada29..04d21ecbf 100644 --- a/lib/features/email/presentation/controller/email_supervisor_controller.dart +++ b/lib/features/email/presentation/controller/email_supervisor_controller.dart @@ -1,6 +1,6 @@ import 'dart:collection'; +import 'package:core/utils/build_utils.dart'; import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; @@ -25,8 +25,15 @@ class EmailSupervisorController extends BaseController { Session? get sessionCurrent => mailboxDashBoardController.sessionCurrent; AccountId? get accountId => mailboxDashBoardController.accountId.value; - RxList get listEmail => mailboxDashBoardController.searchController.isSearchEmailRunning && !kIsWeb ? - mailboxDashBoardController.listResultSearch : mailboxDashBoardController.emailsInCurrentMailbox; + RxList get listEmail { + if (mailboxDashBoardController.searchController.isSearchEmailRunning && !BuildUtils.isWeb) { + return mailboxDashBoardController.listResultSearch; + } else { + return mailboxDashBoardController.emailsInCurrentMailbox; + } + } + + bool get supportedPageView => listEmail.isNotEmpty; @override void onClose() { diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 13f4e07ec..223e1f76c 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -52,6 +52,7 @@ import 'package:tmail_ui_user/features/email/presentation/model/composer_argumen 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'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/download/download_task_state.dart'; @@ -66,7 +67,9 @@ import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants. import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.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/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:uuid/uuid.dart'; class SingleEmailController extends BaseController with AppLoaderMixin { @@ -129,7 +132,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { @override void onInit() { _initializeDebounceTimeIndexPageViewChange(); - _initWorker(); + _registerListenerWorker(); _listenDownloadAttachmentProgressState(); injectRuleFilterBindings( emailSupervisorController.sessionCurrent, @@ -160,7 +163,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { void onClose() { _downloadProgressStateController.close(); _getEmailDeBouncer.cancel(); - _clearWorker(); + _unregisterListenerWorker(); super.onClose(); } @@ -172,14 +175,18 @@ class SingleEmailController extends BaseController with AppLoaderMixin { }); } - void _initWorker() { + void _registerListenerWorker() { emailWorker = ever(emailSupervisorController.selectedEmail, (presentationEmail) { log('SingleEmailController::_initWorker(): $presentationEmail'); if (presentationEmail is PresentationEmail) { if (_currentEmailId != presentationEmail.id) { _currentEmailId = presentationEmail.id; - emailSupervisorController.setCurrentPositionEmailInListEmail(_currentEmailId); - _getEmailDeBouncer.value = emailSupervisorController.currentIndexPageView; + if (emailSupervisorController.supportedPageView) { + emailSupervisorController.setCurrentPositionEmailInListEmail(_currentEmailId); + _getEmailDeBouncer.value = emailSupervisorController.currentIndexPageView; + } else { + _getEmailContentAction(presentationEmail.id); + } _resetToOriginalValue(); if (!presentationEmail.hasRead) { markAsEmailRead(presentationEmail, ReadActions.markAsRead); @@ -192,8 +199,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin { }); } - void _clearWorker() { - emailWorker.call(); + void _unregisterListenerWorker() { + emailWorker.dispose(); } void _listenDownloadAttachmentProgressState() { @@ -984,12 +991,16 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } void closeEmailView(BuildContext context) { - emailSupervisorController.presentationEmailsLoaded.removeWhere((e) => e.emailCurrent?.id == currentEmail?.id); - emailSupervisorController.currentIndexPageView = -1; - _getEmailDeBouncer.value = null; + log('SingleEmailController::closeEmailView(): '); + if (emailSupervisorController.supportedPageView) { + emailSupervisorController.presentationEmailsLoaded.removeWhere((e) => e.emailCurrent?.id == currentEmail?.id); + emailSupervisorController.currentIndexPageView = -1; + _getEmailDeBouncer.value = null; + } mailboxDashBoardController.clearSelectedEmail(); _currentEmailId = null; _resetToOriginalValue(); + _updateRouteOnBrowser(); if (mailboxDashBoardController.searchController.isSearchEmailRunning) { if (responsiveUtils.isWebDesktop(context)) { mailboxDashBoardController.dispatchRoute(DashboardRoutes.thread); @@ -998,6 +1009,28 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } } else { mailboxDashBoardController.dispatchRoute(DashboardRoutes.thread); + if (isOpenEmailNotMailboxFromRoute) { + mailboxDashBoardController.dispatchAction(SelectMailboxDefaultAction()); + } + } + } + + bool get isOpenEmailNotMailboxFromRoute => !emailSupervisorController.supportedPageView + && mailboxDashBoardController.selectedMailbox.value == null; + + void _updateRouteOnBrowser() { + if (BuildUtils.isWeb) { + final selectedMailboxId = mailboxDashBoardController.selectedMailbox.value?.id; + final route = RouteUtils.generateRouteBrowser( + AppRoutes.dashboard, + NavigationRouter( + mailboxId: selectedMailboxId, + dashboardType: mailboxDashBoardController.searchController.isSearchEmailRunning + ? DashboardType.search + : DashboardType.normal + ) + ); + RouteUtils.updateRouteOnBrowser('Mailbox-${selectedMailboxId?.id.value}', route); } } diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index 0340bf368..e7b0a463f 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -1,7 +1,6 @@ import 'package:core/core.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; @@ -55,7 +54,6 @@ class EmailView extends GetWidget { : const BoxDecoration(color: Colors.white), margin: _getMarginEmailView(context), child: Obx(() { - log('EmailView::_buildLoadingView(): ${controller.currentEmail}'); if (controller.currentEmail != null) { return _buildEmailView(context, controller.currentEmail!); } else { @@ -90,21 +88,28 @@ class EmailView extends GetWidget { _buildAppBar(context, email), _buildVacationNotificationMessage(context), const Divider(color: AppColor.colorDividerHorizontal, height: 1), - Expanded( - child: PageView.builder( - physics: kIsWeb ? const NeverScrollableScrollPhysics() : null, - itemCount: controller.emailSupervisorController.listEmail.length, - controller: controller.emailSupervisorController.pageController, - onPageChanged: controller.emailSupervisorController.onPageChanged, - itemBuilder: (context, index) { - return _buildEmailBody(context, controller.emailSupervisorController.listEmail[index]); - }), + Expanded(child: controller.emailSupervisorController.supportedPageView + ? _buildMultipleEmailView(controller.emailSupervisorController.listEmail) + : _buildSingleEmailView(context, email), ), const Divider(color: AppColor.colorDividerHorizontal, height: 1), _buildBottomBar(context, email), ]); } + Widget _buildMultipleEmailView(List listEmails) { + return PageView.builder( + physics: BuildUtils.isWeb ? const NeverScrollableScrollPhysics() : null, + itemCount: listEmails.length, + controller: controller.emailSupervisorController.pageController, + onPageChanged: controller.emailSupervisorController.onPageChanged, + itemBuilder: (context, index) => _buildSingleEmailView(context, listEmails[index]) + ); + } + + Widget _buildSingleEmailView(BuildContext context, PresentationEmail email) { + return _buildEmailBody(context, email); + } bool _supportVerticalDivider(BuildContext context) { if (BuildUtils.isWeb) { @@ -156,7 +161,9 @@ class EmailView extends GetWidget { _popupMenuEmailActionTile(context, email)); } }, - optionsWidget: kIsWeb ? _buildNavigatorPageViewWidgets(context) : null, + optionsWidget: BuildUtils.isWeb && controller.emailSupervisorController.supportedPageView + ? _buildNavigatorPageViewWidgets(context) + : null, )); } @@ -323,7 +330,6 @@ class EmailView extends GetWidget { return controller.viewState.value.fold( (failure) => const SizedBox.shrink(), (success) { - log('EmailView::_buildLoadingView(): $success'); if (success is LoadingState) { return const Align(alignment: Alignment.topCenter, child: Padding( padding: EdgeInsets.all(16), diff --git a/lib/features/login/presentation/login_controller.dart b/lib/features/login/presentation/login_controller.dart index 0a1becefd..340b8aab6 100644 --- a/lib/features/login/presentation/login_controller.dart +++ b/lib/features/login/presentation/login_controller.dart @@ -194,7 +194,7 @@ class LoginController extends ReloadableController { @override void handleReloaded(Session session) { pushAndPop( - RouteUtils.generateRoutePathMobile(AppRoutes.dashboard, NavigationRouter()), + RouteUtils.generateRouteMobile(AppRoutes.dashboard, NavigationRouter()), arguments: session); } diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index ef47d3231..a486a7b88 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -10,6 +10,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/error/method/error_method_response.dart'; import 'package:jmap_dart_client/jmap/core/id.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:model/model.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; @@ -46,7 +47,6 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_action import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories_expand_mode.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/open_mailbox_view_event.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_utils.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/duplicate_name_validator.dart'; @@ -68,6 +68,7 @@ import 'package:tmail_ui_user/features/thread/presentation/model/search_state.da import 'package:tmail_ui_user/features/thread/presentation/model/search_status.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/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:uuid/uuid.dart'; @@ -108,6 +109,8 @@ class MailboxController extends BaseMailboxController { dashboardActionWorker, routerParametersWorker; + PresentationMailbox? get selectedMailbox => mailboxDashBoardController.selectedMailbox.value; + MailboxController( this._getAllMailboxInteractor, this._refreshAllMailboxInteractor, @@ -154,12 +157,9 @@ class MailboxController extends BaseMailboxController { if (success is GetAllMailboxSuccess) { _currentMailboxState = success.currentMailboxState; await buildTree(success.mailboxList); - _setUpMapMailbox(success.mailboxList, defaultMailboxTree.value, folderMailboxTree.value); - _selectMailboxById(); } else if (success is RefreshChangesAllMailboxSuccess) { _currentMailboxState = success.currentMailboxState; await refreshTree(success.mailboxList); - _setUpMapMailbox(success.mailboxList, defaultMailboxTree.value, folderMailboxTree.value); } }); } @@ -167,32 +167,34 @@ class MailboxController extends BaseMailboxController { @override void onDone() { viewState.value.fold( - (failure) { - if (failure is CreateNewMailboxFailure) { - _createNewMailboxFailure(failure); - } else if (failure is SearchMailboxFailure) { - _searchMailboxFailure(failure); - } else if (failure is DeleteMultipleMailboxFailure) { - _deleteMailboxFailure(failure); - } - }, - (success) { - if (success is CreateNewMailboxSuccess) { - _createNewMailboxSuccess(success); - } else if (success is SearchMailboxSuccess) { - _searchMailboxSuccess(success); - } else if (success is DeleteMultipleMailboxAllSuccess) { - _deleteMultipleMailboxSuccess(success.listMailboxIdDeleted, success.currentMailboxState); - } else if (success is DeleteMultipleMailboxHasSomeSuccess) { - _deleteMultipleMailboxSuccess(success.listMailboxIdDeleted, success.currentMailboxState); - } else if ((success is GetAllMailboxSuccess || success is RefreshChangesAllMailboxSuccess) && isSearchActive()) { - _searchMailboxAction(allMailboxes, searchQuery.value); - } else if (success is RenameMailboxSuccess) { - refreshMailboxChanges(currentMailboxState: success.currentMailboxState); - } else if (success is MoveMailboxSuccess) { - _moveMailboxSuccess(success); - } + (failure) { + if (failure is CreateNewMailboxFailure) { + _createNewMailboxFailure(failure); + } else if (failure is SearchMailboxFailure) { + _searchMailboxFailure(failure); + } else if (failure is DeleteMultipleMailboxFailure) { + _deleteMailboxFailure(failure); } + }, + (success) async { + if (success is GetAllMailboxSuccess || success is RefreshChangesAllMailboxSuccess) { + _initialMailboxVariableStorage(); + } else if (success is CreateNewMailboxSuccess) { + _createNewMailboxSuccess(success); + } else if (success is SearchMailboxSuccess) { + _searchMailboxSuccess(success); + } else if (success is DeleteMultipleMailboxAllSuccess) { + _deleteMultipleMailboxSuccess(success.listMailboxIdDeleted, success.currentMailboxState); + } else if (success is DeleteMultipleMailboxHasSomeSuccess) { + _deleteMultipleMailboxSuccess(success.listMailboxIdDeleted, success.currentMailboxState); + } else if ((success is GetAllMailboxSuccess || success is RefreshChangesAllMailboxSuccess) && isSearchActive()) { + _searchMailboxAction(allMailboxes, searchQuery.value); + } else if (success is RenameMailboxSuccess) { + refreshMailboxChanges(currentMailboxState: success.currentMailboxState); + } else if (success is MoveMailboxSuccess) { + _moveMailboxSuccess(success); + } + } ); } @@ -313,64 +315,128 @@ class MailboxController extends BaseMailboxController { } } - void _setUpMapMailbox(List allMailbox, MailboxTree defaultTree, MailboxTree folderTree) { + void _initialMailboxVariableStorage() { + _setMapMailbox(); + _setOutboxMailbox(); + _selectMailboxDefault(); + } + + void _setMapMailbox() { final mapDefaultMailboxIdByRole = { - for (var mailboxNode - in defaultTree.root.childrenItems ?? List.empty()) + for (var mailboxNode in defaultMailboxTree.value.root.childrenItems ?? List.empty()) mailboxNode.item.role!: mailboxNode.item.id }; - final mapDefaultMailbox = { - for (var mailboxNode - in defaultTree.root.childrenItems ?? List.empty()) - mailboxNode.item.role!: mailboxNode.item - }; - final mapMailboxById = { - for (var presentationMailbox in allMailbox) + for (var presentationMailbox in allMailboxes) presentationMailbox.id: presentationMailbox }; mailboxDashBoardController.setMapDefaultMailboxIdByRole(mapDefaultMailboxIdByRole); mailboxDashBoardController.setMapMailboxById(mapMailboxById); + } + void _setOutboxMailbox() { try { - final outboxMailboxIdByRole = mapDefaultMailboxIdByRole[PresentationMailbox.roleOutbox]; + final outboxMailboxIdByRole = mailboxDashBoardController.mapDefaultMailboxIdByRole[PresentationMailbox.roleOutbox]; if (outboxMailboxIdByRole == null) { - final outboxMailboxByName = allMailbox + final outboxMailboxByName = allMailboxes .firstWhere((mailbox) => mailbox.name?.toLowerCase() == PresentationMailbox.lowerCaseOutboxMailboxName); mailboxDashBoardController.setOutboxMailbox(outboxMailboxByName); } else { - mailboxDashBoardController.setOutboxMailbox(mapMailboxById[outboxMailboxIdByRole]!); + mailboxDashBoardController.setOutboxMailbox(mailboxDashBoardController.mapMailboxById[outboxMailboxIdByRole]!); } } catch (e) { - logError('MailboxController::_setUpMapMailboxIdDefault: Not found outbox mailbox'); + logError('MailboxController::_setOutboxMailbox: Not found outbox mailbox'); mailboxDashBoardController.setOutboxMailbox(null); } + } - var mailboxCurrent = mailboxDashBoardController.selectedMailbox.value; + void _selectMailboxDefault() { + final mapDefaultMailbox = { + for (var mailboxNode in defaultMailboxTree.value.root.childrenItems ?? List.empty()) + mailboxNode.item.role!: mailboxNode.item + }; + final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value; + final mapMailboxById = mailboxDashBoardController.mapMailboxById; + final isSearchEmailRunning = mailboxDashBoardController.searchController.isSearchEmailRunning; + + PresentationMailbox? mailboxSelected; if (mailboxCurrent != null) { if (mailboxCurrent.hasRole()) { - mailboxDashBoardController.setNewFirstSelectedMailbox(mapDefaultMailbox.containsKey(mailboxCurrent.role) + mailboxSelected = mapDefaultMailbox.containsKey(mailboxCurrent.role) ? mapDefaultMailbox[mailboxCurrent.role] - : mailboxCurrent); + : mailboxCurrent; } else { - mailboxDashBoardController.setNewFirstSelectedMailbox(mapMailboxById.containsKey(mailboxCurrent.id) + mailboxSelected = mapMailboxById.containsKey(mailboxCurrent.id) ? mapMailboxById[mailboxCurrent.id] - : mailboxCurrent); + : mailboxCurrent; } } else { - if (!mailboxDashBoardController.searchController.isSearchEmailRunning) { + if (!isSearchEmailRunning) { if (mapDefaultMailbox.containsKey(PresentationMailbox.roleInbox)) { - mailboxDashBoardController.setNewFirstSelectedMailbox(mapDefaultMailbox[PresentationMailbox.roleInbox]); + mailboxSelected = mapDefaultMailbox[PresentationMailbox.roleInbox]; } else { - if (allMailbox.isNotEmpty) { - mailboxDashBoardController.setNewFirstSelectedMailbox(allMailbox.first); + if (allMailboxes.isNotEmpty) { + mailboxSelected = allMailboxes.first; } } } } + + if (isHasDataFromRoute) { + if (mailboxIdFromNavigationRouter != null) { + _selectMailboxFromRouter(); + } else if (emailIdFromNavigationRouter != null) { + mailboxDashBoardController.dispatchAction( + SelectEmailByIdAction( + emailIdFromNavigationRouter!, + mailboxIdFromNavigationRouter, + navigationRouter!.dashboardType + ) + ); + } else { + mailboxDashBoardController.setSelectedMailbox(mailboxSelected); + _updateSelectedMailboxRouteOnBrowser(); + } + } else { + mailboxDashBoardController.setSelectedMailbox(mailboxSelected); + _updateSelectedMailboxRouteOnBrowser(); + } + } + + NavigationRouter? get navigationRouter => mailboxDashBoardController.navigationRouter; + + bool get isHasDataFromRoute => BuildUtils.isWeb && navigationRouter != null; + + MailboxId? get mailboxIdFromNavigationRouter => navigationRouter?.mailboxId; + + EmailId? get emailIdFromNavigationRouter => navigationRouter?.emailId; + + void _clearNavigationRouter() { + mailboxDashBoardController.navigationRouter = null; + } + + void _selectMailboxFromRouter() { + final matchedMailboxNode = findMailboxNodeById(mailboxIdFromNavigationRouter!); + if (matchedMailboxNode != null) { + mailboxDashBoardController.setSelectedMailbox(matchedMailboxNode.item); + if (emailIdFromNavigationRouter != null) { + mailboxDashBoardController.dispatchAction( + SelectEmailByIdAction( + emailIdFromNavigationRouter!, + mailboxIdFromNavigationRouter, + navigationRouter!.dashboardType + ) + ); + } else { + _updateSelectedMailboxRouteOnBrowser(); + } + _clearNavigationRouter(); + } else { + _clearNavigationRouter(); + } } void _handleOpenMailbox( @@ -386,6 +452,7 @@ class MailboxController extends BaseMailboxController { _disableAllSearchEmail(); mailboxDashBoardController.setSelectedMailbox(presentationMailboxSelected); + _updateSelectedMailboxRouteOnBrowser(); if (mailboxDashBoardController.isDrawerOpen) { mailboxDashBoardController.closeMailboxMenuDrawer(); @@ -748,7 +815,11 @@ class MailboxController extends BaseMailboxController { void _switchBackToMailboxDefault() { final inboxMailbox = findMailboxNodeByRole(PresentationMailbox.roleInbox); mailboxDashBoardController.setSelectedMailbox(inboxMailbox?.item); - mailboxListScrollController.animateTo(0, duration: const Duration(milliseconds: 500), curve: Curves.fastOutSlowIn); + _updateSelectedMailboxRouteOnBrowser(); + mailboxListScrollController.animateTo( + 0, + duration: const Duration(milliseconds: 500), + curve: Curves.fastOutSlowIn); } void _deleteMailboxFailure(DeleteMultipleMailboxFailure failure) { @@ -996,30 +1067,11 @@ class MailboxController extends BaseMailboxController { } } - void _selectMailboxById() { - if (mailboxDashBoardController.navigationRouter != null) { - final mailboxId = mailboxDashBoardController.navigationRouter?.mailboxId; - if (mailboxId != null) { - final matchedMailboxNode = findMailboxNodeById(mailboxId); - if (matchedMailboxNode != null && currentContext != null) { - mailboxDashBoardController.setSelectedMailbox(matchedMailboxNode.item); - } - } - - final emailId = mailboxDashBoardController.navigationRouter?.emailId; - if (emailId != null) { - mailboxDashBoardController.dispatchAction(SelectEmailByIdAction(emailId)); - } - - mailboxDashBoardController.navigationRouter = null; - } - } - void _handleNavigationRouteParameters(Map? parameters) { - log('MailboxDashBoardController::_handleNavigationRouteParameters(): parameters: $parameters'); + log('MailboxController::_handleNavigationRouteParameters(): parameters: $parameters'); if (parameters != null) { final navigationRouter = RouteUtils.parsingRouteParametersToNavigationRouter(parameters); - log('ThreadController::_handleNavigationRouteParameters():navigationRouter: $navigationRouter'); + log('MailboxController::_handleNavigationRouteParameters():navigationRouter: $navigationRouter'); mailboxDashBoardController.navigationRouter = navigationRouter; } } @@ -1053,6 +1105,20 @@ class MailboxController extends BaseMailboxController { } } + void _updateSelectedMailboxRouteOnBrowser() { + if (BuildUtils.isWeb) { + final selectedMailboxId = selectedMailbox?.id; + final route = RouteUtils.generateRouteBrowser( + AppRoutes.dashboard, + NavigationRouter( + mailboxId: selectedMailboxId, + dashboardType: DashboardType.normal + ) + ); + RouteUtils.updateRouteOnBrowser('Mailbox-${selectedMailboxId?.id.value}', route); + } + } + void closeMailboxScreen(BuildContext context) { _cancelSelectMailbox(); mailboxDashBoardController.closeMailboxMenuDrawer(); diff --git a/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart b/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart index 43a8e50ca..b71a19c6f 100644 --- a/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart +++ b/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart @@ -1,10 +1,12 @@ import 'package:flutter/cupertino.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/email_action_type.dart'; import 'package:model/email/presentation_email.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; +import 'package:tmail_ui_user/main/routes/navigation_router.dart'; class DashBoardAction extends UIAction { static final idle = DashBoardAction(); @@ -115,9 +117,11 @@ class SelectMailboxDefaultAction extends DashBoardAction { class SelectEmailByIdAction extends DashBoardAction { final EmailId emailId; + final MailboxId? mailboxId; + final DashboardType dashboardType; - SelectEmailByIdAction(this.emailId); + SelectEmailByIdAction(this.emailId, this.mailboxId, this.dashboardType); @override - List get props => [emailId]; + List get props => [emailId, mailboxId, dashboardType]; } \ 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 1cfa7d334..da9a5749d 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -326,7 +326,7 @@ class MailboxDashBoardController extends ReloadableController { void _getSessionCurrent() { final arguments = Get.arguments; - log('MailboxDashBoardController::_setSessionCurrent(): arguments = $arguments'); + log('MailboxDashBoardController::_getSessionCurrent(): arguments = $arguments'); if (arguments is Session) { sessionCurrent = arguments; accountId.value = sessionCurrent?.accounts.keys.first; @@ -381,12 +381,13 @@ class MailboxDashBoardController extends ReloadableController { } void setSelectedMailbox(PresentationMailbox? newPresentationMailbox) { - selectedMailbox.value = newPresentationMailbox; - } - - void setNewFirstSelectedMailbox(PresentationMailbox? newPresentationMailbox) { - selectedMailbox.firstRebuild = true; - selectedMailbox.value = newPresentationMailbox; + final previousMailbox = selectedMailbox.value; + if (previousMailbox == newPresentationMailbox) { + selectedMailbox.value = newPresentationMailbox; + selectedMailbox.refresh(); + } else { + selectedMailbox.value = newPresentationMailbox; + } } void setSelectedEmail(PresentationEmail? newPresentationEmail) { @@ -885,7 +886,7 @@ class MailboxDashBoardController extends ReloadableController { Function? onCancelSelectionEmail, } ) { - if (_responsiveUtils.isMobile(context)) { + if (_responsiveUtils.isScreenWithShortestSide(context)) { (ConfirmationDialogActionSheetBuilder(context) ..messageText(actionType.getContentDialog( context, @@ -1020,6 +1021,8 @@ class MailboxDashBoardController extends ReloadableController { @override void handleReloaded(Session session) { + log('MailboxDashBoardController::handleReloaded():'); + _getRouteParameters(); sessionCurrent = session; accountId.value = sessionCurrent?.accounts.keys.first; _getUserProfile(); @@ -1028,7 +1031,6 @@ class MailboxDashBoardController extends ReloadableController { injectRuleFilterBindings(sessionCurrent, accountId.value); injectVacationBindings(sessionCurrent, accountId.value); _getVacationResponse(); - _getRouteParameters(); } void _getRouteParameters() { diff --git a/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart b/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart index db69a6d5c..6a770cad0 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart @@ -227,7 +227,7 @@ class ManageAccountDashBoardController extends ReloadableController { } else { log('ManageAccountDashBoardController::backToMailboxDashBoard(): canBack: FALSE'); pushAndPopAll( - RouteUtils.generateRoutePathMobile(AppRoutes.dashboard, NavigationRouter()), + RouteUtils.generateRouteMobile(AppRoutes.dashboard, NavigationRouter()), arguments: sessionCurrent); } } diff --git a/lib/features/search/presentation/search_email_controller.dart b/lib/features/search/presentation/search_email_controller.dart index 7041e52d8..9694c0135 100644 --- a/lib/features/search/presentation/search_email_controller.dart +++ b/lib/features/search/presentation/search_email_controller.dart @@ -53,7 +53,9 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/search_more_email_ import 'package:tmail_ui_user/features/thread/presentation/mixin/email_action_controller.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.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'; class SearchEmailController extends BaseController with EmailActionController { @@ -727,6 +729,22 @@ class SearchEmailController extends BaseController } } + PresentationEmail generateEmailByPlatform(PresentationEmail currentEmail) { + if (BuildUtils.isWeb) { + final route = RouteUtils.generateRouteBrowser( + AppRoutes.dashboard, + NavigationRouter( + emailId: currentEmail.id, + dashboardType: DashboardType.search + ) + ); + final emailOnWeb = currentEmail.withRouteWeb(route); + return emailOnWeb; + } else { + return currentEmail; + } + } + @override void onClose() { textInputSearchController.dispose(); diff --git a/lib/features/search/presentation/search_email_view.dart b/lib/features/search/presentation/search_email_view.dart index 2c7765b31..ddbacddac 100644 --- a/lib/features/search/presentation/search_email_view.dart +++ b/lib/features/search/presentation/search_email_view.dart @@ -420,31 +420,33 @@ class SearchEmailView extends GetWidget key: const PageStorageKey('list_presentation_email_in_search_view'), itemExtent: _getItemExtent(context), itemCount: listPresentationEmail.length, - itemBuilder: (context, index) => Obx(() => (EmailTileBuilder( - context, - listPresentationEmail[index], - controller.selectionMode.value, - controller.searchQuery, - controller.mailboxDashBoardController.selectedEmail.value?.id == listPresentationEmail[index].id, - isSearchEmailRunning: true, - padding: SearchEmailUtils.getPaddingSearchResultList(context, _responsiveUtils), - paddingDivider: SearchEmailUtils.getPaddingDividerSearchResultList(context, _responsiveUtils), - mailboxCurrent: listPresentationEmail[index].findMailboxContain( - controller.mailboxDashBoardController.mapMailboxById)) - ..addOnPressEmailActionClick((action, email) => - controller.pressEmailAction( - context, - action, - email, - mailboxContain: email.findMailboxContain( - controller.mailboxDashBoardController.mapMailboxById))) - ..addOnMoreActionClick((email, position) => _responsiveUtils.isMobile(context) - ? controller.openContextMenuAction(context, _contextMenuActionTile(context, email)) - : controller.openPopupMenuAction(context, position, _popupMenuActionTile(context, email)))) - .build())) + itemBuilder: (context, index) { + final mailboxContain = _getMailboxContain(listPresentationEmail[index]); + final newPresentationEmail = controller.generateEmailByPlatform(listPresentationEmail[index]); + return Obx(() => (EmailTileBuilder( + context, + newPresentationEmail, + controller.selectionMode.value, + controller.searchQuery, + controller.mailboxDashBoardController.selectedEmail.value?.id == newPresentationEmail.id, + isSearchEmailRunning: true, + padding: SearchEmailUtils.getPaddingSearchResultList(context, _responsiveUtils), + paddingDivider: SearchEmailUtils.getPaddingDividerSearchResultList(context, _responsiveUtils), + mailboxContain: mailboxContain) + ..addOnPressEmailActionClick((action, email) => + controller.pressEmailAction(context, action, email, mailboxContain: mailboxContain)) + ..addOnMoreActionClick((email, position) => _responsiveUtils.isScreenWithShortestSide(context) + ? controller.openContextMenuAction(context, _contextMenuActionTile(context, email)) + : controller.openPopupMenuAction(context, position, _popupMenuActionTile(context, email))) + ).build()); + }) ); } + PresentationMailbox? _getMailboxContain(PresentationEmail currentEmail) { + return currentEmail.findMailboxContain(controller.mailboxDashBoardController.mapMailboxById); + } + double? _getItemExtent(BuildContext context) { if (BuildUtils.isWeb) { return _responsiveUtils.isDesktop(context) ? 52 : 95; @@ -470,7 +472,7 @@ class SearchEmailView extends GetWidget } Widget _markAsEmailSpamOrUnSpamAction(BuildContext context, PresentationEmail email) { - final mailboxContain = email.findMailboxContain(controller.mailboxDashBoardController.mapMailboxById); + final mailboxContain = _getMailboxContain(email); return (EmailActionCupertinoActionSheetActionBuilder( const Key('mark_as_spam_or_un_spam_action'), diff --git a/lib/features/session/presentation/session_controller.dart b/lib/features/session/presentation/session_controller.dart index 910866dce..074f903d7 100644 --- a/lib/features/session/presentation/session_controller.dart +++ b/lib/features/session/presentation/session_controller.dart @@ -58,7 +58,7 @@ class SessionController extends ReloadableController { @override void handleReloaded(Session session) { pushAndPop( - RouteUtils.generateRoutePathMobile(AppRoutes.dashboard, NavigationRouter()), + RouteUtils.generateRouteMobile(AppRoutes.dashboard, NavigationRouter()), arguments: session); } @@ -108,7 +108,7 @@ class SessionController extends ReloadableController { if (apiUrl.isNotEmpty) { _dynamicUrlInterceptors.changeBaseUrl(apiUrl); pushAndPop( - RouteUtils.generateRoutePathMobile(AppRoutes.dashboard, NavigationRouter()), + RouteUtils.generateRouteMobile(AppRoutes.dashboard, NavigationRouter()), arguments: success.session); } else { _goToLogin(); diff --git a/lib/features/thread/domain/state/get_email_by_id_state.dart b/lib/features/thread/domain/state/get_email_by_id_state.dart index 2bbeeaa69..16b3cc6b1 100644 --- a/lib/features/thread/domain/state/get_email_by_id_state.dart +++ b/lib/features/thread/domain/state/get_email_by_id_state.dart @@ -1,16 +1,20 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; -import 'package:model/model.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:tmail_ui_user/main/routes/navigation_router.dart'; class GetEmailByIdLoading extends LoadingState {} class GetEmailByIdSuccess extends UIState { final PresentationEmail email; + final MailboxId? mailboxId; + final DashboardType dashboardType; - GetEmailByIdSuccess(this.email); + GetEmailByIdSuccess(this.email, this.mailboxId, this.dashboardType); @override - List get props => [email]; + List get props => [email, mailboxId, dashboardType]; } class GetEmailByIdFailure extends FeatureFailure { diff --git a/lib/features/thread/domain/usecases/get_email_by_id_interactor.dart b/lib/features/thread/domain/usecases/get_email_by_id_interactor.dart index 91c2a9f30..1416797e7 100644 --- a/lib/features/thread/domain/usecases/get_email_by_id_interactor.dart +++ b/lib/features/thread/domain/usecases/get_email_by_id_interactor.dart @@ -4,8 +4,10 @@ import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; 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/thread/domain/repository/thread_repository.dart'; import 'package:tmail_ui_user/features/thread/domain/state/get_email_by_id_state.dart'; +import 'package:tmail_ui_user/main/routes/navigation_router.dart'; class GetEmailByIdInteractor { final ThreadRepository _threadRepository; @@ -16,13 +18,15 @@ class GetEmailByIdInteractor { AccountId accountId, EmailId emailId, { + MailboxId? mailboxId, + DashboardType dashboardType = DashboardType.normal, Properties? properties, } ) async* { try { yield Right(GetEmailByIdLoading()); final email = await _threadRepository.getEmailById(accountId, emailId, properties: properties); - yield Right(GetEmailByIdSuccess(email)); + yield Right(GetEmailByIdSuccess(email, mailboxId, dashboardType)); } catch (e) { yield Left(GetEmailByIdFailure(e)); } diff --git a/lib/features/thread/presentation/mixin/email_action_controller.dart b/lib/features/thread/presentation/mixin/email_action_controller.dart index 4585617e8..832b9d572 100644 --- a/lib/features/thread/presentation/mixin/email_action_controller.dart +++ b/lib/features/thread/presentation/mixin/email_action_controller.dart @@ -28,6 +28,7 @@ import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_t 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/route_navigation.dart'; +import 'package:tmail_ui_user/main/routes/route_utils.dart'; mixin EmailActionController on ViewAsDialogActionMixin { @@ -47,6 +48,11 @@ mixin EmailActionController on ViewAsDialogActionMixin { void previewEmail(BuildContext context, PresentationEmail presentationEmail) { mailboxDashBoardController.setSelectedEmail(presentationEmail); mailboxDashBoardController.dispatchRoute(DashboardRoutes.emailDetailed); + if (BuildUtils.isWeb && presentationEmail.routeWeb != null) { + RouteUtils.updateRouteOnBrowser( + 'Email-${presentationEmail.id.id.value}', + presentationEmail.routeWeb!); + } } void moveToTrash(PresentationEmail email) async { diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 4f71f0b5b..bbeba73f4 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -3,10 +3,10 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/utils/app_toast.dart'; import 'package:core/utils/app_logger.dart'; +import 'package:core/utils/build_utils.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/sort/comparator.dart'; @@ -85,6 +85,8 @@ class ThreadController extends BaseController with EmailActionController { final listEmailDrag = [].obs; bool _rangeSelectionMode = false; + final openingEmail = RxBool(false); + bool canLoadMore = true; bool canSearchMore = true; bool _isLoadingMore = false; @@ -153,6 +155,8 @@ class ThreadController extends BaseController with EmailActionController { emailList.clear(); } else if (failure is SearchMoreEmailFailure || failure is LoadMoreEmailsFailure) { _isLoadingMore = false; + } else if (failure is GetEmailByIdFailure) { + openingEmail.value = false; } }, (success) { @@ -168,26 +172,11 @@ class ThreadController extends BaseController with EmailActionController { _searchMoreEmailsSuccess(success); } else if (success is SearchingMoreState || success is LoadingMoreState) { _isLoadingMore = true; + } else if (success is GetEmailByIdLoading) { + openingEmail.value = true; } else if (success is GetEmailByIdSuccess) { - if (currentContext != null) { - final mailboxContain = success.email - .findMailboxContain(mailboxDashBoardController.mapMailboxById); - final route = RouteUtils.generateRouteBrowser( - AppRoutes.dashboard, - NavigationRouter( - emailId: success.email.id, - mailboxId: mailboxContain?.id, - dashboardType: searchController.isSearchEmailRunning - ? DashboardType.search - : DashboardType.normal - ) - ); - pressEmailAction( - currentContext!, - EmailActionType.preview, - success.email.withRouteWeb(route), - mailboxContain: mailboxContain); - } + openingEmail.value = false; + _openEmailDetailView(success.email, success.mailboxId, success.dashboardType); } } ); @@ -246,23 +235,14 @@ class ThreadController extends BaseController with EmailActionController { pressEmailSelectionAction(action.context, action.emailAction, action.listEmailSelected); mailboxDashBoardController.clearDashBoardAction(); } else if (action is OpenEmailDetailedFromSuggestionQuickSearchAction) { - final mailboxContain = action.presentationEmail - .findMailboxContain(mailboxDashBoardController.mapMailboxById); - final route = RouteUtils.generateRouteBrowser( - AppRoutes.dashboard, - NavigationRouter( - emailId: action.presentationEmail.id, - mailboxId: mailboxContain?.id, - dashboardType: searchController.isSearchEmailRunning - ? DashboardType.search - : DashboardType.normal - ) - ); + final mailboxContain = action.presentationEmail.findMailboxContain(mailboxDashBoardController.mapMailboxById); + final newEmail = generateEmailByPlatform(action.presentationEmail); pressEmailAction( - action.context, - EmailActionType.preview, - action.presentationEmail.withRouteWeb(route), - mailboxContain: mailboxContain); + action.context, + EmailActionType.preview, + newEmail, + mailboxContain: mailboxContain + ); mailboxDashBoardController.clearDashBoardAction(); } else if (action is StartSearchEmailAction) { cancelSelectEmail(); @@ -272,7 +252,7 @@ class ThreadController extends BaseController with EmailActionController { deleteSelectionEmailsPermanently(action.context, DeleteActionType.all); mailboxDashBoardController.clearDashBoardAction(); } else if (action is SelectEmailByIdAction) { - _openEmailDetailedView(action.emailId); + _getEmailByIdAction(action.emailId, action.mailboxId, action.dashboardType); mailboxDashBoardController.clearDashBoardAction(); } }); @@ -503,11 +483,6 @@ class ThreadController extends BaseController with EmailActionController { : SelectMode.INACTIVE; } - void previewEmail(BuildContext context, PresentationEmail presentationEmailSelected) { - mailboxDashBoardController.setSelectedEmail(presentationEmailSelected); - mailboxDashBoardController.dispatchRoute(DashboardRoutes.emailDetailed); - } - Tuple2 _getSelectionEmailsRange(PresentationEmail presentationEmailSelected) { final emailSelectedIndex = emailList.indexWhere((e) => e.id == presentationEmailSelected.id); final latestEmailSelectedOrUnselectedIndex = emailList.indexWhere((e) => e.id == latestEmailSelectedOrUnselected.value?.id); @@ -833,12 +808,57 @@ class ThreadController extends BaseController with EmailActionController { : KeyEventResult.ignored; } - void _openEmailDetailedView(EmailId emailId) { + PresentationEmail generateEmailByPlatform(PresentationEmail currentEmail) { + if (BuildUtils.isWeb) { + final route = RouteUtils.generateRouteBrowser( + AppRoutes.dashboard, + NavigationRouter( + emailId: currentEmail.id, + mailboxId: currentMailbox?.id, + dashboardType: searchController.isSearchEmailRunning + ? DashboardType.search + : DashboardType.normal + ) + ); + final emailOnWeb = currentEmail.withRouteWeb(route); + return emailOnWeb; + } else { + return currentEmail; + } + } + + void _getEmailByIdAction(EmailId emailId, MailboxId? mailboxId, DashboardType dashboardType) { if (_accountId != null) { consumeState(_getEmailByIdInteractor.execute( _accountId!, emailId, + mailboxId: mailboxId, + dashboardType: dashboardType, properties: ThreadConstants.propertiesDefault)); } } + + void _openEmailDetailView( + PresentationEmail email, + MailboxId? mailboxId, + DashboardType dashboardType + ) { + if (currentContext != null) { + final mailboxContain = email.findMailboxContain(mailboxDashBoardController.mapMailboxById); + final route = RouteUtils.generateRouteBrowser( + AppRoutes.dashboard, + NavigationRouter( + emailId: email.id, + mailboxId: mailboxId, + dashboardType: dashboardType + ) + ); + pressEmailAction( + currentContext!, + EmailActionType.preview, + email.withRouteWeb(route), + mailboxContain: mailboxContain + ); + } + } } \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index b2720f98b..7e7326973 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -251,7 +251,7 @@ class ThreadView extends GetWidget with AppLoaderMixin, ? Padding(padding: const EdgeInsets.symmetric(vertical: 16), child: loadingWidget) : const SizedBox.shrink(); } else { - return success is LoadingState + return success is LoadingState || controller.openingEmail.isTrue ? Padding(padding: const EdgeInsets.symmetric(vertical: 16), child: loadingWidget) : const SizedBox.shrink(); } @@ -283,7 +283,9 @@ class ThreadView extends GetWidget with AppLoaderMixin, padding: EdgeInsets.zero, color: Colors.white, child: Obx(() { - return _buildResultListEmail(context, controller.emailList); + return Visibility( + visible: controller.openingEmail.isFalse, + child: _buildResultListEmail(context, controller.emailList)); }) ); } @@ -333,48 +335,39 @@ class ThreadView extends GetWidget with AppLoaderMixin, itemExtent: _getItemExtent(context), itemCount: listPresentationEmail.length, itemBuilder: (context, index) { + final mailboxContain = _getMailboxContain(listPresentationEmail[index]); + final newPresentationEmail = controller.generateEmailByPlatform(listPresentationEmail[index]); return Obx(() => Draggable>( maxSimultaneousDrags: kIsWeb ? null : 0, data: controller.listEmailDrag, child: (EmailTileBuilder( - context, - listPresentationEmail[index], - controller.mailboxDashBoardController.currentSelectMode.value, - controller.searchQuery, - controller.mailboxDashBoardController.selectedEmail.value?.id == listPresentationEmail[index].id, - mailboxCurrent: controller.searchController.isSearchEmailRunning - ? listPresentationEmail[index].findMailboxContain(controller.mailboxDashBoardController.mapMailboxById) - : controller.currentMailbox, - isSearchEmailRunning: controller.searchController.isSearchEmailRunning) - ..addOnPressEmailActionClick((action, email) => - controller.pressEmailAction( context, - action, - email, - mailboxContain: controller.searchController.isSearchEmailRunning - ? email.findMailboxContain(controller.mailboxDashBoardController.mapMailboxById) - : controller.currentMailbox)) - ..addOnMoreActionClick((email, position) => _responsiveUtils.isMobile(context) - ? controller.openContextMenuAction(context, _contextMenuActionTile(context, email)) - : controller.openPopupMenuAction(context, position, _popupMenuActionTile(context, email))) + newPresentationEmail, + controller.mailboxDashBoardController.currentSelectMode.value, + controller.searchQuery, + controller.mailboxDashBoardController.selectedEmail.value?.id == newPresentationEmail.id, + mailboxContain: mailboxContain, + isSearchEmailRunning: controller.searchController.isSearchEmailRunning) + ..addOnPressEmailActionClick((action, email) => + controller.pressEmailAction(context, action, email, mailboxContain: mailboxContain)) + ..addOnMoreActionClick((email, position) => _responsiveUtils.isScreenWithShortestSide(context) + ? controller.openContextMenuAction(context, _contextMenuActionTile(context, email)) + : controller.openPopupMenuAction(context, position, _popupMenuActionTile(context, email))) ).build(), feedback: _buildFeedBackWidget(context), childWhenDragging: (EmailTileBuilder( context, - listPresentationEmail[index], + newPresentationEmail, controller.mailboxDashBoardController.currentSelectMode.value, controller.searchQuery, - controller.mailboxDashBoardController.selectedEmail.value?.id == listPresentationEmail[index].id, - mailboxCurrent: controller.searchController.isSearchEmailRunning - ? listPresentationEmail[index].findMailboxContain( - controller.mailboxDashBoardController.mapMailboxById) - : controller.currentMailbox, + controller.mailboxDashBoardController.selectedEmail.value?.id == newPresentationEmail.id, + mailboxContain: mailboxContain, isSearchEmailRunning: controller.searchController.isSearchEmailRunning, isDrag: true) ).build(), dragAnchorStrategy: pointerDragAnchorStrategy, onDragStarted: () { - controller.calculateDragValue(listPresentationEmail[index]); + controller.calculateDragValue(newPresentationEmail); }, )); }), @@ -382,6 +375,12 @@ class ThreadView extends GetWidget with AppLoaderMixin, ); } + PresentationMailbox? _getMailboxContain(PresentationEmail currentEmail) { + return controller.searchController.isSearchEmailRunning + ? currentEmail.findMailboxContain(controller.mailboxDashBoardController.mapMailboxById) + : controller.currentMailbox; + } + Widget _buildFeedBackWidget(BuildContext context) { return SizedBox( height: 60, @@ -509,9 +508,7 @@ class ThreadView extends GetWidget with AppLoaderMixin, } Widget _markAsEmailSpamOrUnSpamAction(BuildContext context, PresentationEmail email) { - final mailboxContain = controller.searchController.isSearchEmailRunning - ? email.findMailboxContain(controller.mailboxDashBoardController.mapMailboxById) - : controller.currentMailbox; + final mailboxContain = _getMailboxContain(email); return (EmailActionCupertinoActionSheetActionBuilder( const Key('mark_as_spam_or_un_spam_action'), SvgPicture.asset( diff --git a/lib/features/thread/presentation/widgets/email_tile_builder.dart b/lib/features/thread/presentation/widgets/email_tile_builder.dart index d1e40f13b..71159dd30 100644 --- a/lib/features/thread/presentation/widgets/email_tile_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_builder.dart @@ -13,7 +13,7 @@ class EmailTileBuilder with BaseEmailItemTile { final PresentationEmail _presentationEmail; final BuildContext _context; final SelectMode _selectModeAll; - final PresentationMailbox? mailboxCurrent; + final PresentationMailbox? mailboxContain; final SearchQuery? _searchQuery; final bool isSearchEmailRunning; final EdgeInsets? padding; @@ -31,7 +31,7 @@ class EmailTileBuilder with BaseEmailItemTile { this._isShowingEmailContent, { this.isSearchEmailRunning = false, - this.mailboxCurrent, + this.mailboxContain, this.padding, this.paddingDivider, this.isDrag = false, @@ -83,7 +83,7 @@ class EmailTileBuilder with BaseEmailItemTile { fit: BoxFit.fill)), Expanded(child: buildInformationSender( _presentationEmail, - mailboxCurrent, + mailboxContain, isSearchEmailRunning, _searchQuery)), if (_presentationEmail.hasAttachment == true) diff --git a/lib/features/thread/presentation/widgets/email_tile_web_builder.dart b/lib/features/thread/presentation/widgets/email_tile_web_builder.dart index 2ca5de505..27c30c1c7 100644 --- a/lib/features/thread/presentation/widgets/email_tile_web_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_web_builder.dart @@ -11,9 +11,6 @@ import 'package:model/mailbox/select_mode.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/presentation/mixin/base_email_item_tile.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/navigation_router.dart'; -import 'package:tmail_ui_user/main/routes/route_utils.dart'; import 'package:url_launcher/link.dart'; class EmailTileBuilder with BaseEmailItemTile { @@ -21,7 +18,7 @@ class EmailTileBuilder with BaseEmailItemTile { final PresentationEmail _presentationEmail; final BuildContext _context; final SelectMode _selectModeAll; - final PresentationMailbox? mailboxCurrent; + final PresentationMailbox? mailboxContain; final SearchQuery? _searchQuery; final bool isSearchEmailRunning; final EdgeInsets? padding; @@ -43,7 +40,7 @@ class EmailTileBuilder with BaseEmailItemTile { this._isShowingEmailContent, { this.isSearchEmailRunning = false, - this.mailboxCurrent, + this.mailboxContain, this.padding, this.paddingDivider, this.isDrag = false, @@ -80,56 +77,43 @@ class EmailTileBuilder with BaseEmailItemTile { } Widget _wrapContainerForTile(Widget tile) { + return Container( + margin: _getMarginItem(), + padding: _getPaddingItem(), + decoration: _getDecorationItem(), + alignment: Alignment.center, + child: Link( + uri: _presentationEmail.routeWeb, + builder: (_, __) => tile + ) + ); + } + + EdgeInsets _getMarginItem() { if (responsiveUtils.isDesktop(_context)) { - return Container( - margin: const EdgeInsets.only(top: 3), - padding: const EdgeInsets.symmetric(vertical: 8), - decoration: (_selectModeAll == SelectMode.ACTIVE && - _presentationEmail.selectMode == SelectMode.ACTIVE) || isDrag || _isShowingEmailContent - ? BoxDecoration( - borderRadius: BorderRadius.circular(14), - color: AppColor.colorItemEmailSelectedDesktop) - : null, - child: Link( - uri: RouteUtils.generateRoutePathBrowser( - AppRoutes.dashboard, - NavigationRouter( - emailId: _presentationEmail.id, - mailboxId: mailboxCurrent?.id, - dashboardType: isSearchEmailRunning - ? DashboardType.search - : DashboardType.normal - ) - ), - builder: (_, __) => tile - ) - ); + return const EdgeInsets.only(top: 3); } else { - return Container( - margin: const EdgeInsets.only(top: 3, left: 16, right: 16), - padding: const EdgeInsets.only(bottom: 8, right: 8, top: 8), - decoration: (_selectModeAll == SelectMode.ACTIVE && - _presentationEmail.selectMode == SelectMode.ACTIVE) || isDrag || _isShowingEmailContent - ? BoxDecoration( - borderRadius: BorderRadius.circular(14), - color: AppColor.colorItemEmailSelectedDesktop) - : BoxDecoration( - borderRadius: BorderRadius.circular(0), - color: Colors.white), - alignment: Alignment.center, - child: Link( - uri: RouteUtils.generateRoutePathBrowser( - AppRoutes.dashboard, - NavigationRouter( - emailId: _presentationEmail.id, - mailboxId: mailboxCurrent?.id, - dashboardType: isSearchEmailRunning - ? DashboardType.search - : DashboardType.normal - ) - ), - builder: (_, __) => tile - )); + return const EdgeInsets.only(top: 3, left: 16, right: 16); + } + } + + EdgeInsets _getPaddingItem() { + if (responsiveUtils.isDesktop(_context)) { + return const EdgeInsets.symmetric(vertical: 8); + } else { + return const EdgeInsets.only(bottom: 8, right: 8, top: 8); + } + } + + BoxDecoration _getDecorationItem() { + if ((_selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE) || isDrag || _isShowingEmailContent) { + return BoxDecoration( + borderRadius: BorderRadius.circular(14), + color: AppColor.colorItemEmailSelectedDesktop); + } else { + return BoxDecoration( + borderRadius: BorderRadius.circular(0), + color: Colors.white); } } @@ -168,7 +152,7 @@ class EmailTileBuilder with BaseEmailItemTile { fit: BoxFit.fill)), Expanded(child: buildInformationSender( _presentationEmail, - mailboxCurrent, + mailboxContain, isSearchEmailRunning, _searchQuery )), @@ -264,7 +248,7 @@ class EmailTileBuilder with BaseEmailItemTile { fit: BoxFit.fill)), Expanded(child: buildInformationSender( _presentationEmail, - mailboxCurrent, + mailboxContain, isSearchEmailRunning, _searchQuery )), @@ -385,7 +369,7 @@ class EmailTileBuilder with BaseEmailItemTile { width: 160, child: buildInformationSender( _presentationEmail, - mailboxCurrent, + mailboxContain, isSearchEmailRunning, _searchQuery )), @@ -437,7 +421,7 @@ class EmailTileBuilder with BaseEmailItemTile { : EmailActionType.markAsRead, _presentationEmail)), const SizedBox(width: 5), - if (mailboxCurrent?.isDrafts == false) + if (mailboxContain?.isDrafts == false) ... [ buildIconWeb( minSize: 18, @@ -476,7 +460,7 @@ class EmailTileBuilder with BaseEmailItemTile { : EmailActionType.moveToTrash, _presentationEmail)), const SizedBox(width: 5), - if (mailboxCurrent?.isDrafts == false) + if (mailboxContain?.isDrafts == false) buildIconWebHasPosition( _context, icon: SvgPicture.asset( @@ -501,7 +485,7 @@ class EmailTileBuilder with BaseEmailItemTile { } bool get canDeletePermanently { - return mailboxCurrent?.isTrash == true || mailboxCurrent?.isDrafts == true; + return mailboxContain?.isTrash == true || mailboxContain?.isDrafts == true; } Widget _buildDateTimeForDesktopScreen() { diff --git a/lib/main/routes/route_utils.dart b/lib/main/routes/route_utils.dart index 2aa4b998e..fec274944 100644 --- a/lib/main/routes/route_utils.dart +++ b/lib/main/routes/route_utils.dart @@ -1,17 +1,21 @@ import 'package:core/data/model/query/query_parameter.dart'; import 'package:core/data/network/config/service_path.dart'; +import 'package:core/utils/app_logger.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; 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/login/data/extensions/service_path_extension.dart'; import 'package:tmail_ui_user/main/routes/navigation_router.dart'; +import 'package:universal_html/html.dart' as html; -class RouteUtils { +abstract class RouteUtils { static String get baseOriginUrl => Uri.base.origin; - static String generateRoutePathMobile(String route, NavigationRouter router) { + static String get baseUrl => Uri.base.path; + + static String generateRouteMobile(String route, NavigationRouter router) { ServicePath servicePath = ServicePath(route); if (router.emailId != null) { servicePath = servicePath.withPathParameter(router.emailId!.id.value); @@ -25,7 +29,7 @@ class RouteUtils { return servicePath.path; } - static Uri generateRoutePathBrowser(String route, NavigationRouter router) { + static Uri generateRouteBrowser(String route, NavigationRouter router) { final baseRoutePath = '$baseOriginUrl/#$route'; ServicePath servicePath = ServicePath(baseRoutePath); if (router.emailId != null) { @@ -57,4 +61,10 @@ class RouteUtils { dashboardType: dashboardType, ); } + + static void updateRouteOnBrowser(String title, Uri newRoute) { + log('RouteUtils::updateRouteOnBrowser(): title: $title'); + log('RouteUtils::updateRouteOnBrowser(): newRoute: $newRoute'); + html.window.history.replaceState(null, title, newRoute.toString()); + } } \ No newline at end of file diff --git a/model/lib/email/presentation_email.dart b/model/lib/email/presentation_email.dart index 18036c1bf..f78fc51f4 100644 --- a/model/lib/email/presentation_email.dart +++ b/model/lib/email/presentation_email.dart @@ -1,5 +1,5 @@ -import 'package:core/core.dart'; +import 'package:core/presentation/extensions/string_extension.dart'; import 'package:equatable/equatable.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/core/utc_date.dart'; @@ -7,7 +7,8 @@ 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:model/model.dart'; +import 'package:model/extensions/email_address_extension.dart'; +import 'package:model/mailbox/select_mode.dart'; class PresentationEmail with EquatableMixin { @@ -27,6 +28,7 @@ class PresentationEmail with EquatableMixin { final Map? mailboxIds; final List? mailboxNames; final SelectMode selectMode; + final Uri? routeWeb; PresentationEmail( this.id, @@ -46,6 +48,7 @@ class PresentationEmail with EquatableMixin { this.mailboxIds, this.mailboxNames, this.selectMode = SelectMode.INACTIVE, + this.routeWeb } ); @@ -72,6 +75,8 @@ class PresentationEmail with EquatableMixin { String get mailboxName => mailboxNames?.first?.name ?? ''; + String get routeWebAsString => routeWeb.toString(); + @override List get props => [ id, @@ -86,6 +91,10 @@ class PresentationEmail with EquatableMixin { sentAt, replyTo, preview, - hasAttachment + hasAttachment, + mailboxIds, + mailboxNames, + selectMode, + routeWeb ]; } \ No newline at end of file diff --git a/model/lib/extensions/presentation_email_extension.dart b/model/lib/extensions/presentation_email_extension.dart index bfbea7991..70ad97ba1 100644 --- a/model/lib/extensions/presentation_email_extension.dart +++ b/model/lib/extensions/presentation_email_extension.dart @@ -1,11 +1,18 @@ import 'dart:ui'; -import 'package:core/core.dart'; +import 'package:core/domain/extensions/datetime_extension.dart'; +import 'package:core/presentation/extensions/color_extension.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; -import 'package:model/model.dart'; +import 'package:model/email/email_action_type.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:model/extensions/email_address_extension.dart'; +import 'package:model/extensions/list_email_address_extension.dart'; +import 'package:model/extensions/utc_date_extension.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:model/mailbox/select_mode.dart'; extension PresentationEmailExtension on PresentationEmail { @@ -43,6 +50,7 @@ extension PresentationEmailExtension on PresentationEmail { mailboxIds: mailboxIds, mailboxNames: mailboxNames, selectMode: selectMode == SelectMode.INACTIVE ? SelectMode.ACTIVE : SelectMode.INACTIVE, + routeWeb: routeWeb ); } @@ -64,6 +72,7 @@ extension PresentationEmailExtension on PresentationEmail { mailboxIds: mailboxIds, mailboxNames: mailboxNames, selectMode: selectMode, + routeWeb: routeWeb ); } @@ -111,7 +120,7 @@ extension PresentationEmailExtension on PresentationEmail { case EmailActionType.edit: return Tuple3(to.asList(), cc.asList(), bcc.asList()); default: - return Tuple3([], [], []); + return const Tuple3([], [], []); } } @@ -145,6 +154,7 @@ extension PresentationEmailExtension on PresentationEmail { mailboxIds: mailboxIds, mailboxNames: listMailboxName, selectMode: selectMode, + routeWeb: routeWeb ); } @@ -160,4 +170,26 @@ extension PresentationEmailExtension on PresentationEmail { } return null; } + + PresentationEmail withRouteWeb(Uri routeWeb) { + return PresentationEmail( + this.id, + keywords: keywords, + size: size, + receivedAt: receivedAt, + hasAttachment: hasAttachment, + preview: preview, + subject: subject, + sentAt: sentAt, + from: from, + to: to, + cc: cc, + bcc: bcc, + replyTo: replyTo, + mailboxIds: mailboxIds, + mailboxNames: mailboxNames, + selectMode: selectMode, + routeWeb: routeWeb + ); + } } \ No newline at end of file