From 5983fb66ff890db5cef8d9542ccc2b606aacc353 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 9 Sep 2025 04:15:31 +0700 Subject: [PATCH] TF-4004 Add Preview/Hide condition, action of rule filter --- .../extensions/color_extension.dart | 6 + .../presentation_mailbox_extension.dart | 24 +- .../validator_failure_extension.dart | 6 +- .../extensions/tmail_rule_extension.dart | 10 + ..._toggle_preview_rule_filter_extension.dart | 38 ++ ...rule_filter_action_argument_extension.dart | 14 + .../model/email_rule_filter_action.dart | 2 + .../model/rule_filter_condition_type.dart | 4 - .../rules_filter_creator_controller.dart | 127 +++--- .../rules_filter_creator_view.dart | 378 ++++++++++-------- ...ondition_combiner_bottom_sheet_styles.dart | 6 - .../rule_filter_action_detailed_builder.dart | 19 +- .../rule_filter_action_row_builder.dart | 22 +- ...rule_filter_action_row_mobile_builder.dart | 25 +- .../widgets/rule_filter_action_widget.dart | 92 +++-- .../rule_filter_condition_row_builder.dart | 3 +- .../widgets/rule_filter_condition_widget.dart | 59 ++- .../widgets/rule_filter_title_builder.dart | 55 ++- ...rule_filter_title_with_preview_button.dart | 60 +++ .../widgets/rule_preview_banner.dart | 79 ++++ lib/l10n/intl_messages.arb | 24 ++ lib/main/localizations/app_localizations.dart | 25 ++ 22 files changed, 685 insertions(+), 393 deletions(-) create mode 100644 lib/features/rules_filter_creator/presentation/extensions/handle_toggle_preview_rule_filter_extension.dart delete mode 100644 lib/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart delete mode 100644 lib/features/rules_filter_creator/presentation/styles/rule_condition_combiner_bottom_sheet_styles.dart create mode 100644 lib/features/rules_filter_creator/presentation/widgets/rule_filter_title_with_preview_button.dart create mode 100644 lib/features/rules_filter_creator/presentation/widgets/rule_preview_banner.dart diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index d935f3356..68e0dddbc 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -243,6 +243,7 @@ extension AppColor on Color { static const m3Neutral70 = Color(0xFFAEAAAE); static const m3Neutral90 = Color(0xFFE6E1E5); static const m3SysLightSecondaryBlue = Color(0xFF5C9CE6); + static const m3SysLight = Color(0xFF0157AD); static const grayBackgroundColor = Color(0xFFF3F6F9); static const m3SurfaceBackground = Color(0xFF1C1B1F); static const warningColor = Color(0xFFFFC107); @@ -273,6 +274,11 @@ extension AppColor on Color { static const gray49454F = Color(0xFF49454F); static const lightGrayF9FAFB = Color(0xFFF9FAFB); static const black4D4D4D = Color(0xFF4D4D4D); + static const green166534 = Color(0xFF166534); + static const lightGreenF0FDF4 = Color(0xFFF0FDF4); + static const lightGreenBBF7D0 = Color(0xFFBBF7D0); + static const lightBlueEFF6FF = Color(0xFFEFF6FF); + static const lightBlueBFDBFE = Color(0xFFBFDBFE); static const mapGradientColor = [ [Color(0xFF21D4FD), Color(0xFFB721FF)], diff --git a/lib/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart b/lib/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart index 2b09580b1..01f7b45cc 100644 --- a/lib/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart +++ b/lib/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart @@ -10,37 +10,33 @@ import 'package:tmail_ui_user/main/routes/route_utils.dart'; extension PresentationMailboxExtension on PresentationMailbox { - String getDisplayNameByLocalizations(AppLocalizations appLocalizations) { + String getDisplayName(BuildContext context) { if (isDefault) { switch(role!.value.toLowerCase()) { case PresentationMailbox.inboxRole: - return appLocalizations.inboxMailboxDisplayName; + return AppLocalizations.of(context).inboxMailboxDisplayName; case PresentationMailbox.archiveRole: - return appLocalizations.archiveMailboxDisplayName; + return AppLocalizations.of(context).archiveMailboxDisplayName; case PresentationMailbox.draftsRole: - return appLocalizations.draftsMailboxDisplayName; + return AppLocalizations.of(context).draftsMailboxDisplayName; case PresentationMailbox.sentRole: - return appLocalizations.sentMailboxDisplayName; + return AppLocalizations.of(context).sentMailboxDisplayName; case PresentationMailbox.outboxRole: - return appLocalizations.outboxMailboxDisplayName; + return AppLocalizations.of(context).outboxMailboxDisplayName; case PresentationMailbox.trashRole: - return appLocalizations.trashMailboxDisplayName; + return AppLocalizations.of(context).trashMailboxDisplayName; case PresentationMailbox.spamRole: case PresentationMailbox.junkRole: - return appLocalizations.spamMailboxDisplayName; + return AppLocalizations.of(context).spamMailboxDisplayName; case PresentationMailbox.templatesRole: - return appLocalizations.templatesMailboxDisplayName; + return AppLocalizations.of(context).templatesMailboxDisplayName; case PresentationMailbox.recoveredRole: - return appLocalizations.recoveredMailboxDisplayName; + return AppLocalizations.of(context).recoveredMailboxDisplayName; } } return name?.name ?? ''; } - String getDisplayName(BuildContext context) { - return getDisplayNameByLocalizations(AppLocalizations.of(context)); - } - String getMailboxIcon(ImagePaths imagePaths) { if (hasRole()) { switch(role!.value) { diff --git a/lib/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart b/lib/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart index a6e4e4b14..56a2815f4 100644 --- a/lib/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart +++ b/lib/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart @@ -39,11 +39,11 @@ extension ValicatorFailureExtension on VerifyNameFailure { } } - String getMessageRulesFilter(BuildContext context) { + String getMessageRulesFilter(AppLocalizations appLocalizations) { if (exception is EmptyNameException) { - return AppLocalizations.of(context).this_field_cannot_be_blank; + return appLocalizations.this_field_cannot_be_blank; } else if (exception is NameWithSpaceOnlyException) { - return AppLocalizations.of(context).thisFieldCannotContainOnlySpaces; + return appLocalizations.thisFieldCannotContainOnlySpaces; } else { return ''; } diff --git a/lib/features/manage_account/presentation/extensions/tmail_rule_extension.dart b/lib/features/manage_account/presentation/extensions/tmail_rule_extension.dart index 6e439fa60..358043963 100644 --- a/lib/features/manage_account/presentation/extensions/tmail_rule_extension.dart +++ b/lib/features/manage_account/presentation/extensions/tmail_rule_extension.dart @@ -17,4 +17,14 @@ extension RuleConditionExtension on RuleCondition { String getPreview(AppLocalizations appLocalizations) { return '${field.getTitle(appLocalizations)}, ${comparator.getTitle(appLocalizations).toLowerCase()}: $value'; } + + String getPreviewWhenEditing(AppLocalizations appLocalizations) { + return '${field.getTitle(appLocalizations)} ${comparator.getTitle(appLocalizations).toLowerCase()} "$value"'; + } +} + +extension ListRuleConditionExtension on List { + String getPreviewWhenEditing(AppLocalizations appLocalizations) { + return map((e) => e.getPreviewWhenEditing(appLocalizations)).join(', '); + } } diff --git a/lib/features/rules_filter_creator/presentation/extensions/handle_toggle_preview_rule_filter_extension.dart b/lib/features/rules_filter_creator/presentation/extensions/handle_toggle_preview_rule_filter_extension.dart new file mode 100644 index 000000000..ddada7ed2 --- /dev/null +++ b/lib/features/rules_filter_creator/presentation/extensions/handle_toggle_preview_rule_filter_extension.dart @@ -0,0 +1,38 @@ +import 'package:core/presentation/extensions/capitalize_extension.dart'; +import 'package:flutter/material.dart'; +import 'package:rule_filter/rule_filter/rule_condition_group.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/extensions/tmail_rule_extension.dart'; +import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/list_rule_filter_action_argument_extension.dart'; +import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/rule_condition_extensions.dart'; +import 'package:tmail_ui_user/features/rules_filter_creator/presentation/rules_filter_creator_controller.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +extension HandleTogglePreviewRuleFilterExtension + on RulesFilterCreatorController { + void handleTogglePreviewRuleFilter() { + isPreviewEnabled.value = !isPreviewEnabled.value; + } + + String getConditionPreview(AppLocalizations appLocalizations) { + final conditionCombiner = + (conditionCombinerType.value ?? ConditionCombiner.AND) + .getTitle(appLocalizations) + .toUpperCase(); + final conditionValue = listRuleCondition.getPreviewWhenEditing( + appLocalizations, + ); + + return appLocalizations.ruleFilterConditionPreviewMessage( + conditionCombiner, + conditionValue, + ); + } + + String getActionPreview(BuildContext context) { + final actionValue = listEmailRuleFilterActionSelected.getPreviewWhenEditing( + context, + ); + + return '${AppLocalizations.of(context).actions.inCaps}: $actionValue'; + } +} diff --git a/lib/features/rules_filter_creator/presentation/extensions/list_rule_filter_action_argument_extension.dart b/lib/features/rules_filter_creator/presentation/extensions/list_rule_filter_action_argument_extension.dart index 44e68b0a3..c6eefe553 100644 --- a/lib/features/rules_filter_creator/presentation/extensions/list_rule_filter_action_argument_extension.dart +++ b/lib/features/rules_filter_creator/presentation/extensions/list_rule_filter_action_argument_extension.dart @@ -1,7 +1,21 @@ +import 'package:flutter/cupertino.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_action_arguments.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; extension ListRuleFilterActionArgumentExtension on List { bool isEmptySelectedRuleAction() => every((argument) => argument is EmptyRuleFilterActionArguments); + + String getPreviewWhenEditing(BuildContext context) { + final appLocalizations = AppLocalizations.of(context); + return where((arguments) => arguments.action != null).map((arguments) { + if (arguments is MoveMessageActionArguments) { + return '${arguments.action!.getTitle(appLocalizations)} ${appLocalizations.toFolder.toLowerCase()} "${arguments.mailbox?.getDisplayName(context) ?? ''}"'; + } else { + return arguments.action!.getTitle(appLocalizations); + } + }).join(', '); + } } \ No newline at end of file diff --git a/lib/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart b/lib/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart index 35341ca45..51b4adc55 100644 --- a/lib/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart +++ b/lib/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart @@ -26,4 +26,6 @@ enum EmailRuleFilterAction { } bool get isSupported => this != EmailRuleFilterAction.forwardTo; + + bool get isForwardTo => this == EmailRuleFilterAction.forwardTo; } \ No newline at end of file diff --git a/lib/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart b/lib/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart deleted file mode 100644 index b8ba38ded..000000000 --- a/lib/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart +++ /dev/null @@ -1,4 +0,0 @@ -enum RuleFilterConditionScreenType { - mobile, - desktop -} \ No newline at end of file diff --git a/lib/features/rules_filter_creator/presentation/rules_filter_creator_controller.dart b/lib/features/rules_filter_creator/presentation/rules_filter_creator_controller.dart index 62eb1be55..a3459e2be 100644 --- a/lib/features/rules_filter_creator/presentation/rules_filter_creator_controller.dart +++ b/lib/features/rules_filter_creator/presentation/rules_filter_creator_controller.dart @@ -56,6 +56,7 @@ class RulesFilterCreatorController extends BaseMailboxController { final mailboxSelected = Rxn(); final actionType = CreatorActionType.create.obs; final listRuleCondition = RxList(); + final isPreviewEnabled = RxBool(false); final TextEditingController inputRuleNameController = TextEditingController(); final FocusNode inputRuleNameFocusNode = FocusNode(); @@ -124,17 +125,17 @@ class RulesFilterCreatorController extends BaseMailboxController { @override void handleSuccessViewState(Success success) async { - super.handleSuccessViewState(success); if (success is GetAllMailboxSuccess) { await buildTree(success.mailboxList); if (currentContext != null) { syncAllMailboxWithDisplayName(currentContext!); } } else if (success is GetAllRulesSuccess) { - log('RulesFilterCreatorController::handleSuccessViewState():GetAllRulesSuccess: ${success.rules}'); if (success.rules?.isNotEmpty == true) { _listEmailRule = success.rules!; } + } else { + super.handleSuccessViewState(success); } } @@ -283,21 +284,31 @@ class RulesFilterCreatorController extends BaseMailboxController { } } - void updateRuleName(BuildContext context, String? value) { + void updateRuleName(AppLocalizations appLocalizations, String? value) { _newRuleName = value; - errorRuleName.value = _getErrorStringByInputValue(context, _newRuleName); + errorRuleName.value = _getErrorStringByInputValue( + appLocalizations, + _newRuleName, + ); } - void updateConditionValue(BuildContext context, String? value, int ruleConditionIndex) { - RuleCondition newRuleCondition = RuleCondition( + void updateConditionValue( + AppLocalizations appLocalizations, + String value, + int ruleConditionIndex, + ) { + final newRuleCondition = RuleCondition( field: listRuleCondition[ruleConditionIndex].field, comparator: listRuleCondition[ruleConditionIndex].comparator, - value: value!, + value: value, ); listRuleCondition[ruleConditionIndex] = newRuleCondition; listRuleCondition.refresh(); - String? errorString = _getErrorStringByInputValue(context, listRuleCondition[ruleConditionIndex].value); - RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments( + final errorString = _getErrorStringByInputValue( + appLocalizations, + listRuleCondition[ruleConditionIndex].value, + ); + final newRuleConditionValueArguments = RulesFilterInputFieldArguments( focusNode: listRuleConditionValueArguments[ruleConditionIndex].focusNode, errorText: errorString, controller: listRuleConditionValueArguments[ruleConditionIndex].controller, @@ -310,7 +321,10 @@ class RulesFilterCreatorController extends BaseMailboxController { listRuleConditionValueArguments.refresh(); } - String? _getErrorStringByInputValue(BuildContext context, String? inputValue) { + String? _getErrorStringByInputValue( + AppLocalizations appLocalizations, + String? inputValue, + ) { return verifyNameInteractor.execute( inputValue, [ @@ -320,7 +334,7 @@ class RulesFilterCreatorController extends BaseMailboxController { ).fold( (failure) { if (failure is VerifyNameFailure) { - return failure.getMessageRulesFilter(context); + return failure.getMessageRulesFilter(appLocalizations); } else { return null; } @@ -416,7 +430,10 @@ class RulesFilterCreatorController extends BaseMailboxController { } } - void selectMailbox(BuildContext context, int ruleFilterActionIndex) async { + Future selectMailbox( + BuildContext context, + int ruleFilterActionIndex, + ) async { if (_accountId != null) { final arguments = DestinationPickerArguments( _accountId!, @@ -430,8 +447,9 @@ class RulesFilterCreatorController extends BaseMailboxController { if (destinationMailbox is PresentationMailbox && context.mounted) { mailboxSelected.value = destinationMailbox; errorMailboxSelectedValue.value = _getErrorStringByInputValue( - context, - mailboxSelected.value?.getDisplayName(context)); + AppLocalizations.of(context), + destinationMailbox.getDisplayName(context), + ); RuleFilterActionArguments newRuleFilterAction = MoveMessageActionArguments(mailbox: mailboxSelected.value); listEmailRuleFilterActionSelected[ruleFilterActionIndex] = newRuleFilterAction; } @@ -439,29 +457,30 @@ class RulesFilterCreatorController extends BaseMailboxController { } void createNewRuleFilter(BuildContext context) { - KeyboardUtils.hideKeyboard(context); - - final errorName = _getErrorStringByInputValue(context, _newRuleName); - log('RulesFilterCreatorController::createNewRuleFilter:errorName: $errorName'); + final appLocalizations = AppLocalizations.of(context); + final errorName = _getErrorStringByInputValue( + appLocalizations, + _newRuleName, + ); if (errorName?.isNotEmpty == true) { errorRuleName.value = errorName; inputRuleNameFocusNode.requestFocus(); return; } - if (listRuleCondition.isEmpty - && currentOverlayContext != null - && currentContext != null - ) { + if (listRuleCondition.isEmpty) { appToast.showToastErrorMessage( - currentOverlayContext!, - AppLocalizations.of(currentContext!).youHaveNotAddedConditionToRule); + context, + appLocalizations.youHaveNotAddedConditionToRule, + ); return; } for (var ruleCondition in listRuleCondition) { - final errorConditionString = _getErrorStringByInputValue(context, ruleCondition.value); - log('RulesFilterCreatorController::createNewRuleFilter:errorConditionString: $errorConditionString'); + final errorConditionString = _getErrorStringByInputValue( + appLocalizations, + ruleCondition.value, + ); if (errorConditionString != null) { int ruleConditionIndex = listRuleCondition.indexOf(ruleCondition); RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments( @@ -475,50 +494,51 @@ class RulesFilterCreatorController extends BaseMailboxController { } } - if (listEmailRuleFilterActionSelected.isEmpty - && currentOverlayContext != null - && currentContext != null - ) { + if (listEmailRuleFilterActionSelected.isEmpty) { appToast.showToastErrorMessage( - currentOverlayContext!, - AppLocalizations.of(currentContext!).youHaveNotAddedActionToRule); + context, + appLocalizations.youHaveNotAddedActionToRule, + ); return; } - if (listEmailRuleFilterActionSelected.isEmptySelectedRuleAction() - && currentOverlayContext != null - && currentContext != null - ) { + if (listEmailRuleFilterActionSelected.isEmptySelectedRuleAction()) { appToast.showToastErrorMessage( - currentOverlayContext!, - AppLocalizations.of(currentContext!).youHaveNotSelectedAnyActionForRule); + context, + appLocalizations.youHaveNotSelectedAnyActionForRule, + ); return; } for (var ruleFilterAction in listEmailRuleFilterActionSelected) { if (ruleFilterAction is MoveMessageActionArguments) { - final errorAction = _getErrorStringByInputValue(context, mailboxSelected.value?.getDisplayName(context)); - log('RulesFilterCreatorController::createNewRuleFilter:errorAction: $errorAction'); + final errorAction = _getErrorStringByInputValue( + appLocalizations, + mailboxSelected.value?.getDisplayName(context), + ); if (errorAction?.isNotEmpty == true) { appToast.showToastErrorMessage( context, - AppLocalizations.of(context).notSelectedMailboxToMoveMessage); + appLocalizations.notSelectedMailboxToMoveMessage, + ); return; } } if (ruleFilterAction is MarkAsSpamActionArguments) { final spamMailboxId = getSpamMailboxId(); - log('RulesFilterCreatorController::createNewRuleFilter:spamMailboxId: ${spamMailboxId?.asString}'); if (spamMailboxId == null) { appToast.showToastErrorMessage( context, - AppLocalizations.of(context).spamFolderNotFound); + appLocalizations.spamFolderNotFound, + ); return; } } if (ruleFilterAction is ForwardActionArguments) { - final errorAction = _getErrorStringByInputValue(context, ruleFilterAction.forwardEmail); - log('RulesFilterCreatorController::createNewRuleFilter:errorAction: $errorAction'); + final errorAction = _getErrorStringByInputValue( + appLocalizations, + ruleFilterAction.forwardEmail, + ); if (errorAction?.isNotEmpty == true) { errorForwardEmailValue.value = errorAction; forwardEmailFocusNode.requestFocus(); @@ -661,12 +681,17 @@ class RulesFilterCreatorController extends BaseMailboxController { listEmailRuleFilterActionSelected.removeAt(ruleFilterActionIndex); } - void updateForwardEmailValue(BuildContext context, String? value, int ruleActionIndex) { - String? errorAction = _getErrorStringByInputValue(context, value); - log('RulesFilterCreatorController::createNewRuleFilter:errorAction: $errorAction'); - RuleFilterActionArguments newRuleFilterAction = ForwardActionArguments(forwardEmail: value); - errorForwardEmailValue.value = errorAction; - listEmailRuleFilterActionSelected[ruleActionIndex] = newRuleFilterAction; + void updateForwardEmailValue( + AppLocalizations appLocalizations, + String? value, + int ruleActionIndex, + ) { + errorForwardEmailValue.value = _getErrorStringByInputValue( + appLocalizations, + value, + ); + listEmailRuleFilterActionSelected[ruleActionIndex] = + ForwardActionArguments(forwardEmail: value); listEmailRuleFilterActionSelected.refresh(); } } \ No newline at end of file diff --git a/lib/features/rules_filter_creator/presentation/rules_filter_creator_view.dart b/lib/features/rules_filter_creator/presentation/rules_filter_creator_view.dart index 7a3bf974b..be8d9bbdd 100644 --- a/lib/features/rules_filter_creator/presentation/rules_filter_creator_view.dart +++ b/lib/features/rules_filter_creator/presentation/rules_filter_creator_view.dart @@ -6,14 +6,16 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/widget/default_field/default_close_button_widget.dart'; import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart'; +import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/handle_toggle_preview_rule_filter_extension.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/select_rule_action_field_extension.dart'; -import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_action_arguments.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/rules_filter_creator_controller.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_list_action_widget.dart'; +import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_title_with_preview_button.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_title_builder.dart'; +import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_preview_banner.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class RuleFilterCreatorView extends GetWidget { @@ -25,10 +27,8 @@ class RuleFilterCreatorView extends GetWidget { @override Widget build(BuildContext context) { - final responsiveUtil = controller.responsiveUtils; - final isMobile = responsiveUtil.isMobile(context); - final currentScreenHeight = responsiveUtil.getSizeScreenHeight(context); - final currentScreenWidth = responsiveUtil.getSizeScreenWidth(context); + final responsiveUtils = controller.responsiveUtils; + final isMobile = responsiveUtils.isMobile(context); Widget bodyWidget = Container( decoration: BoxDecoration( @@ -49,23 +49,19 @@ class RuleFilterCreatorView extends GetWidget { blurRadius: 2, ), ], - ), - width: isMobile - ? double.infinity : - math.min( - currentScreenWidth, - 612, - ), + ), + width: _getViewWidth( + isMobile, + responsiveUtils.getSizeScreenWidth(context), + ), constraints: BoxConstraints( - maxHeight: isMobile - ? double.infinity - : math.min( - currentScreenHeight - 100, - 674, - ), + maxHeight: _getViewMaxHeight( + isMobile, + responsiveUtils.getSizeScreenHeight(context), + ), ), clipBehavior: isMobile ? Clip.none : Clip.antiAlias, - child: _buildRulesFilterForm(context, isMobile), + child: _buildRulesFilterForm(context, isMobile, responsiveUtils), ); if (isMobile) { @@ -83,7 +79,11 @@ class RuleFilterCreatorView extends GetWidget { return bodyWidget; } - Widget _buildRulesFilterForm(BuildContext context, bool isMobile) { + Widget _buildRulesFilterForm( + BuildContext context, + bool isMobile, + ResponsiveUtils responsiveUtils, + ) { final appLocalizations = AppLocalizations.of(context); Widget formWidget = Column( @@ -91,7 +91,7 @@ class RuleFilterCreatorView extends GetWidget { children: [ if (!isMobile) Container( - padding: const EdgeInsets.only(top: 24, bottom: 12), + padding: const EdgeInsets.only(top: 24, bottom: 16), alignment: Alignment.center, child: Obx( () => Text( @@ -106,7 +106,7 @@ class RuleFilterCreatorView extends GetWidget { else Container( height: 64, - padding: _getPadding(context), + padding: const EdgeInsetsDirectional.only(start: 8, end: 16), child: Stack( alignment: Alignment.center, children: [ @@ -140,18 +140,15 @@ class RuleFilterCreatorView extends GetWidget { child: SingleChildScrollView( physics: const ClampingScrollPhysics(), child: Padding( - padding: EdgeInsets.symmetric( - horizontal: isMobile ? 16 : 32, - ), + padding: EdgeInsets.symmetric(horizontal: isMobile ? 16 : 32), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Obx( - () => LabelInputFieldBuilder( + () => LabelInputFieldBuilder( label: appLocalizations.ruleName, hintText: appLocalizations.rulesNameHintTextInput, - textEditingController: - controller.inputRuleNameController, + textEditingController: controller.inputRuleNameController, focusNode: controller.inputRuleNameFocusNode, errorText: controller.errorRuleName.value, arrangeHorizontally: false, @@ -163,41 +160,111 @@ class RuleFilterCreatorView extends GetWidget { ), runSpacing: 16, inputFieldMaxWidth: double.infinity, - onTextChange: (value) => - controller.updateRuleName(context, value), - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 24, - ), - child: Text( - appLocalizations.condition, - style: ThemeUtils.textStyleInter600().copyWith( - fontSize: 14, - height: 18 / 14, - color: Colors.black, + onTextChange: (value) => controller.updateRuleName( + appLocalizations, + value, ), ), ), + if (!isMobile) + Obx(() => RuleFilterTitleWithPreviewButton( + imagePaths: controller.imagePaths, + isPreviewEnabled: controller.isPreviewEnabled.value, + onTogglePreviewAction: + controller.handleTogglePreviewRuleFilter, + )) + else + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Text( + appLocalizations.condition, + style: ThemeUtils.textStyleInter600().copyWith( + fontSize: 14, + height: 18 / 14, + color: Colors.black, + ), + ), + ), + if (!isMobile) + Obx(() { + if (controller.isPreviewEnabled.isTrue) { + return RulePreviewBanner( + imagePaths: controller.imagePaths, + message: controller.getConditionPreview( + appLocalizations, + ), + isAction: false, + margin: const EdgeInsetsDirectional.only(bottom: 16), + ); + } else { + return const SizedBox.shrink(); + } + }), Obx( - () => RuleFilterTitle( - conditionCombinerType: controller.conditionCombinerType.value, + () => RuleFilterTitle( + conditionCombinerType: controller + .conditionCombinerType + .value, imagePaths: controller.imagePaths, isMobile: isMobile, onTapActionCallback: (value) => - controller.selectConditionCombiner( - context: context, - combinerType: value, - isMobile: isMobile, - ), + controller.selectConditionCombiner( + context: context, + combinerType: value, + isMobile: isMobile, + ), ), ), const SizedBox(height: 8), - _buildListRuleFilterConditionList( - context: context, - isMobile: isMobile, - ), + Obx(() { + return ListView.builder( + shrinkWrap: true, + primary: false, + itemCount: controller.listRuleCondition.length, + itemBuilder: (context, index) { + final conditionItem = controller + .listRuleConditionValueArguments[index]; + + return RuleFilterConditionWidget( + key: ValueKey(conditionItem.focusNode), + isMobile: isMobile, + ruleCondition: controller.listRuleCondition[index], + imagePaths: controller.imagePaths, + conditionValueErrorText: conditionItem.errorText, + conditionValueFocusNode: conditionItem.focusNode, + textEditingController: conditionItem.controller, + tapRuleConditionFieldCallback: (value) { + controller.selectRuleConditionFieldAction( + context, + value, + controller.listRuleCondition[index].field, + isMobile, + index, + ); + }, + tapRuleConditionComparatorCallback: (value) { + controller.selectRuleConditionComparatorAction( + context, + value, + controller.listRuleCondition[index].comparator, + isMobile, + index, + ); + }, + conditionValueOnChangeAction: (value) { + controller.updateConditionValue( + appLocalizations, + value, + index, + ); + }, + onDeleteRuleConditionAction: () { + controller.tapRemoveCondition(index); + }, + ); + }, + ); + }), Container( constraints: const BoxConstraints(minWidth: 161), height: 36, @@ -225,7 +292,88 @@ class RuleFilterCreatorView extends GetWidget { ), ), ), - _buildListRuleFilterActionList(context), + if (!isMobile) + Obx(() { + if (controller.isPreviewEnabled.isTrue) { + return RulePreviewBanner( + imagePaths: controller.imagePaths, + message: controller.getActionPreview(context), + isAction: true, + margin: const EdgeInsetsDirectional.only( + top: 8, + bottom: 8, + ), + ); + } else { + return const SizedBox.shrink(); + } + }), + Obx(() { + final listActions = controller + .listEmailRuleFilterActionSelected; + + return ListView.builder( + shrinkWrap: true, + primary: false, + itemCount: listActions.length, + padding: const EdgeInsetsDirectional.only(bottom: 8), + itemBuilder: (context, index) { + final action = listActions[index]; + final isForwardTo = + action.action?.isForwardTo == true; + + final errorValue = action is ForwardActionArguments + ? controller.errorForwardEmailValue.value + : controller.errorMailboxSelectedValue.value; + + return RuleFilterActionWidget( + isMobile: isMobile, + mailboxSelected: action is MoveMessageActionArguments + ? action.mailbox + : null, + errorValue: errorValue, + onActionChangeMobile: () { + controller.selectRuleFilterAction( + context, + action.action, + index, + ); + }, + onActionChanged: (newAction) { + if (newAction != action.action) { + controller.updateEmailRuleFilterAction( + context, + newAction, + index, + ); + } + }, + forwardEmailEditingController: isForwardTo + ? controller.forwardEmailController + : null, + forwardEmailFocusNode: isForwardTo + ? controller.forwardEmailFocusNode + : null, + onChangeForwardEmail: (value) { + controller.updateForwardEmailValue( + appLocalizations, + value, + index, + ); + }, + actionSelected: action.action, + onTapActionDetailedCallback: () { + FocusScope.of(context).unfocus(); + controller.selectMailbox(context, index); + }, + onDeleteRuleConditionAction: () { + controller.tapRemoveAction(index); + }, + imagePaths: controller.imagePaths, + ); + }, + ); + }), Obx(() { if (controller.isShowAddAction.value == true) { return Container( @@ -257,7 +405,10 @@ class RuleFilterCreatorView extends GetWidget { vertical: 25, horizontal: isMobile ? 16 : 32, ), - onPositiveAction: () => controller.createNewRuleFilter(context), + onPositiveAction: () { + FocusScope.of(context).unfocus(); + controller.createNewRuleFilter(context); + }, onNegativeAction: () => controller.closeView(context), ), ], @@ -278,117 +429,16 @@ class RuleFilterCreatorView extends GetWidget { } } - Widget _buildListRuleFilterConditionList({ - required BuildContext context, - required bool isMobile, - }) { - return Obx(() { - return ListView.builder( - shrinkWrap: true, - primary: false, - itemCount: controller.listRuleCondition.length, - itemBuilder: (context, index) { - final conditionItem = - controller.listRuleConditionValueArguments[index]; - return RuleFilterConditionWidget( - key: ValueKey(conditionItem.focusNode), - isMobile: isMobile, - ruleCondition: controller.listRuleCondition[index], - imagePaths: controller.imagePaths, - conditionValueErrorText: conditionItem.errorText, - conditionValueFocusNode: conditionItem.focusNode, - textEditingController: conditionItem.controller, - tapRuleConditionFieldCallback: (value) => - controller.selectRuleConditionFieldAction( - context, - value, - controller.listRuleCondition[index].field, - isMobile, - index, - ), - tapRuleConditionComparatorCallback: (value) => - controller.selectRuleConditionComparatorAction( - context, - value, - controller.listRuleCondition[index].comparator, - isMobile, - index, - ), - conditionValueOnChangeAction: (value) => - controller.updateConditionValue(context, value, index), - onDeleteRuleConditionAction: () => - controller.tapRemoveCondition(index), - ); - }, - ); - }); + double _getViewWidth(bool isMobile, double screenWidth) { + return isMobile ? double.infinity : math.min(screenWidth, 612); } - Widget _buildListRuleFilterActionList(BuildContext context) { - return Obx(() { - return ListView.builder( - shrinkWrap: true, - primary: false, - itemCount: controller.listEmailRuleFilterActionSelected.length, - padding: const EdgeInsetsDirectional.only(bottom: 8), - itemBuilder: (context, index) { - final currentAction = controller.listEmailRuleFilterActionSelected[index]; - String? errorValue; - if (currentAction is ForwardActionArguments) { - errorValue = controller.errorForwardEmailValue.value; - } else { - errorValue = controller.errorMailboxSelectedValue.value; - } - return RuleFilterActionWidget( - responsiveUtils: controller.responsiveUtils, - mailboxSelected: currentAction is MoveMessageActionArguments - ? currentAction.mailbox - : null, - errorValue: errorValue, - onActionChangeMobile: () { - controller.selectRuleFilterAction( - context, - currentAction.action, - index, - ); - }, - onActionChanged: (newAction) { - if (newAction != currentAction.action) { - controller.updateEmailRuleFilterAction( - context, - newAction, - index, - ); - } - }, - forwardEmailEditingController: currentAction.action == EmailRuleFilterAction.forwardTo - ? controller.forwardEmailController - : null, - forwardEmailFocusNode: currentAction.action == EmailRuleFilterAction.forwardTo - ? controller.forwardEmailFocusNode - : null, - onChangeForwardEmail: (value) => controller.updateForwardEmailValue(context, value, index), - actionSelected: currentAction.action, - tapActionDetailedCallback: () { - KeyboardUtils.hideKeyboard(context); - controller.selectMailbox(context, index); - }, - onDeleteRuleConditionAction: () => - controller.tapRemoveAction(index), - imagePaths: controller.imagePaths, - ); - }, - ); - }); - } + double _getViewMaxHeight(bool isMobile, double screenHeight) { + final screenHeightWithoutPadding = + screenHeight > 100 ? screenHeight - 100 : screenHeight; - EdgeInsetsGeometry _getPadding(BuildContext context) { - if (controller.responsiveUtils.isPortraitMobile(context)) { - return const EdgeInsetsDirectional.only(start: 8, end: 16); - } else if (controller.responsiveUtils.isLandscapeMobile(context)) { - return const EdgeInsetsDirectional.symmetric(horizontal: 24); - } else { - return const EdgeInsetsDirectional.symmetric(horizontal: 32); - } + return isMobile + ? double.infinity + : math.min(screenHeightWithoutPadding, 674); } } \ No newline at end of file diff --git a/lib/features/rules_filter_creator/presentation/styles/rule_condition_combiner_bottom_sheet_styles.dart b/lib/features/rules_filter_creator/presentation/styles/rule_condition_combiner_bottom_sheet_styles.dart deleted file mode 100644 index 7bd2f30b3..000000000 --- a/lib/features/rules_filter_creator/presentation/styles/rule_condition_combiner_bottom_sheet_styles.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:flutter/material.dart'; - -class RuleConditionCombinerBottomSheetStyles { - static const Color defaultBgColor = Colors.white; - static const double defaultIconRightPadding = 12.0; -} \ No newline at end of file diff --git a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_detailed_builder.dart b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_detailed_builder.dart index d50e12b97..49e55130e 100644 --- a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_detailed_builder.dart +++ b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_detailed_builder.dart @@ -1,10 +1,10 @@ -import 'package:core/core.dart'; +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; import 'package:flutter/material.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart'; -import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class RuleFilterActionDetailed extends StatelessWidget { @@ -12,10 +12,10 @@ class RuleFilterActionDetailed extends StatelessWidget { final EmailRuleFilterAction? actionType; final PresentationMailbox? mailboxSelected; final String? errorValue; - final Function()? tapActionDetailedCallback; final TextEditingController? forwardEmailEditingController; final FocusNode? forwardEmailFocusNode; - final OnChangeFilterInputAction? forwardEmailOnChangeAction; + final OnTextChange? forwardEmailOnChangeAction; + final VoidCallback? onTapActionDetailedCallback; const RuleFilterActionDetailed({ Key? key, @@ -23,24 +23,23 @@ class RuleFilterActionDetailed extends StatelessWidget { this.actionType, this.mailboxSelected, this.errorValue, - this.tapActionDetailedCallback, this.forwardEmailEditingController, this.forwardEmailFocusNode, this.forwardEmailOnChangeAction, + this.onTapActionDetailedCallback, }) : super(key: key); @override Widget build(BuildContext context) { - final borderColor = errorValue?.isNotEmpty == true ? AppColor.colorInputBorderErrorVerifyName : AppColor.colorInputBorderCreateMailbox; switch (actionType) { case EmailRuleFilterAction.moveMessage: return RuleFilterButtonField( value: mailboxSelected, imagePaths: imagePaths, - borderColor: borderColor, - onTapActionCallback: (value) { - tapActionDetailedCallback!(); - }, + borderColor: errorValue?.isNotEmpty == true + ? AppColor.redFF3347 + : AppColor.m3Neutral90, + onTapActionCallback: (_) => onTapActionDetailedCallback?.call(), ); case EmailRuleFilterAction.forwardTo: return DefaultInputFieldWidget( diff --git a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_builder.dart b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_builder.dart index 7961b6243..716fea970 100644 --- a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_builder.dart +++ b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_builder.dart @@ -7,22 +7,23 @@ import 'package:tmail_ui_user/features/base/widget/default_field/default_input_f import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart'; -import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +typedef OnActionChanged = Function(EmailRuleFilterAction? action); + class RuleFilterActionRow extends StatelessWidget { final List actionList; final ImagePaths imagePaths; final OnDeleteRuleConditionAction onDeleteRuleConditionAction; final EmailRuleFilterAction? actionSelected; - final Function(EmailRuleFilterAction?)? onActionChanged; + final OnActionChanged? onActionChanged; final PresentationMailbox? mailboxSelected; final String? errorValue; - final Function()? tapActionDetailedCallback; final TextEditingController? forwardEmailEditingController; final FocusNode? forwardEmailFocusNode; - final OnChangeFilterInputAction? onChangeForwardEmail; + final OnTextChange? onChangeForwardEmail; + final VoidCallback? onTapActionDetailedCallback; const RuleFilterActionRow({ Key? key, @@ -33,7 +34,7 @@ class RuleFilterActionRow extends StatelessWidget { this.onActionChanged, this.mailboxSelected, this.errorValue, - this.tapActionDetailedCallback, + this.onTapActionDetailedCallback, this.forwardEmailEditingController, this.forwardEmailFocusNode, this.onChangeForwardEmail, @@ -41,15 +42,11 @@ class RuleFilterActionRow extends StatelessWidget { @override Widget build(BuildContext context) { - final supportedAction = actionList - .where((action) => action.isSupported) - .toList(); - return Row( children: [ Expanded( child: DropDownButtonWidget( - items: supportedAction, + items: actionList, itemSelected: actionSelected, onChanged: (newAction) => onActionChanged!(newAction), supportSelectionIcon: true, @@ -57,6 +54,7 @@ class RuleFilterActionRow extends StatelessWidget { labelTextStyle: ThemeUtils.textStyleBodyBody3( color: Colors.black, ), + heightItem: 40, hintTextStyle: ThemeUtils.textStyleBodyBody3( color: AppColor.steelGray400, ), @@ -85,11 +83,11 @@ class RuleFilterActionRow extends StatelessWidget { borderColor: errorValue?.isNotEmpty == true ? AppColor.redFF3347 : AppColor.m3Neutral90, - onTapActionCallback: (_) => tapActionDetailedCallback?.call(), + onTapActionCallback: (_) => onTapActionDetailedCallback?.call(), ), ), ] - else if (actionSelected == EmailRuleFilterAction.forwardTo) + else if (actionSelected?.isForwardTo == true) Expanded( child: DefaultInputFieldWidget( errorText: errorValue, diff --git a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_mobile_builder.dart b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_mobile_builder.dart index 979c1bbde..4dc3aa427 100644 --- a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_mobile_builder.dart +++ b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_mobile_builder.dart @@ -1,34 +1,35 @@ -import 'package:core/core.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/theme_utils.dart'; import 'package:flutter/material.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_detailed_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart'; -import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class RuleFilterActionRowMobile extends StatelessWidget { final ImagePaths imagePaths; final EmailRuleFilterAction? actionSelected; - final Function()? tapActionCallback; final PresentationMailbox? mailboxSelected; final String? errorValue; - final Function()? tapActionDetailedCallback; final TextEditingController? forwardEmailEditingController; final FocusNode? forwardEmailFocusNode; - final OnChangeFilterInputAction? onChangeForwardEmail; + final OnTextChange? onChangeForwardEmail; + final VoidCallback? onTapActionDetailedCallback; + final VoidCallback? onTapActionCallback; const RuleFilterActionRowMobile({ Key? key, required this.imagePaths, this.actionSelected, - this.tapActionCallback, this.mailboxSelected, this.errorValue, - this.tapActionDetailedCallback, this.forwardEmailEditingController, this.forwardEmailFocusNode, this.onChangeForwardEmail, + this.onTapActionDetailedCallback, + this.onTapActionCallback, }) : super(key: key); @override @@ -39,14 +40,12 @@ class RuleFilterActionRowMobile extends StatelessWidget { RuleFilterButtonField( value: actionSelected, imagePaths: imagePaths, - onTapActionCallback: (value) { - tapActionCallback!(); - }, + onTapActionCallback: (_) => onTapActionCallback?.call(), hintText: AppLocalizations.of(context).selectAction, ), actionSelected == EmailRuleFilterAction.moveMessage ? Container( - alignment: Alignment.centerLeft, + alignment: AlignmentDirectional.centerStart, padding: const EdgeInsets.symmetric(vertical: 12), child: Text( AppLocalizations.of(context).toFolder, @@ -60,17 +59,17 @@ class RuleFilterActionRowMobile extends StatelessWidget { ), ) : SizedBox( - height: actionSelected == EmailRuleFilterAction.forwardTo ? 12 : 0, + height: actionSelected?.isForwardTo == true ? 12 : 0, ), RuleFilterActionDetailed( imagePaths: imagePaths, actionType: actionSelected, mailboxSelected: mailboxSelected, errorValue: errorValue, - tapActionDetailedCallback: tapActionDetailedCallback, forwardEmailEditingController: forwardEmailEditingController, forwardEmailFocusNode: forwardEmailFocusNode, forwardEmailOnChangeAction: onChangeForwardEmail, + onTapActionDetailedCallback: onTapActionDetailedCallback, ), ], ); diff --git a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_widget.dart b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_widget.dart index ddd65a680..e6bcd7ecd 100644 --- a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_widget.dart +++ b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_action_widget.dart @@ -1,49 +1,46 @@ 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_slidable/flutter_slidable.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_mobile_builder.dart'; -import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart'; class RuleFilterActionWidget extends StatelessWidget { - final ResponsiveUtils responsiveUtils; final ImagePaths imagePaths; + final bool isMobile; final OnDeleteRuleConditionAction onDeleteRuleConditionAction; final EmailRuleFilterAction? actionSelected; - final Function(EmailRuleFilterAction?)? onActionChanged; - final Function()? onActionChangeMobile; final PresentationMailbox? mailboxSelected; final String? errorValue; - final Function()? tapActionDetailedCallback; + final VoidCallback? onTapActionDetailedCallback; final TextEditingController? forwardEmailEditingController; final FocusNode? forwardEmailFocusNode; - final OnChangeFilterInputAction? onChangeForwardEmail; + final OnTextChange? onChangeForwardEmail; + final OnActionChanged? onActionChanged; + final VoidCallback? onActionChangeMobile; const RuleFilterActionWidget({ Key? key, - required this.responsiveUtils, required this.imagePaths, + required this.isMobile, required this.onDeleteRuleConditionAction, this.actionSelected, - this.onActionChanged, this.mailboxSelected, this.errorValue, - this.tapActionDetailedCallback, this.forwardEmailEditingController, this.forwardEmailFocusNode, this.onChangeForwardEmail, this.onActionChangeMobile, + this.onTapActionDetailedCallback, + this.onActionChanged, }) : super(key: key); @override Widget build(BuildContext context) { - final isMobile = responsiveUtils.isMobile(context); - if (!isMobile) { return Container( padding: const EdgeInsetsDirectional.only(start: 12), @@ -55,17 +52,19 @@ class RuleFilterActionWidget extends StatelessWidget { height: 72, alignment: Alignment.center, child: RuleFilterActionRow( - actionList: EmailRuleFilterAction.values, + actionList: EmailRuleFilterAction.values + .where((action) => action.isSupported) + .toList(), actionSelected: actionSelected, onActionChanged: onActionChanged, mailboxSelected: mailboxSelected, errorValue: errorValue, - tapActionDetailedCallback: tapActionDetailedCallback, imagePaths: imagePaths, onDeleteRuleConditionAction: onDeleteRuleConditionAction, forwardEmailEditingController: forwardEmailEditingController, forwardEmailFocusNode: forwardEmailFocusNode, onChangeForwardEmail: onChangeForwardEmail, + onTapActionDetailedCallback: onTapActionDetailedCallback, ), ); } else { @@ -92,39 +91,38 @@ class RuleFilterActionWidget extends StatelessWidget { ], ), child: Builder( - builder: (context) { - SlidableController? slideController = Slidable.of(context); - return ValueListenableBuilder( - valueListenable: slideController?.direction ?? ValueNotifier(0), - builder: (context, value, _) { - final borderRadius = value != -1 - ? BorderRadius.circular(10) - : const BorderRadius.only( - bottomLeft: Radius.circular(10), - topLeft: Radius.circular(10), - ); - return Container( - padding: const EdgeInsetsDirectional.all(8), - decoration: BoxDecoration( - color: AppColor.lightGrayF9FAFB, - borderRadius: borderRadius, - ), - alignment: Alignment.center, - child: RuleFilterActionRowMobile( - imagePaths: imagePaths, - actionSelected: actionSelected, - mailboxSelected: mailboxSelected, - errorValue: errorValue, - tapActionDetailedCallback: tapActionDetailedCallback, - forwardEmailEditingController: forwardEmailEditingController, - forwardEmailFocusNode: forwardEmailFocusNode, - onChangeForwardEmail: onChangeForwardEmail, - tapActionCallback: onActionChangeMobile, - ), - ); - }, - ); - } + builder: (context) { + SlidableController? slideController = Slidable.of(context); + return ValueListenableBuilder( + valueListenable: slideController?.direction ?? ValueNotifier(0), + builder: (context, value, _) { + return Container( + padding: const EdgeInsetsDirectional.all(8), + decoration: BoxDecoration( + color: AppColor.lightGrayF9FAFB, + borderRadius: value != -1 + ? BorderRadius.circular(10) + : const BorderRadius.only( + bottomLeft: Radius.circular(10), + topLeft: Radius.circular(10), + ), + ), + alignment: Alignment.center, + child: RuleFilterActionRowMobile( + imagePaths: imagePaths, + actionSelected: actionSelected, + mailboxSelected: mailboxSelected, + errorValue: errorValue, + forwardEmailEditingController: forwardEmailEditingController, + forwardEmailFocusNode: forwardEmailFocusNode, + onChangeForwardEmail: onChangeForwardEmail, + onTapActionCallback: onActionChangeMobile, + onTapActionDetailedCallback: onTapActionDetailedCallback, + ), + ); + }, + ); + }, ), ), ); diff --git a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_condition_row_builder.dart b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_condition_row_builder.dart index bc14403ff..ce49b6bb8 100644 --- a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_condition_row_builder.dart +++ b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_condition_row_builder.dart @@ -7,7 +7,6 @@ import 'package:rule_filter/rule_filter/rule_condition.dart'; import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart'; import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart'; -import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -21,7 +20,7 @@ class RuleFilterConditionRow extends StatelessWidget { final String? conditionValueErrorText; final TextEditingController textEditingController; final FocusNode? conditionValueFocusNode; - final OnChangeFilterInputAction? conditionValueOnChangeAction; + final OnTextChange? conditionValueOnChangeAction; const RuleFilterConditionRow({ Key? key, diff --git a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart index c28d2b791..2cc9e0502 100644 --- a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart +++ b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart @@ -3,12 +3,11 @@ import 'package:core/presentation/resources/image_paths.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:rule_filter/rule_filter/rule_condition.dart'; +import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_row_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart'; -typedef OnChangeFilterInputAction = Function(String? value); - class RuleFilterConditionWidget extends StatelessWidget { final bool isMobile; final RuleCondition ruleCondition; @@ -17,7 +16,7 @@ class RuleFilterConditionWidget extends StatelessWidget { final OnRuleTapActionCallback tapRuleConditionComparatorCallback; final String? conditionValueErrorText; final FocusNode? conditionValueFocusNode; - final OnChangeFilterInputAction? conditionValueOnChangeAction; + final OnTextChange? conditionValueOnChangeAction; final ImagePaths imagePaths; final OnDeleteRuleConditionAction onDeleteRuleConditionAction; @@ -37,6 +36,19 @@ class RuleFilterConditionWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final conditionRowWidget = RuleFilterConditionRow( + isMobile: isMobile, + ruleCondition: ruleCondition, + tapRuleConditionFieldCallback: tapRuleConditionFieldCallback, + tapRuleConditionComparatorCallback: tapRuleConditionComparatorCallback, + conditionValueErrorText: conditionValueErrorText, + textEditingController: textEditingController, + conditionValueFocusNode: conditionValueFocusNode, + conditionValueOnChangeAction: conditionValueOnChangeAction, + onDeleteRuleConditionAction: onDeleteRuleConditionAction, + imagePaths: imagePaths, + ); + if (isMobile) { return Padding( padding: const EdgeInsetsDirectional.only(top: 8), @@ -66,35 +78,23 @@ class RuleFilterConditionWidget extends StatelessWidget { return ValueListenableBuilder( valueListenable: slideController?.direction ?? ValueNotifier(0), builder: (context, value, _) { - final borderRadius = value != -1 - ? BorderRadius.circular(10) - : const BorderRadius.only( - bottomLeft: Radius.circular(10), - topLeft: Radius.circular(10), - ); return Container( padding: const EdgeInsetsDirectional.all(8), decoration: BoxDecoration( color: AppColor.lightGrayF9FAFB, - borderRadius: borderRadius, + borderRadius: value != -1 + ? BorderRadius.circular(10) + : const BorderRadius.only( + bottomLeft: Radius.circular(10), + topLeft: Radius.circular(10), + ), ), alignment: Alignment.center, - child: RuleFilterConditionRow( - isMobile: isMobile, - ruleCondition: ruleCondition, - tapRuleConditionFieldCallback: tapRuleConditionFieldCallback, - tapRuleConditionComparatorCallback: tapRuleConditionComparatorCallback, - conditionValueErrorText: conditionValueErrorText, - textEditingController: textEditingController, - conditionValueFocusNode: conditionValueFocusNode, - conditionValueOnChangeAction: conditionValueOnChangeAction, - onDeleteRuleConditionAction: onDeleteRuleConditionAction, - imagePaths: imagePaths, - ), + child: conditionRowWidget, ); } ); - } + }, ), ), ); @@ -107,18 +107,7 @@ class RuleFilterConditionWidget extends StatelessWidget { borderRadius: BorderRadius.all(Radius.circular(10)), ), alignment: Alignment.center, - child: RuleFilterConditionRow( - isMobile: isMobile, - ruleCondition: ruleCondition, - tapRuleConditionFieldCallback: tapRuleConditionFieldCallback, - tapRuleConditionComparatorCallback: tapRuleConditionComparatorCallback, - conditionValueErrorText: conditionValueErrorText, - textEditingController: textEditingController, - conditionValueFocusNode: conditionValueFocusNode, - conditionValueOnChangeAction: conditionValueOnChangeAction, - onDeleteRuleConditionAction: onDeleteRuleConditionAction, - imagePaths: imagePaths, - ), + child: conditionRowWidget, ); } } diff --git a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_title_builder.dart b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_title_builder.dart index a55991365..6c418a281 100644 --- a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_title_builder.dart +++ b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_title_builder.dart @@ -25,6 +25,24 @@ class RuleFilterTitle extends StatelessWidget { Widget build(BuildContext context) { final appLocalizations = AppLocalizations.of(context); + final titleBeforeWidget = Text( + appLocalizations.conditionTitleRulesFilterBeforeCombiner, + style: ThemeUtils.textStyleInter400.copyWith( + fontSize: 14, + height: 18 / 14, + color: Colors.black, + ), + ); + + final titleAfterWidget = Text( + appLocalizations.conditionTitleRulesFilterAfterCombiner, + style: ThemeUtils.textStyleInter400.copyWith( + fontSize: 14, + height: 18 / 14, + color: Colors.black, + ), + ); + if (isMobile) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -32,14 +50,7 @@ class RuleFilterTitle extends StatelessWidget { children: [ Row( children: [ - Text( - appLocalizations.conditionTitleRulesFilterBeforeCombiner, - style: ThemeUtils.textStyleInter400.copyWith( - fontSize: 14, - height: 18 / 14, - color: Colors.black, - ), - ), + titleBeforeWidget, Expanded( child: Container( padding: const EdgeInsets.symmetric(horizontal: 12), @@ -54,14 +65,7 @@ class RuleFilterTitle extends StatelessWidget { ], ), const SizedBox(height: 8), - Text( - appLocalizations.conditionTitleRulesFilterAfterCombiner, - style: ThemeUtils.textStyleInter400.copyWith( - fontSize: 14, - height: 18 / 14, - color: Colors.black, - ), - ) + titleAfterWidget, ], ); } @@ -71,14 +75,7 @@ class RuleFilterTitle extends StatelessWidget { runAlignment: WrapAlignment.center, runSpacing: 8, children: [ - Text( - appLocalizations.conditionTitleRulesFilterBeforeCombiner, - style: ThemeUtils.textStyleInter400.copyWith( - fontSize: 14, - height: 18 / 14, - color: Colors.black, - ), - ), + titleBeforeWidget, Container( padding: const EdgeInsets.symmetric(horizontal: 12), width: 158, @@ -86,6 +83,7 @@ class RuleFilterTitle extends StatelessWidget { items: ConditionCombiner.values, itemSelected: conditionCombinerType, supportSelectionIcon: true, + heightItem: 40, labelTextStyle: ThemeUtils.textStyleBodyBody3( color: Colors.black, ), @@ -95,14 +93,7 @@ class RuleFilterTitle extends StatelessWidget { onChanged: onTapActionCallback, ), ), - Text( - appLocalizations.conditionTitleRulesFilterAfterCombiner, - style: ThemeUtils.textStyleInter400.copyWith( - fontSize: 14, - height: 18 / 14, - color: Colors.black, - ), - ) + titleAfterWidget, ], ); } diff --git a/lib/features/rules_filter_creator/presentation/widgets/rule_filter_title_with_preview_button.dart b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_title_with_preview_button.dart new file mode 100644 index 000000000..80104a6a9 --- /dev/null +++ b/lib/features/rules_filter_creator/presentation/widgets/rule_filter_title_with_preview_button.dart @@ -0,0 +1,60 @@ + +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/theme_utils.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class RuleFilterTitleWithPreviewButton extends StatelessWidget { + final ImagePaths imagePaths; + final bool isPreviewEnabled; + final VoidCallback onTogglePreviewAction; + + const RuleFilterTitleWithPreviewButton({ + super.key, + required this.imagePaths, + required this.isPreviewEnabled, + required this.onTogglePreviewAction, + }); + + @override + Widget build(BuildContext context) { + final appLocalizations = AppLocalizations.of(context); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Row( + children: [ + Expanded( + child: Text( + appLocalizations.condition, + style: ThemeUtils.textStyleInter600().copyWith( + fontSize: 14, + height: 18 / 14, + color: Colors.black, + ), + ), + ), + TMailButtonWidget( + text: isPreviewEnabled + ? appLocalizations.hide + : appLocalizations.preview, + icon: isPreviewEnabled ? imagePaths.icEyeOff : imagePaths.icEye, + iconSize: 12, + iconColor: AppColor.primaryMain, + backgroundColor: Colors.transparent, + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 8), + textStyle: ThemeUtils.textStyleInter400.copyWith( + fontSize: 11, + height: 16 / 11, + letterSpacing: 0.5, + color: AppColor.primaryMain, + ), + onTapActionCallback: onTogglePreviewAction, + ) + ], + ), + ); + } +} diff --git a/lib/features/rules_filter_creator/presentation/widgets/rule_preview_banner.dart b/lib/features/rules_filter_creator/presentation/widgets/rule_preview_banner.dart new file mode 100644 index 000000000..0d81fcfff --- /dev/null +++ b/lib/features/rules_filter_creator/presentation/widgets/rule_preview_banner.dart @@ -0,0 +1,79 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/theme_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class RulePreviewBanner extends StatelessWidget { + final ImagePaths imagePaths; + final String message; + final bool isAction; + final EdgeInsetsGeometry? margin; + + const RulePreviewBanner({ + super.key, + required this.imagePaths, + required this.message, + required this.isAction, + this.margin, + }); + + @override + Widget build(BuildContext context) { + final appLocalizations = AppLocalizations.of(context); + + return Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(8)), + color: isAction ? AppColor.lightGreenF0FDF4 : AppColor.lightBlueEFF6FF, + border: Border.all( + color: + isAction ? AppColor.lightGreenBBF7D0 : AppColor.lightBlueBFDBFE, + ), + ), + padding: const EdgeInsets.all(4), + margin: margin, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.asset( + isAction ? imagePaths.icCheck : imagePaths.icInfoCircleOutline, + width: 20, + height: 20, + colorFilter: isAction + ? AppColor.green166534.asFilter() + : AppColor.m3SysLight.asFilter(), + fit: BoxFit.fill, + ), + const SizedBox(width: 8), + Expanded( + child: Text.rich( + TextSpan( + style: ThemeUtils.textStyleInter400.copyWith( + fontSize: 14, + height: 20 / 14, + letterSpacing: 0.1, + color: isAction ? AppColor.green166534 : AppColor.m3SysLight, + ), + children: [ + TextSpan( + text: '${appLocalizations.preview}:', + style: ThemeUtils.textStyleInter700().copyWith( + fontSize: 14, + height: 20 / 14, + letterSpacing: 0.1, + color: + isAction ? AppColor.green166534 : AppColor.m3SysLight, + ), + ), + TextSpan(text: ' $message'), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index be8e55886..753c6abdf 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -4765,5 +4765,29 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "preview": "Preview", + "@preview": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "ruleFilterConditionPreviewMessage": "{conditionCombinerName} of the conditions: {conditionValue}", + "@ruleFilterConditionPreviewMessage": { + "type": "text", + "placeholders_order": [ + "conditionCombinerName", + "conditionValue" + ], + "placeholders": { + "conditionCombinerName": {}, + "conditionValue": {} + } + }, + "actions": "Actions", + "@actions": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 02b1e07dd..ac67efb07 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -5034,4 +5034,29 @@ class AppLocalizations { name: 'createRule', ); } + + String get preview { + return Intl.message( + 'Preview', + name: 'preview', + ); + } + + String ruleFilterConditionPreviewMessage(String conditionCombinerName, String conditionValue) { + return Intl.message( + '$conditionCombinerName of the conditions: $conditionValue', + name: 'ruleFilterConditionPreviewMessage', + args: [ + conditionCombinerName, + conditionValue, + ], + ); + } + + String get actions { + return Intl.message( + 'Actions', + name: 'actions', + ); + } }