diff --git a/lib/features/search/email/presentation/extension/handle_email_more_action_extension.dart b/lib/features/search/email/presentation/extension/handle_email_more_action_extension.dart index a9501fcb0..888fa6159 100644 --- a/lib/features/search/email/presentation/extension/handle_email_more_action_extension.dart +++ b/lib/features/search/email/presentation/extension/handle_email_more_action_extension.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:model/email/email_action_type.dart'; + import 'package:model/email/email_action_type.dart'; import 'package:model/email/presentation_email.dart'; import 'package:model/extensions/presentation_mailbox_extension.dart'; import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart'; @@ -10,7 +10,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; extension HandleEmailMoreActionExtension on SearchEmailController { - void handleEmailMoreAction( + Future handleEmailMoreAction( BuildContext context, PresentationEmail presentationEmail, RelativeRect? position, @@ -24,7 +24,7 @@ extension HandleEmailMoreActionExtension on SearchEmailController { if (!isDrafts) EmailActionType.editAsNewEmail, ]; - if (listEmailActions.isEmpty) return; + if (listEmailActions.isEmpty) return Future.value(); if (position == null) { final contextMenuActions = listEmailActions @@ -35,7 +35,7 @@ extension HandleEmailMoreActionExtension on SearchEmailController { )) .toList(); - openBottomSheetContextMenuAction( + return openBottomSheetContextMenuAction( context: context, itemActions: contextMenuActions, onContextMenuActionClick: (menuAction) { @@ -70,7 +70,7 @@ extension HandleEmailMoreActionExtension on SearchEmailController { ); }).toList(); - openPopupMenuAction(context, position, popupMenuItems); + return openPopupMenuAction(context, position, popupMenuItems); } } } diff --git a/lib/features/search/email/presentation/extensions/handle_email_action_extension.dart b/lib/features/search/email/presentation/extensions/handle_email_action_extension.dart deleted file mode 100644 index 96fe93059..000000000 --- a/lib/features/search/email/presentation/extensions/handle_email_action_extension.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:model/email/email_action_type.dart'; -import 'package:model/email/presentation_email.dart'; -import 'package:model/extensions/presentation_mailbox_extension.dart'; -import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart'; -import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_email_action.dart'; -import 'package:tmail_ui_user/features/search/email/presentation/search_email_controller.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/routes/route_navigation.dart'; - -extension HandleEmailActionExtension on SearchEmailController { - void handleEmailMoreActionClick( - BuildContext context, - RelativeRect position, - PresentationEmail presentationEmail, - ) { - final mailboxContain = presentationEmail.mailboxContain; - final isDrafts = mailboxContain?.isDrafts ?? false; - final isSpam = mailboxContain?.isSpam ?? false; - - final popupMenuItems = [ - isSpam ? EmailActionType.unSpam : EmailActionType.moveToSpam, - if (isDrafts == false) EmailActionType.editAsNewEmail, - ].map((actionType) { - return PopupMenuItem( - padding: EdgeInsets.zero, - child: PopupMenuItemActionWidget( - menuAction: PopupMenuItemEmailAction( - actionType, - AppLocalizations.of(context), - imagePaths, - ), - menuActionClick: (menuAction) { - popBack(); - pressEmailAction( - context, - menuAction.action, - presentationEmail, - mailboxContain: mailboxContain, - ); - }, - ), - ); - }).toList(); - - openPopupMenuAction(context, position, popupMenuItems); - } -} diff --git a/lib/features/thread/presentation/mixin/base_email_item_tile.dart b/lib/features/thread/presentation/mixin/base_email_item_tile.dart index ee65770ec..60d2640e2 100644 --- a/lib/features/thread/presentation/mixin/base_email_item_tile.dart +++ b/lib/features/thread/presentation/mixin/base_email_item_tile.dart @@ -22,7 +22,7 @@ import 'package:tmail_ui_user/features/thread/presentation/styles/item_email_til import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; typedef OnPressEmailActionClick = void Function(EmailActionType, PresentationEmail); -typedef OnMoreActionClick = void Function(PresentationEmail, RelativeRect?); +typedef OnMoreActionClick = Future Function(PresentationEmail, RelativeRect?); mixin BaseEmailItemTile { diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 1b407ee11..3b81a0c8a 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -609,7 +609,7 @@ class ThreadView extends GetWidget ); } - void _handleEmailContextMenuAction( + Future _handleEmailContextMenuAction( BuildContext context, PresentationEmail presentationEmail, RelativeRect? position @@ -639,7 +639,7 @@ class ThreadView extends GetWidget )) .toList(); - controller.openBottomSheetContextMenuAction( + return controller.openBottomSheetContextMenuAction( context: context, itemActions: contextMenuActions, onContextMenuActionClick: (menuAction) { @@ -672,7 +672,7 @@ class ThreadView extends GetWidget ); }).toList(); - controller.openPopupMenuAction(context, position, popupMenuItems); + return controller.openPopupMenuAction(context, position, popupMenuItems); } } diff --git a/lib/features/thread/presentation/widgets/desktop_list_email_action_hover_widget.dart b/lib/features/thread/presentation/widgets/desktop_list_email_action_hover_widget.dart new file mode 100644 index 000000000..44008621b --- /dev/null +++ b/lib/features/thread/presentation/widgets/desktop_list_email_action_hover_widget.dart @@ -0,0 +1,174 @@ +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:model/email/email_action_type.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:model/extensions/presentation_mailbox_extension.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/thread/presentation/mixin/base_email_item_tile.dart'; +import 'package:tmail_ui_user/features/thread/presentation/styles/item_email_tile_styles.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class DesktopListEmailActionHoverWidget extends StatefulWidget { + final PresentationEmail presentationEmail; + final bool isHovered; + final bool canDeletePermanently; + final bool isSearchEmailRunning; + final PresentationMailbox? mailboxContain; + final OnPressEmailActionClick? emailActionClick; + final OnMoreActionClick? onMoreActionClick; + + const DesktopListEmailActionHoverWidget({ + super.key, + required this.presentationEmail, + required this.isHovered, + required this.canDeletePermanently, + required this.isSearchEmailRunning, + required this.mailboxContain, + required this.emailActionClick, + required this.onMoreActionClick, + }); + + @override + State createState() => + _DesktopListEmailActionHoverWidgetState(); +} + +class _DesktopListEmailActionHoverWidgetState + extends State with BaseEmailItemTile { + final _imagePaths = Get.find(); + final _responsiveUtils = Get.find(); + + bool _popupMenuVisible = false; + + @override + Widget build(BuildContext context) { + final listActionWidget = [ + if (widget.isHovered) ...[ + TMailButtonWidget.fromIcon( + icon: _imagePaths.icOpenInNewTab, + iconColor: ItemEmailTileStyles.actionIconColor, + iconSize: _getIconSize(), + padding: _getPaddingIcon(context), + backgroundColor: Colors.transparent, + tooltipMessage: AppLocalizations.of(context).openInNewTab, + onTapActionCallback: () => widget.emailActionClick?.call( + EmailActionType.openInNewTab, + widget.presentationEmail, + ), + ), + if (!widget.presentationEmail.isDraft) + TMailButtonWidget.fromIcon( + icon: widget.presentationEmail.hasRead + ? _imagePaths.icUnread + : _imagePaths.icRead, + iconColor: ItemEmailTileStyles.actionIconColor, + iconSize: _getIconSize(), + padding: _getPaddingIcon(context), + backgroundColor: Colors.transparent, + tooltipMessage: widget.presentationEmail.hasRead + ? AppLocalizations.of(context).mark_as_unread + : AppLocalizations.of(context).mark_as_read, + onTapActionCallback: () => widget.emailActionClick?.call( + widget.presentationEmail.hasRead + ? EmailActionType.markAsUnread + : EmailActionType.markAsRead, + widget.presentationEmail, + ), + ), + if (widget.mailboxContain != null && + widget.mailboxContain?.isDrafts == false) ...[ + TMailButtonWidget.fromIcon( + icon: _imagePaths.icMove, + iconColor: ItemEmailTileStyles.actionIconColor, + iconSize: _getIconSize(), + padding: _getPaddingIcon(context), + backgroundColor: Colors.transparent, + tooltipMessage: AppLocalizations.of(context).move, + onTapActionCallback: () => widget.emailActionClick?.call( + EmailActionType.moveToMailbox, + widget.presentationEmail, + ), + ), + ], + TMailButtonWidget.fromIcon( + icon: _imagePaths.icDeleteComposer, + iconColor: ItemEmailTileStyles.actionIconColor, + iconSize: 16, + padding: _getPaddingIcon(context), + backgroundColor: Colors.transparent, + tooltipMessage: widget.canDeletePermanently + ? AppLocalizations.of(context).delete_permanently + : AppLocalizations.of(context).move_to_trash, + onTapActionCallback: () => widget.emailActionClick?.call( + widget.canDeletePermanently + ? EmailActionType.deletePermanently + : EmailActionType.moveToTrash, + widget.presentationEmail, + ), + ), + ], + if (_shouldShowPopupMenu) ...[ + TMailButtonWidget.fromIcon( + icon: _imagePaths.icMoreVertical, + iconColor: ItemEmailTileStyles.actionIconColor, + iconSize: _getIconSize(), + padding: _getPaddingIcon(context), + backgroundColor: _popupMenuVisible + ? Theme.of(context).colorScheme.outline.withOpacity(0.08) + : Colors.transparent, + tooltipMessage: AppLocalizations.of(context).more, + onTapActionAtPositionCallback: (position) { + if (_responsiveUtils.isScreenWithShortestSide(context)) { + widget.onMoreActionClick?.call(widget.presentationEmail, null); + } else { + _onPopupMenuVisibleChange(true); + + widget.onMoreActionClick + ?.call(widget.presentationEmail, position) + .whenComplete(() => _onPopupMenuVisibleChange(false)); + } + }, + ), + const SizedBox(width: 16), + ] else ...[ + buildMailboxContain( + context, + widget.isSearchEmailRunning, + widget.presentationEmail, + ), + if (widget.presentationEmail.hasAttachment == true) + Padding( + padding: const EdgeInsetsDirectional.only(start: 8), + child: buildIconAttachment(), + ), + Padding( + padding: const EdgeInsetsDirectional.only(end: 20, start: 8), + child: buildDateTime(context, widget.presentationEmail), + ), + ] + ]; + + if (listActionWidget.isEmpty) return const SizedBox.shrink(); + + return Row(children: listActionWidget); + } + + double _getIconSize() => 18; + + EdgeInsets _getPaddingIcon(BuildContext context) { + return const EdgeInsets.all(5); + } + + bool get _shouldShowPopupMenu => widget.isHovered || _popupMenuVisible; + + void _onPopupMenuVisibleChange(bool visible) { + if (_popupMenuVisible != visible && mounted) { + setState(() { + _popupMenuVisible = visible; + }); + } + } +} diff --git a/lib/features/thread/presentation/widgets/email_tile_web_builder.dart b/lib/features/thread/presentation/widgets/email_tile_web_builder.dart index a50f3cdb8..7ffa36f98 100644 --- a/lib/features/thread/presentation/widgets/email_tile_web_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_web_builder.dart @@ -1,6 +1,5 @@ import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/extensions/tap_down_details_extension.dart'; import 'package:core/presentation/views/button/icon_button_web.dart'; import 'package:core/presentation/views/responsive/responsive_widget.dart'; import 'package:flutter/material.dart'; @@ -12,7 +11,8 @@ import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/select_mode.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/presentation/mixin/base_email_item_tile.dart'; -import 'package:tmail_ui_user/features/thread/presentation/styles/item_email_tile_styles.dart'; +import 'package:tmail_ui_user/features/thread/presentation/widgets/desktop_list_email_action_hover_widget.dart'; +import 'package:tmail_ui_user/features/thread/presentation/widgets/web_tablet_body_email_item_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class EmailTileBuilder extends StatefulWidget { @@ -174,142 +174,19 @@ class _EmailTileBuilderState extends State with BaseEmailItem ) ), ), - tablet: Material( - type: MaterialType.transparency, - child: InkWell( - onTap: () => widget.emailActionClick?.call( - EmailActionType.preview, - widget.presentationEmail - ), - hoverColor: Theme.of(context).colorScheme.outline.withOpacity(0.08), - onHover: (value) => _hoverNotifier.value = value, - child: Container( - padding: widget.padding ?? _getPaddingItem(context), - decoration: _getDecorationItem(), - alignment: Alignment.center, - child: Stack( - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () => widget.emailActionClick?.call( - EmailActionType.selection, - widget.presentationEmail - ), - child: Padding( - padding: const EdgeInsetsDirectional.only(top: 8, end: 12), - child: ValueListenableBuilder( - valueListenable: _hoverNotifier, - builder: (context, value, child) { - return _buildAvatarIcon( - context: context, - isHovered: value - ); - } - ) - ) - ), - ), - Expanded(child: Column(children: [ - Row(children: [ - if (!widget.presentationEmail.hasRead) - Padding( - padding: const EdgeInsetsDirectional.only(end: 5), - child: buildIconUnreadStatus(), - ), - Expanded( - child: buildInformationSender( - context, - widget.presentationEmail, - widget.mailboxContain, - widget.isSearchEmailRunning, - widget.searchQuery - ) - ), - ValueListenableBuilder( - valueListenable: _hoverNotifier, - builder: (context, value, child) { - if (value) { - return const SizedBox(width: 120); - } else { - return _buildDateTimeForMobileTabletScreen(context); - } - } - ), - ]), - const SizedBox(height: 2), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (widget.presentationEmail.hasCalendarEvent) - buildCalendarEventIcon( - context: context, - presentationEmail: widget.presentationEmail - ), - if (widget.presentationEmail.isMarkAsImportant && widget.isSenderImportantFlagEnabled) - buildMarkAsImportantIcon(context), - Expanded( - child: buildEmailTitle( - context, - widget.presentationEmail, - widget.isSearchEmailRunning, - widget.searchQuery - ) - ), - buildMailboxContain( - context, - widget.isSearchEmailRunning, - widget.presentationEmail - ), - if (widget.presentationEmail.hasStarred) - Padding( - padding: const EdgeInsetsDirectional.only(start: 8), - child: buildIconStar(), - ) - ], - ), - const SizedBox(height: 2), - Row(children: [ - Expanded( - child: buildEmailPartialContent( - context, - widget.presentationEmail, - widget.isSearchEmailRunning, - widget.searchQuery - ) - ), - ]), - ])) - ] - ), - ValueListenableBuilder( - valueListenable: _hoverNotifier, - builder: (context, value, child) { - if (value) { - return Positioned( - top: 0, - right: 0, - child: Transform( - transform: Matrix4.translationValues( - 0.0, - widget.selectAllMode == SelectMode.INACTIVE ? -5.0 : 0.0, - 0.0 - ), - child: _buildListActionButtonWhenHover(context) - ) - ); - } else { - return const SizedBox.shrink(); - } - } - ), - ], - ), - ), - ), + tablet: WebTabletBodyEmailItemWidget( + presentationEmail: widget.presentationEmail, + selectAllMode: widget.selectAllMode, + canDeletePermanently: canDeletePermanently, + isSearchEmailRunning: widget.isSearchEmailRunning, + isShowingEmailContent: widget.isShowingEmailContent, + isDrag: widget.isDrag, + isSenderImportantFlagEnabled: widget.isSenderImportantFlagEnabled, + padding: widget.padding, + searchQuery: widget.searchQuery, + mailboxContain: widget.mailboxContain, + emailActionClick: widget.emailActionClick, + onMoreActionClick: widget.onMoreActionClick, ), desktop: Padding( padding: const EdgeInsetsDirectional.only( @@ -440,12 +317,16 @@ class _EmailTileBuilderState extends State with BaseEmailItem const SizedBox(width: 16), ValueListenableBuilder( valueListenable: _hoverNotifier, - builder: (context, value, child) { - if (value) { - return _buildListActionButtonWhenHover(context); - } else { - return _buildDateTimeForDesktopScreen(context); - } + builder: (_, value, __) { + return DesktopListEmailActionHoverWidget( + presentationEmail: widget.presentationEmail, + isHovered: value, + canDeletePermanently: canDeletePermanently, + isSearchEmailRunning: widget.isSearchEmailRunning, + mailboxContain: widget.mailboxContain, + emailActionClick: widget.emailActionClick, + onMoreActionClick: widget.onMoreActionClick, + ); } ), ]), @@ -481,121 +362,10 @@ class _EmailTileBuilderState extends State with BaseEmailItem } } - double _getIconSize(BuildContext context) { - return responsiveUtils.isDesktop(context) ? 18 : 16; - } - - EdgeInsets _getPaddingIcon(BuildContext context) { - return const EdgeInsets.all(5); - } - - Widget _buildListActionButtonWhenHover(BuildContext context) { - return Row(children: [ - buildSVGIconButton( - icon: imagePaths.icOpenInNewTab, - iconColor: ItemEmailTileStyles.actionIconColor, - iconSize: _getIconSize(context), - padding: _getPaddingIcon(context), - tooltip: AppLocalizations.of(context).openInNewTab, - onTap: () => widget.emailActionClick?.call( - EmailActionType.openInNewTab, - widget.presentationEmail - ) - ), - if(!widget.presentationEmail.isDraft) - buildSVGIconButton( - icon: widget.presentationEmail.hasRead ? imagePaths.icUnread: imagePaths.icRead, - iconColor: ItemEmailTileStyles.actionIconColor, - iconSize: _getIconSize(context), - padding: _getPaddingIcon(context), - tooltip: widget.presentationEmail.hasRead - ? AppLocalizations.of(context).mark_as_unread - : AppLocalizations.of(context).mark_as_read, - onTap: () => widget.emailActionClick?.call( - widget.presentationEmail.hasRead ? EmailActionType.markAsUnread : EmailActionType.markAsRead, - widget.presentationEmail - ) - ), - if (widget.mailboxContain != null && widget.mailboxContain?.isDrafts == false) - ... [ - buildSVGIconButton( - icon: imagePaths.icMove, - iconColor: ItemEmailTileStyles.actionIconColor, - iconSize: _getIconSize(context), - padding: _getPaddingIcon(context), - tooltip: AppLocalizations.of(context).move, - onTap: () => widget.emailActionClick?.call( - EmailActionType.moveToMailbox, - widget.presentationEmail - ) - ), - ], - buildSVGIconButton( - icon: imagePaths.icDeleteComposer, - iconColor: ItemEmailTileStyles.actionIconColor, - iconSize: responsiveUtils.isDesktop(context) ? 16 : 14, - padding: _getPaddingIcon(context), - tooltip: canDeletePermanently - ? AppLocalizations.of(context).delete_permanently - : AppLocalizations.of(context).move_to_trash, - onTap: () => widget.emailActionClick?.call( - canDeletePermanently ? EmailActionType.deletePermanently : EmailActionType.moveToTrash, - widget.presentationEmail - ) - ), - buildSVGIconButton( - icon: imagePaths.icMore, - iconColor: ItemEmailTileStyles.actionIconColor, - iconSize: _getIconSize(context), - padding: _getPaddingIcon(context), - tooltip: AppLocalizations.of(context).more, - onTapDown: (tapDetails) { - if (responsiveUtils.isScreenWithShortestSide(context)) { - widget.onMoreActionClick?.call(widget.presentationEmail, null); - } else { - widget.onMoreActionClick?.call(widget.presentationEmail, tapDetails.getPosition(context)); - } - } - ), - if (responsiveUtils.isDesktop(context)) const SizedBox(width: 16), - ]); - } - bool get canDeletePermanently { return widget.mailboxContain?.isTrash == true || widget.mailboxContain?.isDrafts == true || widget.mailboxContain?.isSpam == true; } - Widget _buildDateTimeForDesktopScreen(BuildContext context) { - return Row(children: [ - buildMailboxContain( - context, - widget.isSearchEmailRunning, - widget.presentationEmail - ), - if (widget.presentationEmail.hasAttachment == true) - Padding( - padding: const EdgeInsetsDirectional.only(start: 8), - child: buildIconAttachment()), - Padding( - padding: const EdgeInsetsDirectional.only(end: 20, start: 8), - child: buildDateTime(context, widget.presentationEmail)) - ]); - } - - Widget _buildDateTimeForMobileTabletScreen(BuildContext context) { - return Row(children: [ - buildIconAnsweredOrForwarded(width: 16, height: 16, presentationEmail: widget.presentationEmail), - if (widget.presentationEmail.hasAttachment == true) - Padding( - padding: const EdgeInsetsDirectional.only(start: 8), - child: buildIconAttachment()), - Padding( - padding: const EdgeInsetsDirectional.only(end: 4, start: 8), - child: buildDateTime(context, widget.presentationEmail)), - buildIconChevron() - ]); - } - Widget _buildSubjectAndContent() { return LayoutBuilder(builder: (context, constraints) { return Row(children: [ diff --git a/lib/features/thread/presentation/widgets/web_tablet_body_email_item_widget.dart b/lib/features/thread/presentation/widgets/web_tablet_body_email_item_widget.dart new file mode 100644 index 000000000..b1b9024a7 --- /dev/null +++ b/lib/features/thread/presentation/widgets/web_tablet_body_email_item_widget.dart @@ -0,0 +1,388 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:model/email/email_action_type.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:model/extensions/presentation_mailbox_extension.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:model/mailbox/select_mode.dart'; +import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; +import 'package:tmail_ui_user/features/thread/presentation/mixin/base_email_item_tile.dart'; +import 'package:tmail_ui_user/features/thread/presentation/styles/item_email_tile_styles.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class WebTabletBodyEmailItemWidget extends StatefulWidget { + final PresentationEmail presentationEmail; + final SelectMode selectAllMode; + final bool canDeletePermanently; + final bool isSearchEmailRunning; + final bool isShowingEmailContent; + final bool isShowDateTimeView; + final bool isDrag; + final bool isSenderImportantFlagEnabled; + final EdgeInsetsGeometry? padding; + final SearchQuery? searchQuery; + final PresentationMailbox? mailboxContain; + final OnPressEmailActionClick? emailActionClick; + final OnMoreActionClick? onMoreActionClick; + + const WebTabletBodyEmailItemWidget({ + super.key, + required this.presentationEmail, + required this.selectAllMode, + required this.canDeletePermanently, + required this.isSearchEmailRunning, + required this.isShowingEmailContent, + required this.isDrag, + required this.isSenderImportantFlagEnabled, + required this.padding, + required this.searchQuery, + required this.mailboxContain, + required this.emailActionClick, + required this.onMoreActionClick, + this.isShowDateTimeView = false, + }); + + @override + State createState() => + _WebTabletBodyEmailItemWidgetState(); +} + +class _WebTabletBodyEmailItemWidgetState + extends State with BaseEmailItemTile { + final _imagePaths = Get.find(); + final _responsiveUtils = Get.find(); + + bool _isHover = false; + bool _popupMenuVisible = false; + + @override + Widget build(BuildContext context) { + return Material( + type: MaterialType.transparency, + child: InkWell( + onTap: () => widget.emailActionClick?.call( + EmailActionType.preview, + widget.presentationEmail, + ), + hoverColor: Theme.of(context).colorScheme.outline.withOpacity(0.08), + onHover: (value) { + setState(() { + _isHover = value; + }); + }, + child: Container( + padding: widget.padding ?? _getPaddingItem(context), + decoration: _getDecorationItem(), + alignment: Alignment.center, + child: Stack( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () => widget.emailActionClick?.call( + EmailActionType.selection, + widget.presentationEmail), + child: Padding( + padding: const EdgeInsetsDirectional.only( + top: 8, + end: 12, + ), + child: _buildAvatarIcon( + context: context, + isHovered: _isHover, + ))), + ), + Expanded( + child: Column( + children: [ + Row(children: [ + if (!widget.presentationEmail.hasRead) + Padding( + padding: const EdgeInsetsDirectional.only(end: 5), + child: buildIconUnreadStatus(), + ), + Expanded( + child: buildInformationSender( + context, + widget.presentationEmail, + widget.mailboxContain, + widget.isSearchEmailRunning, + widget.searchQuery, + ), + ), + if (_shouldShowPopupMenu) + const SizedBox(width: 120) + else + _buildDateTimeForMobileTabletScreen(context), + ]), + const SizedBox(height: 2), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.presentationEmail.hasCalendarEvent) + buildCalendarEventIcon( + context: context, + presentationEmail: widget.presentationEmail, + ), + if (widget.presentationEmail.isMarkAsImportant && + widget.isSenderImportantFlagEnabled) + buildMarkAsImportantIcon(context), + Expanded( + child: buildEmailTitle( + context, + widget.presentationEmail, + widget.isSearchEmailRunning, + widget.searchQuery, + ), + ), + buildMailboxContain( + context, + widget.isSearchEmailRunning, + widget.presentationEmail, + ), + if (widget.presentationEmail.hasStarred) + Padding( + padding: const EdgeInsetsDirectional.only( + start: 8, + ), + child: buildIconStar(), + ) + ], + ), + const SizedBox(height: 2), + Row( + children: [ + Expanded( + child: buildEmailPartialContent( + context, + widget.presentationEmail, + widget.isSearchEmailRunning, + widget.searchQuery, + ), + ), + ], + ), + ], + ), + ) + ], + ), + Positioned( + top: 0, + right: 0, + child: Transform( + transform: Matrix4.translationValues( + 0.0, + widget.selectAllMode == SelectMode.INACTIVE ? -5.0 : 0.0, + 0.0, + ), + child: Row( + children: [ + if (_isHover) ...[ + TMailButtonWidget.fromIcon( + icon: _imagePaths.icOpenInNewTab, + iconColor: ItemEmailTileStyles.actionIconColor, + iconSize: _getIconSize(), + padding: _getPaddingIcon(context), + backgroundColor: Colors.transparent, + tooltipMessage: + AppLocalizations.of(context).openInNewTab, + onTapActionCallback: () => + widget.emailActionClick?.call( + EmailActionType.openInNewTab, + widget.presentationEmail, + ), + ), + if (!widget.presentationEmail.isDraft) + TMailButtonWidget.fromIcon( + icon: widget.presentationEmail.hasRead + ? _imagePaths.icUnread + : _imagePaths.icRead, + iconColor: ItemEmailTileStyles.actionIconColor, + iconSize: _getIconSize(), + padding: _getPaddingIcon(context), + backgroundColor: Colors.transparent, + tooltipMessage: widget.presentationEmail.hasRead + ? AppLocalizations.of(context).mark_as_unread + : AppLocalizations.of(context).mark_as_read, + onTapActionCallback: () => + widget.emailActionClick?.call( + widget.presentationEmail.hasRead + ? EmailActionType.markAsUnread + : EmailActionType.markAsRead, + widget.presentationEmail, + ), + ), + if (widget.mailboxContain != null && + widget.mailboxContain?.isDrafts == false) ...[ + TMailButtonWidget.fromIcon( + icon: _imagePaths.icMove, + iconColor: ItemEmailTileStyles.actionIconColor, + iconSize: _getIconSize(), + padding: _getPaddingIcon(context), + backgroundColor: Colors.transparent, + tooltipMessage: AppLocalizations.of(context).move, + onTapActionCallback: () => + widget.emailActionClick?.call( + EmailActionType.moveToMailbox, + widget.presentationEmail, + ), + ), + ], + TMailButtonWidget.fromIcon( + icon: _imagePaths.icDeleteComposer, + iconColor: ItemEmailTileStyles.actionIconColor, + iconSize: 14, + padding: _getPaddingIcon(context), + backgroundColor: Colors.transparent, + tooltipMessage: widget.canDeletePermanently + ? AppLocalizations.of(context).delete_permanently + : AppLocalizations.of(context).move_to_trash, + onTapActionCallback: () => + widget.emailActionClick?.call( + widget.canDeletePermanently + ? EmailActionType.deletePermanently + : EmailActionType.moveToTrash, + widget.presentationEmail, + ), + ), + ], + if (_shouldShowPopupMenu) + TMailButtonWidget.fromIcon( + icon: _imagePaths.icMoreVertical, + iconColor: ItemEmailTileStyles.actionIconColor, + iconSize: _getIconSize(), + padding: _getPaddingIcon(context), + backgroundColor: _popupMenuVisible + ? Theme.of(context) + .colorScheme + .outline + .withOpacity(0.08) + : Colors.transparent, + tooltipMessage: AppLocalizations.of(context).more, + onTapActionAtPositionCallback: (position) { + if (_responsiveUtils + .isScreenWithShortestSide(context)) { + widget.onMoreActionClick + ?.call(widget.presentationEmail, null); + } else { + _onPopupMenuVisibleChange(true); + + widget.onMoreActionClick + ?.call(widget.presentationEmail, position) + .whenComplete( + () => _onPopupMenuVisibleChange(false)); + } + }, + ) + ], + ), + ), + ) + ], + ), + ), + ), + ); + } + + EdgeInsetsGeometry _getPaddingItem(BuildContext context) { + if (responsiveUtils.isDesktop(context)) { + return const EdgeInsets.symmetric(vertical: 4); + } else if (responsiveUtils.isTablet(context)) { + return const EdgeInsetsDirectional.symmetric(vertical: 8, horizontal: 24); + } else { + return const EdgeInsetsDirectional.symmetric(vertical: 8, horizontal: 12); + } + } + + BoxDecoration? _getDecorationItem() { + if (((widget.selectAllMode == SelectMode.ACTIVE && + widget.presentationEmail.selectMode == SelectMode.ACTIVE) || + widget.isDrag) && + responsiveUtils.isDesktop(context)) { + return const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8)), + color: AppColor.blue100, + ); + } else if (widget.isShowingEmailContent && + responsiveUtils.isTabletLarge(context)) { + return const BoxDecoration(color: AppColor.blue100); + } else { + return null; + } + } + + Widget _buildAvatarIcon({ + required BuildContext context, + bool isHovered = false, + double? iconSize, + TextStyle? textStyle, + }) { + if (widget.presentationEmail.isSelected || + (responsiveUtils.isScreenWithShortestSide(context) && + widget.selectAllMode == SelectMode.ACTIVE) || + isHovered) { + return buildIconAvatarSelection( + context, + widget.presentationEmail, + iconSize: iconSize ?? 48, + textStyle: textStyle, + ); + } else { + return buildIconAvatarText( + widget.presentationEmail, + iconSize: iconSize ?? 48, + textStyle: textStyle, + ); + } + } + + Widget _buildDateTimeForMobileTabletScreen(BuildContext context) { + return Row( + children: [ + buildIconAnsweredOrForwarded( + width: 16, + height: 16, + presentationEmail: widget.presentationEmail, + ), + if (widget.presentationEmail.hasAttachment == true) + Padding( + padding: const EdgeInsetsDirectional.only(start: 8), + child: buildIconAttachment(), + ), + Padding( + padding: const EdgeInsetsDirectional.only(end: 4, start: 8), + child: buildDateTime( + context, + widget.presentationEmail, + ), + ), + buildIconChevron(), + ], + ); + } + + double _getIconSize() => 16; + + EdgeInsets _getPaddingIcon(BuildContext context) { + return const EdgeInsets.all(5); + } + + bool get _shouldShowPopupMenu => _isHover || _popupMenuVisible; + + void _onPopupMenuVisibleChange(bool visible) { + if (_popupMenuVisible != visible && mounted) { + setState(() { + _popupMenuVisible = visible; + }); + } + } +}