From caa981e5098bb278b223cb712cb8744bbf4781d4 Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 20 Nov 2023 14:53:47 +0700 Subject: [PATCH] TF-1973 Fix selection mode still displayed when dragging or moving email Signed-off-by: dab246 (cherry picked from commit b02fc22a1c7761bc08b6668d97c36487f05f9874) --- .../email/presentation/search_email_view.dart | 46 +- .../mixin/base_email_item_tile.dart | 5 +- .../thread/presentation/thread_view.dart | 38 +- .../widgets/email_tile_builder.dart | 117 ++- .../widgets/email_tile_web_builder.dart | 893 ++++++++++-------- model/lib/email/presentation_email.dart | 2 + 6 files changed, 585 insertions(+), 516 deletions(-) diff --git a/lib/features/search/email/presentation/search_email_view.dart b/lib/features/search/email/presentation/search_email_view.dart index cd655dcb5..d35c4eb09 100644 --- a/lib/features/search/email/presentation/search_email_view.dart +++ b/lib/features/search/email/presentation/search_email_view.dart @@ -414,22 +414,38 @@ class SearchEmailView extends GetWidget itemCount: listPresentationEmail.length, itemBuilder: (context, index) { final currentPresentationEmail = listPresentationEmail[index]; - return Obx(() => (EmailTileBuilder( + return Obx(() => EmailTileBuilder( + presentationEmail: currentPresentationEmail, + selectAllMode: controller.selectionMode.value, + searchQuery: controller.searchQuery, + isShowingEmailContent: controller.mailboxDashBoardController.selectedEmail.value?.id == currentPresentationEmail.id, + isSearchEmailRunning: true, + padding: SearchEmailUtils.getPaddingSearchResultList(context, controller.responsiveUtils), + paddingDivider: SearchEmailUtils.getPaddingDividerSearchResultList(context, controller.responsiveUtils), + mailboxContain: currentPresentationEmail.mailboxContain, + emailActionClick: (action, email) { + controller.pressEmailAction( context, - currentPresentationEmail, - controller.selectionMode.value, - controller.searchQuery, - controller.mailboxDashBoardController.selectedEmail.value?.id == currentPresentationEmail.id, - isSearchEmailRunning: true, - padding: SearchEmailUtils.getPaddingSearchResultList(context, controller.responsiveUtils), - paddingDivider: SearchEmailUtils.getPaddingDividerSearchResultList(context, controller.responsiveUtils), - mailboxContain: currentPresentationEmail.mailboxContain) - ..addOnPressEmailActionClick((action, email) => - controller.pressEmailAction(context, action, email, mailboxContain: currentPresentationEmail.mailboxContain)) - ..addOnMoreActionClick((email, position) => controller.responsiveUtils.isScreenWithShortestSide(context) - ? controller.openContextMenuAction(context, _contextMenuActionTile(context, email)) - : controller.openPopupMenuAction(context, position, _popupMenuActionTile(context, email))) - ).build()); + action, + email, + mailboxContain: currentPresentationEmail.mailboxContain + ); + }, + onMoreActionClick: (email, position) { + if (controller.responsiveUtils.isScreenWithShortestSide(context)) { + controller.openContextMenuAction( + context, + _contextMenuActionTile(context, email) + ); + } else { + controller.openPopupMenuAction( + context, + position, + _popupMenuActionTile(context, email) + ); + } + }, + )); }) ); } 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 33021a622..b15477b18 100644 --- a/lib/features/thread/presentation/mixin/base_email_item_tile.dart +++ b/lib/features/thread/presentation/mixin/base_email_item_tile.dart @@ -16,7 +16,6 @@ import 'package:model/email/presentation_email.dart'; import 'package:model/extensions/presentation_email_extension.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/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/presentation/styles/item_email_tile_styles.dart'; @@ -263,7 +262,7 @@ mixin BaseEmailItemTile { ? const EdgeInsetsDirectional.symmetric(horizontal: 4) : const EdgeInsetsDirectional.all(12), child: SvgPicture.asset( - email.selectMode == SelectMode.ACTIVE + email.isSelected ? imagePaths.icSelected : imagePaths.icUnSelected, fit: BoxFit.fill), @@ -273,7 +272,7 @@ mixin BaseEmailItemTile { alignment: Alignment.center, color: Colors.transparent, child: SvgPicture.asset( - email.selectMode == SelectMode.ACTIVE + email.isSelected ? imagePaths.icSelected : imagePaths.icUnSelected, width: 24, height: 24)); diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 264b2ef27..52ed54f44 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -1,5 +1,4 @@ import 'package:core/core.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -352,8 +351,8 @@ class ThreadView extends GetWidget }, child: Focus( focusNode: controller.focusNodeKeyBoard, - autofocus: kIsWeb, - onKey: kIsWeb ? controller.handleKeyEvent : null, + autofocus: PlatformInfo.isWeb, + onKey: PlatformInfo.isWeb ? controller.handleKeyEvent : null, child: ListView.builder( controller: controller.listEmailController, physics: const AlwaysScrollableScrollPhysics(), @@ -400,16 +399,15 @@ class ThreadView extends GetWidget final isShowingEmailContent = controller.mailboxDashBoardController.selectedEmail.value?.id == presentationEmail.id; final selectModeAll = controller.mailboxDashBoardController.currentSelectMode.value; - return (EmailTileBuilder( - context, - presentationEmail, - selectModeAll, - controller.searchQuery, - isShowingEmailContent, + return EmailTileBuilder( + presentationEmail: presentationEmail, + selectAllMode: selectModeAll, + isShowingEmailContent: isShowingEmailContent, + searchQuery: controller.searchQuery, mailboxContain: presentationEmail.mailboxContain, isSearchEmailRunning: controller.searchController.isSearchEmailRunning, isDrag: true - )).build(); + ); } Widget _buildEmailItemNotDraggable(BuildContext context, PresentationEmail presentationEmail) { @@ -457,18 +455,16 @@ class ThreadView extends GetWidget ), ), confirmDismiss: (direction) => controller.swipeEmailAction(context, presentationEmail, direction), - child: (EmailTileBuilder( - context, - presentationEmail, - selectModeAll, - controller.searchQuery, - isShowingEmailContent, + child: EmailTileBuilder( + presentationEmail: presentationEmail, + selectAllMode: selectModeAll, + isShowingEmailContent: isShowingEmailContent, + searchQuery: controller.searchQuery, mailboxContain: presentationEmail.mailboxContain, - isSearchEmailRunning: controller.searchController.isSearchEmailRunning - ) - ..addOnPressEmailActionClick((action, email) => _handleEmailActionClicked(context, email, action)) - ..addOnMoreActionClick((email, position) => _handleEmailContextMenuAction(context, email, position)) - ).build(), + isSearchEmailRunning: controller.searchController.isSearchEmailRunning, + emailActionClick: (action, email) => _handleEmailActionClicked(context, email, action), + onMoreActionClick: (email, position) => _handleEmailContextMenuAction(context, email, position), + ), ); } diff --git a/lib/features/thread/presentation/widgets/email_tile_builder.dart b/lib/features/thread/presentation/widgets/email_tile_builder.dart index d507267d6..4043223eb 100644 --- a/lib/features/thread/presentation/widgets/email_tile_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_builder.dart @@ -8,72 +8,69 @@ 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'; -class EmailTileBuilder with BaseEmailItemTile { +class EmailTileBuilder extends StatelessWidget with BaseEmailItemTile { - final PresentationEmail _presentationEmail; - final BuildContext _context; - final SelectMode _selectModeAll; + final PresentationEmail presentationEmail; + final SelectMode selectAllMode; final PresentationMailbox? mailboxContain; - final SearchQuery? _searchQuery; + final SearchQuery? searchQuery; final bool isSearchEmailRunning; final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? paddingDivider; final bool isDrag; - final bool _isShowingEmailContent; + final bool isShowingEmailContent; + final OnPressEmailActionClick? emailActionClick; + final OnMoreActionClick? onMoreActionClick; - OnPressEmailActionClick? _emailActionClick; + EmailTileBuilder({ + super.key, + required this.presentationEmail, + required this.selectAllMode, + required this.isShowingEmailContent, + this.searchQuery, + this.isSearchEmailRunning = false, + this.mailboxContain, + this.padding, + this.paddingDivider, + this.isDrag = false, + this.emailActionClick, + this.onMoreActionClick, + }); - EmailTileBuilder( - this._context, - this._presentationEmail, - this._selectModeAll, - this._searchQuery, - this._isShowingEmailContent, - { - this.isSearchEmailRunning = false, - this.mailboxContain, - this.padding, - this.paddingDivider, - this.isDrag = false, - } - ); - - void addOnPressEmailActionClick(OnPressEmailActionClick actionClick) { - _emailActionClick = actionClick; - } - - void addOnMoreActionClick(OnMoreActionClick onMoreActionClick) {} - - Widget build() { + @override + Widget build(BuildContext context) { return Material( color: Colors.transparent, child: Column( children: [ ListTile( - tileColor: _isShowingEmailContent ? AppColor.colorItemEmailSelectedDesktop : null, + tileColor: isShowingEmailContent ? AppColor.colorItemEmailSelectedDesktop : null, contentPadding: padding ?? const EdgeInsetsDirectional.symmetric(horizontal: 16, vertical: 5), - onTap: () => _emailActionClick?.call( + onTap: () => emailActionClick?.call( EmailActionType.preview, - _presentationEmail), - onLongPress: () => _emailActionClick?.call( + presentationEmail), + onLongPress: () => emailActionClick?.call( EmailActionType.selection, - _presentationEmail), + presentationEmail), leading: GestureDetector( - onTap: () => _emailActionClick?.call( - _selectModeAll == SelectMode.ACTIVE + onTap: () => emailActionClick?.call( + selectAllMode == SelectMode.ACTIVE ? EmailActionType.selection : EmailActionType.preview, - _presentationEmail), + presentationEmail), child: Container( width: 56, height: 56, color: Colors.transparent, alignment: Alignment.center, - child: _buildAvatarIcon()), + child: selectAllMode == SelectMode.ACTIVE + ? buildIconAvatarSelection(context, presentationEmail) + : buildIconAvatarText(presentationEmail) + ) ), title: Row( children: [ - if (!_presentationEmail.hasRead) + if (!presentationEmail.hasRead) Padding( padding: const EdgeInsetsDirectional.only(end: 5), child: SvgPicture.asset( @@ -82,19 +79,19 @@ class EmailTileBuilder with BaseEmailItemTile { height: 9, fit: BoxFit.fill)), Expanded(child: buildInformationSender( - _context, - _presentationEmail, + context, + presentationEmail, mailboxContain, isSearchEmailRunning, - _searchQuery)), - buildIconAnsweredOrForwarded(width: 16, height: 16, presentationEmail: _presentationEmail), - if (_presentationEmail.hasAttachment == true) + searchQuery)), + buildIconAnsweredOrForwarded(width: 16, height: 16, presentationEmail: presentationEmail), + if (presentationEmail.hasAttachment == true) Padding( padding: const EdgeInsetsDirectional.only(start: 8), child: buildIconAttachment()), Padding( padding: const EdgeInsetsDirectional.only(end: 4, start: 8), - child: buildDateTime(_context, _presentationEmail)), + child: buildDateTime(context, presentationEmail)), buildIconChevron(), ], ), @@ -107,18 +104,18 @@ class EmailTileBuilder with BaseEmailItemTile { child: Row( mainAxisSize: MainAxisSize.min, children: [ - if (_presentationEmail.hasCalendarEvent) - buildCalendarEventIcon(context: _context, presentationEmail: _presentationEmail), + if (presentationEmail.hasCalendarEvent) + buildCalendarEventIcon(context: context, presentationEmail: presentationEmail), Expanded(child: buildEmailTitle( - _context, - _presentationEmail, + context, + presentationEmail, isSearchEmailRunning, - _searchQuery)), + searchQuery)), buildMailboxContain( - _context, + context, isSearchEmailRunning, - _presentationEmail), - if (_presentationEmail.hasStarred) + presentationEmail), + if (presentationEmail.hasStarred) Padding( padding: const EdgeInsetsDirectional.only(start: 8), child: buildIconStar(), @@ -129,10 +126,10 @@ class EmailTileBuilder with BaseEmailItemTile { padding: const EdgeInsetsDirectional.only(top: 6), child: Row(children: [ Expanded(child: buildEmailPartialContent( - _context, - _presentationEmail, + context, + presentationEmail, isSearchEmailRunning, - _searchQuery)), + searchQuery)), ]) ), ], @@ -145,12 +142,4 @@ class EmailTileBuilder with BaseEmailItemTile { ), ); } - - Widget _buildAvatarIcon() { - if (_selectModeAll == SelectMode.ACTIVE) { - return buildIconAvatarSelection(_context, _presentationEmail); - } else { - return buildIconAvatarText(_presentationEmail); - } - } } \ No newline at end of file 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 7d37f1d59..ffea0e1b0 100644 --- a/lib/features/thread/presentation/widgets/email_tile_web_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_web_builder.dart @@ -14,89 +14,434 @@ 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/main/localizations/app_localizations.dart'; -class EmailTileBuilder with BaseEmailItemTile { +class EmailTileBuilder extends StatefulWidget { - final PresentationEmail _presentationEmail; - final BuildContext _context; - final SelectMode _selectModeAll; + final PresentationEmail presentationEmail; + final SelectMode selectAllMode; final PresentationMailbox? mailboxContain; - final SearchQuery? _searchQuery; + final SearchQuery? searchQuery; final bool isSearchEmailRunning; final EdgeInsets? padding; final EdgeInsets? paddingDivider; final bool isDrag; - final bool _isShowingEmailContent; + final bool isShowingEmailContent; + final OnPressEmailActionClick? emailActionClick; + final OnMoreActionClick? onMoreActionClick; - OnPressEmailActionClick? _emailActionClick; - OnMoreActionClick? _onMoreActionClick; + const EmailTileBuilder({ + super.key, + required this.presentationEmail, + required this.selectAllMode, + required this.isShowingEmailContent, + this.searchQuery, + this.isSearchEmailRunning = false, + this.mailboxContain, + this.padding, + this.paddingDivider, + this.isDrag = false, + this.emailActionClick, + this.onMoreActionClick, + }); - bool isHoverItem = false; - bool isHoverIcon = false; + @override + State createState() => _EmailTileBuilderState(); +} - EmailTileBuilder( - this._context, - this._presentationEmail, - this._selectModeAll, - this._searchQuery, - this._isShowingEmailContent, - { - this.isSearchEmailRunning = false, - this.mailboxContain, - this.padding, - this.paddingDivider, - this.isDrag = false, - } - ); +class _EmailTileBuilderState extends State with BaseEmailItemTile { + final ValueNotifier _hoverNotifier = ValueNotifier(false); - void addOnPressEmailActionClick(OnPressEmailActionClick actionClick) { - _emailActionClick = actionClick; - } - - void addOnMoreActionClick(OnMoreActionClick onMoreActionClick) { - _onMoreActionClick = onMoreActionClick; - } - - void _onHoverIconChanged(bool isHover, StateSetter setState) { - setState(() => isHoverIcon = isHover); - } - - void _onHoverItemChanged(bool isHover, StateSetter setState) { - setState(() => isHoverItem = isHover); - } - - Widget build() { + @override + Widget build(BuildContext context) { return Container( - color: Colors.transparent, - padding: padding, + margin: _getMarginItem(context), + padding: _getPaddingItem(context), + decoration: _getDecorationItem(), + alignment: Alignment.center, child: ResponsiveWidget( responsiveUtils: responsiveUtils, - mobile: _wrapContainerForTile(_buildListTile()), - tablet: _wrapContainerForTile(_buildListTileTablet()), - desktop: _wrapContainerForTile(_buildListTileForDesktop()), + mobile: Stack( + alignment: Alignment.bottomCenter, + children: [ + InkWell( + onTap: () => widget.emailActionClick?.call( + EmailActionType.preview, + widget.presentationEmail + ), + onLongPress: () => widget.emailActionClick?.call( + EmailActionType.selection, + widget.presentationEmail + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + onTap: () => widget.emailActionClick?.call( + widget.selectAllMode == SelectMode.ACTIVE + ? EmailActionType.selection + : EmailActionType.preview, + widget.presentationEmail + ), + child: Padding( + padding: const EdgeInsetsDirectional.only(top: 8, end: 12), + child: _buildAvatarIcon(context: context), + ), + ), + Expanded( + child: Column(children: [ + Row(children: [ + if (!widget.presentationEmail.hasRead) + Padding( + padding: const EdgeInsetsDirectional.only(end: 5), + child: SvgPicture.asset( + imagePaths.icUnreadStatus, + width: 9, + height: 9, + fit: BoxFit.fill + ) + ), + Expanded( + child: buildInformationSender( + context, + widget.presentationEmail, + widget.mailboxContain, + widget.isSearchEmailRunning, + widget.searchQuery + ) + ), + buildIconAnsweredOrForwarded( + width: 16, + height: 16, + presentationEmail: widget.presentationEmail + ), + if (widget.presentationEmail.hasAttachment == true) + Padding( + padding: const EdgeInsetsDirectional.only(start: 8), + child: SvgPicture.asset( + imagePaths.icAttachment, + width: 16, + height: 16, + fit: BoxFit.fill + ) + ), + Padding( + padding: const EdgeInsetsDirectional.only( + end: 4, + start: 8 + ), + child: buildDateTime(context, widget.presentationEmail) + ), + buildIconChevron() + ]), + const SizedBox(height: 8), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.presentationEmail.hasCalendarEvent) + buildCalendarEventIcon( + context: context, + presentationEmail: widget.presentationEmail + ), + 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: 8), + Row(children: [ + Expanded( + child: buildEmailPartialContent( + context, + widget.presentationEmail, + widget.isSearchEmailRunning, + widget.searchQuery + ) + ), + ]), + ]), + ) + ] + ) + ), + if (widget.selectAllMode == SelectMode.INACTIVE) + Transform( + transform: Matrix4.translationValues(0.0, 10.0, 0.0), + child: const Divider( + color: AppColor.lineItemListColor, + height: 1 + ) + ), + ], + ), + tablet: InkWell( + onTap: () => widget.emailActionClick?.call( + EmailActionType.preview, + widget.presentationEmail + ), + onHover: (value) => _hoverNotifier.value = value, + child: Stack( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + 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: Stack( + alignment: Alignment.bottomCenter, + children: [ + Column(children: [ + Row(children: [ + if (!widget.presentationEmail.hasRead) + Padding( + padding: const EdgeInsetsDirectional.only(end: 5), + child: SvgPicture.asset( + imagePaths.icUnreadStatus, + width: 9, + height: 9, + fit: BoxFit.fill + ) + ), + 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: 8), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.presentationEmail.hasCalendarEvent) + buildCalendarEventIcon( + context: context, + presentationEmail: widget.presentationEmail + ), + 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: 8), + Row(children: [ + Expanded( + child: buildEmailPartialContent( + context, + widget.presentationEmail, + widget.isSearchEmailRunning, + widget.searchQuery + ) + ), + ]), + ]), + if (widget.selectAllMode == SelectMode.INACTIVE) + Transform( + transform: Matrix4.translationValues(0.0, 10.0, 0.0), + child: const Divider(color: AppColor.lineItemListColor, height: 1), + ), + ], + )) + ] + ), + 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(); + } + } + ), + ], + ), + ), + desktop: InkWell( + onTap: () => widget.emailActionClick?.call( + EmailActionType.preview, + widget.presentationEmail + ), + onHover: (value) => _hoverNotifier.value = value, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + Row(children: [ + Container( + padding: const EdgeInsetsDirectional.symmetric(horizontal: 16), + alignment: Alignment.center, + child: !widget.presentationEmail.hasRead + ? SvgPicture.asset( + imagePaths.icUnreadStatus, + width: 9, + height: 9, + fit: BoxFit.fill + ) + : const SizedBox(width: 9) + ), + buildIconWeb( + icon: SvgPicture.asset( + widget.presentationEmail.hasStarred + ? imagePaths.icStar + : imagePaths.icUnStar, + width: 16, + height: 16, + fit: BoxFit.fill + ), + tooltip: widget.presentationEmail.hasStarred + ? AppLocalizations.of(context).starred + : AppLocalizations.of(context).not_starred, + onTap: () => widget.emailActionClick?.call( + widget.presentationEmail.hasStarred + ? EmailActionType.unMarkAsStarred + : EmailActionType.markAsStarred, + widget.presentationEmail + ) + ), + buildIconWeb( + icon: buildIconAnsweredOrForwarded(presentationEmail: widget.presentationEmail), + tooltip: messageToolTipForAnsweredOrForwarded(context, widget.presentationEmail), + iconPadding: const EdgeInsetsDirectional.only(end: 12), + splashRadius: 1 + ), + InkWell( + onTap: () => widget.emailActionClick?.call( + EmailActionType.selection, + widget.presentationEmail + ), + child: ValueListenableBuilder( + valueListenable: _hoverNotifier, + builder: (context, value, child) { + return _buildAvatarIcon( + context: context, + isHovered: value, + iconSize: 32, + textStyle: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.white + ) + ); + } + ), + ), + const SizedBox(width: 10), + SizedBox( + width: 160, + child: buildInformationSender( + context, + widget.presentationEmail, + widget.mailboxContain, + widget.isSearchEmailRunning, + widget.searchQuery + ) + ), + const SizedBox(width: 24), + Expanded(child: _buildSubjectAndContent()), + const SizedBox(width: 16), + ValueListenableBuilder( + valueListenable: _hoverNotifier, + builder: (context, value, child) { + if (value) { + return _buildListActionButtonWhenHover(context); + } else { + return _buildDateTimeForDesktopScreen(context); + } + } + ), + ]), + if ( widget.selectAllMode == SelectMode.INACTIVE) + Transform( + transform: Matrix4.translationValues(0.0, 10, 0.0), + child: const Padding( + padding: EdgeInsetsDirectional.only(start: 120), + child: Divider(color: AppColor.lineItemListColor, height: 1) + ), + ) + ]), + ), ), ); } - Widget _wrapContainerForTile(Widget tile) { - return Container( - margin: _getMarginItem(), - padding: _getPaddingItem(), - decoration: _getDecorationItem(), - alignment: Alignment.center, - child: tile - ); - } - - EdgeInsetsGeometry _getMarginItem() { - if (responsiveUtils.isDesktop(_context)) { + EdgeInsetsGeometry _getMarginItem(BuildContext context) { + if (responsiveUtils.isDesktop(context)) { return const EdgeInsets.only(top: 3); } else { return const EdgeInsetsDirectional.only(top: 3, start: 16, end: 16); } } - EdgeInsetsGeometry _getPaddingItem() { - if (responsiveUtils.isDesktop(_context)) { + EdgeInsetsGeometry _getPaddingItem(BuildContext context) { + if (responsiveUtils.isDesktop(context)) { return const EdgeInsets.symmetric(vertical: 8); } else { return const EdgeInsetsDirectional.only(bottom: 8, end: 8, top: 8); @@ -104,7 +449,7 @@ class EmailTileBuilder with BaseEmailItemTile { } BoxDecoration _getDecorationItem() { - if ((_selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE) || isDrag || _isShowingEmailContent) { + if ((widget.selectAllMode == SelectMode.ACTIVE && widget.presentationEmail.selectMode == SelectMode.ACTIVE) || widget.isDrag || widget.isShowingEmailContent) { return BoxDecoration( borderRadius: BorderRadius.circular(14), color: AppColor.colorItemEmailSelectedDesktop); @@ -115,398 +460,119 @@ class EmailTileBuilder with BaseEmailItemTile { } } - Widget _buildListTile() { - return Stack(alignment: Alignment.bottomCenter, - children: [ - InkWell( - onTap: () => _emailActionClick?.call( - EmailActionType.preview, - _presentationEmail), - onLongPress: () => _emailActionClick?.call( - EmailActionType.selection, - _presentationEmail), - child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - InkWell( - onTap: () => _emailActionClick?.call( - _selectModeAll == SelectMode.ACTIVE - ? EmailActionType.selection - : EmailActionType.preview, - _presentationEmail), - child: Padding( - padding: const EdgeInsetsDirectional.only(top: 8, end: 12), - child: _buildAvatarIcon(), - ), - ), - Expanded( - child: Column(children: [ - Row(children: [ - if (!_presentationEmail.hasRead) - Padding( - padding: const EdgeInsetsDirectional.only(end: 5), - child: SvgPicture.asset( - imagePaths.icUnreadStatus, - width: 9, - height: 9, - fit: BoxFit.fill)), - Expanded(child: buildInformationSender( - _context, - _presentationEmail, - mailboxContain, - isSearchEmailRunning, - _searchQuery - )), - buildIconAnsweredOrForwarded(width: 16, height: 16, presentationEmail: _presentationEmail), - if (_presentationEmail.hasAttachment == true) - Padding( - padding: const EdgeInsetsDirectional.only(start: 8), - child: SvgPicture.asset( - imagePaths.icAttachment, - width: 16, - height: 16, - fit: BoxFit.fill)), - Padding( - padding: const EdgeInsetsDirectional.only(end: 4, start: 8), - child: buildDateTime(_context, _presentationEmail)), - buildIconChevron() - ]), - const SizedBox(height: 8), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (_presentationEmail.hasCalendarEvent) - buildCalendarEventIcon(context: _context, presentationEmail: _presentationEmail), - Expanded(child: buildEmailTitle( - _context, - _presentationEmail, - isSearchEmailRunning, - _searchQuery - )), - buildMailboxContain( - _context, - isSearchEmailRunning, - _presentationEmail - ), - if (_presentationEmail.hasStarred) - Padding( - padding: const EdgeInsetsDirectional.only(start: 8), - child: buildIconStar(), - ), - ], - ), - const SizedBox(height: 8), - Row(children: [ - Expanded(child: buildEmailPartialContent( - _context, - _presentationEmail, - isSearchEmailRunning, - _searchQuery - )), - ]), - ]), - ) - ]) - ), - if (_selectModeAll == SelectMode.INACTIVE) - Transform( - transform: Matrix4.translationValues(0.0, 10.0, 0.0), - child: const Divider(color: AppColor.lineItemListColor, height: 1)), - ], - ); + double _getIconSize(BuildContext context) { + return responsiveUtils.isDesktop(context) ? 18 : 16; } - Widget _buildListTileTablet() { - return StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return InkWell( - onTap: () => _emailActionClick?.call( - EmailActionType.preview, - _presentationEmail), - onHover: (value) => _onHoverItemChanged(value, setState), - child: Stack( - children: [ - Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - InkWell( - onTap: () => _emailActionClick?.call(EmailActionType.selection, _presentationEmail), - onHover: (value) => _onHoverIconChanged(value, setState), - child: Padding( - padding: const EdgeInsetsDirectional.only(top: 8, end: 12), - child: _buildAvatarIcon())), - Expanded(child: Stack(alignment: Alignment.bottomCenter, - children: [ - Column(children: [ - Row(children: [ - if (!_presentationEmail.hasRead) - Padding( - padding: const EdgeInsetsDirectional.only(end: 5), - child: SvgPicture.asset( - imagePaths.icUnreadStatus, - width: 9, - height: 9, - fit: BoxFit.fill)), - Expanded(child: buildInformationSender( - _context, - _presentationEmail, - mailboxContain, - isSearchEmailRunning, - _searchQuery - )), - if (isHoverItem) - const SizedBox(width: 120) - else - _buildDateTimeForMobileTabletScreen() - ]), - const SizedBox(height: 8), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (_presentationEmail.hasCalendarEvent) - buildCalendarEventIcon(context: _context, presentationEmail: _presentationEmail), - Expanded(child: buildEmailTitle( - _context, - _presentationEmail, - isSearchEmailRunning, - _searchQuery - )), - buildMailboxContain( - _context, - isSearchEmailRunning, - _presentationEmail - ), - if (_presentationEmail.hasStarred) - Padding( - padding: const EdgeInsetsDirectional.only(start: 8), - child: buildIconStar(), - ) - ], - ), - const SizedBox(height: 8), - Row(children: [ - Expanded(child: buildEmailPartialContent( - _context, - _presentationEmail, - isSearchEmailRunning, - _searchQuery - )), - ]), - ]), - if (_selectModeAll == SelectMode.INACTIVE) - Transform( - transform: Matrix4.translationValues(0.0, 10.0, 0.0), - child: const Divider(color: AppColor.lineItemListColor, height: 1), - ), - ], - )) - ]), - if (isHoverItem) - Positioned( - top: 0, - right: 0, - child: Transform( - transform: Matrix4.translationValues( - 0.0, - _selectModeAll == SelectMode.INACTIVE ? -5.0 : 0.0, - 0.0), - child: _buildListActionButtonWhenHover())) - ], - ), - ); - }); - } - - Widget _buildListTileForDesktop() { - return StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return InkWell( - onTap: () => _emailActionClick?.call( - EmailActionType.preview, - _presentationEmail), - onHover: (value) => _onHoverItemChanged(value, setState), - child: Stack(alignment: Alignment.bottomCenter, children: [ - Row(children: [ - Container( - padding: const EdgeInsetsDirectional.symmetric(horizontal: 16), - alignment: Alignment.center, - child: !_presentationEmail.hasRead - ? SvgPicture.asset( - imagePaths.icUnreadStatus, - width: 9, - height: 9, - fit: BoxFit.fill) - : const SizedBox(width: 9)), - buildIconWeb( - icon: SvgPicture.asset( - _presentationEmail.hasStarred - ? imagePaths.icStar - : imagePaths.icUnStar, - width: 16, - height: 16, - fit: BoxFit.fill), - tooltip: _presentationEmail.hasStarred - ? AppLocalizations.of(_context).starred - : AppLocalizations.of(_context).not_starred, - onTap: () => _emailActionClick?.call( - _presentationEmail.hasStarred - ? EmailActionType.unMarkAsStarred - : EmailActionType.markAsStarred, - _presentationEmail)), - buildIconWeb( - icon: buildIconAnsweredOrForwarded(presentationEmail: _presentationEmail), - tooltip: messageToolTipForAnsweredOrForwarded(context, _presentationEmail), - iconPadding: const EdgeInsetsDirectional.only(end: 12), - splashRadius: 1), - InkWell( - onTap: () => _emailActionClick?.call(EmailActionType.selection, _presentationEmail), - onHover: (value) => _onHoverIconChanged(value, setState), - child: _buildAvatarIcon( - iconSize: 32, - textStyle: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Colors.white - ) - ), - ), - const SizedBox(width: 10), - SizedBox( - width: 160, - child: buildInformationSender( - _context, - _presentationEmail, - mailboxContain, - isSearchEmailRunning, - _searchQuery - )), - const SizedBox(width: 24), - Expanded(child: _buildSubjectAndContent()), - const SizedBox(width: 16), - if (isHoverItem) - _buildListActionButtonWhenHover() - else - _buildDateTimeForDesktopScreen() - ]), - if ( _selectModeAll == SelectMode.INACTIVE) - Transform( - transform: Matrix4.translationValues(0.0, 10, 0.0), - child: const Padding( - padding: EdgeInsetsDirectional.only(start: 120), - child: Divider(color: AppColor.lineItemListColor, height: 1)), - ) - ]), - ); - }); - } - - double _getIconSize() { - return responsiveUtils.isDesktop(_context) ? 18 : 16; - } - - EdgeInsets _getPaddingIcon() { - return responsiveUtils.isDesktop(_context) + EdgeInsets _getPaddingIcon(BuildContext context) { + return responsiveUtils.isDesktop(context) ? const EdgeInsets.all(8) : const EdgeInsets.all(5); } - Widget _buildListActionButtonWhenHover() { + Widget _buildListActionButtonWhenHover(BuildContext context) { return Row(children: [ buildSVGIconButton( icon: imagePaths.icOpenInNewTab, iconColor: AppColor.colorActionButtonHover, - iconSize: _getIconSize(), - padding: _getPaddingIcon(), - tooltip: AppLocalizations.of(_context).openInNewTab, - onTap: () => _emailActionClick?.call( + iconSize: _getIconSize(context), + padding: _getPaddingIcon(context), + tooltip: AppLocalizations.of(context).openInNewTab, + onTap: () => widget.emailActionClick?.call( EmailActionType.openInNewTab, - _presentationEmail + widget.presentationEmail ) ), - if(!_presentationEmail.isDraft) + if(!widget.presentationEmail.isDraft) buildSVGIconButton( - icon: _presentationEmail.hasRead ? imagePaths.icRead: imagePaths.icUnread, + icon: widget.presentationEmail.hasRead ? imagePaths.icRead: imagePaths.icUnread, iconColor: AppColor.colorActionButtonHover, - iconSize: _getIconSize(), - padding: _getPaddingIcon(), - tooltip: _presentationEmail.hasRead - ? AppLocalizations.of(_context).mark_as_unread - : AppLocalizations.of(_context).mark_as_read, - onTap: () => _emailActionClick?.call( - _presentationEmail.hasRead ? EmailActionType.markAsUnread : EmailActionType.markAsRead, - _presentationEmail + 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 (mailboxContain != null && mailboxContain?.isDrafts == false) + if (widget.mailboxContain != null && widget.mailboxContain?.isDrafts == false) ... [ buildSVGIconButton( icon: imagePaths.icMove, iconColor: AppColor.colorActionButtonHover, - iconSize: _getIconSize(), - padding: _getPaddingIcon(), - tooltip: AppLocalizations.of(_context).move, - onTap: () => _emailActionClick?.call( + iconSize: _getIconSize(context), + padding: _getPaddingIcon(context), + tooltip: AppLocalizations.of(context).move, + onTap: () => widget.emailActionClick?.call( EmailActionType.moveToMailbox, - _presentationEmail + widget.presentationEmail ) ), ], buildSVGIconButton( icon: imagePaths.icDeleteComposer, iconColor: AppColor.colorActionButtonHover, - iconSize: responsiveUtils.isDesktop(_context) ? 16 : 14, - padding: _getPaddingIcon(), + iconSize: responsiveUtils.isDesktop(context) ? 16 : 14, + padding: _getPaddingIcon(context), tooltip: canDeletePermanently - ? AppLocalizations.of(_context).delete_permanently - : AppLocalizations.of(_context).move_to_trash, - onTap: () => _emailActionClick?.call( + ? AppLocalizations.of(context).delete_permanently + : AppLocalizations.of(context).move_to_trash, + onTap: () => widget.emailActionClick?.call( canDeletePermanently ? EmailActionType.deletePermanently : EmailActionType.moveToTrash, - _presentationEmail + widget.presentationEmail ) ), buildSVGIconButton( icon: imagePaths.icMore, iconColor: AppColor.colorActionButtonHover, - iconSize: _getIconSize(), - padding: _getPaddingIcon(), - tooltip: AppLocalizations.of(_context).more, + iconSize: _getIconSize(context), + padding: _getPaddingIcon(context), + tooltip: AppLocalizations.of(context).more, onTapDown: (tapDetails) { - if (responsiveUtils.isScreenWithShortestSide(_context)) { - _onMoreActionClick?.call(_presentationEmail, null); + if (responsiveUtils.isScreenWithShortestSide(context)) { + widget.onMoreActionClick?.call(widget.presentationEmail, null); } else { - _onMoreActionClick?.call(_presentationEmail, tapDetails.getPosition(_context)); + widget.onMoreActionClick?.call(widget.presentationEmail, tapDetails.getPosition(context)); } } ), - if (responsiveUtils.isDesktop(_context)) const SizedBox(width: 16), + if (responsiveUtils.isDesktop(context)) const SizedBox(width: 16), ]); } bool get canDeletePermanently { - return mailboxContain?.isTrash == true || mailboxContain?.isDrafts == true || mailboxContain?.isSpam == true; + return widget.mailboxContain?.isTrash == true || widget.mailboxContain?.isDrafts == true || widget.mailboxContain?.isSpam == true; } - Widget _buildDateTimeForDesktopScreen() { + Widget _buildDateTimeForDesktopScreen(BuildContext context) { return Row(children: [ buildMailboxContain( - _context, - isSearchEmailRunning, - _presentationEmail + context, + widget.isSearchEmailRunning, + widget.presentationEmail ), - if (_presentationEmail.hasAttachment == true) + 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, _presentationEmail)) + child: buildDateTime(context, widget.presentationEmail)) ]); } - Widget _buildDateTimeForMobileTabletScreen() { + Widget _buildDateTimeForMobileTabletScreen(BuildContext context) { return Row(children: [ - buildIconAnsweredOrForwarded(width: 16, height: 16, presentationEmail: _presentationEmail), - if (_presentationEmail.hasAttachment == true) + 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, _presentationEmail)), + child: buildDateTime(context, widget.presentationEmail)), buildIconChevron() ]); } @@ -514,48 +580,49 @@ class EmailTileBuilder with BaseEmailItemTile { Widget _buildSubjectAndContent() { return LayoutBuilder(builder: (context, constraints) { return Row(children: [ - if (_presentationEmail.hasCalendarEvent) - buildCalendarEventIcon(context: _context, presentationEmail: _presentationEmail), - if (_presentationEmail.getEmailTitle().isNotEmpty) + if (widget.presentationEmail.hasCalendarEvent) + buildCalendarEventIcon(context: context, presentationEmail: widget.presentationEmail), + if (widget.presentationEmail.getEmailTitle().isNotEmpty) Container( constraints: BoxConstraints(maxWidth: constraints.maxWidth / 2), padding: const EdgeInsetsDirectional.only(end: 12), child: buildEmailTitle( - _context, - _presentationEmail, - isSearchEmailRunning, - _searchQuery + context, + widget.presentationEmail, + widget.isSearchEmailRunning, + widget.searchQuery )), Expanded(child: Container( child: buildEmailPartialContent( - _context, - _presentationEmail, - isSearchEmailRunning, - _searchQuery + context, + widget.presentationEmail, + widget.isSearchEmailRunning, + widget.searchQuery )) ), ]); }); } - bool get _isSelectionActivated { - return isHoverIcon || - isHoverItem || - _presentationEmail.selectMode == SelectMode.ACTIVE || - (responsiveUtils.isMobile(_context) && _selectModeAll == SelectMode.ACTIVE); - } - - Widget _buildAvatarIcon({double? iconSize, TextStyle? textStyle}) { - if (_isSelectionActivated) { + 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, - _presentationEmail, + context, + widget.presentationEmail, iconSize: iconSize ?? 48, textStyle: textStyle ); } else { return buildIconAvatarText( - _presentationEmail, + widget.presentationEmail, iconSize: iconSize ?? 48, textStyle: textStyle ); diff --git a/model/lib/email/presentation_email.dart b/model/lib/email/presentation_email.dart index fbdbaa7cf..4f18ee1fd 100644 --- a/model/lib/email/presentation_email.dart +++ b/model/lib/email/presentation_email.dart @@ -114,6 +114,8 @@ class PresentationEmail with EquatableMixin { bool get withAttachments => hasAttachment == true; + bool get isSelected => selectMode == SelectMode.ACTIVE; + String get routeWebAsString => routeWeb.toString(); bool get pushNotificationActivated => !isDraft && !hasRead;