Cherry pick commit fix issue #4292 to master (#4454)

* Add folder info when open mail list from Label

(cherry picked from commit de0bb4ee02030ca90126766a8713a6826d791acd)

* Add unit test for `hasMailboxLabel` function in BaseEmailItemTile mixin

(cherry picked from commit 18f8d1847acccd6f95006b93903f0caa6c0408df)

* TF-4292 Add E2E test for test case `Add folder info when open mail list from Label` on mobile

(cherry picked from commit 1c209e333510db161522e4449ad7fb2e5d28399f)
This commit is contained in:
Dat Vu
2026-04-16 15:45:45 +07:00
committed by GitHub
parent 6fc88f780c
commit c5eb4a011d
13 changed files with 288 additions and 107 deletions
@@ -0,0 +1,57 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:labels/labels.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_list_view.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import '../../base/base_test_scenario.dart';
import '../../mixin/provisioning_label_scenario_mixin.dart';
import '../../robots/label_robot.dart';
import '../../robots/thread_robot.dart';
class DisplayFolderInfoWhenOpenMailFromTagScenario extends BaseTestScenario
with ProvisioningLabelScenarioMixin {
const DisplayFolderInfoWhenOpenMailFromTagScenario(super.$);
@override
Future<void> runTestLogic() async {
const emailUser = String.fromEnvironment('BASIC_AUTH_EMAIL');
if (emailUser.isEmpty) {
fail('Missing --dart-define=BASIC_AUTH_EMAIL for this integration scenario');
}
final threadRobot = ThreadRobot($);
final labelRobot = LabelRobot($);
final labels = await provisionLabelsByDisplayNames(
['Tag 1'],
);
await $.pumpAndSettle();
expect(labels, isNotEmpty, reason: 'Provisioning label "Tag 1" failed');
final newLabel = labels.first;
await provisionEmail(
buildEmailsForLabel(
label: newLabel,
toEmail: emailUser,
count: 2,
),
requestReadReceipt: false,
folderLocationRole: PresentationMailbox.roleTrash,
);
await $.pumpAndSettle(duration: const Duration(seconds: 2));
await threadRobot.openMailbox();
await _expectLabelListViewVisible();
await labelRobot.openLabelByName(newLabel.safeDisplayName);
await _expectFolderInfoDisplayed();
}
Future<void> _expectLabelListViewVisible() =>
expectViewVisible($(LabelListView));
Future<void> _expectFolderInfoDisplayed() async {
await expectViewVisible($(AppLocalizations().trashMailboxDisplayName));
}
}
@@ -0,0 +1,9 @@
import '../../base/test_base.dart';
import '../../scenarios/labels/display_folder_info_when_open_mail_from_tag_scenario.dart';
void main() {
TestBase().runPatrolTest(
description: 'Should display folder info when open mail list from tag',
scenarioBuilder: ($) => DisplayFolderInfoWhenOpenMailFromTagScenario($),
);
}
@@ -15,6 +15,7 @@ import 'package:tmail_ui_user/features/labels/presentation/models/label_action_t
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/handle_label_action_type_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/handle_label_action_type_extension.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/handle_mailbox_action_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/handle_mailbox_action_extension.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/open_app_grid_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/open_app_grid_extension.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/toggle_expand_folders_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/toggle_expand_folders_extension.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart';
@@ -406,8 +407,8 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
final selectedMailbox = dashboardController.selectedMailbox.value; final selectedMailbox = dashboardController.selectedMailbox.value;
Id? labelIdSelected; Id? labelIdSelected;
if (selectedMailbox is PresentationLabelMailbox) { if (selectedMailbox?.isLabelMailbox == true) {
labelIdSelected = selectedMailbox.id.id; labelIdSelected = selectedMailbox?.labelId;
} }
if (isLabelAvailable) { if (isLabelAvailable) {
@@ -17,7 +17,7 @@ import 'package:tmail_ui_user/main/routes/route_utils.dart';
extension PresentationMailboxExtension on PresentationMailbox { extension PresentationMailboxExtension on PresentationMailbox {
String getDisplayName(BuildContext context) { String getDisplayName(BuildContext context) {
if (this is PresentationLabelMailbox) { if (isLabelMailbox) {
return (this as PresentationLabelMailbox).label.safeDisplayName; return (this as PresentationLabelMailbox).label.safeDisplayName;
} }
@@ -52,7 +52,7 @@ extension PresentationMailboxExtension on PresentationMailbox {
} }
String getDisplayNameWithoutContext(AppLocalizations appLocalizations) { String getDisplayNameWithoutContext(AppLocalizations appLocalizations) {
if (this is PresentationLabelMailbox) { if (isLabelMailbox) {
return (this as PresentationLabelMailbox).label.safeDisplayName; return (this as PresentationLabelMailbox).label.safeDisplayName;
} }
@@ -20,6 +20,7 @@ import 'package:tmail_ui_user/features/base/widget/keyboard/keyboard_handler_wra
import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart'; import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart';
import 'package:tmail_ui_user/features/base/widget/scrollbar_list_view.dart'; import 'package:tmail_ui_user/features/base/widget/scrollbar_list_view.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/presentation_email_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/presentation_email_extension.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/recent_search.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/recent_search.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_ai_needs_action_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_ai_needs_action_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/labels/handle_logic_label_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/labels/handle_logic_label_extension.dart';
@@ -770,6 +771,9 @@ class SearchEmailView extends GetWidget<SearchEmailController>
final listLabels = dashboardController.labelController.labels; final listLabels = dashboardController.labelController.labels;
final isLabelMailboxOpened =
dashboardController.selectedMailbox.value?.isLabelMailbox == true;
List<Label>? emailLabels; List<Label>? emailLabels;
if (isLabelAvailable) { if (isLabelAvailable) {
@@ -783,6 +787,7 @@ class SearchEmailView extends GetWidget<SearchEmailController>
isShowingEmailContent: isShowingEmailContent, isShowingEmailContent: isShowingEmailContent,
isSenderImportantFlagEnabled: isSenderImportantFlagEnabled, isSenderImportantFlagEnabled: isSenderImportantFlagEnabled,
isSearchEmailRunning: true, isSearchEmailRunning: true,
isLabelMailboxOpened: isLabelMailboxOpened,
isAINeedsActionEnabled: isAINeedsActionEnabled, isAINeedsActionEnabled: isAINeedsActionEnabled,
labels: emailLabels, labels: emailLabels,
padding: SearchEmailViewStyle.getPaddingSearchResultList( padding: SearchEmailViewStyle.getPaddingSearchResultList(
@@ -10,7 +10,6 @@ import 'package:core/presentation/views/text/text_overflow_builder.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:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.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/presentation_email_extension.dart'; import 'package:model/extensions/presentation_email_extension.dart';
@@ -26,15 +25,16 @@ typedef OnMoreActionClick = Future<void> Function(PresentationEmail, RelativeRec
mixin BaseEmailItemTile { mixin BaseEmailItemTile {
final responsiveUtils = Get.find<ResponsiveUtils>(); ResponsiveUtils get responsiveUtils;
final imagePaths = Get.find<ImagePaths>();
ImagePaths get imagePaths;
Widget buildMailboxContain( Widget buildMailboxContain(
BuildContext context, BuildContext context,
bool isSearchEmailRunning, bool showMailboxLabel,
PresentationEmail email PresentationEmail email
) { ) {
if (hasMailboxLabel(isSearchEmailRunning, email)) { if (hasMailboxLabel(showMailboxLabel, email)) {
return Container( return Container(
margin: const EdgeInsetsDirectional.only(start: 8), margin: const EdgeInsetsDirectional.only(start: 8),
padding: const EdgeInsetsDirectional.symmetric(horizontal: 8), padding: const EdgeInsetsDirectional.symmetric(horizontal: 8),
@@ -68,12 +68,12 @@ mixin BaseEmailItemTile {
Color buildTextColorForReadEmail(PresentationEmail email) => Color buildTextColorForReadEmail(PresentationEmail email) =>
email.hasRead ? AppColor.steelGray400 : Colors.black; email.hasRead ? AppColor.steelGray400 : Colors.black;
bool hasMailboxLabel(bool isSearchEmailRunning, PresentationEmail email) { bool hasMailboxLabel(bool showMailboxLabel, PresentationEmail email) {
return isSearchEmailRunning && email.mailboxContain != null; return showMailboxLabel && email.mailboxContain != null;
} }
String informationSender(PresentationEmail email, PresentationMailbox? mailbox) { String informationSender(PresentationEmail email, PresentationMailbox? mailbox) {
if (mailbox?.isSent == true || mailbox?.isDrafts == true || mailbox?.isOutbox == true) { if (mailbox?.isOutgoingMailbox == true) {
return email.recipientsName(); return email.recipientsName();
} else { } else {
return email.getSenderName(); return email.getSenderName();
@@ -19,7 +19,7 @@ import 'package:tmail_ui_user/features/labels/presentation/mixin/label_sub_menu_
import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.dart';
import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart';
import 'package:tmail_ui_user/features/mailbox/domain/state/move_folder_content_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/move_folder_content_state.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/presentation_label_mailbox.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/spam_report_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/spam_report_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_ai_needs_action_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_ai_needs_action_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_open_context_menu_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_open_context_menu_extension.dart';
@@ -530,6 +530,9 @@ class ThreadView extends GetWidget<ThreadController>
final isAINeedsActionEnabled = dashboardController.isAINeedsActionEnabled; final isAINeedsActionEnabled = dashboardController.isAINeedsActionEnabled;
final isLabelMailboxOpened =
dashboardController.selectedMailbox.value?.isLabelMailbox == true;
return EmailTileBuilder( return EmailTileBuilder(
key: Key('email_tile_builder_${presentationEmail.id?.asString}'), key: Key('email_tile_builder_${presentationEmail.id?.asString}'),
presentationEmail: presentationEmail, presentationEmail: presentationEmail,
@@ -538,6 +541,7 @@ class ThreadView extends GetWidget<ThreadController>
searchQuery: controller.searchQuery, searchQuery: controller.searchQuery,
mailboxContain: presentationEmail.mailboxContain, mailboxContain: presentationEmail.mailboxContain,
isSearchEmailRunning: isSearchEmailRunning, isSearchEmailRunning: isSearchEmailRunning,
isLabelMailboxOpened: isLabelMailboxOpened,
isDrag: true, isDrag: true,
isSenderImportantFlagEnabled: isSenderImportantFlagEnabled, isSenderImportantFlagEnabled: isSenderImportantFlagEnabled,
isAINeedsActionEnabled: isAINeedsActionEnabled, isAINeedsActionEnabled: isAINeedsActionEnabled,
@@ -546,70 +550,6 @@ class ThreadView extends GetWidget<ThreadController>
} }
Widget _buildEmailItemNotDraggable(BuildContext context, PresentationEmail presentationEmail) { Widget _buildEmailItemNotDraggable(BuildContext context, PresentationEmail presentationEmail) {
final backgroundWidget = Container(
color: AppColor.colorItemRecipientSelected,
padding: const EdgeInsetsDirectional.only(start: 16),
alignment: AlignmentDirectional.centerStart,
child: Row(
children: [
CircleAvatar(
backgroundColor: AppColor.colorSpamReportBannerBackground,
radius: 24,
child: !presentationEmail.hasRead
? SvgPicture.asset(
controller.imagePaths.icMarkAsRead,
fit: BoxFit.fill,
)
: SvgPicture.asset(
controller.imagePaths.icUnreadEmail,
fit: BoxFit.fill,
colorFilter: AppColor.primaryColor.asFilter(),
),
),
const SizedBox(width: 11),
Text(
!presentationEmail.hasRead
? AppLocalizations.of(context).mark_as_read
: AppLocalizations.of(context).mark_as_unread,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 15,
color: AppColor.primaryColor,
),
),
],
),
);
final isInArchiveMailbox = controller.isInArchiveMailbox(presentationEmail);
final secondaryBackgroundWidget = !isInArchiveMailbox
? Container(
color: AppColor.colorItemRecipientSelected,
padding: const EdgeInsetsDirectional.only(end: 16),
alignment: AlignmentDirectional.centerEnd,
child: Row(
children: [
const Spacer(),
CircleAvatar(
backgroundColor: AppColor.colorSpamReportBannerBackground,
radius: 24,
child: SvgPicture.asset(
controller.imagePaths.icMailboxArchived,
fit: BoxFit.fill,
),
),
const SizedBox(width: 11),
Text(
AppLocalizations.of(context).archiveMessage,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 15,
color: AppColor.primaryColor,
),
),
],
),
)
: null;
final dashboardController = controller.mailboxDashBoardController; final dashboardController = controller.mailboxDashBoardController;
return Obx(() { return Obx(() {
@@ -629,14 +569,17 @@ class ThreadView extends GetWidget<ThreadController>
final isLabelAvailable = controller final isLabelAvailable = controller
.mailboxDashBoardController.isLabelAvailable; .mailboxDashBoardController.isLabelAvailable;
final listLabels = final listLabels =
controller.mailboxDashBoardController.labelController.labels; controller.mailboxDashBoardController.labelController.labels;
List<Label>? emailLabels; final isLabelMailboxOpened =
controller.selectedMailbox?.isLabelMailbox == true;
if (isLabelAvailable) { List<Label>? emailLabels;
emailLabels = presentationEmail.getLabelList(listLabels);
} if (isLabelAvailable) {
emailLabels = presentationEmail.getLabelList(listLabels);
}
return Dismissible( return Dismissible(
key: ValueKey<EmailId?>(presentationEmail.id), key: ValueKey<EmailId?>(presentationEmail.id),
@@ -645,8 +588,10 @@ class ThreadView extends GetWidget<ThreadController>
selectModeAll, selectModeAll,
presentationEmail presentationEmail
), ),
background: backgroundWidget, background: _buildEmailSwipeBackground(context, presentationEmail),
secondaryBackground: secondaryBackgroundWidget, secondaryBackground: controller.isInArchiveMailbox(presentationEmail)
? null
: _buildEmailSwipeSecondaryBackground(context),
confirmDismiss: (direction) => controller.swipeEmailAction( confirmDismiss: (direction) => controller.swipeEmailAction(
presentationEmail, presentationEmail,
direction, direction,
@@ -661,6 +606,7 @@ class ThreadView extends GetWidget<ThreadController>
mailboxContain: presentationEmail.mailboxContain, mailboxContain: presentationEmail.mailboxContain,
isSearchEmailRunning: isSearchEmailRunning, isSearchEmailRunning: isSearchEmailRunning,
isAINeedsActionEnabled: isAINeedsActionEnabled, isAINeedsActionEnabled: isAINeedsActionEnabled,
isLabelMailboxOpened: isLabelMailboxOpened,
labels: emailLabels, labels: emailLabels,
emailActionClick: _handleEmailActionClicked, emailActionClick: _handleEmailActionClicked,
onMoreActionClick: (email, position) async { onMoreActionClick: (email, position) async {
@@ -695,6 +641,65 @@ class ThreadView extends GetWidget<ThreadController>
}); });
} }
Widget _buildEmailSwipeBackground(BuildContext context, PresentationEmail presentationEmail) {
return Container(
color: AppColor.colorItemRecipientSelected,
padding: const EdgeInsetsDirectional.only(start: 16),
alignment: AlignmentDirectional.centerStart,
child: Row(
children: [
CircleAvatar(
backgroundColor: AppColor.colorSpamReportBannerBackground,
radius: 24,
child: !presentationEmail.hasRead
? SvgPicture.asset(controller.imagePaths.icMarkAsRead, fit: BoxFit.fill)
: SvgPicture.asset(
controller.imagePaths.icUnreadEmail,
fit: BoxFit.fill,
colorFilter: AppColor.primaryColor.asFilter(),
),
),
const SizedBox(width: 11),
Text(
!presentationEmail.hasRead
? AppLocalizations.of(context).mark_as_read
: AppLocalizations.of(context).mark_as_unread,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 15,
color: AppColor.primaryColor,
),
),
],
),
);
}
Widget _buildEmailSwipeSecondaryBackground(BuildContext context) {
return Container(
color: AppColor.colorItemRecipientSelected,
padding: const EdgeInsetsDirectional.only(end: 16),
alignment: AlignmentDirectional.centerEnd,
child: Row(
children: [
const Spacer(),
CircleAvatar(
backgroundColor: AppColor.colorSpamReportBannerBackground,
radius: 24,
child: SvgPicture.asset(controller.imagePaths.icMailboxArchived, fit: BoxFit.fill),
),
const SizedBox(width: 11),
Text(
AppLocalizations.of(context).archiveMessage,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 15,
color: AppColor.primaryColor,
),
),
],
),
);
}
void _handleEmailActionClicked( void _handleEmailActionClicked(
EmailActionType actionType, EmailActionType actionType,
PresentationEmail presentationEmail PresentationEmail presentationEmail
@@ -772,8 +777,7 @@ class ThreadView extends GetWidget<ThreadController>
isFilterMessageActive: controller.mailboxDashBoardController.filterMessageOption.value != FilterMessageOption.all, isFilterMessageActive: controller.mailboxDashBoardController.filterMessageOption.value != FilterMessageOption.all,
isFavoriteFolder: controller.selectedMailbox?.isFavorite == true, isFavoriteFolder: controller.selectedMailbox?.isFavorite == true,
isActionRequiredFolder: controller.selectedMailbox?.isActionRequired == true, isActionRequiredFolder: controller.selectedMailbox?.isActionRequired == true,
isLabelMailbox: isLabelMailbox: controller.selectedMailbox?.isLabelMailbox == true,
controller.selectedMailbox is PresentationLabelMailbox,
), ),
); );
} }
@@ -1,4 +1,5 @@
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -15,6 +16,7 @@ class DesktopListEmailActionHoverWidget extends StatefulWidget {
final bool isHovered; final bool isHovered;
final bool canDeletePermanently; final bool canDeletePermanently;
final bool isSearchEmailRunning; final bool isSearchEmailRunning;
final bool isLabelMailboxOpened;
final PresentationMailbox? mailboxContain; final PresentationMailbox? mailboxContain;
final OnPressEmailActionClick? emailActionClick; final OnPressEmailActionClick? emailActionClick;
final OnMoreActionClick? onMoreActionClick; final OnMoreActionClick? onMoreActionClick;
@@ -25,6 +27,7 @@ class DesktopListEmailActionHoverWidget extends StatefulWidget {
required this.isHovered, required this.isHovered,
required this.canDeletePermanently, required this.canDeletePermanently,
required this.isSearchEmailRunning, required this.isSearchEmailRunning,
required this.isLabelMailboxOpened,
required this.mailboxContain, required this.mailboxContain,
required this.emailActionClick, required this.emailActionClick,
required this.onMoreActionClick, required this.onMoreActionClick,
@@ -37,8 +40,6 @@ class DesktopListEmailActionHoverWidget extends StatefulWidget {
class _DesktopListEmailActionHoverWidgetState class _DesktopListEmailActionHoverWidgetState
extends State<DesktopListEmailActionHoverWidget> with BaseEmailItemTile { extends State<DesktopListEmailActionHoverWidget> with BaseEmailItemTile {
final _imagePaths = Get.find<ImagePaths>();
bool _popupMenuVisible = false; bool _popupMenuVisible = false;
@override @override
@@ -46,7 +47,7 @@ class _DesktopListEmailActionHoverWidgetState
final listActionWidget = [ final listActionWidget = [
if (widget.isHovered) ...[ if (widget.isHovered) ...[
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: _imagePaths.icOpenInNewTab, icon: imagePaths.icOpenInNewTab,
iconColor: ItemEmailTileStyles.actionIconHoverColor, iconColor: ItemEmailTileStyles.actionIconHoverColor,
iconSize: _getIconSize(), iconSize: _getIconSize(),
padding: _getPaddingIcon(), padding: _getPaddingIcon(),
@@ -60,8 +61,8 @@ class _DesktopListEmailActionHoverWidgetState
if (!widget.presentationEmail.isDraft) if (!widget.presentationEmail.isDraft)
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: widget.presentationEmail.hasRead icon: widget.presentationEmail.hasRead
? _imagePaths.icUnread ? imagePaths.icUnread
: _imagePaths.icRead, : imagePaths.icRead,
iconColor: ItemEmailTileStyles.actionIconHoverColor, iconColor: ItemEmailTileStyles.actionIconHoverColor,
iconSize: _getIconSize(), iconSize: _getIconSize(),
padding: _getPaddingIcon(), padding: _getPaddingIcon(),
@@ -80,7 +81,7 @@ class _DesktopListEmailActionHoverWidgetState
if (widget.mailboxContain != null && if (widget.mailboxContain != null &&
widget.mailboxContain?.isDrafts == false) ...[ widget.mailboxContain?.isDrafts == false) ...[
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: _imagePaths.icMove, icon: imagePaths.icMove,
iconColor: ItemEmailTileStyles.actionIconHoverColor, iconColor: ItemEmailTileStyles.actionIconHoverColor,
iconSize: _getIconSize(), iconSize: _getIconSize(),
padding: _getPaddingIcon(), padding: _getPaddingIcon(),
@@ -94,7 +95,7 @@ class _DesktopListEmailActionHoverWidgetState
), ),
], ],
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: _imagePaths.icDeleteComposer, icon: imagePaths.icDeleteComposer,
iconColor: ItemEmailTileStyles.actionIconHoverColor, iconColor: ItemEmailTileStyles.actionIconHoverColor,
iconSize: _getIconSize(), iconSize: _getIconSize(),
padding: _getPaddingIcon(), padding: _getPaddingIcon(),
@@ -113,7 +114,7 @@ class _DesktopListEmailActionHoverWidgetState
], ],
if (_shouldShowPopupMenu) ...[ if (_shouldShowPopupMenu) ...[
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: _imagePaths.icMoreVertical, icon: imagePaths.icMoreVertical,
iconColor: ItemEmailTileStyles.actionIconHoverColor, iconColor: ItemEmailTileStyles.actionIconHoverColor,
iconSize: _getIconSize(), iconSize: _getIconSize(),
padding: _getPaddingIcon(), padding: _getPaddingIcon(),
@@ -134,7 +135,7 @@ class _DesktopListEmailActionHoverWidgetState
] else ...[ ] else ...[
buildMailboxContain( buildMailboxContain(
context, context,
widget.isSearchEmailRunning, widget.isSearchEmailRunning || widget.isLabelMailboxOpened,
widget.presentationEmail, widget.presentationEmail,
), ),
if (widget.presentationEmail.hasAttachment == true) if (widget.presentationEmail.hasAttachment == true)
@@ -173,4 +174,10 @@ class _DesktopListEmailActionHoverWidgetState
}); });
} }
} }
@override
ImagePaths get imagePaths => Get.find<ImagePaths>();
@override
ResponsiveUtils get responsiveUtils => Get.find<ResponsiveUtils>();
} }
@@ -1,5 +1,8 @@
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:labels/model/label.dart'; import 'package:labels/model/label.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';
@@ -24,6 +27,7 @@ class EmailTileBuilder extends StatelessWidget with BaseEmailItemTile {
final bool isSenderImportantFlagEnabled; final bool isSenderImportantFlagEnabled;
final bool isAINeedsActionEnabled; final bool isAINeedsActionEnabled;
final bool autoWrapTagsByMaxWidth; final bool autoWrapTagsByMaxWidth;
final bool isLabelMailboxOpened;
final List<Label>? labels; final List<Label>? labels;
final OnPressEmailActionClick? emailActionClick; final OnPressEmailActionClick? emailActionClick;
final OnMoreActionClick? onMoreActionClick; final OnMoreActionClick? onMoreActionClick;
@@ -39,6 +43,7 @@ class EmailTileBuilder extends StatelessWidget with BaseEmailItemTile {
this.isSenderImportantFlagEnabled = true, this.isSenderImportantFlagEnabled = true,
this.isAINeedsActionEnabled = true, this.isAINeedsActionEnabled = true,
this.autoWrapTagsByMaxWidth = false, this.autoWrapTagsByMaxWidth = false,
this.isLabelMailboxOpened = false,
this.mailboxContain, this.mailboxContain,
this.padding, this.padding,
this.isDrag = false, this.isDrag = false,
@@ -120,7 +125,7 @@ class EmailTileBuilder extends StatelessWidget with BaseEmailItemTile {
searchQuery)), searchQuery)),
buildMailboxContain( buildMailboxContain(
context, context,
isSearchEmailRunning, isSearchEmailRunning || isLabelMailboxOpened,
presentationEmail), presentationEmail),
if (presentationEmail.hasStarred) if (presentationEmail.hasStarred)
Padding( Padding(
@@ -229,4 +234,10 @@ class EmailTileBuilder extends StatelessWidget with BaseEmailItemTile {
bool get _shouldShowAIAction => bool get _shouldShowAIAction =>
isAINeedsActionEnabled && presentationEmail.hasNeedAction; isAINeedsActionEnabled && presentationEmail.hasNeedAction;
@override
ImagePaths get imagePaths => Get.find<ImagePaths>();
@override
ResponsiveUtils get responsiveUtils => Get.find<ResponsiveUtils>();
} }
@@ -1,9 +1,12 @@
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart'; import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/presentation/views/button/icon_button_web.dart'; import 'package:core/presentation/views/button/icon_button_web.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:core/presentation/views/responsive/responsive_widget.dart'; import 'package:core/presentation/views/responsive/responsive_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:labels/model/label.dart'; import 'package:labels/model/label.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';
@@ -31,6 +34,7 @@ class EmailTileBuilder extends StatefulWidget {
final bool isSenderImportantFlagEnabled; final bool isSenderImportantFlagEnabled;
final bool isAINeedsActionEnabled; final bool isAINeedsActionEnabled;
final bool autoWrapTagsByMaxWidth; final bool autoWrapTagsByMaxWidth;
final bool isLabelMailboxOpened;
final List<Label>? labels; final List<Label>? labels;
final OnPressEmailActionClick? emailActionClick; final OnPressEmailActionClick? emailActionClick;
final OnMoreActionClick? onMoreActionClick; final OnMoreActionClick? onMoreActionClick;
@@ -45,6 +49,7 @@ class EmailTileBuilder extends StatefulWidget {
this.isSearchEmailRunning = false, this.isSearchEmailRunning = false,
this.isSenderImportantFlagEnabled = true, this.isSenderImportantFlagEnabled = true,
this.autoWrapTagsByMaxWidth = false, this.autoWrapTagsByMaxWidth = false,
this.isLabelMailboxOpened = false,
this.mailboxContain, this.mailboxContain,
this.padding, this.padding,
this.isDrag = false, this.isDrag = false,
@@ -160,7 +165,7 @@ class _EmailTileBuilderState extends State<EmailTileBuilder> with BaseEmailItem
), ),
buildMailboxContain( buildMailboxContain(
context, context,
widget.isSearchEmailRunning, widget.isSearchEmailRunning || widget.isLabelMailboxOpened,
widget.presentationEmail widget.presentationEmail
), ),
if (widget.presentationEmail.hasStarred) if (widget.presentationEmail.hasStarred)
@@ -185,6 +190,7 @@ class _EmailTileBuilderState extends State<EmailTileBuilder> with BaseEmailItem
selectAllMode: widget.selectAllMode, selectAllMode: widget.selectAllMode,
canDeletePermanently: canDeletePermanently, canDeletePermanently: canDeletePermanently,
isSearchEmailRunning: widget.isSearchEmailRunning, isSearchEmailRunning: widget.isSearchEmailRunning,
isLabelMailboxOpened: widget.isLabelMailboxOpened,
isShowingEmailContent: widget.isShowingEmailContent, isShowingEmailContent: widget.isShowingEmailContent,
isDrag: widget.isDrag, isDrag: widget.isDrag,
isSenderImportantFlagEnabled: widget.isSenderImportantFlagEnabled, isSenderImportantFlagEnabled: widget.isSenderImportantFlagEnabled,
@@ -324,6 +330,7 @@ class _EmailTileBuilderState extends State<EmailTileBuilder> with BaseEmailItem
isHovered: value, isHovered: value,
canDeletePermanently: canDeletePermanently, canDeletePermanently: canDeletePermanently,
isSearchEmailRunning: widget.isSearchEmailRunning, isSearchEmailRunning: widget.isSearchEmailRunning,
isLabelMailboxOpened: widget.isLabelMailboxOpened,
mailboxContain: widget.mailboxContain, mailboxContain: widget.mailboxContain,
emailActionClick: widget.emailActionClick, emailActionClick: widget.emailActionClick,
onMoreActionClick: widget.onMoreActionClick, onMoreActionClick: widget.onMoreActionClick,
@@ -621,4 +628,10 @@ class _EmailTileBuilderState extends State<EmailTileBuilder> with BaseEmailItem
_hoverNotifier.dispose(); _hoverNotifier.dispose();
super.dispose(); super.dispose();
} }
@override
ImagePaths get imagePaths => Get.find<ImagePaths>();
@override
ResponsiveUtils get responsiveUtils => Get.find<ResponsiveUtils>();
} }
@@ -1,5 +1,6 @@
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -22,6 +23,7 @@ class WebTabletBodyEmailItemWidget extends StatefulWidget {
final bool canDeletePermanently; final bool canDeletePermanently;
final bool isSearchEmailRunning; final bool isSearchEmailRunning;
final bool isShowingEmailContent; final bool isShowingEmailContent;
final bool isLabelMailboxOpened;
final bool isShowDateTimeView; final bool isShowDateTimeView;
final bool isDrag; final bool isDrag;
final bool isSenderImportantFlagEnabled; final bool isSenderImportantFlagEnabled;
@@ -40,6 +42,7 @@ class WebTabletBodyEmailItemWidget extends StatefulWidget {
required this.selectAllMode, required this.selectAllMode,
required this.canDeletePermanently, required this.canDeletePermanently,
required this.isSearchEmailRunning, required this.isSearchEmailRunning,
required this.isLabelMailboxOpened,
required this.isShowingEmailContent, required this.isShowingEmailContent,
required this.isDrag, required this.isDrag,
required this.isSenderImportantFlagEnabled, required this.isSenderImportantFlagEnabled,
@@ -61,8 +64,6 @@ class WebTabletBodyEmailItemWidget extends StatefulWidget {
class _WebTabletBodyEmailItemWidgetState class _WebTabletBodyEmailItemWidgetState
extends State<WebTabletBodyEmailItemWidget> with BaseEmailItemTile { extends State<WebTabletBodyEmailItemWidget> with BaseEmailItemTile {
final _imagePaths = Get.find<ImagePaths>();
bool _isHover = false; bool _isHover = false;
bool _popupMenuVisible = false; bool _popupMenuVisible = false;
@@ -152,7 +153,7 @@ class _WebTabletBodyEmailItemWidgetState
), ),
buildMailboxContain( buildMailboxContain(
context, context,
widget.isSearchEmailRunning, widget.isSearchEmailRunning || widget.isLabelMailboxOpened,
widget.presentationEmail, widget.presentationEmail,
), ),
if (widget.presentationEmail.hasStarred) if (widget.presentationEmail.hasStarred)
@@ -184,7 +185,7 @@ class _WebTabletBodyEmailItemWidgetState
children: [ children: [
if (_isHover) ...[ if (_isHover) ...[
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: _imagePaths.icOpenInNewTab, icon: imagePaths.icOpenInNewTab,
iconColor: ItemEmailTileStyles.actionIconHoverColor, iconColor: ItemEmailTileStyles.actionIconHoverColor,
iconSize: _getIconSize(), iconSize: _getIconSize(),
padding: _getPaddingIcon(), padding: _getPaddingIcon(),
@@ -200,8 +201,8 @@ class _WebTabletBodyEmailItemWidgetState
if (!widget.presentationEmail.isDraft) if (!widget.presentationEmail.isDraft)
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: widget.presentationEmail.hasRead icon: widget.presentationEmail.hasRead
? _imagePaths.icUnread ? imagePaths.icUnread
: _imagePaths.icRead, : imagePaths.icRead,
iconColor: ItemEmailTileStyles.actionIconHoverColor, iconColor: ItemEmailTileStyles.actionIconHoverColor,
iconSize: _getIconSize(), iconSize: _getIconSize(),
padding: _getPaddingIcon(), padding: _getPaddingIcon(),
@@ -220,7 +221,7 @@ class _WebTabletBodyEmailItemWidgetState
if (widget.mailboxContain != null && if (widget.mailboxContain != null &&
widget.mailboxContain?.isDrafts == false) ...[ widget.mailboxContain?.isDrafts == false) ...[
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: _imagePaths.icMove, icon: imagePaths.icMove,
iconColor: ItemEmailTileStyles.actionIconHoverColor, iconColor: ItemEmailTileStyles.actionIconHoverColor,
iconSize: _getIconSize(), iconSize: _getIconSize(),
padding: _getPaddingIcon(), padding: _getPaddingIcon(),
@@ -234,7 +235,7 @@ class _WebTabletBodyEmailItemWidgetState
), ),
], ],
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: _imagePaths.icDeleteComposer, icon: imagePaths.icDeleteComposer,
iconColor: ItemEmailTileStyles.actionIconHoverColor, iconColor: ItemEmailTileStyles.actionIconHoverColor,
iconSize: _getIconSize(), iconSize: _getIconSize(),
padding: _getPaddingIcon(), padding: _getPaddingIcon(),
@@ -253,7 +254,7 @@ class _WebTabletBodyEmailItemWidgetState
], ],
if (_shouldShowPopupMenu) if (_shouldShowPopupMenu)
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: _imagePaths.icMoreVertical, icon: imagePaths.icMoreVertical,
iconColor: ItemEmailTileStyles.actionIconHoverColor, iconColor: ItemEmailTileStyles.actionIconHoverColor,
iconSize: _getIconSize(), iconSize: _getIconSize(),
padding: _getPaddingIcon(), padding: _getPaddingIcon(),
@@ -464,4 +465,10 @@ class _WebTabletBodyEmailItemWidgetState
}, },
); );
} }
@override
ImagePaths get imagePaths => Get.find<ImagePaths>();
@override
ResponsiveUtils get responsiveUtils => Get.find<ResponsiveUtils>();
} }
@@ -90,6 +90,8 @@ extension PresentationMailboxExtension on PresentationMailbox {
bool get isOutbox => name?.name == PresentationMailbox.outboxRole || role == PresentationMailbox.roleOutbox; bool get isOutbox => name?.name == PresentationMailbox.outboxRole || role == PresentationMailbox.roleOutbox;
bool get isOutgoingMailbox => isSent || isDrafts || isOutbox;
bool get isArchive => role == PresentationMailbox.roleArchive; bool get isArchive => role == PresentationMailbox.roleArchive;
bool get isRecovered => role == PresentationMailbox.roleRecovered; bool get isRecovered => role == PresentationMailbox.roleRecovered;
@@ -0,0 +1,65 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/thread/presentation/mixin/base_email_item_tile.dart';
class _TestEmailItemTile with BaseEmailItemTile {
@override
ImagePaths get imagePaths => ImagePaths();
@override
ResponsiveUtils get responsiveUtils => ResponsiveUtils();
}
void main() {
late _TestEmailItemTile tile;
setUp(() {
tile = _TestEmailItemTile();
});
final inboxFolder = PresentationMailbox(MailboxId(Id('inbox')));
group('BaseEmailItemTile.hasMailboxLabel', () {
test('should return false when showMailboxLabel is false and mailboxContain null', () {
final email = PresentationEmail();
final result = tile.hasMailboxLabel(false, email);
expect(result, isFalse);
});
test('should return false when showMailboxLabel is false even if mailboxContain exists',
() {
final email = PresentationEmail(
mailboxContain: inboxFolder,
);
final result = tile.hasMailboxLabel(false, email);
expect(result, isFalse);
});
test('should return false when showMailboxLabel is true but mailboxContain null', () {
final email = PresentationEmail();
final result = tile.hasMailboxLabel(true, email);
expect(result, isFalse);
});
test('should return true when showMailboxLabel is true and mailboxContain exists', () {
final email = PresentationEmail(
mailboxContain: inboxFolder,
);
final result = tile.hasMailboxLabel(true, email);
expect(result, isTrue);
});
});
}