TF-1715 Perform email actions in Favorite folder

This commit is contained in:
dab246
2025-10-30 14:44:10 +07:00
committed by Dat H. Pham
parent dfaf745ce9
commit fc6cc5719b
9 changed files with 41 additions and 30 deletions
@@ -26,6 +26,7 @@ import 'package:model/email/presentation_email.dart';
import 'package:model/email/read_actions.dart'; import 'package:model/email/read_actions.dart';
import 'package:model/extensions/email_address_extension.dart'; import 'package:model/extensions/email_address_extension.dart';
import 'package:model/extensions/list_email_address_extension.dart'; import 'package:model/extensions/list_email_address_extension.dart';
import 'package:model/extensions/mailbox_id_extension.dart';
import 'package:model/extensions/presentation_email_extension.dart'; import 'package:model/extensions/presentation_email_extension.dart';
import 'package:model/extensions/presentation_mailbox_extension.dart'; import 'package:model/extensions/presentation_mailbox_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/presentation_mailbox.dart';
@@ -713,9 +714,11 @@ class EmailActionReactor {
PresentationMailbox? selectedMailbox, PresentationMailbox? selectedMailbox,
bool isSearchEmailRunning = false, bool isSearchEmailRunning = false,
}) { }) {
if (selectedMailbox == null || isSearchEmailRunning) { if (selectedMailbox == null ||
isSearchEmailRunning ||
selectedMailbox.id.isFavoriteMailboxId == true) {
return presentationEmail.findMailboxContain(mapMailbox); return presentationEmail.findMailboxContain(mapMailbox);
} }
return selectedMailbox; return selectedMailbox;
} }
@@ -12,6 +12,7 @@ import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/email/presentation/model/email_loaded.dart'; import 'package:tmail_ui_user/features/email/presentation/model/email_loaded.dart';
import 'package:tmail_ui_user/features/email/presentation/styles/email_view_app_bar_widget_styles.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/email_view_app_bar_widget_styles.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_back_button.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_back_button.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnEmailActionClick = void Function(PresentationEmail, EmailActionType); typedef OnEmailActionClick = void Function(PresentationEmail, EmailActionType);
@@ -81,7 +82,7 @@ class EmailViewAppBarWidget extends StatelessWidget {
child: EmailViewBackButton( child: EmailViewBackButton(
imagePaths: _imagePaths, imagePaths: _imagePaths,
onBackAction: onBackAction, onBackAction: onBackAction,
mailboxContain: mailboxContain, backButtonLabel: mailboxContain?.getDisplayName(context) ?? '',
isSearchActivated: isSearchActivated, isSearchActivated: isSearchActivated,
maxWidth: constraints.maxWidth, maxWidth: constraints.maxWidth,
), ),
@@ -3,9 +3,7 @@ import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:core/utils/direction_utils.dart'; import 'package:core/utils/direction_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/email/presentation/styles/email_view_back_button_styles.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/email_view_back_button_styles.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class EmailViewBackButton extends StatelessWidget { class EmailViewBackButton extends StatelessWidget {
@@ -14,7 +12,7 @@ class EmailViewBackButton extends StatelessWidget {
final bool isSearchActivated; final bool isSearchActivated;
final VoidCallback onBackAction; final VoidCallback onBackAction;
final double maxWidth; final double maxWidth;
final PresentationMailbox? mailboxContain; final String backButtonLabel;
const EmailViewBackButton({ const EmailViewBackButton({
super.key, super.key,
@@ -22,14 +20,14 @@ class EmailViewBackButton extends StatelessWidget {
required this.onBackAction, required this.onBackAction,
required this.isSearchActivated, required this.isSearchActivated,
required this.maxWidth, required this.maxWidth,
this.mailboxContain, required this.backButtonLabel,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!isSearchActivated) { if (!isSearchActivated) {
return TMailButtonWidget( return TMailButtonWidget(
text: mailboxContain?.getDisplayName(context) ?? '', text: backButtonLabel,
icon: DirectionUtils.isDirectionRTLByLanguage(context) icon: DirectionUtils.isDirectionRTLByLanguage(context)
? imagePaths.icArrowRight ? imagePaths.icArrowRight
: imagePaths.icBack, : imagePaths.icBack,
@@ -1347,7 +1347,8 @@ class MailboxDashBoardController extends ReloadableController
.where((email) => email.id != null) .where((email) => email.id != null)
.map((e) => MapEntry(e.id!, e.hasRead)) .map((e) => MapEntry(e.id!, e.hasRead))
); );
if (searchController.isSearchEmailRunning){ if (searchController.isSearchEmailRunning ||
selectedMailbox.value?.id.isFavoriteMailboxId == true) {
final Map<MailboxId,List<EmailId>> mapListEmailSelectedByMailBoxId = {}; final Map<MailboxId,List<EmailId>> mapListEmailSelectedByMailBoxId = {};
for (var element in listEmails) { for (var element in listEmails) {
final mailbox = element.findMailboxContain(mapMailboxById); final mailbox = element.findMailboxContain(mapMailboxById);
@@ -5,6 +5,7 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/email_action_type.dart'; import 'package:model/email/email_action_type.dart';
import 'package:model/email/presentation_email.dart'; import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/list_presentation_email_extension.dart'; import 'package:model/extensions/list_presentation_email_extension.dart';
import 'package:model/extensions/mailbox_id_extension.dart';
import 'package:model/extensions/presentation_email_extension.dart'; import 'package:model/extensions/presentation_email_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_action.dart';
@@ -48,7 +49,8 @@ extension HandleActionTypeForEmailSelection on MailboxDashBoardController {
final mapEmailIdsByMailboxId = <MailboxId, List<EmailId>>{}; final mapEmailIdsByMailboxId = <MailboxId, List<EmailId>>{};
if (searchController.isSearchEmailRunning) { if (searchController.isSearchEmailRunning ||
selectedMailbox.value?.id.isFavoriteMailboxId == true) {
for (final email in emails) { for (final email in emails) {
final mailboxId = email.firstMailboxIdAvailable; final mailboxId = email.firstMailboxIdAvailable;
final emailId = email.id; final emailId = email.id;
@@ -2,6 +2,7 @@ import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/presentation_email.dart'; import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/mailbox_id_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
extension MoveEmailsToMailboxExtension on MailboxDashBoardController { extension MoveEmailsToMailboxExtension on MailboxDashBoardController {
@@ -9,7 +10,10 @@ extension MoveEmailsToMailboxExtension on MailboxDashBoardController {
required Map<MailboxId,List<EmailId>> originalMailboxIdsWithEmailIds, required Map<MailboxId,List<EmailId>> originalMailboxIdsWithEmailIds,
required MailboxId destinationMailboxId, required MailboxId destinationMailboxId,
}) { }) {
if (destinationMailboxId == selectedMailbox.value?.id) return; if (destinationMailboxId == selectedMailbox.value?.id ||
selectedMailbox.value?.id.isFavoriteMailboxId == true) {
return;
}
final currentEmails = List<PresentationEmail>.from( final currentEmails = List<PresentationEmail>.from(
emailsInCurrentMailbox, emailsInCurrentMailbox,
@@ -180,7 +180,7 @@ class ThreadController extends BaseController with EmailActionController {
openingEmail.value = true; openingEmail.value = true;
} else if (success is GetEmailByIdSuccess) { } else if (success is GetEmailByIdSuccess) {
openingEmail.value = false; openingEmail.value = false;
if (searchController.isSearchEmailRunning) { if (isSearchActive) {
_openEmailSearchedFromLocationBar( _openEmailSearchedFromLocationBar(
email: success.email, email: success.email,
searchQuery: searchQuery searchQuery: searchQuery
@@ -362,7 +362,7 @@ class ThreadController extends BaseController with EmailActionController {
}); });
ever(mailboxDashBoardController.viewState, (viewState) { ever(mailboxDashBoardController.viewState, (viewState) {
if (mailboxDashBoardController.searchController.isSearchEmailRunning) return; if (isSearchActive) return;
final reactionState = viewState.getOrElse(() => UIState.idle); final reactionState = viewState.getOrElse(() => UIState.idle);
if (reactionState is MarkAsMailboxReadAllSuccess) { if (reactionState is MarkAsMailboxReadAllSuccess) {
_handleMarkEmailsAsReadByMailboxId(reactionState.mailboxId); _handleMarkEmailsAsReadByMailboxId(reactionState.mailboxId);
@@ -1036,7 +1036,7 @@ class ThreadController extends BaseController with EmailActionController {
leadingSVGIcon: newFilterOption.getIconToast(imagePaths)); leadingSVGIcon: newFilterOption.getIconToast(imagePaths));
} }
if (searchController.isSearchEmailRunning) { if (isSearchActive) {
_searchEmail(); _searchEmail();
} else { } else {
refreshAllEmail(); refreshAllEmail();
@@ -1125,7 +1125,7 @@ class ThreadController extends BaseController with EmailActionController {
mapMailboxById: mailboxDashBoardController.mapMailboxById, mapMailboxById: mailboxDashBoardController.mapMailboxById,
selectedMailbox: selectedMailbox, selectedMailbox: selectedMailbox,
searchQuery: searchController.searchQuery, searchQuery: searchController.searchQuery,
isSearchEmailRunning: searchController.isSearchEmailRunning isSearchEmailRunning: isSearchActive,
); );
mailboxDashBoardController.updateEmailList(newEmailListSynced); mailboxDashBoardController.updateEmailList(newEmailListSynced);
if (mailboxDashBoardController.isSelectionEnabled()) { if (mailboxDashBoardController.isSelectionEnabled()) {
@@ -1184,7 +1184,7 @@ class ThreadController extends BaseController with EmailActionController {
mapMailboxById: mailboxDashBoardController.mapMailboxById, mapMailboxById: mailboxDashBoardController.mapMailboxById,
selectedMailbox: selectedMailbox, selectedMailbox: selectedMailbox,
searchQuery: searchController.searchQuery, searchQuery: searchController.searchQuery,
isSearchEmailRunning: searchController.isSearchEmailRunning isSearchEmailRunning: isSearchActive,
); );
mailboxDashBoardController.emailsInCurrentMailbox.addAll(resultEmailSearchList); mailboxDashBoardController.emailsInCurrentMailbox.addAll(resultEmailSearchList);
} }
@@ -1225,7 +1225,7 @@ class ThreadController extends BaseController with EmailActionController {
moveEmailsToTrash(selectionEmail); moveEmailsToTrash(selectionEmail);
break; break;
case EmailActionType.deletePermanently: case EmailActionType.deletePermanently:
final mailboxContainCurrent = searchController.isSearchEmailRunning final mailboxContainCurrent = isSearchActive
? selectionEmail.getCurrentMailboxContain(mailboxDashBoardController.mapMailboxById) ? selectionEmail.getCurrentMailboxContain(mailboxDashBoardController.mapMailboxById)
: selectedMailbox; : selectedMailbox;
if (mailboxContainCurrent != null && currentContext != null) { if (mailboxContainCurrent != null && currentContext != null) {
@@ -1351,13 +1351,13 @@ class ThreadController extends BaseController with EmailActionController {
AppRoutes.dashboard, AppRoutes.dashboard,
router: NavigationRouter( router: NavigationRouter(
emailId: currentEmail.id, emailId: currentEmail.id,
mailboxId: searchController.isSearchEmailRunning mailboxId: isSearchActive
? currentEmail.mailboxContain?.mailboxId ? currentEmail.mailboxContain?.mailboxId
: selectedMailboxId, : selectedMailboxId,
searchQuery: searchController.isSearchEmailRunning searchQuery: isSearchActive
? searchQuery ? searchQuery
: null, : null,
dashboardType: searchController.isSearchEmailRunning dashboardType: isSearchActive
? DashboardType.search ? DashboardType.search
: DashboardType.normal : DashboardType.normal
) )
@@ -6,11 +6,11 @@ import 'package:core/utils/platform_info.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/base/widget/keyboard/keyboard_handler_wrapper.dart'; import 'package:tmail_ui_user/features/base/widget/keyboard/keyboard_handler_wrapper.dart';
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
import 'package:tmail_ui_user/features/email/presentation/styles/email_view_app_bar_widget_styles.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/email_view_app_bar_widget_styles.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_bottom_bar_widget.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_bottom_bar_widget.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart'; import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
@@ -40,7 +40,7 @@ class ThreadDetailView extends GetWidget<ThreadDetailController> {
isSearchRunning: controller.isSearchRunning, isSearchRunning: controller.isSearchRunning,
closeThreadDetailAction: controller.closeThreadDetailAction, closeThreadDetailAction: controller.closeThreadDetailAction,
isThreadDetailEnabled: controller.isThreadDetailEnabled, isThreadDetailEnabled: controller.isThreadDetailEnabled,
mailboxContain: _getMailboxContain(), backButtonLabel: _getBackButtonLabel(context),
threadActionReady: controller.emailsInThreadDetailInfo.isNotEmpty, threadActionReady: controller.emailsInThreadDetailInfo.isNotEmpty,
threadDetailIsStarred: controller.threadDetailIsStarred, threadDetailIsStarred: controller.threadDetailIsStarred,
threadDetailCanPermanentlyDelete: controller.threadDetailCanPermanentlyDelete, threadDetailCanPermanentlyDelete: controller.threadDetailCanPermanentlyDelete,
@@ -235,12 +235,15 @@ class ThreadDetailView extends GetWidget<ThreadDetailController> {
} }
} }
PresentationMailbox? _getMailboxContain() { String _getBackButtonLabel(BuildContext context) {
if (controller.isSearchRunning) { if (controller.isSearchRunning) {
return null; return '';
} }
return controller
return controller.mailboxDashBoardController.selectedMailbox.value; .mailboxDashBoardController
.selectedMailbox
.value
?.getDisplayName(context) ?? '';
} }
bool showLoadingView(Either<Failure, Success> viewState) { bool showLoadingView(Either<Failure, Success> viewState) {
@@ -5,7 +5,6 @@ import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:core/utils/platform_info.dart'; import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:model/email/email_action_type.dart'; import 'package:model/email/email_action_type.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/email/presentation/styles/email_view_app_bar_widget_styles.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/email_view_app_bar_widget_styles.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_back_button.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_back_button.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -25,7 +24,7 @@ class ThreadDetailAppBar extends StatelessWidget {
required this.threadDetailIsStarred, required this.threadDetailIsStarred,
required this.isThreadDetailEnabled, required this.isThreadDetailEnabled,
required this.threadDetailCanPermanentlyDelete, required this.threadDetailCanPermanentlyDelete,
this.mailboxContain, required this.backButtonLabel,
this.optionWidgets = const [], this.optionWidgets = const [],
this.onThreadActionClick, this.onThreadActionClick,
this.onThreadMoreActionClick, this.onThreadMoreActionClick,
@@ -40,7 +39,7 @@ class ThreadDetailAppBar extends StatelessWidget {
final bool threadDetailIsStarred; final bool threadDetailIsStarred;
final bool isThreadDetailEnabled; final bool isThreadDetailEnabled;
final bool threadDetailCanPermanentlyDelete; final bool threadDetailCanPermanentlyDelete;
final PresentationMailbox? mailboxContain; final String backButtonLabel;
final List<Widget> optionWidgets; final List<Widget> optionWidgets;
final OnThreadActionClick? onThreadActionClick; final OnThreadActionClick? onThreadActionClick;
final OnThreadMoreActionClick? onThreadMoreActionClick; final OnThreadMoreActionClick? onThreadMoreActionClick;
@@ -100,7 +99,7 @@ class ThreadDetailAppBar extends StatelessWidget {
Widget backButton = EmailViewBackButton( Widget backButton = EmailViewBackButton(
imagePaths: imagePaths, imagePaths: imagePaths,
onBackAction: closeThreadDetailAction, onBackAction: closeThreadDetailAction,
mailboxContain: mailboxContain, backButtonLabel: backButtonLabel,
isSearchActivated: isSearchRunning, isSearchActivated: isSearchRunning,
maxWidth: constraints.maxWidth, maxWidth: constraints.maxWidth,
); );