TF-3766 Change context menu style for other widgets on mobile

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2025-06-13 01:14:16 +07:00
committed by Dat H. Pham
parent 3dce663b98
commit 2d175633e7
36 changed files with 759 additions and 612 deletions
+2 -11
View File
@@ -234,24 +234,15 @@ class ThemeUtils {
color: Colors.black,
);
static TextStyle textStyleInter400() => const TextStyle(
static const TextStyle textStyleInter400 = TextStyle(
fontFamily: ConstantsUI.fontApp,
fontWeight: FontWeight.normal,
letterSpacing: 0.01,
letterSpacing: -0.13,
fontSize: 13,
height: 16 / 13,
color: Colors.black,
);
static final TextStyle textStyleInter400 = TextStyle(
fontFamily: ConstantsUI.fontApp,
fontWeight: FontWeight.w400,
letterSpacing: -0.15,
fontSize: 16,
height: 21.01 / 16,
color: AppColor.gray424244.withOpacity(0.9),
);
static TextStyle textStyleM3LabelLarge({Color? color}) => TextStyle(
fontFamily: ConstantsUI.fontApp,
fontWeight: FontWeight.w500,
@@ -11,51 +11,49 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
mixin PopupContextMenuActionMixin {
void openContextMenuAction(
Future<void> openContextMenuAction(
BuildContext context,
List<Widget> actionTiles,
{
Widget? cancelButton,
Key? key,
List<ContextMenuItemAction>? itemActions,
OnContextMenuActionClick? onContextMenuActionClick,
}
) async {
if (actionTiles.isNotEmpty) {
await (CupertinoActionSheetBuilder(context, key: key)
..addTiles(actionTiles)
..addCancelButton(cancelButton ?? buildCancelButton(context)))
.show();
} else {
await showModalBottomSheet(
context: context,
showDragHandle: true,
useSafeArea: true,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16.0),
topRight: Radius.circular(16.0),
),
await (CupertinoActionSheetBuilder(context)
..addTiles(actionTiles)
..addCancelButton(buildCancelButton(context)))
.show();
}
Future<void> openBottomSheetContextMenuAction({
required BuildContext context,
required List<ContextMenuItemAction> itemActions,
required OnContextMenuActionClick onContextMenuActionClick,
Key? key,
}) async {
await showModalBottomSheet(
context: context,
showDragHandle: true,
useSafeArea: true,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16.0),
topRight: Radius.circular(16.0),
),
backgroundColor: Colors.white,
barrierColor: Colors.black.withOpacity(0.2),
builder: (_) {
return PointerInterceptor(
child: Container(
color: Colors.white,
padding: const EdgeInsetsDirectional.only(bottom: 24),
child: ContextMenuDialogView(
actions: itemActions ?? [],
onContextMenuActionClick: (menuAction) =>
onContextMenuActionClick?.call(menuAction),
),
),
backgroundColor: Colors.white,
barrierColor: Colors.black.withOpacity(0.2),
builder: (_) {
return PointerInterceptor(
child: Container(
key: key,
color: Colors.white,
padding: const EdgeInsetsDirectional.only(bottom: 24),
child: ContextMenuDialogView(
actions: itemActions,
onContextMenuActionClick: onContextMenuActionClick,
),
);
},
);
}
),
);
},
);
}
void openPopupMenuAction(
@@ -1,4 +1,3 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
@@ -17,6 +16,76 @@ class ContextMenuDialogItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
SvgPicture? icon;
SvgPicture? selectedIcon;
bool isSelected = false;
if (menuAction is ContextMenuItemActionRequiredIcon) {
final specificMenuAction =
menuAction as ContextMenuItemActionRequiredIcon;
icon = SvgPicture.asset(
specificMenuAction.actionIcon,
width: 20,
height: 20,
colorFilter: specificMenuAction.actionIconColor.asFilter(),
fit: BoxFit.fill,
);
} else if (menuAction is ContextMenuItemActionRequiredSelectedIcon) {
final specificMenuAction =
menuAction as ContextMenuItemActionRequiredSelectedIcon;
selectedIcon = SvgPicture.asset(
specificMenuAction.selectedIcon,
width: 20,
height: 20,
colorFilter: specificMenuAction.selectedIconColor.asFilter(),
fit: BoxFit.fill,
);
isSelected = specificMenuAction.selectedAction == menuAction.action;
} else if (menuAction is ContextMenuItemActionRequiredFull) {
final specificMenuAction =
menuAction as ContextMenuItemActionRequiredFull;
icon = SvgPicture.asset(
specificMenuAction.actionIcon,
width: 20,
height: 20,
colorFilter: specificMenuAction.actionIconColor.asFilter(),
fit: BoxFit.fill,
);
selectedIcon = SvgPicture.asset(
specificMenuAction.selectedIcon,
width: 20,
height: 20,
colorFilter: specificMenuAction.selectedIconColor.asFilter(),
fit: BoxFit.fill,
);
isSelected = specificMenuAction.selectedAction == menuAction.action;
}
Widget? iconWidget = icon != null
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Container(
height: 32,
width: 32,
alignment: Alignment.center,
child: icon,
),
)
: null;
Widget? selectedIconWidget = selectedIcon != null
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Container(
height: 32,
width: 32,
alignment: Alignment.center,
child: selectedIcon,
),
)
: null;
return Material(
type: MaterialType.transparency,
child: InkWell(
@@ -25,31 +94,24 @@ class ContextMenuDialogItem extends StatelessWidget {
height: 48,
child: Row(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Container(
height: 32,
width: 32,
alignment: Alignment.center,
child: SvgPicture.asset(
menuAction.actionIcon,
width: 20,
height: 20,
colorFilter: menuAction.actionIconColor.asFilter(),
fit: BoxFit.fill,
),
),
),
if (iconWidget != null) iconWidget else const SizedBox(width: 24),
Expanded(
child: Text(
menuAction.actionName,
style: ThemeUtils.textStyleInter400.copyWith(
color: menuAction.actionNameColor,
letterSpacing: -0.15,
fontSize: 16,
height: 21.01 / 16,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
if (isSelected && selectedIconWidget != null)
selectedIconWidget
else
const SizedBox(width: 24),
],
),
),
@@ -12,13 +12,41 @@ abstract class ContextMenuItemAction<T> with EquatableMixin {
@override
List<Object?> get props => [action];
String get actionIcon;
Color get actionIconColor => AppColor.gray424244.withOpacity(0.72);
String get actionName;
Color get actionNameColor => AppColor.gray424244.withOpacity(0.9);
void onClick(OnContextMenuActionClick callback) => callback(this);
}
mixin OptionalIcon {
String get actionIcon;
Color get actionIconColor => AppColor.gray424244.withOpacity(0.72);
}
mixin OptionalSelectedIcon<T> {
String get selectedIcon;
Color get selectedIconColor => AppColor.primaryMain;
}
abstract class ContextMenuItemActionRequiredIcon<T>
extends ContextMenuItemAction<T> with OptionalIcon {
ContextMenuItemActionRequiredIcon(super.action);
}
abstract class ContextMenuItemActionRequiredSelectedIcon<T>
extends ContextMenuItemAction<T> with OptionalSelectedIcon<T> {
final T? selectedAction;
ContextMenuItemActionRequiredSelectedIcon(super.action, this.selectedAction);
}
abstract class ContextMenuItemActionRequiredFull<T>
extends ContextMenuItemAction<T> with OptionalIcon, OptionalSelectedIcon<T> {
final T selectedAction;
ContextMenuItemActionRequiredFull(super.action, this.selectedAction);
}
@@ -14,6 +14,7 @@ import 'package:rule_filter/rule_filter/rule_condition_group.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/font_name_type.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/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class DropDownButtonWidget<T> extends StatelessWidget {
@@ -173,6 +174,8 @@ class DropDownButtonWidget<T> extends StatelessWidget {
}
String _getTextItemDropdown(BuildContext context, {required T? item}) {
final appLocalizations = AppLocalizations.of(context);
if (item is Identity) {
return item.name ?? '';
}
@@ -183,16 +186,16 @@ class DropDownButtonWidget<T> extends StatelessWidget {
return item.name;
}
if (item is rule_condition.Field) {
return item.getTitle(context);
return item.getTitle(appLocalizations);
}
if (item is rule_condition.Comparator) {
return item.getTitle(context);
return item.getTitle(appLocalizations);
}
if (item is EmailRuleFilterAction) {
return item.getTitle(context);
return item.getTitle(appLocalizations);
}
if (item is ConditionCombiner) {
return item.getTitle(context);
return item.getTitle(appLocalizations);
}
return hintText ?? '';
}
@@ -556,9 +556,8 @@ class EmailView extends GetWidget<SingleEmailController> {
.toList();
if (position == null) {
controller.openContextMenuAction(
context,
[],
controller.openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) {
popBack();
@@ -5,7 +5,7 @@ import 'package:tmail_ui_user/features/composer/presentation/extensions/email_ac
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ContextItemEmailAction
extends ContextMenuItemAction<EmailActionType> {
extends ContextMenuItemActionRequiredIcon<EmailActionType> {
final AppLocalizations appLocalizations;
final ImagePaths imagePaths;
@@ -7,6 +7,7 @@ import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_utils.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
extension HandleMailboxActionExtension on MailboxController {
@@ -25,20 +26,21 @@ extension HandleMailboxActionExtension on MailboxController {
mailboxDashBoardController.enableSpamReport,
deletedMessageVaultSupported,
isSubAddressingSupported,
imagePaths,
AppLocalizations.of(context),
);
if (contextMenuActions.isEmpty) {
return;
}
openContextMenuAction(
context,
contextMenuMailboxActionTiles(
openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) => handleMailboxAction(
context,
imagePaths,
menuAction.action,
mailbox,
contextMenuActions,
handleMailboxAction: handleMailboxAction,
),
);
}
@@ -76,6 +78,8 @@ extension HandleMailboxActionExtension on MailboxController {
mailboxDashBoardController.enableSpamReport,
deletedMessageVaultSupported,
isSubAddressingSupported,
imagePaths,
AppLocalizations.of(context),
);
if (contextMenuActions.isEmpty) {
@@ -83,15 +87,14 @@ extension HandleMailboxActionExtension on MailboxController {
}
if (responsiveUtils.isScreenWithShortestSide(context)) {
openContextMenuAction(
context,
contextMenuMailboxActionTiles(
openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) => handleMailboxAction(
context,
imagePaths,
menuAction.action,
mailbox,
contextMenuActions,
handleMailboxAction: handleMailboxAction,
)
),
);
} else {
openPopupMenuAction(
@@ -1,19 +1,12 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/mailbox/mailbox_constants.dart';
import 'package:model/model.dart';
import 'package:model/extensions/presentation_mailbox_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/base/widget/popup_item_widget.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/context_item_mailbox_action.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_utils.dart';
import 'package:tmail_ui_user/main/error/capability_validator.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
mixin MailboxWidgetMixin {
@@ -97,65 +90,35 @@ mixin MailboxWidgetMixin {
PresentationMailbox mailbox,
bool spamReportEnabled,
bool deletedMessageVaultSupported,
bool subaddressingSupported,
bool isSubAddressingSupported,
) {
if (mailbox.isDefault) {
return _listActionForDefaultMailbox(mailbox, spamReportEnabled, deletedMessageVaultSupported);
return _listActionForDefaultMailbox(
mailbox,
spamReportEnabled,
deletedMessageVaultSupported,
);
} else if (mailbox.isPersonal) {
return _listActionForPersonalMailbox(mailbox, subaddressingSupported);
return _listActionForPersonalMailbox(mailbox, isSubAddressingSupported);
} else {
return _listActionForTeamMailbox(mailbox);
}
}
void openMailboxMenuActionOnMobile(
BuildContext context,
ImagePaths imagePaths,
PresentationMailbox mailbox,
MailboxController controller
) {
final bool deletedMessageVaultSupported = MailboxUtils.isDeletedMessageVaultSupported(
controller.mailboxDashBoardController.sessionCurrent,
controller.mailboxDashBoardController.accountId.value);
final bool subaddressingSupported = isSubaddressingSupported(
controller.mailboxDashBoardController.sessionCurrent,
controller.mailboxDashBoardController.accountId.value);
final contextMenuActions = listContextMenuItemAction(
mailbox,
controller.mailboxDashBoardController.enableSpamReport,
deletedMessageVaultSupported,
subaddressingSupported,
imagePaths,
AppLocalizations.of(context),
);
if (contextMenuActions.isEmpty) {
return;
}
controller.openContextMenuAction(
context,
[],
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) => controller.handleMailboxAction(
context,
menuAction.action,
mailbox,
),
);
}
List<ContextMenuItemMailboxAction> listContextMenuItemAction(
PresentationMailbox mailbox,
bool spamReportEnabled,
bool deletedMessageVaultSupported,
bool subaddressingSupported,
bool isSubAddressingSupported,
ImagePaths imagePaths,
AppLocalizations appLocalizations,
) {
final mailboxActionsSupported = _listActionForAllMailboxType(mailbox, spamReportEnabled, deletedMessageVaultSupported, subaddressingSupported);
final mailboxActionsSupported = _listActionForAllMailboxType(
mailbox,
spamReportEnabled,
deletedMessageVaultSupported,
isSubAddressingSupported,
);
final listContextMenuItemAction = mailboxActionsSupported
.map((action) => ContextMenuItemMailboxAction(
@@ -168,61 +131,6 @@ mixin MailboxWidgetMixin {
return listContextMenuItemAction;
}
void openMailboxMenuActionOnWeb(
BuildContext context,
ImagePaths imagePaths,
ResponsiveUtils responsiveUtils,
RelativeRect position,
PresentationMailbox mailbox,
MailboxController controller
) {
final bool deletedMessageVaultSupported = MailboxUtils.isDeletedMessageVaultSupported(
controller.mailboxDashBoardController.sessionCurrent,
controller.mailboxDashBoardController.accountId.value);
final bool subaddressingSupported = isSubaddressingSupported(
controller.mailboxDashBoardController.sessionCurrent,
controller.mailboxDashBoardController.accountId.value);
final contextMenuActions = listContextMenuItemAction(
mailbox,
controller.mailboxDashBoardController.enableSpamReport,
deletedMessageVaultSupported,
subaddressingSupported,
imagePaths,
AppLocalizations.of(context),
);
if (contextMenuActions.isEmpty) {
return;
}
if (responsiveUtils.isScreenWithShortestSide(context)) {
controller.openContextMenuAction(
context,
[],
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) => controller.handleMailboxAction(
context,
menuAction.action,
mailbox,
),
);
} else {
controller.openPopupMenuAction(
context,
position,
popupMenuMailboxActionTiles(
context,
imagePaths,
mailbox,
contextMenuActions,
handleMailboxAction: controller.handleMailboxAction
)
);
}
}
List<PopupMenuEntry> popupMenuMailboxActionTiles(
BuildContext context,
ImagePaths imagePaths,
@@ -243,20 +151,6 @@ mixin MailboxWidgetMixin {
.toList();
}
static bool isSubaddressingSupported(Session? session, AccountId? accountId) {
if (session == null || accountId == null) {
return false;
}
if (!CapabilityIdentifier.jmapTeamMailboxes.isSupported(session, accountId)) {
return false;
}
return (session.getCapabilityProperties(accountId, CapabilityIdentifier.jmapTeamMailboxes)
?.props[0] as Map<String, dynamic>?)
?[subaddressingSupported]
?? false;
}
PopupMenuItem _buildPopupMenuItem(
BuildContext context,
ImagePaths imagePaths,
@@ -5,7 +5,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_action
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ContextMenuItemMailboxAction
extends ContextMenuItemAction<MailboxActions> {
extends ContextMenuItemActionRequiredIcon<MailboxActions> {
final AppLocalizations appLocalizations;
final ImagePaths imagePaths;
@@ -16,7 +16,7 @@ class CountOfEmailsWidget extends StatelessWidget {
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: PlatformInfo.isMobile
? ThemeUtils.textStyleInter400()
? ThemeUtils.textStyleInter400
: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.black,
),
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
mixin FilterEmailPopupMenuMixin {
final _imagePaths = Get.find<ImagePaths>();
@@ -68,7 +69,7 @@ mixin FilterEmailPopupMenuMixin {
: null),
const SizedBox(width: 12),
Expanded(child: Text(
option.getName(context),
option.getName(AppLocalizations.of(context)),
style: const TextStyle(
fontSize: 15,
color: Colors.black,
@@ -5,7 +5,6 @@ import 'package:core/presentation/views/dialog/confirmation_dialog_builder.dart'
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
@@ -22,8 +21,9 @@ import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new
import 'package:tmail_ui_user/features/manage_account/domain/usecases/delete_email_rule_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_email_rule_filter_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_rules_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/widgets/email_rule_bottom_sheet_action_tile_builder.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/context_item_email_rule_type_action.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/email_rule_action_type.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/creator_action_type.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rules_filter_creator_arguments.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -241,45 +241,41 @@ class EmailRulesController extends BaseController {
}
void openEditRuleMenuAction(BuildContext context, TMailRule rule) {
openContextMenuAction(
context,
[
_editEmailRuleActionTile(context, rule),
_deleteEmailRuleActionTile(context, rule),
],
final contextMenuActions = [
EmailRuleActionType.edit,
EmailRuleActionType.delete,
].map((filter) {
return ContextItemEmailRuleTypeAction(
filter,
AppLocalizations.of(context),
imagePaths,
);
}).toList();
openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (action) {
popBack();
_handleRuleFilterActionType(context, rule, action.action);
},
);
}
Widget _deleteEmailRuleActionTile(BuildContext context, TMailRule rule) {
return (EmailRuleBottomSheetActionTileBuilder(
const Key('delete_emailRule_action'),
SvgPicture.asset(
imagePaths.icDeleteComposer,
colorFilter: AppColor.colorActionDeleteConfirmDialog.asFilter()),
AppLocalizations.of(context).deleteRule,
rule,
iconLeftPadding: const EdgeInsets.only(left: 12, right: 16),
iconRightPadding: const EdgeInsets.only(right: 12),
textStyleAction: const TextStyle(
fontSize: 17, color: AppColor.colorActionDeleteConfirmDialog),
)..onActionClick((rule) {
popBack();
deleteEmailRule(context, rule);
})).build();
}
Widget _editEmailRuleActionTile(BuildContext context, TMailRule rule) {
return (EmailRuleBottomSheetActionTileBuilder(
const Key('edit_emailRule_action'),
SvgPicture.asset(imagePaths.icEdit),
AppLocalizations.of(context).editRule,
rule,
iconLeftPadding: const EdgeInsets.only(left: 12, right: 16),
iconRightPadding: const EdgeInsets.only(right: 12))
..onActionClick((rule) {
popBack();
void _handleRuleFilterActionType(
BuildContext context,
TMailRule rule,
EmailRuleActionType actionType,
) {
switch (actionType) {
case EmailRuleActionType.edit:
editEmailRule(context, rule);
}))
.build();
break;
case EmailRuleActionType.delete:
deleteEmailRule(context, rule);
break;
case EmailRuleActionType.add:
break;
}
}
}
@@ -0,0 +1,29 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/email_rule_action_type.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ContextItemEmailRuleTypeAction
extends ContextMenuItemActionRequiredIcon<EmailRuleActionType> {
final AppLocalizations appLocalizations;
final ImagePaths imagePaths;
ContextItemEmailRuleTypeAction(
super.action,
this.appLocalizations,
this.imagePaths,
);
@override
String get actionIcon => action.getIcon(imagePaths);
@override
String get actionName => action.getName(appLocalizations);
@override
Color get actionIconColor => action.getIconColor();
@override
Color get actionNameColor => action.getNameColor();
}
@@ -0,0 +1,49 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
enum EmailRuleActionType {
add,
edit,
delete;
String getName(AppLocalizations appLocalizations) {
switch(this) {
case EmailRuleActionType.add:
return appLocalizations.addNewRule;
case EmailRuleActionType.edit:
return appLocalizations.editRule;
case EmailRuleActionType.delete:
return appLocalizations.deleteRule;
}
}
String getIcon(ImagePaths imagePaths) {
switch(this) {
case EmailRuleActionType.add:
return imagePaths.icAddNewFolder;
case EmailRuleActionType.edit:
return imagePaths.icEdit;
case EmailRuleActionType.delete:
return imagePaths.icDeleteComposer;
}
}
Color getIconColor() {
if (this == EmailRuleActionType.delete) {
return AppColor.redFF3347;
} else {
return AppColor.gray424244.withOpacity(0.72);
}
}
Color getNameColor() {
if (this == EmailRuleActionType.delete) {
return AppColor.redFF3347;
} else {
return AppColor.gray424244.withOpacity(0.79);
}
}
}
@@ -1,51 +1,50 @@
import 'package:flutter/cupertino.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart' as rule_condition;
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:rule_filter/rule_filter/rule_condition_group.dart' as rule_combiner;
extension RuleConditionFieldExtension on rule_condition.Field {
String getTitle(BuildContext context) {
String getTitle(AppLocalizations appLocalizations) {
switch(this) {
case rule_condition.Field.from:
return AppLocalizations.of(context).ruleFilterAddressFromField;
return appLocalizations.ruleFilterAddressFromField;
case rule_condition.Field.to:
return AppLocalizations.of(context).ruleFilterAddressToField;
return appLocalizations.ruleFilterAddressToField;
case rule_condition.Field.cc:
return AppLocalizations.of(context).ruleFilterAddressCcField;
return appLocalizations.ruleFilterAddressCcField;
case rule_condition.Field.recipient:
return AppLocalizations.of(context).recipient;
return appLocalizations.recipient;
case rule_condition.Field.subject:
return AppLocalizations.of(context).subject;
return appLocalizations.subject;
}
}
}
extension RuleConditionComparatorExtension on rule_condition.Comparator {
String getTitle(BuildContext context) {
String getTitle(AppLocalizations appLocalizations) {
switch(this) {
case rule_condition.Comparator.contains:
return AppLocalizations.of(context).contains;
return appLocalizations.contains;
case rule_condition.Comparator.notContains:
return AppLocalizations.of(context).notContains;
return appLocalizations.notContains;
case rule_condition.Comparator.exactlyEquals:
return AppLocalizations.of(context).exactlyEquals;
return appLocalizations.exactlyEquals;
case rule_condition.Comparator.notExactlyEquals:
return AppLocalizations.of(context).notExactlyEquals;
return appLocalizations.notExactlyEquals;
}
}
}
extension RuleConditionCombinerExtension on rule_combiner.ConditionCombiner {
String getTitle(BuildContext context) {
String getTitle(AppLocalizations appLocalizations) {
switch(this) {
case rule_combiner.ConditionCombiner.AND:
return AppLocalizations.of(context).all;
return appLocalizations.all;
case rule_combiner.ConditionCombiner.OR:
return AppLocalizations.of(context).any;
return appLocalizations.any;
}
}
}
@@ -0,0 +1,144 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart';
import 'package:rule_filter/rule_filter/rule_condition_group.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/context_menu/context_item_condition_combiner_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/context_menu/context_item_rule_condition_comparator_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/context_menu/context_item_rule_condition_field_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/context_menu/context_item_rule_filter_action.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_condition_type.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';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
extension SelectRuleActionFieldExtension on RulesFilterCreatorController {
void unFocusAllInputField() {
inputRuleNameFocusNode.unfocus();
FocusManager.instance.primaryFocus?.unfocus();
}
void selectRuleConditionCombinerAction(
BuildContext context,
ConditionCombiner conditionCombiner,
) {
unFocusAllInputField();
final contextMenuActions = ConditionCombiner.values.map((filter) {
return ContextItemConditionCombinerAction(
filter,
conditionCombiner,
AppLocalizations.of(context),
imagePaths,
);
}).toList();
openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) {
popBack();
selectConditionCombiner(menuAction.action);
},
);
}
void selectRuleConditionFieldAction(
BuildContext context,
Field? currentField,
Field selectedField,
RuleFilterConditionScreenType screenType,
int index,
) {
if (screenType == RuleFilterConditionScreenType.mobile) {
unFocusAllInputField();
final contextMenuActions = Field.values.map((field) {
return ContextItemRuleConditionFieldAction(
field,
selectedField,
AppLocalizations.of(context),
imagePaths,
);
}).toList();
openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) {
popBack();
updateRuleConditionField(menuAction.action, index);
},
);
} else {
updateRuleConditionField(currentField, index);
}
}
void selectRuleConditionComparatorAction(
BuildContext context,
Comparator? currentComparator,
Comparator selectedComparator,
RuleFilterConditionScreenType screenType,
int index,
) {
if (screenType == RuleFilterConditionScreenType.mobile) {
unFocusAllInputField();
final contextMenuActions = Comparator.values.map((comparator) {
return ContextItemRuleConditionComparatorAction(
comparator,
selectedComparator,
AppLocalizations.of(context),
imagePaths,
);
}).toList();
openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) {
popBack();
updateRuleConditionComparator(menuAction.action, index);
},
);
} else {
updateRuleConditionComparator(currentComparator, index);
}
}
void selectRuleFilterAction(
BuildContext context,
EmailRuleFilterAction? selectedFilterAction,
int index,
) {
final contextMenuActions = EmailRuleFilterAction.values.map((filterAction) {
if (filterAction.isSupported) {
return ContextItemRuleFilterAction(
filterAction,
selectedFilterAction,
AppLocalizations.of(context),
imagePaths,
);
} else {
return null;
}
}).whereNotNull().toList();
if (contextMenuActions.isEmpty) return;
unFocusAllInputField();
openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) {
popBack();
if (menuAction.action !=
listEmailRuleFilterActionSelected[index].action) {
updateEmailRuleFilterAction(context, menuAction.action, index);
}
},
);
}
}
@@ -0,0 +1,24 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:rule_filter/rule_filter/rule_condition_group.dart';
import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/rule_condition_extensions.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ContextItemConditionCombinerAction
extends ContextMenuItemActionRequiredSelectedIcon<ConditionCombiner> {
final AppLocalizations appLocalizations;
final ImagePaths imagePaths;
ContextItemConditionCombinerAction(
super.action,
super.selectedAction,
this.appLocalizations,
this.imagePaths,
);
@override
String get actionName => action.getTitle(appLocalizations);
@override
String get selectedIcon => imagePaths.icFilterSelected;
}
@@ -0,0 +1,24 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart';
import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/rule_condition_extensions.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ContextItemRuleConditionComparatorAction
extends ContextMenuItemActionRequiredSelectedIcon<Comparator> {
final AppLocalizations appLocalizations;
final ImagePaths imagePaths;
ContextItemRuleConditionComparatorAction(
super.action,
super.selectedAction,
this.appLocalizations,
this.imagePaths,
);
@override
String get actionName => action.getTitle(appLocalizations);
@override
String get selectedIcon => imagePaths.icFilterSelected;
}
@@ -0,0 +1,24 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart';
import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/rule_condition_extensions.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ContextItemRuleConditionFieldAction
extends ContextMenuItemActionRequiredSelectedIcon<Field> {
final AppLocalizations appLocalizations;
final ImagePaths imagePaths;
ContextItemRuleConditionFieldAction(
super.action,
super.selectedAction,
this.appLocalizations,
this.imagePaths,
);
@override
String get actionName => action.getTitle(appLocalizations);
@override
String get selectedIcon => imagePaths.icFilterSelected;
}
@@ -0,0 +1,23 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ContextItemRuleFilterAction
extends ContextMenuItemActionRequiredSelectedIcon<EmailRuleFilterAction> {
final AppLocalizations appLocalizations;
final ImagePaths imagePaths;
ContextItemRuleFilterAction(
super.action,
super.selectedAction,
this.appLocalizations,
this.imagePaths,
);
@override
String get actionName => action.getTitle(appLocalizations);
@override
String get selectedIcon => imagePaths.icFilterSelected;
}
@@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
enum EmailRuleFilterAction {
@@ -9,24 +8,22 @@ enum EmailRuleFilterAction {
markAsSpam,
forwardTo;
String getTitle(BuildContext context) {
String getTitle(AppLocalizations appLocalizations) {
switch(this) {
case EmailRuleFilterAction.moveMessage:
return AppLocalizations.of(context).moveMessage;
return appLocalizations.moveMessage;
case EmailRuleFilterAction.maskAsSeen:
return AppLocalizations.of(context).maskAsSeen;
return appLocalizations.maskAsSeen;
case EmailRuleFilterAction.starIt:
return AppLocalizations.of(context).starIt;
return appLocalizations.starIt;
case EmailRuleFilterAction.rejectIt:
return AppLocalizations.of(context).rejectIt;
return appLocalizations.rejectIt;
case EmailRuleFilterAction.markAsSpam:
return AppLocalizations.of(context).markAsSpam;
return appLocalizations.markAsSpam;
case EmailRuleFilterAction.forwardTo:
return AppLocalizations.of(context).forwardTo;
return appLocalizations.forwardTo;
}
}
bool getSupported() {
return this != EmailRuleFilterAction.forwardTo;
}
bool get isSupported => this != EmailRuleFilterAction.forwardTo;
}
@@ -66,7 +66,7 @@ class RulesFilterCreatorController extends BaseMailboxController {
final TextEditingController forwardEmailController = TextEditingController();
final FocusNode forwardEmailFocusNode = FocusNode();
final listEmailRuleFilterActionSelected = RxList<RuleFilterActionArguments>();
int maxCountAction = EmailRuleFilterAction.values.where((action) => action.getSupported() == true).length - 1;
int maxCountAction = EmailRuleFilterAction.values.where((action) => action.isSupported).length - 1;
final isShowAddAction = Rxn<bool>();
String? _newRuleName;
@@ -330,7 +330,7 @@ class RulesFilterCreatorController extends BaseMailboxController {
);
}
void selectRuleConditionField(rule_condition.Field? newField, int? ruleConditionIndex) {
void updateRuleConditionField(rule_condition.Field? newField, int? ruleConditionIndex) {
if (newField != null && ruleConditionIndex != null) {
RuleCondition newRuleCondition = RuleCondition(
field: newField,
@@ -342,7 +342,7 @@ class RulesFilterCreatorController extends BaseMailboxController {
}
}
void selectRuleConditionComparator(rule_condition.Comparator? newComparator, int? ruleConditionIndex) {
void updateRuleConditionComparator(rule_condition.Comparator? newComparator, int? ruleConditionIndex) {
if (newComparator != null && ruleConditionIndex != null) {
RuleCondition newRuleCondition = RuleCondition(
field: listRuleCondition[ruleConditionIndex].field,
@@ -354,7 +354,7 @@ class RulesFilterCreatorController extends BaseMailboxController {
}
}
void selectEmailRuleFilterAction(
void updateEmailRuleFilterAction(
BuildContext context,
EmailRuleFilterAction? newAction,
int ruleFilterActionIndex
@@ -3,24 +3,16 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart' as rule_condition;
import 'package:rule_filter/rule_filter/rule_condition_group.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/model/email_rule_filter_action.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/rule_filter_condition_type.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/styles/rule_filter_action_styles.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_action_bottom_sheet_action_tile_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_condition_combiner_bottomsheet_action_tile_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_condition_comparator_bottom_sheet_action_tile_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_condition_field_bottom_sheet_action_tile_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_list.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_title_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'model/rule_filter_condition_type.dart';
class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
@@ -183,7 +175,7 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
responsiveUtils: controller.responsiveUtils,
actionList: controller.listEmailRuleFilterActionSelected,
onActionChanged: (newAction, index) {
controller.selectEmailRuleFilterAction(context, newAction, index);
controller.updateEmailRuleFilterAction(context, newAction, index);
},
forwardEmailEditingController: controller.forwardEmailController,
forwardEmailFocusNode: controller.forwardEmailFocusNode,
@@ -344,7 +336,7 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
responsiveUtils: controller.responsiveUtils,
actionList: controller.listEmailRuleFilterActionSelected,
onActionChanged: (newAction, index) {
controller.selectEmailRuleFilterAction(context, newAction, index);
controller.updateEmailRuleFilterAction(context, newAction, index);
},
forwardEmailEditingController: controller.forwardEmailController,
forwardEmailFocusNode: controller.forwardEmailFocusNode,
@@ -463,14 +455,12 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
Obx(() => RuleFilterTitle(
conditionCombinerType: controller.conditionCombinerType.value,
ruleFilterConditionScreenType: RuleFilterConditionScreenType.mobile,
tapActionCallback: (value) => {
controller.openContextMenuAction(
tapActionCallback: (_) {
controller.selectRuleConditionCombinerAction(
context,
_bottomSheetRuleConditionCombinerActionTiles(
context,
controller.conditionCombinerType.value,
)
),
controller.conditionCombinerType.value
?? ConditionCombiner.AND,
);
},
)),
const SizedBox(height: 24),
@@ -519,14 +509,10 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
responsiveUtils: controller.responsiveUtils,
actionList: controller.listEmailRuleFilterActionSelected,
onActionChangeMobile: (currentAction, index) {
KeyboardUtils.hideKeyboard(context);
controller.openContextMenuAction(
controller.selectRuleFilterAction(
context,
_bottomSheetActionRuleFilterActionTiles(
context,
currentAction,
index
)
currentAction,
index
);
},
forwardEmailEditingController: controller.forwardEmailController,
@@ -631,34 +617,22 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
conditionValueErrorText: controller.listRuleConditionValueArguments[index].errorText,
conditionValueFocusNode: controller.listRuleConditionValueArguments[index].focusNode,
conditionValueEditingController: controller.listRuleConditionValueArguments[index].controller,
tapRuleConditionFieldCallback: (value) => {
if (ruleFilterConditionScreenType == RuleFilterConditionScreenType.mobile) {
controller.openContextMenuAction(
tapRuleConditionFieldCallback: (value) =>
controller.selectRuleConditionFieldAction(
context,
_bottomSheetRuleConditionFieldActionTiles(
context,
controller.listRuleCondition[index].field,
index,
)
value,
controller.listRuleCondition[index].field,
ruleFilterConditionScreenType,
index,
),
} else {
controller.selectRuleConditionField(value, index)
}
},
tapRuleConditionComparatorCallback: (value) => {
if (ruleFilterConditionScreenType == RuleFilterConditionScreenType.mobile) {
controller.openContextMenuAction(
tapRuleConditionComparatorCallback: (value) =>
controller.selectRuleConditionComparatorAction(
context,
_bottomSheetRuleConditionComparatorActionTiles(
context,
controller.listRuleCondition[index].comparator,
index,
)
value,
controller.listRuleCondition[index].comparator,
ruleFilterConditionScreenType,
index,
),
} else {
controller.selectRuleConditionComparator(value, index),
}
},
conditionValueOnChangeAction: (value) =>
controller.updateConditionValue(context, value, index),
tapRemoveRuleFilterConditionCallback: () => controller.tapRemoveCondition(index),
@@ -670,147 +644,4 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
);
});
}
List<Widget> _bottomSheetRuleConditionFieldActionTiles(
BuildContext context,
rule_condition.Field? fieldSelected,
int? ruleConditionIndex,
) {
return rule_condition.Field.values
.map((field) =>
_buildRuleConditionFieldWidget(context, field, fieldSelected, ruleConditionIndex))
.toList();
}
Widget _buildRuleConditionFieldWidget(
BuildContext context,
rule_condition.Field field,
rule_condition.Field? fieldSelected,
int? ruleConditionIndex,
) {
return (RuleConditionFieldSheetActionTileBuilder(
field.getTitle(context),
fieldSelected,
field,
iconLeftPadding: const EdgeInsets.only(left: 12, right: 16),
iconRightPadding: const EdgeInsets.only(right: 12),
actionSelected: SvgPicture.asset(
controller.imagePaths.icFilterSelected,
width: 20,
height: 20,
fit: BoxFit.fill))
..onActionClick((field) {
controller.selectRuleConditionField(field, ruleConditionIndex);
popBack();
}))
.build();
}
List<Widget> _bottomSheetRuleConditionComparatorActionTiles(
BuildContext context,
rule_condition.Comparator? comparatorSelected,
int? ruleConditionIndex,
) {
return rule_condition.Comparator.values
.map((comparator) =>
_buildRuleConditionComparatorWidget(context, comparator, comparatorSelected, ruleConditionIndex))
.toList();
}
Widget _buildRuleConditionComparatorWidget(
BuildContext context,
rule_condition.Comparator comparator,
rule_condition.Comparator? comparatorSelected,
int? ruleConditionIndex,
) {
return (RuleConditionComparatorSheetActionTileBuilder(
comparator.getTitle(context),
comparatorSelected,
comparator,
iconLeftPadding: const EdgeInsets.only(left: 12, right: 16),
iconRightPadding: const EdgeInsets.only(right: 12),
actionSelected: SvgPicture.asset(
controller.imagePaths.icFilterSelected,
width: 20,
height: 20,
fit: BoxFit.fill))
..onActionClick((comparator) {
controller.selectRuleConditionComparator(comparator, ruleConditionIndex);
popBack();
}))
.build();
}
List<Widget> _bottomSheetRuleConditionCombinerActionTiles(
BuildContext context,
ConditionCombiner? combinerSelected,
) {
return ConditionCombiner.values
.map((combiner) =>
_buildRuleConditionCombinerWidget(context, combiner, combinerSelected))
.toList();
}
Widget _buildRuleConditionCombinerWidget(
BuildContext context,
ConditionCombiner combiner,
ConditionCombiner? combinerSelected,
) {
return (RuleConditionCombinerSheetActionTileBuilder(
combiner.getTitle(context),
combiner,
combinerSelected,
iconLeftPadding: const EdgeInsets.only(left: 12, right: 16),
iconRightPadding: const EdgeInsets.only(right: 12),
actionSelected: SvgPicture.asset(
controller.imagePaths.icFilterSelected,
width: 20,
height: 20,
fit: BoxFit.fill))
..onActionClick((combiner) {
controller.selectConditionCombiner(combiner);
popBack();
}))
.build();
}
List<Widget> _bottomSheetActionRuleFilterActionTiles(
BuildContext context,
EmailRuleFilterAction? ruleActionSelected,
int ruleActionIndex,
) {
final supportedAction = EmailRuleFilterAction.values
.where((ruleAction) => ruleAction.getSupported() == true)
.toList();
return supportedAction
.map((ruleAction) =>
_buildRuleActionWidget(context, ruleAction, ruleActionSelected, ruleActionIndex))
.toList();
}
Widget _buildRuleActionWidget(
BuildContext context,
EmailRuleFilterAction ruleAction,
EmailRuleFilterAction? ruleActionSelected,
int ruleActionIndex,
) {
return (RuleActionSheetActionTileBuilder(
ruleAction.getTitle(context),
ruleActionSelected,
ruleAction,
iconLeftPadding: const EdgeInsets.only(left: 12, right: 16),
iconRightPadding: const EdgeInsets.only(right: 12),
actionSelected: SvgPicture.asset(
controller.imagePaths.icFilterSelected,
width: 20,
height: 20,
fit: BoxFit.fill))
..onActionClick((ruleAction) {
if (ruleAction != controller.listEmailRuleFilterActionSelected[ruleActionIndex].action) {
controller.selectEmailRuleFilterAction(context, ruleAction, ruleActionIndex);
}
popBack();
}))
.build();
}
}
@@ -40,7 +40,7 @@ class RuleFilterActionRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
final supportedAction = actionList.where((action) => action.getSupported() == true).toList();
final supportedAction = actionList.where((action) => action.isSupported).toList();
return Row(
crossAxisAlignment: actionSelected == EmailRuleFilterAction.moveMessage ? CrossAxisAlignment.center : CrossAxisAlignment.start,
children: [
@@ -11,6 +11,7 @@ import 'package:rule_filter/rule_filter/rule_condition_group.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_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/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnTapActionCallback<T> = Function(T? value);
@@ -63,20 +64,22 @@ class RuleFilterButtonField<T> extends StatelessWidget {
}
String _getName(BuildContext context, T? value) {
final appLocalizations = AppLocalizations.of(context);
if (value is PresentationMailbox) {
return value.getDisplayName(context);
}
if (value is rule_condition.Field) {
return value.getTitle(context);
return value.getTitle(appLocalizations);
}
if (value is rule_condition.Comparator) {
return value.getTitle(context);
return value.getTitle(appLocalizations);
}
if (value is EmailRuleFilterAction) {
return value.getTitle(context);
return value.getTitle(appLocalizations);
}
if (value is ConditionCombiner) {
return value.getTitle(context);
return value.getTitle(appLocalizations);
}
return hintText ?? '';
}
@@ -36,9 +36,8 @@ extension HandleEmailMoreActionExtension on SearchEmailController {
))
.toList();
openContextMenuAction(
context,
[],
openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) {
pressEmailAction(
@@ -0,0 +1,23 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ContextItemReceiveTimeTypeAction
extends ContextMenuItemActionRequiredSelectedIcon<EmailReceiveTimeType> {
final AppLocalizations appLocalizations;
final ImagePaths imagePaths;
ContextItemReceiveTimeTypeAction(
super.action,
super.selectedAction,
this.appLocalizations,
this.imagePaths,
);
@override
String get actionName => action.getTitleByAppLocalizations(appLocalizations);
@override
String get selectedIcon => imagePaths.icFilterSelected;
}
@@ -0,0 +1,23 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ContextItemSortOrderTypeAction
extends ContextMenuItemActionRequiredSelectedIcon<EmailSortOrderType> {
final AppLocalizations appLocalizations;
final ImagePaths imagePaths;
ContextItemSortOrderTypeAction(
super.action,
super.selectedAction,
this.appLocalizations,
this.imagePaths,
);
@override
String get actionName => action.getTitleByAppLocalizations(appLocalizations);
@override
String get selectedIcon => imagePaths.icFilterSelected;
}
@@ -22,15 +22,15 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/qu
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/quick_search/recent_search_item_tile_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart';
import 'package:tmail_ui_user/features/search/email/presentation/extension/handle_email_more_action_extension.dart';
import 'package:tmail_ui_user/features/search/email/presentation/model/context_item_receive_time_type_action.dart';
import 'package:tmail_ui_user/features/search/email/presentation/model/context_item_sort_order_type_action.dart';
import 'package:tmail_ui_user/features/search/email/presentation/model/search_more_state.dart';
import 'package:tmail_ui_user/features/search/email/presentation/search_email_controller.dart';
import 'package:tmail_ui_user/features/search/email/presentation/styles/search_email_view_style.dart';
import 'package:tmail_ui_user/features/search/email/presentation/utils/search_email_utils.dart';
import 'package:tmail_ui_user/features/search/email/presentation/widgets/app_bar_selection_mode.dart';
import 'package:tmail_ui_user/features/search/email/presentation/widgets/email_receive_time_action_tile_widget.dart';
import 'package:tmail_ui_user/features/search/email/presentation/widgets/email_receive_time_cupertino_action_sheet_action_builder.dart';
import 'package:tmail_ui_user/features/search/email/presentation/widgets/email_sort_by_action_tile_widget.dart';
import 'package:tmail_ui_user/features/search/email/presentation/widgets/email_sort_by_cupertino_action_sheet_action_builder.dart';
import 'package:tmail_ui_user/features/search/email/presentation/widgets/empty_search_email_widget.dart';
import 'package:tmail_ui_user/features/search/email/presentation/widgets/search_email_loading_bar_widget.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
@@ -352,17 +352,25 @@ class SearchEmailView extends GetWidget<SearchEmailController>
}
void _openContextMenuDateFilter(BuildContext context) {
controller.openContextMenuAction(
context,
_emailReceiveTimeCupertinoActionTile(
context,
final contextMenuActions = EmailReceiveTimeType.values.map((timeType) {
return ContextItemReceiveTimeTypeAction(
timeType,
controller.emailReceiveTimeType.value,
(receiveTime) => controller.selectReceiveTimeQuickSearchFilter(
context,
receiveTime
)
),
AppLocalizations.of(context),
controller.imagePaths,
);
}).toList();
controller.openBottomSheetContextMenuAction(
key: const Key('date_time_filter_context_menu'),
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) {
controller.selectReceiveTimeQuickSearchFilter(
context,
menuAction.action,
);
},
);
}
@@ -379,14 +387,25 @@ class SearchEmailView extends GetWidget<SearchEmailController>
}
void _openContextMenuSortFilter(BuildContext context) {
controller.openContextMenuAction(
context,
_emailSortOrderCupertinoActionTitle(
context,
final contextMenuActions = EmailSortOrderType.values.map((orderType) {
return ContextItemSortOrderTypeAction(
orderType,
controller.emailSortOrderType.value,
controller.selectSortOrderQuickSearchFilter
),
key: const Key('sort_filter_context_menu')
AppLocalizations.of(context),
controller.imagePaths,
);
}).toList();
controller.openBottomSheetContextMenuAction(
key: const Key('sort_filter_context_menu'),
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) {
controller.selectSortOrderQuickSearchFilter(
context,
menuAction.action,
);
},
);
}
@@ -406,32 +425,6 @@ class SearchEmailView extends GetWidget<SearchEmailController>
.toList();
}
List<Widget> _emailReceiveTimeCupertinoActionTile(
BuildContext context,
EmailReceiveTimeType? receiveTimeSelected,
Function(EmailReceiveTimeType)? onCallBack
) {
return EmailReceiveTimeType.values
.map((timeType) => (EmailReceiveTimeCupertinoActionSheetActionBuilder(
timeType.getTitle(context),
timeType,
timeTypeCurrent: receiveTimeSelected,
iconLeftPadding: controller.responsiveUtils.isMobile(context)
? const EdgeInsetsDirectional.only(start: 12, end: 16)
: const EdgeInsetsDirectional.only(end: 12),
iconRightPadding: controller.responsiveUtils.isMobile(context)
? const EdgeInsetsDirectional.only(end: 12)
: EdgeInsets.zero,
actionSelected: SvgPicture.asset(
controller.imagePaths.icFilterSelected,
width: 20,
height: 20,
fit: BoxFit.fill))
..onActionClick((timeType) => onCallBack?.call(timeType)))
.build())
.toList();
}
List<PopupMenuEntry> _popupMenuEmailSortOrderType(
BuildContext context,
EmailSortOrderType? sortTypeSelected,
@@ -449,36 +442,6 @@ class SearchEmailView extends GetWidget<SearchEmailController>
)).toList();
}
List<Widget> _emailSortOrderCupertinoActionTitle(
BuildContext context,
EmailSortOrderType? sortOrderSelected,
Function(BuildContext context, EmailSortOrderType)? onCallBack,
) {
return EmailSortOrderType.values
.map(
(sortType) => (
EmailSortByCupertinoActionSheetActionBuilder(
sortType.getTitle(context),
sortType,
sortTypeCurrent: sortOrderSelected,
iconLeftPadding: controller.responsiveUtils.isMobile(context)
? const EdgeInsetsDirectional.only(start: 12, end: 16)
: const EdgeInsetsDirectional.only(start: 12),
iconRightPadding: controller.responsiveUtils.isMobile(context)
? const EdgeInsetsDirectional.only(end: 12)
: EdgeInsetsDirectional.zero,
actionSelected: SvgPicture.asset(
controller.imagePaths.icFilterSelected,
width: 20,
height: 20,
fit: BoxFit.fill
)
)
..onActionClick((sortType) => onCallBack?.call(context, sortType))
).build()
).toList();
}
Widget _buildShowAllResultSearchButton(BuildContext context, String textSearch) {
return Material(
color: Colors.transparent,
@@ -283,9 +283,8 @@ class SearchMailboxView extends GetWidget<SearchMailboxController>
}
if (controller.responsiveUtils.isScreenWithShortestSide(context) || position == null) {
controller.openContextMenuAction(
context,
[],
controller.openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) => controller.handleMailboxAction(
context,
@@ -79,16 +79,16 @@ extension FilterMessageOptionExtension on FilterMessageOption {
}
}
String getName(BuildContext context) {
String getName(AppLocalizations appLocalizations) {
switch(this) {
case FilterMessageOption.all:
return '';
case FilterMessageOption.unread:
return AppLocalizations.of(context).unread;
return appLocalizations.unread;
case FilterMessageOption.attachments:
return AppLocalizations.of(context).with_attachments;
return appLocalizations.with_attachments;
case FilterMessageOption.starred:
return AppLocalizations.of(context).starred;
return appLocalizations.starred;
}
}
@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/context_item_filter_message_option_action.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
extension HandleSelectMessageFilterExtension on ThreadController {
void handleSelectMessageFilter(
BuildContext context,
FilterMessageOption selectedOption,
) {
final contextMenuActions = [
FilterMessageOption.attachments,
FilterMessageOption.unread,
FilterMessageOption.starred,
].map((filter) {
return ContextItemFilterMessageOptionAction(
filter,
selectedOption,
AppLocalizations.of(context),
imagePaths,
);
}).toList();
openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (action) => filterMessagesAction(action.action),
);
}
}
@@ -0,0 +1,26 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ContextItemFilterMessageOptionAction
extends ContextMenuItemActionRequiredFull<FilterMessageOption> {
final AppLocalizations appLocalizations;
final ImagePaths imagePaths;
ContextItemFilterMessageOptionAction(
super.action,
super.selectedAction,
this.appLocalizations,
this.imagePaths,
);
@override
String get actionIcon => action.getContextMenuIcon(imagePaths);
@override
String get actionName => action.getName(appLocalizations);
@override
String get selectedIcon => imagePaths.icFilterSelected;
}
@@ -28,6 +28,7 @@ import 'package:tmail_ui_user/features/thread/domain/state/empty_trash_folder_st
import 'package:tmail_ui_user/features/thread/domain/state/get_all_email_state.dart';
import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/extensions/handle_pull_to_refresh_list_email_extension.dart';
import 'package:tmail_ui_user/features/thread/presentation/extensions/handle_select_message_filter_extension.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/loading_more_status.dart';
import 'package:tmail_ui_user/features/thread/presentation/styles/item_email_tile_styles.dart';
@@ -39,7 +40,6 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/bottom_bar_th
import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.dart'
if (dart.library.html) 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_web_builder.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/empty_emails_widget.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/scroll_to_top_button_widget.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/spam_banner/spam_report_banner_widget.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/thread_view_loading_bar_widget.dart';
@@ -84,10 +84,7 @@ class ThreadView extends GetWidget<ThreadController>
cancelEditThreadAction: controller.cancelSelectEmail,
emailSelectionAction: controller.pressEmailSelectionAction,
onContextMenuFilterEmailAction: controller.responsiveUtils.isScreenWithShortestSide(context)
? (filterOption) => controller.openContextMenuAction(
context,
_filterMessagesCupertinoActionTile(context, filterOption)
)
? (filterOption) => controller.handleSelectMessageFilter(context, filterOption)
: null,
onPopupMenuFilterEmailAction: !controller.responsiveUtils.isScreenWithShortestSide(context)
? (filterOption, position) => controller.openPopupMenuAction(
@@ -342,41 +339,6 @@ class ThreadView extends GetWidget<ThreadController>
});
}
List<Widget> _filterMessagesCupertinoActionTile(BuildContext context, FilterMessageOption optionCurrent) {
final listFilter = [
FilterMessageOption.attachments,
FilterMessageOption.unread,
FilterMessageOption.starred,
];
return listFilter.map((filter) => (FilterMessageCupertinoActionSheetActionBuilder(
Key('filter_email_${filter.name}'),
SvgPicture.asset(
filter.getContextMenuIcon(controller.imagePaths),
width: 20,
height: 20,
fit: BoxFit.fill,
colorFilter: filter == FilterMessageOption.attachments
? AppColor.colorTextButton.asFilter()
: null),
filter.getName(context),
filter,
optionCurrent: optionCurrent,
iconLeftPadding: controller.responsiveUtils.isMobile(context)
? const EdgeInsets.only(left: 12, right: 16)
: const EdgeInsets.only(right: 12),
iconRightPadding: controller.responsiveUtils.isMobile(context)
? const EdgeInsets.only(right: 12)
: EdgeInsets.zero,
actionSelected: SvgPicture.asset(
controller.imagePaths.icFilterSelected,
width: 20,
height: 20,
fit: BoxFit.fill))
..onActionClick(controller.filterMessagesAction))
.build()).toList();
}
Widget _buildResultListEmail(BuildContext context, List<PresentationEmail> listPresentationEmail) {
return listPresentationEmail.isNotEmpty
? _buildListEmailBody(context, listPresentationEmail)
@@ -685,9 +647,8 @@ class ThreadView extends GetWidget<ThreadController>
))
.toList();
controller.openContextMenuAction(
context,
[],
controller.openBottomSheetContextMenuAction(
context: context,
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) {
controller.handleEmailActionType(
@@ -52,7 +52,7 @@ class EmptyEmailsWidget extends StatelessWidget {
Text(
key: const Key('empty_email_sub_message'),
AppLocalizations.of(context).startToComposeEmails,
style: ThemeUtils.textStyleInter400().copyWith(
style: ThemeUtils.textStyleInter400.copyWith(
letterSpacing: -0.15,
fontSize: 16,
height: 21.01 / 16,