TF-4004 Add Preview/Hide condition, action of rule filter

This commit is contained in:
dab246
2025-09-09 04:15:31 +07:00
committed by Dat H. Pham
parent a0123c41b0
commit 5983fb66ff
22 changed files with 685 additions and 393 deletions
@@ -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)],
@@ -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) {
@@ -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 '';
}
@@ -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<RuleCondition> {
String getPreviewWhenEditing(AppLocalizations appLocalizations) {
return map((e) => e.getPreviewWhenEditing(appLocalizations)).join(', ');
}
}
@@ -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';
}
}
@@ -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<RuleFilterActionArguments> {
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(', ');
}
}
@@ -26,4 +26,6 @@ enum EmailRuleFilterAction {
}
bool get isSupported => this != EmailRuleFilterAction.forwardTo;
bool get isForwardTo => this == EmailRuleFilterAction.forwardTo;
}
@@ -1,4 +0,0 @@
enum RuleFilterConditionScreenType {
mobile,
desktop
}
@@ -56,6 +56,7 @@ class RulesFilterCreatorController extends BaseMailboxController {
final mailboxSelected = Rxn<PresentationMailbox>();
final actionType = CreatorActionType.create.obs;
final listRuleCondition = RxList<RuleCondition>();
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<void> 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();
}
}
@@ -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<RulesFilterCreatorController> {
@@ -25,10 +27,8 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
@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<RulesFilterCreatorController> {
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<RulesFilterCreatorController> {
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<RulesFilterCreatorController> {
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<RulesFilterCreatorController> {
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<RulesFilterCreatorController> {
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<RulesFilterCreatorController> {
),
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<RulesFilterCreatorController> {
),
),
),
_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<RulesFilterCreatorController> {
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<RulesFilterCreatorController> {
}
}
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);
}
}
@@ -1,6 +0,0 @@
import 'package:flutter/material.dart';
class RuleConditionCombinerBottomSheetStyles {
static const Color defaultBgColor = Colors.white;
static const double defaultIconRightPadding = 12.0;
}
@@ -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<PresentationMailbox>(
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(
@@ -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<EmailRuleFilterAction> 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<EmailRuleFilterAction>(
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,
@@ -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<EmailRuleFilterAction>(
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,
),
],
);
@@ -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<int>(
valueListenable: slideController?.direction ?? ValueNotifier<int>(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<int>(
valueListenable: slideController?.direction ?? ValueNotifier<int>(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,
),
);
},
);
},
),
),
);
@@ -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,
@@ -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<int>(
valueListenable: slideController?.direction ?? ValueNotifier<int>(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,
);
}
}
@@ -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,
],
);
}
@@ -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,
)
],
),
);
}
}
@@ -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'),
],
),
),
),
],
),
);
}
}
+24
View File
@@ -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": {}
}
}
@@ -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',
);
}
}