TF-1715 Load emails in favorite folder with filters

This commit is contained in:
dab246
2025-10-30 15:50:19 +07:00
committed by Dat H. Pham
parent fc6cc5719b
commit 00324d4c56
17 changed files with 72 additions and 31 deletions
@@ -26,7 +26,6 @@ import 'package:model/email/presentation_email.dart';
import 'package:model/email/read_actions.dart';
import 'package:model/extensions/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_mailbox_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
@@ -716,7 +715,7 @@ class EmailActionReactor {
}) {
if (selectedMailbox == null ||
isSearchEmailRunning ||
selectedMailbox.id.isFavoriteMailboxId == true) {
selectedMailbox.isFavorite == true) {
return presentationEmail.findMailboxContain(mapMailbox);
}
@@ -4,6 +4,7 @@ import 'package:core/presentation/views/list/tree_view.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:model/extensions/presentation_mailbox_extension.dart';
import 'package:model/extensions/session_extension.dart';
import 'package:model/mailbox/expand_mode.dart';
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
@@ -196,6 +197,7 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
.mailboxDashBoardController
.selectedMailbox
.value,
isHighlighted: isFolderHighlighted(mailboxNode),
onOpenMailboxFolderClick: (mailboxNode) =>
mailboxNode != null
? controller.openMailbox(context, mailboxNode.item)
@@ -240,6 +242,17 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
}).toList();
}
bool get isSearchByStarredOnly {
final searchController =
controller.mailboxDashBoardController.searchController;
return searchController.isSearchEmailRunning &&
searchController.searchEmailFilter.value.isOnlyStarredApplied;
}
bool isFolderHighlighted(MailboxNode mailboxNode) =>
mailboxNode.item.isFavorite && isSearchByStarredOnly;
Widget buildListMailbox(BuildContext context) {
final isDesktop = controller.responsiveUtils.isDesktop(context);
@@ -21,6 +21,13 @@ mixin MailboxWidgetMixin {
bool spamReportEnabled,
bool deletedMessageVaultSupported
) {
if (mailbox.isFavorite) {
return [
if (PlatformInfo.isWeb)
MailboxActions.openInNewTab,
];
}
return [
if (PlatformInfo.isWeb)
MailboxActions.openInNewTab,
@@ -25,6 +25,7 @@ class LabelMailboxItemWidget extends StatefulWidget {
final bool isItemHovered;
final bool isSelected;
final bool isSelectedFolderInModal;
final bool isHighlighted;
final TextStyle? textStyle;
final OnClickOpenMenuMailboxNodeAction? onMenuActionClick;
final OnEmptyMailboxActionCallback? onEmptyMailboxActionCallback;
@@ -38,6 +39,7 @@ class LabelMailboxItemWidget extends StatefulWidget {
this.isItemHovered = false,
this.isSelected = false,
this.isSelectedFolderInModal = false,
this.isHighlighted = false,
this.textStyle,
this.onMenuActionClick,
this.onEmptyMailboxActionCallback,
@@ -218,7 +220,7 @@ class _LabelMailboxItemWidgetState extends State<LabelMailboxItemWidget> {
bool _showMoreButton(BuildContext context) => PlatformInfo.isWeb;
TextStyle get _displayNameTextStyle {
if (widget.isSelected) {
if (widget.isSelected || widget.isHighlighted) {
return ThemeUtils.textStyleInter700(
color: _responsiveUtils.isDesktop(context) ? null : AppColor.iconFolder,
fontSize: 14,
@@ -30,6 +30,7 @@ class MailboxItemWidget extends StatefulWidget {
final Color? iconColor;
final double? itemHeight;
final String? iconSelected;
final bool isHighlighted;
final OnClickExpandMailboxNodeAction? onExpandFolderActionClick;
final OnClickOpenMailboxNodeAction? onOpenMailboxFolderClick;
@@ -43,6 +44,7 @@ class MailboxItemWidget extends StatefulWidget {
super.key,
required this.mailboxNode,
this.mailboxDisplayed = MailboxDisplayed.mailbox,
this.isHighlighted = false,
this.mailboxNodeSelected,
this.mailboxActions,
this.mailboxIdAlreadySelected,
@@ -116,6 +118,7 @@ class _MailboxItemWidgetState extends State<MailboxItemWidget> {
mailboxNode: widget.mailboxNode,
isItemHovered: _isItemHovered,
isSelected: _isSelected,
isHighlighted: widget.isHighlighted,
textStyle: widget.textStyle,
onMenuActionClick: widget.onMenuActionClick,
onEmptyMailboxActionCallback: widget.onEmptyMailboxActionCallback,
@@ -174,6 +177,7 @@ class _MailboxItemWidgetState extends State<MailboxItemWidget> {
mailboxNode: widget.mailboxNode,
isItemHovered: _isItemHovered,
isSelected: _isSelected,
isHighlighted: widget.isHighlighted,
textStyle: widget.textStyle,
onMenuActionClick: widget.onMenuActionClick,
onEmptyMailboxActionCallback: widget.onEmptyMailboxActionCallback,
@@ -224,6 +228,7 @@ class _MailboxItemWidgetState extends State<MailboxItemWidget> {
mailboxNode: widget.mailboxNode,
isItemHovered: _isItemHovered,
isSelected: _isSelected,
isHighlighted: widget.isHighlighted,
textStyle: widget.textStyle,
onMenuActionClick: widget.onMenuActionClick,
onEmptyMailboxActionCallback: widget.onEmptyMailboxActionCallback,
@@ -273,6 +278,7 @@ class _MailboxItemWidgetState extends State<MailboxItemWidget> {
mailboxNode: widget.mailboxNode,
showTrailing: false,
isSelected: _isSelected,
isHighlighted: widget.isHighlighted,
isItemHovered: _isItemHovered,
textStyle: widget.textStyle,
isSelectedFolderInModal: isSelectedFolderInModal,
@@ -306,7 +312,9 @@ class _MailboxItemWidgetState extends State<MailboxItemWidget> {
Color get backgroundColorItem {
if (widget.mailboxDisplayed == MailboxDisplayed.mailbox) {
return _isSelected ? AppColor.blue100 : Colors.transparent;
return _isSelected || widget.isHighlighted
? AppColor.blue100
: Colors.transparent;
}
return Colors.white;
}
@@ -1348,7 +1348,7 @@ class MailboxDashBoardController extends ReloadableController
.map((e) => MapEntry(e.id!, e.hasRead))
);
if (searchController.isSearchEmailRunning ||
selectedMailbox.value?.id.isFavoriteMailboxId == true) {
selectedMailbox.value?.isFavorite == true) {
final Map<MailboxId,List<EmailId>> mapListEmailSelectedByMailBoxId = {};
for (var element in listEmails) {
final mailbox = element.findMailboxContain(mapMailboxById);
@@ -5,8 +5,8 @@ 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: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_mailbox_extension.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_to_mailbox_request.dart';
@@ -50,7 +50,7 @@ extension HandleActionTypeForEmailSelection on MailboxDashBoardController {
final mapEmailIdsByMailboxId = <MailboxId, List<EmailId>>{};
if (searchController.isSearchEmailRunning ||
selectedMailbox.value?.id.isFavoriteMailboxId == true) {
selectedMailbox.value?.isFavorite == true) {
for (final email in emails) {
final mailboxId = email.firstMailboxIdAvailable;
final emailId = email.id;
@@ -2,7 +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/mailbox/mailbox.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/mailbox_id_extension.dart';
import 'package:model/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
extension MoveEmailsToMailboxExtension on MailboxDashBoardController {
@@ -11,7 +11,7 @@ extension MoveEmailsToMailboxExtension on MailboxDashBoardController {
required MailboxId destinationMailboxId,
}) {
if (destinationMailboxId == selectedMailbox.value?.id ||
selectedMailbox.value?.id.isFavoriteMailboxId == true) {
selectedMailbox.value?.isFavorite == true) {
return;
}
@@ -545,7 +545,8 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
if (!controller.searchController.isSearchEmailRunning)
FilterMessageOption.attachments,
FilterMessageOption.unread,
FilterMessageOption.starred,
if (controller.selectedMailbox.value?.isFavorite != true)
FilterMessageOption.starred,
].map((filterOption) {
return PopupMenuItem(
padding: EdgeInsets.zero,
@@ -198,8 +198,8 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin {
to.isNotEmpty ||
text?.value.trim().isNotEmpty == true ||
subject?.trim().isNotEmpty == true ||
hasKeyword.isNotEmpty == true ||
notKeyword.isNotEmpty == true ||
hasKeyword.isNotEmpty ||
notKeyword.isNotEmpty ||
emailReceiveTimeType != EmailReceiveTimeType.allTime ||
sortOrderType != SearchEmailFilter.defaultSortOrder ||
(mailbox != null && mailbox?.id != PresentationMailbox.unifiedMailbox.id) ||
@@ -208,6 +208,18 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin {
bool get isContainFlagged => hasKeyword.contains(KeyWordIdentifier.emailFlagged.value);
bool get isOnlyStarredApplied => from.isEmpty &&
to.isEmpty &&
text?.value.trim().isNotEmpty != true &&
subject?.trim().isNotEmpty != true &&
hasKeyword.firstOrNull == KeyWordIdentifier.emailFlagged.value &&
notKeyword.isEmpty &&
emailReceiveTimeType == EmailReceiveTimeType.allTime &&
sortOrderType == SearchEmailFilter.defaultSortOrder &&
(mailbox == null || mailbox?.id == PresentationMailbox.unifiedMailbox.id) &&
!hasAttachment &&
!unread;
@override
List<Object?> get props => [
from,
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:model/extensions/mailbox_id_extension.dart';
import 'package:model/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/popup_menu_item_filter_message_action.dart';
@@ -16,7 +16,7 @@ extension HandleOpenContextMenuFilterEmailActionExtension on ThreadController {
final popupMenuItems = [
FilterMessageOption.attachments,
FilterMessageOption.unread,
if (selectedMailboxId?.isFavoriteMailboxId != true)
if (selectedMailbox?.isFavorite != true)
FilterMessageOption.starred,
].map((filterOption) {
return PopupMenuItem(
@@ -1,7 +1,7 @@
import 'package:core/utils/app_logger.dart';
import 'package:dartz/dartz.dart';
import 'package:model/extensions/mailbox_id_extension.dart';
import 'package:model/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart';
@@ -49,7 +49,7 @@ extension HandlePullToRefreshListEmailExtension on ThreadController {
accountId,
),
getLatestChanges: true,
useCache: selectedMailboxId?.isFavoriteMailboxId != true,
useCache: selectedMailbox?.isFavorite != true,
));
}
}
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:model/extensions/mailbox_id_extension.dart';
import 'package:model/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/context_item_filter_message_option_action.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart';
@@ -14,7 +14,7 @@ extension HandleSelectMessageFilterExtension on ThreadController {
final contextMenuActions = [
FilterMessageOption.attachments,
FilterMessageOption.unread,
if (selectedMailboxId?.isFavoriteMailboxId != true)
if (selectedMailbox?.isFavorite != true)
FilterMessageOption.starred,
].map((filter) {
return ContextItemFilterMessageOptionAction(
@@ -507,7 +507,7 @@ class ThreadController extends BaseController with EmailActionController {
mailboxDashBoardController.updateRefreshAllEmailState(Right(RefreshAllEmailSuccess()));
if (success.currentMailboxId != selectedMailboxId &&
selectedMailboxId?.isFavoriteMailboxId != true) {
selectedMailbox?.isFavorite != true) {
log('ThreadController::_getAllEmailSuccess: GetAllForMailboxId = ${success.currentMailboxId?.asString} | SELECTED_MAILBOX_ID = ${selectedMailboxId?.asString} | SELECTED_MAILBOX_NAME = ${selectedMailbox?.name?.name}');
return;
}
@@ -548,7 +548,7 @@ class ThreadController extends BaseController with EmailActionController {
void _refreshChangesAllEmailSuccess(RefreshChangesAllEmailSuccess success) {
if (success.currentMailboxId != selectedMailboxId &&
selectedMailboxId?.isFavoriteMailboxId != true) {
selectedMailbox?.isFavorite != true) {
log('ThreadController::_refreshChangesAllEmailSuccess: RefreshedMailboxId = ${success.currentMailboxId?.asString} | SELECTED_MAILBOX_ID = ${selectedMailboxId?.asString} | SELECTED_MAILBOX_NAME = ${selectedMailbox?.name?.name}');
return;
}
@@ -591,7 +591,7 @@ class ThreadController extends BaseController with EmailActionController {
_accountId!,
),
getLatestChanges: getLatestChanges,
useCache: selectedMailboxId?.isFavoriteMailboxId != true,
useCache: selectedMailbox?.isFavorite != true,
));
} else {
consumeState(Stream.value(Left(GetAllEmailFailure(NotFoundSessionException()))));
@@ -599,7 +599,7 @@ class ThreadController extends BaseController with EmailActionController {
}
EmailFilter getEmailFilterForLoadMailbox({PresentationEmail? oldestEmail}) {
if (selectedMailboxId?.isFavoriteMailboxId == true) {
if (selectedMailbox?.isFavorite == true) {
return EmailFilter(
filter: getFilterConditionForLoadMailbox(oldestEmail: oldestEmail),
);
@@ -613,7 +613,7 @@ class ThreadController extends BaseController with EmailActionController {
}
Filter getFilterConditionForLoadMailbox({PresentationEmail? oldestEmail}) {
if (selectedMailboxId?.isFavoriteMailboxId == true) {
if (selectedMailbox?.isFavorite == true) {
switch (mailboxDashBoardController.filterMessageOption.value) {
case FilterMessageOption.unread:
return EmailFilterCondition(
@@ -711,7 +711,7 @@ class ThreadController extends BaseController with EmailActionController {
try {
if (searchController.isSearchEmailRunning && PlatformInfo.isWeb) {
await _refreshChangeSearchEmail();
} else if (selectedMailboxId?.isFavoriteMailboxId == true) {
} else if (selectedMailbox?.isFavorite == true) {
await _refreshChangeListEmailsInFavoriteFolder();
} else {
await _refreshChangeListEmail();
@@ -868,14 +868,14 @@ class ThreadController extends BaseController with EmailActionController {
filter: getFilterConditionForLoadMailbox(oldestEmail: oldestEmail),
properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!),
lastEmailId: oldestEmail?.id,
useCache: selectedMailboxId?.isFavoriteMailboxId != true,
useCache: selectedMailbox?.isFavorite != true,
)
));
}
}
bool _validatePresentationEmail(PresentationEmail email) {
return (_belongToCurrentMailboxId(email) || selectedMailboxId?.isFavoriteMailboxId == true)
return (_belongToCurrentMailboxId(email) || selectedMailbox?.isFavorite == true)
&& _notDuplicatedInCurrentList(email);
}
@@ -3,7 +3,6 @@ import 'package:jmap_dart_client/jmap/core/reference_id.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/extensions/keyword_identifier_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
extension MailboxIdExtension on MailboxId {
String generatePath() {
@@ -32,7 +31,4 @@ extension MailboxIdExtension on MailboxId {
}
String get asString => id.value;
bool get isFavoriteMailboxId =>
this == PresentationMailbox.favoriteFolder.id;
}
@@ -34,6 +34,8 @@ extension PresentationMailboxExtension on PresentationMailbox {
bool get isSpam => role == PresentationMailbox.roleSpam || role == PresentationMailbox.roleJunk;
bool get isFavorite => role == PresentationMailbox.roleFavorite;
bool get isTrash => role == PresentationMailbox.roleTrash;
bool get isDrafts => role == PresentationMailbox.roleDrafts;
+2 -1
View File
@@ -24,7 +24,7 @@ class PresentationMailbox with EquatableMixin {
static final PresentationMailbox favoriteFolder = PresentationMailbox(
MailboxId(Id(favoriteRole)),
name: MailboxName('Starred'),
role: Role(favoriteRole),
role: roleFavorite,
);
static final roleInbox = Role(inboxRole);
@@ -37,6 +37,7 @@ class PresentationMailbox with EquatableMixin {
static final roleJunk = Role(junkRole);
static final roleArchive = Role(archiveRole);
static final roleRecovered = Role(recoveredRole);
static final roleFavorite = Role(favoriteRole);
final MailboxId id;
final MailboxName? name;