diff --git a/lib/features/rules_filter_creator/presentation/model/rules_filter_input_field_arguments.dart b/lib/features/rules_filter_creator/presentation/model/rules_filter_input_field_arguments.dart new file mode 100644 index 000000000..b9aa4674b --- /dev/null +++ b/lib/features/rules_filter_creator/presentation/model/rules_filter_input_field_arguments.dart @@ -0,0 +1,22 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; + +class RulesFilterInputFieldArguments with EquatableMixin { + final FocusNode inputRuleConditionValueFocusNode; + final String errorRuleConditionValue; + final TextEditingController inputRuleConditionValueController; + + RulesFilterInputFieldArguments({ + required this.inputRuleConditionValueFocusNode, + required this.errorRuleConditionValue, + required this.inputRuleConditionValueController, + }); + + @override + List get props => [ + inputRuleConditionValueFocusNode, + errorRuleConditionValue, + inputRuleConditionValueController, + ]; +} 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 7e7826e6b..5ad7a84a7 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 @@ -41,6 +41,8 @@ import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/dialog_router.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; +import 'model/rules_filter_input_field_arguments.dart'; + class RulesFilterCreatorController extends BaseMailboxController { final _appToast = Get.find(); @@ -50,7 +52,6 @@ class RulesFilterCreatorController extends BaseMailboxController { GetAllRulesInteractor? _getAllRulesInteractor; final errorRuleName = Rxn(); - final listErrorRuleConditionValue = RxList(); final errorRuleActionValue = Rxn(); final emailRuleFilterActionSelected = Rxn(); final mailboxSelected = Rxn(); @@ -58,9 +59,8 @@ class RulesFilterCreatorController extends BaseMailboxController { final listRuleCondition = RxList(); final TextEditingController inputRuleNameController = TextEditingController(); - final List listInputConditionValueController = []; final FocusNode inputRuleNameFocusNode = FocusNode(); - final List listInputRuleConditionFocusNode = []; + final listRuleConditionValueArguments = RxList(); String? _newRuleName; @@ -111,12 +111,10 @@ class RulesFilterCreatorController extends BaseMailboxController { void onClose() { log('RulesFilterCreatorController::onClose():'); inputRuleNameFocusNode.dispose(); - for (var inputRuleConditionFocusNode in listInputRuleConditionFocusNode) { - inputRuleConditionFocusNode.dispose(); - } inputRuleNameController.dispose(); - for (var inputConditionValueController in listInputConditionValueController) { - inputConditionValueController.dispose(); + for (var ruleConditionValueArguments in listRuleConditionValueArguments) { + ruleConditionValueArguments.inputRuleConditionValueFocusNode.dispose(); + ruleConditionValueArguments.inputRuleConditionValueController.dispose(); } super.onClose(); } @@ -153,9 +151,12 @@ class RulesFilterCreatorController extends BaseMailboxController { value: '' ); listRuleCondition.add(newRuleCondition); - listInputConditionValueController.add(TextEditingController()); - listInputRuleConditionFocusNode.add(FocusNode()); - listErrorRuleConditionValue.add(''); + RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments( + inputRuleConditionValueFocusNode: FocusNode(), + errorRuleConditionValue: '', + inputRuleConditionValueController: TextEditingController(), + ); + listRuleConditionValueArguments.add(newRuleConditionValueArguments); emailRuleFilterActionSelected.value = EmailRuleFilterAction.moveMessage; if (_emailAddress != null) { RuleCondition firstRuleCondition = RuleCondition( @@ -165,7 +166,10 @@ class RulesFilterCreatorController extends BaseMailboxController { ); listRuleCondition[0] = firstRuleCondition; listRuleCondition.refresh(); - _setValueInputField(listInputConditionValueController[0], listRuleCondition[0].value.obs.value); + _setValueInputField( + listRuleConditionValueArguments[0].inputRuleConditionValueController, + listRuleCondition[0].value + ); } if (_mailboxDestination != null) { mailboxSelected.value = _mailboxDestination; @@ -179,11 +183,17 @@ class RulesFilterCreatorController extends BaseMailboxController { value: _currentTMailRule!.condition.value ); listRuleCondition.add(currentRule); - listInputConditionValueController.add(TextEditingController()); - listInputRuleConditionFocusNode.add(FocusNode()); - listErrorRuleConditionValue.add(''); + RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments( + inputRuleConditionValueFocusNode: FocusNode(), + errorRuleConditionValue: '', + inputRuleConditionValueController: TextEditingController(), + ); + listRuleConditionValueArguments.add(newRuleConditionValueArguments); emailRuleFilterActionSelected.value = EmailRuleFilterAction.moveMessage; - _setValueInputField(listInputConditionValueController[0], listRuleCondition[0].value.obs.value); + _setValueInputField( + listRuleConditionValueArguments[0].inputRuleConditionValueController, + listRuleCondition[0].value + ); _newRuleName = _currentTMailRule!.name; _setValueInputField(inputRuleNameController, _newRuleName ?? ''); _getAllMailboxAction(); @@ -220,19 +230,24 @@ class RulesFilterCreatorController extends BaseMailboxController { void updateConditionValue(BuildContext context, String? value, int ruleConditionIndex) { RuleCondition newRuleCondition = RuleCondition( - field: listRuleCondition[ruleConditionIndex].field.obs.value, - comparator: listRuleCondition[ruleConditionIndex].comparator.obs.value, + field: listRuleCondition[ruleConditionIndex].field, + comparator: listRuleCondition[ruleConditionIndex].comparator, value: value!, ); listRuleCondition[ruleConditionIndex] = newRuleCondition; listRuleCondition.refresh(); - String? errorString = _getErrorStringByInputValue(context, listRuleCondition[ruleConditionIndex].value.obs.value); - if (listErrorRuleConditionValue.length > ruleConditionIndex) { - listErrorRuleConditionValue[ruleConditionIndex] = errorString ?? ''; + String? errorString = _getErrorStringByInputValue(context, listRuleCondition[ruleConditionIndex].value); + RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments( + inputRuleConditionValueFocusNode: listRuleConditionValueArguments[ruleConditionIndex].inputRuleConditionValueFocusNode, + errorRuleConditionValue: errorString ?? '', + inputRuleConditionValueController: listRuleConditionValueArguments[ruleConditionIndex].inputRuleConditionValueController, + ); + if (listRuleConditionValueArguments.length > ruleConditionIndex) { + listRuleConditionValueArguments[ruleConditionIndex] = newRuleConditionValueArguments; } else { - listErrorRuleConditionValue.add(errorString ?? ''); + listRuleConditionValueArguments.add(newRuleConditionValueArguments); } - listErrorRuleConditionValue.refresh(); + listRuleConditionValueArguments.refresh(); } String? _getErrorStringByInputValue(BuildContext context, String? inputValue) { @@ -249,23 +264,27 @@ class RulesFilterCreatorController extends BaseMailboxController { } void selectRuleConditionField(rule_condition.Field? newField, int? ruleConditionIndex) { - RuleCondition newRuleCondition = RuleCondition( - field: newField!, - comparator: listRuleCondition[ruleConditionIndex!].comparator.obs.value, - value: listRuleCondition[ruleConditionIndex].value.obs.value, - ); - listRuleCondition[ruleConditionIndex] = newRuleCondition; - listRuleCondition.refresh(); + if (newField != null && ruleConditionIndex != null) { + RuleCondition newRuleCondition = RuleCondition( + field: newField, + comparator: listRuleCondition[ruleConditionIndex].comparator, + value: listRuleCondition[ruleConditionIndex].value, + ); + listRuleCondition[ruleConditionIndex] = newRuleCondition; + listRuleCondition.refresh(); + } } void selectRuleConditionComparator(rule_condition.Comparator? newComparator, int? ruleConditionIndex) { - RuleCondition newRuleCondition = RuleCondition( - field: listRuleCondition[ruleConditionIndex!].field.obs.value, - comparator: newComparator!, - value: listRuleCondition[ruleConditionIndex].value.obs.value, - ); - listRuleCondition[ruleConditionIndex] = newRuleCondition; - listRuleCondition.refresh(); + if (newComparator != null && ruleConditionIndex != null) { + RuleCondition newRuleCondition = RuleCondition( + field: listRuleCondition[ruleConditionIndex].field, + comparator: newComparator, + value: listRuleCondition[ruleConditionIndex].value, + ); + listRuleCondition[ruleConditionIndex] = newRuleCondition; + listRuleCondition.refresh(); + } } void selectEmailRuleFilterAction(EmailRuleFilterAction? newAction) { @@ -302,16 +321,19 @@ class RulesFilterCreatorController extends BaseMailboxController { return; } - List errorCondition = []; - for (var ruleCondition in listRuleCondition) { - if (_getErrorStringByInputValue(context, ruleCondition.value) != null) { - errorCondition.add(_getErrorStringByInputValue(context, ruleCondition.value)!); - } - } - if (errorCondition.isNotEmpty) { - listErrorRuleConditionValue.value = errorCondition; - for (var inputRuleConditionFocusNode in listInputRuleConditionFocusNode) { - inputRuleConditionFocusNode.requestFocus(); + if (listRuleCondition.isNotEmpty) { + for (var ruleCondition in listRuleCondition) { + String? errorString = _getErrorStringByInputValue(context, ruleCondition.value); + if (errorString != null) { + int ruleConditionIndex = listRuleCondition.indexOf(ruleCondition); + RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments( + inputRuleConditionValueFocusNode: listRuleConditionValueArguments[ruleConditionIndex].inputRuleConditionValueFocusNode, + errorRuleConditionValue: errorString, + inputRuleConditionValueController: listRuleConditionValueArguments[ruleConditionIndex].inputRuleConditionValueController, + ); + listRuleConditionValueArguments[ruleConditionIndex] = newRuleConditionValueArguments; + listRuleConditionValueArguments[listRuleCondition.indexOf(ruleCondition)].inputRuleConditionValueFocusNode.requestFocus(); + } } return; } @@ -386,15 +408,15 @@ class RulesFilterCreatorController extends BaseMailboxController { value: '' ); listRuleCondition.add(newRuleCondition); - listInputConditionValueController.add(TextEditingController()); - listInputRuleConditionFocusNode.add(FocusNode()); - listErrorRuleConditionValue.add(''); + listRuleConditionValueArguments.add(RulesFilterInputFieldArguments( + inputRuleConditionValueFocusNode: FocusNode(), + errorRuleConditionValue: '', + inputRuleConditionValueController: TextEditingController(), + )); } void tapRemoveCondition(int ruleConditionIndex) { listRuleCondition.removeAt(ruleConditionIndex); - listInputConditionValueController.removeAt(ruleConditionIndex); - listInputRuleConditionFocusNode.removeAt(ruleConditionIndex); - listErrorRuleConditionValue.removeAt(ruleConditionIndex); + listRuleConditionValueArguments.removeAt(ruleConditionIndex); } } \ 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 980a503a2..ff370cebb 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 @@ -146,32 +146,7 @@ class RuleFilterCreatorView extends GetWidget { fontSize: 16, color: Colors.black)), const SizedBox(height: 24), - Obx(() { - return ListView.separated( - shrinkWrap: true, - itemCount: controller.listRuleCondition.length, - itemBuilder: (context, index) { - return RuleFilterConditionWidget( - ruleFilterConditionScreenType: RuleFilterConditionScreenType.desktop, - ruleCondition: controller.listRuleCondition[index], - imagePaths: _imagePaths, - conditionValueErrorText: controller.listErrorRuleConditionValue[index], - conditionValueFocusNode: controller.listInputRuleConditionFocusNode[index], - conditionValueEditingController: controller.listInputConditionValueController[index], - tapRuleConditionFieldCallback: (value) => - controller.selectRuleConditionField(value, index), - tapRuleConditionComparatorCallback: (value) => - controller.selectRuleConditionComparator(value, index), - conditionValueOnChangeAction: (value) => - controller.updateConditionValue(context, value, index), - tapRemoveRuleFilterConditionCallback: () => controller.tapRemoveCondition(index), - ); - }, - separatorBuilder: (context, index) { - return const SizedBox(height: 12,); - }, - ); - }), + _buildListRuleFilterConditionList(context, RuleFilterConditionScreenType.desktop), Container( padding: const EdgeInsets.only(top: 8), child: InkWell( @@ -322,32 +297,7 @@ class RuleFilterCreatorView extends GetWidget { fontSize: 16, color: Colors.black)), const SizedBox(height: 24), - Obx(() { - return ListView.separated( - shrinkWrap: true, - itemCount: controller.listRuleCondition.length, - itemBuilder: (context, index) { - return RuleFilterConditionWidget( - ruleFilterConditionScreenType: RuleFilterConditionScreenType.tablet, - ruleCondition: controller.listRuleCondition[index], - imagePaths: _imagePaths, - conditionValueErrorText: controller.listErrorRuleConditionValue.elementAtOrNull(index), - conditionValueFocusNode: controller.listInputRuleConditionFocusNode[index], - conditionValueEditingController: controller.listInputConditionValueController[index], - tapRuleConditionFieldCallback: (value) => - controller.selectRuleConditionField(value, index), - tapRuleConditionComparatorCallback: (value) => - controller.selectRuleConditionComparator(value, index), - conditionValueOnChangeAction: (value) => - controller.updateConditionValue(context, value, index), - tapRemoveRuleFilterConditionCallback: () => controller.tapRemoveCondition(index), - ); - }, - separatorBuilder: (context, index) { - return const SizedBox(height: 12,); - }, - ); - }), + _buildListRuleFilterConditionList(context, RuleFilterConditionScreenType.tablet), Container( padding: const EdgeInsets.only(top: 8), child: InkWell( @@ -504,44 +454,7 @@ class RuleFilterCreatorView extends GetWidget { fontSize: 16, color: Colors.black)), const SizedBox(height: 24), - Obx(() { - return ListView.separated( - shrinkWrap: true, - itemCount: controller.listRuleCondition.length, - itemBuilder: (context, index) { - return RuleFilterConditionWidget( - ruleFilterConditionScreenType: RuleFilterConditionScreenType.mobile, - ruleCondition: controller.listRuleCondition[index], - imagePaths: _imagePaths, - conditionValueErrorText: controller.listErrorRuleConditionValue.elementAtOrNull(index), - conditionValueFocusNode: controller.listInputRuleConditionFocusNode[index], - conditionValueEditingController: controller.listInputConditionValueController[index], - tapRuleConditionFieldCallback: (value) => controller.openContextMenuAction( - context, - _bottomSheetRuleConditionFieldActionTiles( - context, - controller.listRuleCondition[index].field.obs.value, - index, - ) - ), - tapRuleConditionComparatorCallback: (value) => controller.openContextMenuAction( - context, - _bottomSheetRuleConditionComparatorActionTiles( - context, - controller.listRuleCondition[index].comparator.obs.value, - index, - ) - ), - conditionValueOnChangeAction: (value) => - controller.updateConditionValue(context, value, index), - tapRemoveRuleFilterConditionCallback: () => controller.tapRemoveCondition(index), - ); - }, - separatorBuilder: (context, index) { - return const SizedBox(height: 12,); - }, - ); - }), + _buildListRuleFilterConditionList(context, RuleFilterConditionScreenType.mobile), Container( padding: const EdgeInsets.only(top: 12), child: InkWell( @@ -668,6 +581,63 @@ class RuleFilterCreatorView extends GetWidget { ); } + Widget _buildListRuleFilterConditionList( + BuildContext context, + RuleFilterConditionScreenType ruleFilterConditionScreenType + ) { + return Obx(() { + return ListView.separated( + shrinkWrap: true, + itemCount: controller.listRuleCondition.length, + itemBuilder: (context, index) { + return RuleFilterConditionWidget( + key: ValueKey(controller.listRuleConditionValueArguments[index].inputRuleConditionValueFocusNode), + ruleFilterConditionScreenType: ruleFilterConditionScreenType, + ruleCondition: controller.listRuleCondition[index], + imagePaths: _imagePaths, + conditionValueErrorText: controller.listRuleConditionValueArguments[index].errorRuleConditionValue, + conditionValueFocusNode: controller.listRuleConditionValueArguments[index].inputRuleConditionValueFocusNode, + conditionValueEditingController: controller.listRuleConditionValueArguments[index].inputRuleConditionValueController, + tapRuleConditionFieldCallback: (value) => { + if (ruleFilterConditionScreenType == RuleFilterConditionScreenType.mobile) { + controller.openContextMenuAction( + context, + _bottomSheetRuleConditionFieldActionTiles( + context, + controller.listRuleCondition[index].field, + index, + ) + ), + } else { + controller.selectRuleConditionField(value, index) + } + }, + tapRuleConditionComparatorCallback: (value) => { + if (ruleFilterConditionScreenType == RuleFilterConditionScreenType.mobile) { + controller.openContextMenuAction( + context, + _bottomSheetRuleConditionComparatorActionTiles( + context, + controller.listRuleCondition[index].comparator, + index, + ) + ), + } else { + controller.selectRuleConditionComparator(value, index), + } + }, + conditionValueOnChangeAction: (value) => + controller.updateConditionValue(context, value, index), + tapRemoveRuleFilterConditionCallback: () => controller.tapRemoveCondition(index), + ); + }, + separatorBuilder: (context, index) { + return const SizedBox(height: 12,); + }, + ); + }); + } + List _bottomSheetRuleConditionFieldActionTiles( BuildContext context, rule_condition.Field? fieldSelected, 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 729bbeb28..f980ff5b9 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 @@ -1,6 +1,7 @@ import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/utils/keyboard_utils.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart'; @@ -39,25 +40,63 @@ class RuleFilterConditionWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: AppColor.colorBackgroundFieldConditionRulesFilter, - borderRadius: BorderRadius.circular(12), + return Slidable( + enabled: ruleFilterConditionScreenType == RuleFilterConditionScreenType.mobile ? true : false, + endActionPane: ActionPane( + extentRatio: 0.1, + motion: const BehindMotion(), + children: [ + CustomSlidableAction( + padding: const EdgeInsets.only(right: 12), + borderRadius: const BorderRadius.only(topRight: Radius.circular(12), bottomRight: Radius.circular(12)), + onPressed: (_) => tapRemoveRuleFilterConditionCallback!(), + backgroundColor: AppColor.colorBackgroundFieldConditionRulesFilter, + child: CircleAvatar( + backgroundColor: AppColor.colorRemoveRuleFilterConditionButton, + radius: 110, + child: SvgPicture.asset( + imagePaths!.icMinimize, + fit: BoxFit.fill, + colorFilter: AppColor.colorDeletePermanentlyButton.asFilter(), + ), + ) + ) + ] ), - child: _buildRuleFilterCondition( - context, - ruleFilterConditionScreenType, - ruleCondition, - tapRuleConditionFieldCallback, - tapRuleConditionComparatorCallback, - conditionValueErrorText, - conditionValueEditingController, - conditionValueFocusNode, - conditionValueOnChangeAction, - tapRemoveRuleFilterConditionCallback, - imagePaths, - ) + child: Builder(builder: (context) { + SlidableController? slideController = Slidable.of(context); + return ValueListenableBuilder( + valueListenable: slideController?.direction ?? ValueNotifier(0), + builder: (context, value, _) { + var borderRadius = value != -1 ? + BorderRadius.circular(12) : + const BorderRadius.only( + bottomLeft: Radius.circular(12), + topLeft: Radius.circular(12) + ); + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: AppColor.colorBackgroundFieldConditionRulesFilter, + borderRadius: borderRadius, + ), + child: _buildRuleFilterCondition( + context, + ruleFilterConditionScreenType, + ruleCondition, + tapRuleConditionFieldCallback, + tapRuleConditionComparatorCallback, + conditionValueErrorText, + conditionValueEditingController, + conditionValueFocusNode, + conditionValueOnChangeAction, + tapRemoveRuleFilterConditionCallback, + imagePaths, + ) + ); + } + ); + }) ); } @@ -144,7 +183,11 @@ Widget _buildRuleFilterCondition( editingController: conditionValueEditingController, ) ), - _buildRemoveRuleFilterConditionButton(tapRemoveRuleFilterConditionCallback, imagePaths), + Container( + padding: const EdgeInsets.only(left: 12), + alignment: Alignment.center, + child: _buildRemoveRuleFilterConditionButton(tapRemoveRuleFilterConditionCallback, imagePaths) + ), ], ); default: @@ -156,20 +199,16 @@ Widget _buildRemoveRuleFilterConditionButton ( Function()? tapRemoveRuleFilterConditionCallback, ImagePaths? imagePath, ) { - return Container( - padding: const EdgeInsets.only(left: 12), - alignment: Alignment.center, - child: InkWell( - onTap: tapRemoveRuleFilterConditionCallback, - child: CircleAvatar( - backgroundColor: AppColor.colorRemoveRuleFilterConditionButton, - radius: 22, - child: SvgPicture.asset( - imagePath!.icMinimize, - fit: BoxFit.fill, - colorFilter: AppColor.colorDeletePermanentlyButton.asFilter(), - ), - ) - ), + return InkWell( + onTap: tapRemoveRuleFilterConditionCallback, + child: CircleAvatar( + backgroundColor: AppColor.colorRemoveRuleFilterConditionButton, + radius: 22, + child: SvgPicture.asset( + imagePath!.icMinimize, + fit: BoxFit.fill, + colorFilter: AppColor.colorDeletePermanentlyButton.asFilter(), + ), + ) ); } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 166983d37..0e85d0963 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -788,6 +788,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.3" + flutter_slidable: + dependency: "direct main" + description: + name: flutter_slidable + sha256: cc4231579e3eae41ae166660df717f4bad1359c87f4a4322ad8ba1befeb3d2be + url: "https://pub.dev" + source: hosted + version: "3.0.0" flutter_staggered_grid_view: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d0ead2e9d..9eb234041 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -204,6 +204,8 @@ dependencies: super_tag_editor: 0.2.0 + flutter_slidable: 3.0.0 + dev_dependencies: flutter_test: sdk: flutter