From dc3b4d46dde1243855d5ebbc5082ff54f059538e Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 5 Sep 2025 16:09:16 +0700 Subject: [PATCH] TF-4004 Change new design for list email rules view --- .../extensions/color_extension.dart | 2 - .../email_rules/email_rules_controller.dart | 42 ++--- .../email_rules/email_rules_view.dart | 55 +++++-- .../widgets/count_name_of_rules_widget.dart | 2 +- .../widgets/email_rule_item_widget.dart | 152 ++++++++++++------ .../widgets/list_email_rules_widget.dart | 117 ++++++-------- .../extensions/tmail_rule_extension.dart | 20 +++ lib/l10n/intl_messages.arb | 6 - lib/main/localizations/app_localizations.dart | 7 - rule_filter/lib/rule_filter/rule_id.dart | 2 + 10 files changed, 230 insertions(+), 175 deletions(-) create mode 100644 lib/features/manage_account/presentation/extensions/tmail_rule_extension.dart diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index 95bc852ca..d935f3356 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -123,8 +123,6 @@ extension AppColor on Color { static const colorBorderWrapIconStyleCode = Color(0xFFE4E4E4); static const colorBackgroundWrapIconStyleCode = Color(0xFFF2F3F5); static const colorBackgroundSnackBar = Color(0xFF343438); - static const colorBackgroundHeaderListRuleFilter = Color(0xFFFAF7F7); - static const colorBorderListRuleFilter = Color(0xFFE7E8EC); static const colorBackgroundFieldConditionRulesFilter = Color(0xFFF2F3F5); static const colorDeletePermanentlyButton = Color(0xffE64646); static const colorBackgroundNotificationVacationSetting= Color(0xFFFFF5C2); diff --git a/lib/features/manage_account/presentation/email_rules/email_rules_controller.dart b/lib/features/manage_account/presentation/email_rules/email_rules_controller.dart index b5e036cd1..54f7f1b03 100644 --- a/lib/features/manage_account/presentation/email_rules/email_rules_controller.dart +++ b/lib/features/manage_account/presentation/email_rules/email_rules_controller.dart @@ -1,5 +1,4 @@ import 'package:core/presentation/state/success.dart'; -import 'package:core/presentation/views/bottom_popup/confirmation_dialog_action_sheet_builder.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/utils/platform_info.dart'; import 'package:flutter/material.dart'; @@ -61,7 +60,6 @@ class EmailRulesController extends BaseController { @override void handleSuccessViewState(Success success) { - super.handleSuccessViewState(success); if (success is GetAllRulesSuccess) { if (success.rules?.isNotEmpty == true) { listEmailRule.addAll(success.rules!); @@ -72,6 +70,8 @@ class EmailRulesController extends BaseController { _createNewRuleFilterSuccess(success); } else if (success is EditEmailRuleFilterSuccess) { _editEmailRuleFilterSuccess(success); + } else { + super.handleSuccessViewState(success); } } @@ -153,28 +153,16 @@ class EmailRulesController extends BaseController { } } - void deleteEmailRule(BuildContext context, TMailRule emailRule) { - if (responsiveUtils.isMobile(context)) { - (ConfirmationDialogActionSheetBuilder(context) - ..messageText(AppLocalizations.of(context).messageConfirmationDialogDeleteEmailRule(emailRule.name)) - ..onCancelAction(AppLocalizations.of(context).cancel, () => - popBack()) - ..onConfirmAction(AppLocalizations.of(context).delete, () { - popBack(); - _handleDeleteEmailRuleAction(emailRule); - })) - .show(); - } else { - MessageDialogActionManager().showConfirmDialogAction( - context, - title: AppLocalizations.of(context).deleteEmailRule, - AppLocalizations.of(context).messageConfirmationDialogDeleteEmailRule(emailRule.name), - AppLocalizations.of(context).delete, - cancelTitle: AppLocalizations.of(context).cancel, - onConfirmAction: () => _handleDeleteEmailRuleAction(emailRule), - onCloseButtonAction: popBack, - ); - } + void _deleteEmailRule(BuildContext context, TMailRule emailRule) { + MessageDialogActionManager().showConfirmDialogAction( + context, + title: AppLocalizations.of(context).deleteEmailRule, + AppLocalizations.of(context).messageConfirmationDialogDeleteEmailRule(emailRule.name), + AppLocalizations.of(context).delete, + cancelTitle: AppLocalizations.of(context).cancel, + onConfirmAction: () => _handleDeleteEmailRuleAction(emailRule), + onCloseButtonAction: popBack, + ); } void _handleDeleteEmailRuleAction(TMailRule emailRule) { @@ -248,12 +236,12 @@ class EmailRulesController extends BaseController { itemActions: contextMenuActions, onContextMenuActionClick: (action) { popBack(); - _handleRuleFilterActionType(context, rule, action.action); + handleRuleFilterActionType(context, rule, action.action); }, ); } - void _handleRuleFilterActionType( + void handleRuleFilterActionType( BuildContext context, TMailRule rule, EmailRuleActionType actionType, @@ -263,7 +251,7 @@ class EmailRulesController extends BaseController { editEmailRule(context, rule); break; case EmailRuleActionType.delete: - deleteEmailRule(context, rule); + _deleteEmailRule(context, rule); break; case EmailRuleActionType.add: break; diff --git a/lib/features/manage_account/presentation/email_rules/email_rules_view.dart b/lib/features/manage_account/presentation/email_rules/email_rules_view.dart index 37403e798..1c38bec65 100644 --- a/lib/features/manage_account/presentation/email_rules/email_rules_view.dart +++ b/lib/features/manage_account/presentation/email_rules/email_rules_view.dart @@ -5,9 +5,11 @@ import 'package:tmail_ui_user/features/manage_account/presentation/base/setting_ import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/email_rules_controller.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/widgets/add_rule_button_widget.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/widgets/count_name_of_rules_widget.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/widgets/list_email_rules_widget.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/widgets/no_rules_widget.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/model/email_rule_action_type.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/widgets/setting_explanation_widget.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/widgets/setting_header_widget.dart'; @@ -81,18 +83,49 @@ class EmailRulesView extends GetWidget { ), ), ], - Obx(() => CountNameOfRulesWidget( - countRules: controller.listEmailRule.length, - margin: EdgeInsetsDirectional.only( - start: isDesktop ? 0 : 16, - top: 24, - ), - )), - NoRulesWidget( - imagePaths: controller.imagePaths, - responsiveUtils: controller.responsiveUtils, - onAddRuleAction: controller.goToCreateNewRule, + Obx( + () => CountNameOfRulesWidget( + countRules: controller.listEmailRule.length, + margin: EdgeInsetsDirectional.only( + start: isDesktop ? 0 : 16, + top: 24, + ), + ), ), + Obx(() { + if (controller.listEmailRule.isEmpty) { + return NoRulesWidget( + imagePaths: controller.imagePaths, + responsiveUtils: controller.responsiveUtils, + onAddRuleAction: controller.goToCreateNewRule, + ); + } else { + return Expanded( + child: ListEmailRulesWidget( + listEmailRule: controller.listEmailRule, + imagePaths: controller.imagePaths, + responsiveUtils: controller.responsiveUtils, + onDeleteEmailRuleAction: (rule) => + controller.handleRuleFilterActionType( + context, + rule, + EmailRuleActionType.delete, + ), + onEditEmailRuleAction: (rule) => + controller.handleRuleFilterActionType( + context, + rule, + EmailRuleActionType.edit, + ), + onMoreEmailRuleAction: (rule) => + controller.openEditRuleMenuAction( + context, + rule, + ), + ), + ); + } + }), ], ), ), diff --git a/lib/features/manage_account/presentation/email_rules/widgets/count_name_of_rules_widget.dart b/lib/features/manage_account/presentation/email_rules/widgets/count_name_of_rules_widget.dart index 127c09590..bd5a1041b 100644 --- a/lib/features/manage_account/presentation/email_rules/widgets/count_name_of_rules_widget.dart +++ b/lib/features/manage_account/presentation/email_rules/widgets/count_name_of_rules_widget.dart @@ -23,7 +23,7 @@ class CountNameOfRulesWidget extends StatelessWidget { children: [ Flexible( child: Text( - AppLocalizations.of(context).nameOfRules, + AppLocalizations.of(context).headerNameOfRules, style: ThemeUtils.textStyleInter600().copyWith( fontSize: 14, height: 20 / 14, diff --git a/lib/features/manage_account/presentation/email_rules/widgets/email_rule_item_widget.dart b/lib/features/manage_account/presentation/email_rules/widgets/email_rule_item_widget.dart index cc767a41e..f0e83174f 100644 --- a/lib/features/manage_account/presentation/email_rules/widgets/email_rule_item_widget.dart +++ b/lib/features/manage_account/presentation/email_rules/widgets/email_rule_item_widget.dart @@ -2,79 +2,129 @@ 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/views/button/icon_button_web.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:rule_filter/rule_filter/tmail_rule.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/email_rules_controller.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/extensions/tmail_rule_extension.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +typedef OnEditEmailRuleAction = Function(TMailRule rule); +typedef OnDeleteEmailRuleAction = Function(TMailRule rule); +typedef OnMoreEmailRuleAction = Function(TMailRule rule); + class EmailRulesItemWidget extends StatelessWidget { - final _responsiveUtils = Get.find(); - final _imagePaths = Get.find(); - final _emailRuleController = Get.find(); - + final ImagePaths imagePaths; + final ResponsiveUtils responsiveUtils; final TMailRule rule; + final OnEditEmailRuleAction? onEditEmailRuleAction; + final OnDeleteEmailRuleAction? onDeleteEmailRuleAction; + final OnMoreEmailRuleAction? onMoreEmailRuleAction; - EmailRulesItemWidget({ + const EmailRulesItemWidget({ Key? key, + required this.imagePaths, + required this.responsiveUtils, required this.rule, + this.onEditEmailRuleAction, + this.onDeleteEmailRuleAction, + this.onMoreEmailRuleAction, }) : super(key: key); @override Widget build(BuildContext context) { + final isMobile = responsiveUtils.isMobile(context); + return Container( - padding: EdgeInsetsDirectional.only( - top: 15, - bottom: 15, - start: _responsiveUtils.isMobile(context) ? 16 : 24, - end: _responsiveUtils.isMobile(context) ? 0 : 24 + decoration: const BoxDecoration( + color: AppColor.lightGrayF9FAFB, + borderRadius: BorderRadius.all(Radius.circular(10)), ), - color: Colors.white, - child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text(rule.name, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: 16, - fontWeight: FontWeight.w400, - color: Colors.black)), - const Spacer(), - if (_responsiveUtils.isMobile(context)) - buildIconWeb( - icon: SvgPicture.asset( - _imagePaths.icOpenEditRule, - fit: BoxFit.fill, + height: 72, + padding: const EdgeInsetsDirectional.symmetric(horizontal: 32), + margin: const EdgeInsetsDirectional.only(top: 8), + child: Row( + children: [ + if (!isMobile) ...[ + ConstrainedBox( + constraints: BoxConstraints(maxWidth: context.width / 2), + child: Text( + rule.name, + style: ThemeUtils.textStyleBodyBody3( + color: Colors.black, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), ), - iconPadding: EdgeInsets.zero, - onTap: () => - _emailRuleController.openEditRuleMenuAction(context, rule), - ) - else - ...[ - TMailButtonWidget.fromIcon( - icon: _imagePaths.icCompose, - iconColor: AppColor.primaryColor, - iconSize: 24, - padding: const EdgeInsets.all(5), - backgroundColor: Colors.transparent, - tooltipMessage: AppLocalizations.of(context).editRule, - margin: const EdgeInsetsDirectional.only(end: 5), - onTapActionCallback: () => - _emailRuleController.editEmailRule(context, rule), + Expanded( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Container( + decoration: BoxDecoration( + color: AppColor.gray49454F.withValues(alpha: 0.08), + borderRadius: const BorderRadius.all( + Radius.circular(16), + ), + ), + margin: const EdgeInsetsDirectional.symmetric( + horizontal: 24, + ), + padding: const EdgeInsets.symmetric( + vertical: 4, + horizontal: 12, + ), + child: Text( + rule.getPreview(AppLocalizations.of(context)), + style: ThemeUtils.textStyleBodyBody3( + color: Colors.black, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ) + ], + ), ), TMailButtonWidget.fromIcon( - icon: _imagePaths.icDeleteRule, - iconColor: AppColor.primaryColor, - iconSize: 24, - padding: const EdgeInsets.all(5), + icon: imagePaths.icEdit, + iconSize: 20, + iconColor: AppColor.steelGrayA540, backgroundColor: Colors.transparent, - tooltipMessage: AppLocalizations.of(context).deleteRule, - onTapActionCallback: () => - _emailRuleController.deleteEmailRule(context, rule), + margin: const EdgeInsetsDirectional.only(end: 12), + onTapActionCallback: () => onEditEmailRuleAction?.call(rule), ), - ] - ]), + TMailButtonWidget.fromIcon( + icon: imagePaths.icDeleteComposer, + iconSize: 20, + iconColor: AppColor.steelGrayA540, + backgroundColor: Colors.transparent, + onTapActionCallback: () => onDeleteEmailRuleAction?.call(rule), + ), + ] else ...[ + Expanded( + child: Text( + rule.name, + style: ThemeUtils.textStyleBodyBody3( + color: Colors.black, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + TMailButtonWidget.fromIcon( + icon: imagePaths.icMoreVertical, + iconSize: 20, + iconColor: AppColor.steelGrayA540, + backgroundColor: Colors.transparent, + onTapActionCallback: () => onMoreEmailRuleAction?.call(rule), + ), + ], + ], + ), ); } } diff --git a/lib/features/manage_account/presentation/email_rules/widgets/list_email_rules_widget.dart b/lib/features/manage_account/presentation/email_rules/widgets/list_email_rules_widget.dart index 02ae4de27..090010d2e 100644 --- a/lib/features/manage_account/presentation/email_rules/widgets/list_email_rules_widget.dart +++ b/lib/features/manage_account/presentation/email_rules/widgets/list_email_rules_widget.dart @@ -1,81 +1,58 @@ -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/utils/theme_utils.dart'; -import 'package:core/utils/app_logger.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:rule_filter/rule_filter/rule_id.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/email_rules_controller.dart'; +import 'package:rule_filter/rule_filter/tmail_rule.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/widgets/email_rule_item_widget.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -class ListEmailRulesWidget extends GetWidget { - const ListEmailRulesWidget({Key? key}) : super(key: key); +class ListEmailRulesWidget extends StatelessWidget { + final List listEmailRule; + final ImagePaths imagePaths; + final ResponsiveUtils responsiveUtils; + final OnEditEmailRuleAction? onEditEmailRuleAction; + final OnDeleteEmailRuleAction? onDeleteEmailRuleAction; + final OnMoreEmailRuleAction? onMoreEmailRuleAction; + + const ListEmailRulesWidget({ + Key? key, + required this.listEmailRule, + required this.imagePaths, + required this.responsiveUtils, + this.onEditEmailRuleAction, + this.onDeleteEmailRuleAction, + this.onMoreEmailRuleAction, + }) : super(key: key); @override Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - color: AppColor.colorBackgroundWrapIconStyleCode, - borderRadius: BorderRadius.circular(16), - border: Border.all( - width: 1, - color: AppColor.colorBorderListRuleFilter) + final isMobile = responsiveUtils.isMobile(context); + + return Padding( + padding: const EdgeInsetsDirectional.only( + top: 12, ), - child: ClipRRect( - borderRadius: BorderRadius.circular(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Container( - width: double.infinity, - decoration: const BoxDecoration( - color: AppColor.colorBackgroundHeaderListRuleFilter, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16)), - ), - padding: const EdgeInsets.symmetric( - vertical: 28, - horizontal: 24, - ), - child: Text(AppLocalizations.of(context).headerNameOfRules, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: 16, - fontWeight: FontWeight.w500, - color: AppColor.colorTextButtonHeaderThread)), - ), - Obx(() { - if (controller.listEmailRule.isNotEmpty) { - return const Divider(); - } else { - return const SizedBox.shrink(); - } - }), - Obx(() { - log('ListEmailRulesWidget::build(): ${controller.listEmailRule}'); - return ListView.separated( - shrinkWrap: true, - itemCount: controller.listEmailRule.length, - primary: false, - padding: EdgeInsets.zero, - itemBuilder: (context, index) { - final ruleWithId = controller.listEmailRule[index] - .copyWith(id: RuleId(id: Id(index.toString()))); - log('ListEmailRulesWidget::build(): $ruleWithId'); - return EmailRulesItemWidget(rule: ruleWithId); - }, - separatorBuilder: (context, index) { - if (controller.listEmailRule.isNotEmpty) { - return const Divider(); - } else { - return const SizedBox.shrink(); - } - }, - ); - }), - ]), + child: ListView.builder( + shrinkWrap: true, + itemCount: listEmailRule.length, + primary: false, + padding: EdgeInsetsDirectional.only( + bottom: 16, + start: isMobile ? 16 : 0, + end: isMobile ? 16 : 0, + ), + itemBuilder: (context, index) { + final ruleWithId = listEmailRule[index].copyWith( + id: RuleId.fromString(index.toString()), + ); + return EmailRulesItemWidget( + rule: ruleWithId, + responsiveUtils: responsiveUtils, + imagePaths: imagePaths, + onEditEmailRuleAction: onEditEmailRuleAction, + onDeleteEmailRuleAction: onDeleteEmailRuleAction, + onMoreEmailRuleAction: onMoreEmailRuleAction, + ); + }, ), ); } diff --git a/lib/features/manage_account/presentation/extensions/tmail_rule_extension.dart b/lib/features/manage_account/presentation/extensions/tmail_rule_extension.dart new file mode 100644 index 000000000..6e439fa60 --- /dev/null +++ b/lib/features/manage_account/presentation/extensions/tmail_rule_extension.dart @@ -0,0 +1,20 @@ +import 'package:rule_filter/rule_filter/rule_condition.dart'; +import 'package:rule_filter/rule_filter/tmail_rule.dart'; +import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/rule_condition_extensions.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +extension TmailRuleExtension on TMailRule { + String getPreview(AppLocalizations appLocalizations) { + final firstCondition = conditionGroup?.conditions.firstOrNull ?? condition; + if (firstCondition != null) { + return firstCondition.getPreview(appLocalizations); + } + return ''; + } +} + +extension RuleConditionExtension on RuleCondition { + String getPreview(AppLocalizations appLocalizations) { + return '${field.getTitle(appLocalizations)}, ${comparator.getTitle(appLocalizations).toLowerCase()}: $value'; + } +} diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 4ff285bcd..0dd510f57 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -4706,12 +4706,6 @@ "placeholders_order": [], "placeholders": {} }, - "nameOfRules": "Name of Rules", - "@nameOfRules": { - "type": "text", - "placeholders_order": [], - "placeholders": {} - }, "noRulesConfigured": "No Rules Configured", "@noRulesConfigured": { "type": "text", diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index d36848853..81e3d215a 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -4965,13 +4965,6 @@ class AppLocalizations { ); } - String get nameOfRules { - return Intl.message( - 'Name of Rules', - name: 'nameOfRules', - ); - } - String get noRulesConfigured { return Intl.message( 'No Rules Configured', diff --git a/rule_filter/lib/rule_filter/rule_id.dart b/rule_filter/lib/rule_filter/rule_id.dart index 89161b2e6..92febbb55 100644 --- a/rule_filter/lib/rule_filter/rule_id.dart +++ b/rule_filter/lib/rule_filter/rule_id.dart @@ -10,6 +10,8 @@ class RuleId with EquatableMixin { required this.id, }); + factory RuleId.fromString(String id) => RuleId(id: Id(id)); + @override List get props => [id]; }