TF-4233 Show popup menu when click three dot on label item
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
|
import 'package:labels/model/label.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/label_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
|
|
||||||
|
extension HandleLabelActionTypeExtension on LabelController {
|
||||||
|
void handleLabelActionType({
|
||||||
|
required LabelActionType actionType,
|
||||||
|
required AccountId? accountId,
|
||||||
|
Label? label,
|
||||||
|
}) {
|
||||||
|
switch (actionType) {
|
||||||
|
case LabelActionType.create:
|
||||||
|
openCreateNewLabelModal(accountId);
|
||||||
|
break;
|
||||||
|
case LabelActionType.edit:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import 'package:tmail_ui_user/features/labels/domain/state/get_all_label_state.d
|
|||||||
import 'package:tmail_ui_user/features/labels/domain/usecases/create_new_label_interactor.dart';
|
import 'package:tmail_ui_user/features/labels/domain/usecases/create_new_label_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/usecases/get_all_label_interactor.dart';
|
import 'package:tmail_ui_user/features/labels/domain/usecases/get_all_label_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/label_interactor_bindings.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/label_interactor_bindings.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_context_menu_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/state/get_label_setting_state.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/state/get_label_setting_state.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_label_setting_state_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_label_setting_state_interactor.dart';
|
||||||
@@ -24,7 +25,7 @@ import 'package:tmail_ui_user/main/exceptions/logic_exception.dart';
|
|||||||
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
class LabelController extends BaseController {
|
class LabelController extends BaseController with LabelContextMenuMixin {
|
||||||
final labels = <Label>[].obs;
|
final labels = <Label>[].obs;
|
||||||
final labelListExpandMode = Rx(ExpandMode.EXPAND);
|
final labelListExpandMode = Rx(ExpandMode.EXPAND);
|
||||||
final isLabelSettingEnabled = RxBool(false);
|
final isLabelSettingEnabled = RxBool(false);
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:labels/model/label.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/popup_menu_item_label_action.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox/presentation/utils/labels/label_method_action_define.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
|
mixin LabelContextMenuMixin on PopupContextMenuActionMixin {
|
||||||
|
Future<void> openLabelContextMenuAction(
|
||||||
|
BuildContext context,
|
||||||
|
ImagePaths imagePaths,
|
||||||
|
Label label,
|
||||||
|
RelativeRect position,
|
||||||
|
OnLabelActionTypeCallback onLabelActionTypeCallback,
|
||||||
|
) async {
|
||||||
|
final listLabelAction = [LabelActionType.edit];
|
||||||
|
|
||||||
|
final popupMenuItems = listLabelAction
|
||||||
|
.map((actionType) => _buildLabelPopupMenuItem(
|
||||||
|
label,
|
||||||
|
actionType,
|
||||||
|
AppLocalizations.of(context),
|
||||||
|
imagePaths,
|
||||||
|
onLabelActionTypeCallback,
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return openPopupMenuAction(
|
||||||
|
context,
|
||||||
|
position,
|
||||||
|
popupMenuItems,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PopupMenuItem _buildLabelPopupMenuItem(
|
||||||
|
Label label,
|
||||||
|
LabelActionType actionType,
|
||||||
|
AppLocalizations appLocalizations,
|
||||||
|
ImagePaths imagePaths,
|
||||||
|
OnLabelActionTypeCallback onLabelActionTypeCallback,
|
||||||
|
) {
|
||||||
|
return PopupMenuItem(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: PopupMenuItemActionWidget(
|
||||||
|
menuAction: PopupMenuItemLabelAction(
|
||||||
|
actionType,
|
||||||
|
appLocalizations,
|
||||||
|
imagePaths,
|
||||||
|
),
|
||||||
|
menuActionClick: (menuAction) => _onPerformLabelActionType(
|
||||||
|
label,
|
||||||
|
actionType,
|
||||||
|
onLabelActionTypeCallback,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onPerformLabelActionType(
|
||||||
|
Label label,
|
||||||
|
LabelActionType actionType,
|
||||||
|
OnLabelActionTypeCallback onLabelActionTypeCallback,
|
||||||
|
) {
|
||||||
|
popBack();
|
||||||
|
onLabelActionTypeCallback(label, actionType);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
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 LabelActionType {
|
||||||
|
create,
|
||||||
|
edit;
|
||||||
|
|
||||||
|
String getContextMenuTitle(AppLocalizations appLocalizations) {
|
||||||
|
switch (this) {
|
||||||
|
case LabelActionType.create:
|
||||||
|
return appLocalizations.create;
|
||||||
|
case LabelActionType.edit:
|
||||||
|
return appLocalizations.edit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getModalTitle(AppLocalizations appLocalizations) {
|
||||||
|
switch (this) {
|
||||||
|
case LabelActionType.create:
|
||||||
|
return appLocalizations.createANewLabel;
|
||||||
|
case LabelActionType.edit:
|
||||||
|
return appLocalizations.updateTheLabelName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getContextMenuIcon(ImagePaths imagePaths) {
|
||||||
|
switch (this) {
|
||||||
|
case LabelActionType.create:
|
||||||
|
return imagePaths.icAddNewFolder;
|
||||||
|
case LabelActionType.edit:
|
||||||
|
return imagePaths.icRenameMailbox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getPopupMenuTitleColor() => Colors.black;
|
||||||
|
|
||||||
|
Color getPopupMenuIconColor() => AppColor.steelGrayA540;
|
||||||
|
|
||||||
|
Color getContextMenuIconColor() =>
|
||||||
|
AppColor.gray424244.withValues(alpha: 0.72);
|
||||||
|
|
||||||
|
Color getContextMenuTitleColor() =>
|
||||||
|
AppColor.gray424244.withValues(alpha: 0.9);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/model/popup_menu_item_action.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
|
class PopupMenuItemLabelAction
|
||||||
|
extends PopupMenuItemActionRequiredIcon<LabelActionType> {
|
||||||
|
final AppLocalizations appLocalizations;
|
||||||
|
final ImagePaths imagePaths;
|
||||||
|
|
||||||
|
PopupMenuItemLabelAction(
|
||||||
|
super.action,
|
||||||
|
this.appLocalizations,
|
||||||
|
this.imagePaths,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get actionIcon => action.getContextMenuIcon(imagePaths);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get actionName => action.getContextMenuTitle(appLocalizations);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color get actionIconColor => action.getPopupMenuIconColor();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color get actionNameColor => action.getPopupMenuTitleColor();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get hoverIcon => imagePaths.icThumbsUp;
|
||||||
|
}
|
||||||
@@ -10,6 +10,9 @@ import 'package:model/extensions/session_extension.dart';
|
|||||||
import 'package:model/mailbox/expand_mode.dart';
|
import 'package:model/mailbox/expand_mode.dart';
|
||||||
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
|
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_action_type_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/handle_label_action_type_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/handle_mailbox_action_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/handle_mailbox_action_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/open_app_grid_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/open_app_grid_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/toggle_expand_folders_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/toggle_expand_folders_extension.dart';
|
||||||
@@ -263,6 +266,9 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
|
|||||||
|
|
||||||
Widget buildListMailbox(BuildContext context) {
|
Widget buildListMailbox(BuildContext context) {
|
||||||
final isDesktop = controller.responsiveUtils.isDesktop(context);
|
final isDesktop = controller.responsiveUtils.isDesktop(context);
|
||||||
|
final isMobileResponsive = controller
|
||||||
|
.responsiveUtils
|
||||||
|
.isScreenWithShortestSide(context);
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
controller: controller.mailboxListScrollController,
|
controller: controller.mailboxListScrollController,
|
||||||
@@ -342,7 +348,7 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
|
|||||||
: const Offstage(),
|
: const Offstage(),
|
||||||
)),
|
)),
|
||||||
buildLabelsBar(context, isDesktop),
|
buildLabelsBar(context, isDesktop),
|
||||||
buildLabelsList(context, isDesktop),
|
buildLabelsList(context, isDesktop, isMobileResponsive),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -373,7 +379,10 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
|
|||||||
countLabels: countLabels,
|
countLabels: countLabels,
|
||||||
onToggleLabelListState: labelController.toggleLabelListState,
|
onToggleLabelListState: labelController.toggleLabelListState,
|
||||||
onAddNewLabel: () =>
|
onAddNewLabel: () =>
|
||||||
labelController.openCreateNewLabelModal(accountId),
|
labelController.handleLabelActionType(
|
||||||
|
actionType: LabelActionType.create,
|
||||||
|
accountId: accountId,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
@@ -381,17 +390,19 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildLabelsList(BuildContext context, bool isDesktop) {
|
Widget buildLabelsList(
|
||||||
|
BuildContext context,
|
||||||
|
bool isDesktop,
|
||||||
|
bool isMobileResponsive,
|
||||||
|
) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
final isLabelAvailable = controller
|
final dashboardController = controller.mailboxDashBoardController;
|
||||||
.mailboxDashBoardController
|
|
||||||
.isLabelAvailable;
|
|
||||||
|
|
||||||
final labelController =
|
final isLabelAvailable = dashboardController.isLabelAvailable;
|
||||||
controller.mailboxDashBoardController.labelController;
|
|
||||||
|
|
||||||
final selectedMailbox = controller.mailboxDashBoardController
|
final labelController = dashboardController.labelController;
|
||||||
.selectedMailbox.value;
|
|
||||||
|
final selectedMailbox = dashboardController.selectedMailbox.value;
|
||||||
Id? labelIdSelected;
|
Id? labelIdSelected;
|
||||||
if (selectedMailbox is PresentationLabelMailbox) {
|
if (selectedMailbox is PresentationLabelMailbox) {
|
||||||
labelIdSelected = selectedMailbox.id.id;
|
labelIdSelected = selectedMailbox.id.id;
|
||||||
@@ -409,10 +420,18 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
|
|||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
labelIdSelected: labelIdSelected,
|
labelIdSelected: labelIdSelected,
|
||||||
|
isMobileResponsive: isMobileResponsive,
|
||||||
onOpenLabelCallback: (label) => controller.openMailbox(
|
onOpenLabelCallback: (label) => controller.openMailbox(
|
||||||
context,
|
context,
|
||||||
PresentationLabelMailbox.initial(label),
|
PresentationLabelMailbox.initial(label),
|
||||||
),
|
),
|
||||||
|
onOpenContextMenu: (label, position) =>
|
||||||
|
dashboardController.openLabelContextMenuAction(
|
||||||
|
context,
|
||||||
|
controller.imagePaths,
|
||||||
|
label,
|
||||||
|
position,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: const Offstage(),
|
: const Offstage(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/utils/platform_info.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:labels/model/label.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_action_type_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
||||||
|
|
||||||
|
extension HandleLabelActionTypeExtension on MailboxDashBoardController {
|
||||||
|
Future<void> openLabelContextMenuAction(
|
||||||
|
BuildContext context,
|
||||||
|
ImagePaths imagePaths,
|
||||||
|
Label label,
|
||||||
|
RelativeRect position,
|
||||||
|
) async {
|
||||||
|
if (PlatformInfo.isWeb) {
|
||||||
|
isPopupMenuOpened.value = true;
|
||||||
|
}
|
||||||
|
return labelController
|
||||||
|
.openLabelContextMenuAction(
|
||||||
|
context,
|
||||||
|
imagePaths,
|
||||||
|
label,
|
||||||
|
position,
|
||||||
|
_onPerformLabelActionType,
|
||||||
|
)
|
||||||
|
.whenComplete(() {
|
||||||
|
if (PlatformInfo.isWeb) {
|
||||||
|
isPopupMenuOpened.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onPerformLabelActionType(Label label, LabelActionType actionType) {
|
||||||
|
labelController.handleLabelActionType(
|
||||||
|
actionType: actionType,
|
||||||
|
accountId: accountId.value,
|
||||||
|
label: label,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:labels/labels.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
|
|
||||||
|
typedef OnOpenLabelContextMenuAction = Future<void> Function(
|
||||||
|
Label label,
|
||||||
|
RelativeRect position,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef OnLabelActionTypeCallback = void Function(
|
||||||
|
Label label,
|
||||||
|
LabelActionType actionType,
|
||||||
|
);
|
||||||
@@ -1,20 +1,25 @@
|
|||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:labels/extensions/label_extension.dart';
|
import 'package:labels/extensions/label_extension.dart';
|
||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_item_widget_styles.dart';
|
import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_item_widget_styles.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox/presentation/styles/trailing_mailbox_item_widget_styles.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox/presentation/utils/labels/label_method_action_define.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_icon_widget.dart';
|
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_icon_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_name_widget.dart';
|
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_name_widget.dart';
|
||||||
|
|
||||||
typedef OnOpenLabelCallback = void Function(Label label);
|
typedef OnOpenLabelCallback = void Function(Label label);
|
||||||
|
|
||||||
class LabelListItem extends StatelessWidget {
|
class LabelListItem extends StatefulWidget {
|
||||||
final Label label;
|
final Label label;
|
||||||
final ImagePaths imagePaths;
|
final ImagePaths imagePaths;
|
||||||
final bool isDesktop;
|
final bool isDesktop;
|
||||||
final bool isSelected;
|
final bool isSelected;
|
||||||
|
final bool isMobileResponsive;
|
||||||
final OnOpenLabelCallback onOpenLabelCallback;
|
final OnOpenLabelCallback onOpenLabelCallback;
|
||||||
|
final OnOpenLabelContextMenuAction? onOpenContextMenu;
|
||||||
|
|
||||||
const LabelListItem({
|
const LabelListItem({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -23,11 +28,30 @@ class LabelListItem extends StatelessWidget {
|
|||||||
required this.onOpenLabelCallback,
|
required this.onOpenLabelCallback,
|
||||||
this.isDesktop = false,
|
this.isDesktop = false,
|
||||||
this.isSelected = false,
|
this.isSelected = false,
|
||||||
|
this.isMobileResponsive = false,
|
||||||
|
this.onOpenContextMenu,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
State<LabelListItem> createState() => _LabelListItemState();
|
||||||
final borderRadius = BorderRadius.all(
|
}
|
||||||
|
|
||||||
|
class _LabelListItemState extends State<LabelListItem> {
|
||||||
|
bool _isContextMenuVisible = false;
|
||||||
|
bool _isItemHovered = false;
|
||||||
|
|
||||||
|
late final BorderRadius _borderRadius;
|
||||||
|
late final EdgeInsetsGeometry _itemPadding;
|
||||||
|
late final double _itemHeight;
|
||||||
|
late final double _iconSpacing;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
final isDesktop = widget.isDesktop;
|
||||||
|
|
||||||
|
_borderRadius = BorderRadius.all(
|
||||||
Radius.circular(
|
Radius.circular(
|
||||||
isDesktop
|
isDesktop
|
||||||
? MailboxItemWidgetStyles.borderRadius
|
? MailboxItemWidgetStyles.borderRadius
|
||||||
@@ -35,41 +59,57 @@ class LabelListItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_itemPadding = EdgeInsetsDirectional.symmetric(
|
||||||
|
horizontal: isDesktop
|
||||||
|
? MailboxItemWidgetStyles.itemPadding
|
||||||
|
: MailboxItemWidgetStyles.mobileItemPadding,
|
||||||
|
);
|
||||||
|
|
||||||
|
_itemHeight = isDesktop
|
||||||
|
? MailboxItemWidgetStyles.height
|
||||||
|
: MailboxItemWidgetStyles.mobileHeight;
|
||||||
|
|
||||||
|
_iconSpacing = isDesktop
|
||||||
|
? MailboxItemWidgetStyles.labelIconSpace
|
||||||
|
: MailboxItemWidgetStyles.mobileLabelIconSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get _isMenuButtonVisible => _isContextMenuVisible || _isItemHovered;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return Material(
|
return Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: borderRadius,
|
borderRadius: _borderRadius,
|
||||||
onTap: () => onOpenLabelCallback(label),
|
onHover: _handleHoverChanged,
|
||||||
|
onTap: () => widget.onOpenLabelCallback(widget.label),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
height: _itemHeight,
|
||||||
|
padding: _itemPadding,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: borderRadius,
|
borderRadius: _borderRadius,
|
||||||
color: _backgroundColorItem,
|
color: _backgroundColorItem,
|
||||||
),
|
),
|
||||||
padding: EdgeInsetsDirectional.symmetric(
|
|
||||||
horizontal: isDesktop
|
|
||||||
? MailboxItemWidgetStyles.itemPadding
|
|
||||||
: MailboxItemWidgetStyles.mobileItemPadding,
|
|
||||||
),
|
|
||||||
height: isDesktop
|
|
||||||
? MailboxItemWidgetStyles.height
|
|
||||||
: MailboxItemWidgetStyles.mobileHeight,
|
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
LabelIconWidget(
|
_LabelIcon(
|
||||||
icon: imagePaths.icTag,
|
icon: widget.imagePaths.icTag,
|
||||||
color: label.backgroundColor,
|
color: widget.label.backgroundColor,
|
||||||
padding: EdgeInsetsDirectional.only(
|
spacing: _iconSpacing,
|
||||||
end: isDesktop
|
|
||||||
? MailboxItemWidgetStyles.labelIconSpace
|
|
||||||
: MailboxItemWidgetStyles.mobileLabelIconSpace,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: LabelNameWidget(
|
child: LabelNameWidget(
|
||||||
name: label.safeDisplayName,
|
name: widget.label.safeDisplayName,
|
||||||
isDesktop: isDesktop,
|
isDesktop: widget.isDesktop,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
_ContextMenuButton(
|
||||||
|
visible: _isMenuButtonVisible,
|
||||||
|
backgroundColor: _menuButtonBackgroundColor(context),
|
||||||
|
imagePaths: widget.imagePaths,
|
||||||
|
onTap: _handleMenuTap,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -78,5 +118,90 @@ class LabelListItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Color get _backgroundColorItem =>
|
Color get _backgroundColorItem =>
|
||||||
isSelected ? AppColor.blue100 : Colors.transparent;
|
widget.isSelected ? AppColor.blue100 : Colors.transparent;
|
||||||
|
|
||||||
|
Color _menuButtonBackgroundColor(BuildContext context) {
|
||||||
|
if (_isContextMenuVisible) {
|
||||||
|
return Theme.of(context).colorScheme.outline.withValues(alpha: 0.08);
|
||||||
|
}
|
||||||
|
return TrailingMailboxItemWidgetStyles.menuIconBackgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleHoverChanged(bool hovered) {
|
||||||
|
if (_isItemHovered == hovered) return;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_isItemHovered = hovered;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setContextMenuVisible(bool visible) {
|
||||||
|
if (_isContextMenuVisible == visible) return;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_isContextMenuVisible = visible;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleMenuTap(RelativeRect position) {
|
||||||
|
if (!widget.isMobileResponsive) {
|
||||||
|
_setContextMenuVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.onOpenContextMenu?.call(widget.label, position).whenComplete(() {
|
||||||
|
if (mounted && !widget.isMobileResponsive) {
|
||||||
|
_setContextMenuVisible(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LabelIcon extends StatelessWidget {
|
||||||
|
final String icon;
|
||||||
|
final Color? color;
|
||||||
|
final double spacing;
|
||||||
|
|
||||||
|
const _LabelIcon({
|
||||||
|
required this.icon,
|
||||||
|
required this.color,
|
||||||
|
required this.spacing,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LabelIconWidget(
|
||||||
|
icon: icon,
|
||||||
|
color: color,
|
||||||
|
padding: EdgeInsetsDirectional.only(end: spacing),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ContextMenuButton extends StatelessWidget {
|
||||||
|
final bool visible;
|
||||||
|
final Color backgroundColor;
|
||||||
|
final ImagePaths imagePaths;
|
||||||
|
final ValueChanged<RelativeRect> onTap;
|
||||||
|
|
||||||
|
const _ContextMenuButton({
|
||||||
|
required this.visible,
|
||||||
|
required this.backgroundColor,
|
||||||
|
required this.imagePaths,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Offstage(
|
||||||
|
offstage: !visible,
|
||||||
|
child: TMailButtonWidget.fromIcon(
|
||||||
|
margin: TrailingMailboxItemWidgetStyles.menuIconMargin,
|
||||||
|
icon: imagePaths.icMoreVertical,
|
||||||
|
iconSize: TrailingMailboxItemWidgetStyles.menuIconSize,
|
||||||
|
padding: TrailingMailboxItemWidgetStyles.menuIconPadding,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
onTapActionAtPositionCallback: onTap,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:core/presentation/resources/image_paths.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox/presentation/utils/labels/label_method_action_define.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_list_item.dart';
|
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_list_item.dart';
|
||||||
|
|
||||||
class LabelListView extends StatelessWidget {
|
class LabelListView extends StatelessWidget {
|
||||||
@@ -10,6 +11,8 @@ class LabelListView extends StatelessWidget {
|
|||||||
final bool isDesktop;
|
final bool isDesktop;
|
||||||
final Id? labelIdSelected;
|
final Id? labelIdSelected;
|
||||||
final OnOpenLabelCallback onOpenLabelCallback;
|
final OnOpenLabelCallback onOpenLabelCallback;
|
||||||
|
final bool isMobileResponsive;
|
||||||
|
final OnOpenLabelContextMenuAction? onOpenContextMenu;
|
||||||
|
|
||||||
const LabelListView({
|
const LabelListView({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -18,6 +21,8 @@ class LabelListView extends StatelessWidget {
|
|||||||
required this.onOpenLabelCallback,
|
required this.onOpenLabelCallback,
|
||||||
this.isDesktop = false,
|
this.isDesktop = false,
|
||||||
this.labelIdSelected,
|
this.labelIdSelected,
|
||||||
|
this.isMobileResponsive = false,
|
||||||
|
this.onOpenContextMenu,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -36,6 +41,8 @@ class LabelListView extends StatelessWidget {
|
|||||||
isSelected: label.id == labelIdSelected,
|
isSelected: label.id == labelIdSelected,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
onOpenLabelCallback: onOpenLabelCallback,
|
onOpenLabelCallback: onOpenLabelCallback,
|
||||||
|
isMobileResponsive: isMobileResponsive,
|
||||||
|
onOpenContextMenu: onOpenContextMenu,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"@@last_modified": "2026-01-19T17:48:08.806042",
|
"@@last_modified": "2026-01-20T09:45:15.272706",
|
||||||
"initializing_data": "Initializing data...",
|
"initializing_data": "Initializing data...",
|
||||||
"@initializing_data": {
|
"@initializing_data": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -5353,5 +5353,17 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"editLabel": "Edit label",
|
||||||
|
"@editLabel": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"updateTheLabelName": "Update the label name",
|
||||||
|
"@updateTheLabelName": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5680,4 +5680,18 @@ class AppLocalizations {
|
|||||||
name: 'allLabels',
|
name: 'allLabels',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get editLabel {
|
||||||
|
return Intl.message(
|
||||||
|
'Edit label',
|
||||||
|
name: 'editLabel',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get updateTheLabelName {
|
||||||
|
return Intl.message(
|
||||||
|
'Update the label name',
|
||||||
|
name: 'updateTheLabelName',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user