From 4f981878c38a60faf50764ea63db2bc95c77f5c8 Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 14 Nov 2022 16:30:25 +0700 Subject: [PATCH] TF-934 Implement open email in new tab --- core/lib/core.dart | 1 + .../tap_down_details_extension.dart | 17 ++ .../views/button/icon_button_web.dart | 56 ++++- .../base/mixin/popup_menu_widget_mixin.dart | 46 ++-- .../email/presentation/email_view.dart | 226 +++++++++--------- .../mixin/email_action_controller.dart | 12 + .../presentation/thread_controller.dart | 5 +- .../thread/presentation/thread_view.dart | 150 +++++++++--- .../widgets/email_tile_web_builder.dart | 152 ++++++------ lib/l10n/intl_messages.arb | 8 +- lib/main/localizations/app_localizations.dart | 7 + model/lib/email/email_action_type.dart | 1 + .../presentation_email_extension.dart | 2 +- 13 files changed, 432 insertions(+), 251 deletions(-) create mode 100644 core/lib/presentation/extensions/tap_down_details_extension.dart diff --git a/core/lib/core.dart b/core/lib/core.dart index 5cc4a0374..6020093dd 100644 --- a/core/lib/core.dart +++ b/core/lib/core.dart @@ -10,6 +10,7 @@ export 'presentation/extensions/html_extension.dart'; export 'presentation/extensions/compare_string_extension.dart'; export 'presentation/extensions/compare_list_extensions.dart'; export 'presentation/extensions/string_extension.dart'; +export 'presentation/extensions/tap_down_details_extension.dart'; export 'domain/extensions/media_type_extension.dart'; // Exceptions diff --git a/core/lib/presentation/extensions/tap_down_details_extension.dart b/core/lib/presentation/extensions/tap_down_details_extension.dart new file mode 100644 index 000000000..d6101d719 --- /dev/null +++ b/core/lib/presentation/extensions/tap_down_details_extension.dart @@ -0,0 +1,17 @@ + +import 'package:flutter/material.dart'; + +extension TapDownDetailsExtension on TapDownDetails { + + RelativeRect getPosition(BuildContext context) { + final screenSize = MediaQuery.of(context).size; + final offset = globalPosition; + final position = RelativeRect.fromLTRB( + offset.dx, + offset.dy, + screenSize.width - offset.dx, + screenSize.height - offset.dy, + ); + return position; + } +} \ No newline at end of file diff --git a/core/lib/presentation/views/button/icon_button_web.dart b/core/lib/presentation/views/button/icon_button_web.dart index fccb13e81..826844468 100644 --- a/core/lib/presentation/views/button/icon_button_web.dart +++ b/core/lib/presentation/views/button/icon_button_web.dart @@ -1,9 +1,12 @@ import 'package:core/core.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; typedef IconWebCallback = void Function(); typedef IconWebHasPositionCallback = void Function(RelativeRect); +typedef OnTapIconButtonCallbackAction = void Function(); +typedef OnTapDownIconButtonCallbackAction = void Function(TapDownDetails TapDetails); Widget buildIconWeb({ required Widget icon, @@ -32,6 +35,47 @@ Widget buildIconWeb({ ); } +Widget buildSVGIconButton({ + required String icon, + String? tooltip, + EdgeInsets? padding, + double? iconSize, + Color? iconColor, + OnTapIconButtonCallbackAction? onTap, + OnTapDownIconButtonCallbackAction? onTapDown, +}) { + Widget iconWidget = Padding( + padding: padding ?? const EdgeInsets.all(8), + child: SvgPicture.asset( + icon, + width: iconSize, + height: iconSize, + fit: BoxFit.fill, + color: iconColor, + ), + ); + + Widget itemChild; + if (tooltip != null) { + itemChild = Tooltip( + message: tooltip, + child: iconWidget, + ); + } else { + itemChild = iconWidget; + } + + return Material( + color: Colors.transparent, + child: InkWell( + onTap: onTap, + onTapDown: onTapDown, + customBorder: const CircleBorder(), + child: itemChild, + ) + ); +} + Widget buildIconWebHasPosition(BuildContext context, { required Widget icon, String? tooltip, @@ -43,18 +87,10 @@ Widget buildIconWebHasPosition(BuildContext context, { shape: const CircleBorder(), child: InkWell( onTapDown: (detail) { - final screenSize = MediaQuery.of(context).size; - final offset = detail.globalPosition; - final position = RelativeRect.fromLTRB( - offset.dx, - offset.dy, - screenSize.width - offset.dx, - screenSize.height - offset.dy, - ); - onTapDown?.call(position); + onTapDown?.call(detail.getPosition(context)); }, onTap: () => onTap?.call(), - borderRadius: const BorderRadius.all(const Radius.circular(12)), + borderRadius: const BorderRadius.all(Radius.circular(12)), child: Tooltip( message: tooltip ?? '', child: icon, diff --git a/lib/features/base/mixin/popup_menu_widget_mixin.dart b/lib/features/base/mixin/popup_menu_widget_mixin.dart index 4afa1c7cf..a6f08d157 100644 --- a/lib/features/base/mixin/popup_menu_widget_mixin.dart +++ b/lib/features/base/mixin/popup_menu_widget_mixin.dart @@ -4,27 +4,39 @@ import 'package:flutter_svg/flutter_svg.dart'; mixin PopupMenuWidgetMixin { - Widget popupItem(String iconAction, String nameAction, { + Widget popupItem( + String iconAction, + String nameAction, { Color? colorIcon, + double? iconSize, TextStyle? styleName, - Function? onCallbackAction + EdgeInsets? padding, + Function()? onCallbackAction }) { return InkWell( - onTap: () => onCallbackAction?.call(), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), - child: SizedBox( - child: Row(children: [ - SvgPicture.asset(iconAction, width: 20, height: 20, fit: BoxFit.fill, color: colorIcon), - const SizedBox(width: 12), - Expanded(child: Text(nameAction, - style: styleName ?? const TextStyle( - fontSize: 17, - fontWeight: FontWeight.normal, - color: Colors.black))), - ]) - ), - ) + onTap: onCallbackAction, + child: Padding( + padding: padding ?? const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: SizedBox( + child: Row(children: [ + SvgPicture.asset( + iconAction, + width: iconSize ?? 20, + height: iconSize ?? 20, + fit: BoxFit.fill, + color: colorIcon + ), + const SizedBox(width: 12), + Expanded(child: Text( + nameAction, + style: styleName ?? const TextStyle( + fontSize: 17, + fontWeight: FontWeight.normal, + color: Colors.black) + )), + ]) + ), + ) ); } } \ No newline at end of file diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index 2b7c4b82a..02e73ec09 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/base/mixin/popup_menu_widget_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_email_address_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/app_bar_mail_widget_builder.dart'; @@ -16,7 +17,8 @@ import 'package:tmail_ui_user/features/manage_account/presentation/extensions/va import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -class EmailView extends GetWidget { +class EmailView extends GetWidget + with PopupMenuWidgetMixin { static const double maxSizeFullDisplayEmailAddressArrowDownButton = 30.0; @@ -766,49 +768,58 @@ class EmailView extends GetWidget { Widget _markAsEmailUnreadAction(BuildContext context, PresentationEmail email) { return (EmailActionCupertinoActionSheetActionBuilder( - const Key('mark_as_unread_action'), - SvgPicture.asset( - imagePaths.icUnreadEmail, - width: 24, - height: 24, - fit: BoxFit.fill, - color: AppColor.colorTextButton), - AppLocalizations.of(context).mark_as_unread, - email, - iconLeftPadding: responsiveUtils.isMobile(context) - ? const EdgeInsets.only(left: 12, right: 16) - : const EdgeInsets.only(right: 12), - iconRightPadding: responsiveUtils.isMobile(context) - ? const EdgeInsets.only(right: 12) - : EdgeInsets.zero) - ..onActionClick((email) => controller.handleEmailAction(context, email, EmailActionType.markAsUnread))) - .build(); + const Key('mark_as_unread_action'), + SvgPicture.asset( + imagePaths.icUnreadEmail, + width: 24, + height: 24, + fit: BoxFit.fill, + color: AppColor.colorTextButton + ), + AppLocalizations.of(context).mark_as_unread, + email, + iconLeftPadding: responsiveUtils.isMobile(context) + ? const EdgeInsets.only(left: 12, right: 16) + : const EdgeInsets.only(right: 12), + iconRightPadding: responsiveUtils.isMobile(context) + ? const EdgeInsets.only(right: 12) + : EdgeInsets.zero) + ..onActionClick((email) => controller.handleEmailAction( + context, + email, + EmailActionType.markAsUnread) + ) + ).build(); } Widget _markAsEmailSpamOrUnSpamAction(BuildContext context, PresentationEmail email) { final currentMailbox = controller.getMailboxContain(email); return (EmailActionCupertinoActionSheetActionBuilder( - const Key('mark_as_spam_or_un_spam_action'), - SvgPicture.asset( - currentMailbox?.isSpam == true ? imagePaths.icNotSpam : imagePaths.icMailboxSpam, - width: 24, - height: 24, - fit: BoxFit.fill, - color: AppColor.colorTextButton), - currentMailbox?.isSpam == true - ? AppLocalizations.of(context).remove_from_spam - : AppLocalizations.of(context).mark_as_spam, - email, - iconLeftPadding: responsiveUtils.isMobile(context) - ? const EdgeInsets.only(left: 12, right: 16) - : const EdgeInsets.only(right: 12), - iconRightPadding: responsiveUtils.isMobile(context) - ? const EdgeInsets.only(right: 12) - : EdgeInsets.zero) - ..onActionClick((email) => controller.handleEmailAction(context, email, - currentMailbox?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam))) - .build(); + const Key('mark_as_spam_or_un_spam_action'), + SvgPicture.asset( + currentMailbox?.isSpam == true ? imagePaths.icNotSpam : imagePaths.icSpam, + width: 24, + height: 24, + fit: BoxFit.fill, + color: AppColor.colorTextButton + ), + currentMailbox?.isSpam == true + ? AppLocalizations.of(context).remove_from_spam + : AppLocalizations.of(context).mark_as_spam, + email, + iconLeftPadding: responsiveUtils.isMobile(context) + ? const EdgeInsets.only(left: 12, right: 16) + : const EdgeInsets.only(right: 12), + iconRightPadding: responsiveUtils.isMobile(context) + ? const EdgeInsets.only(right: 12) + : EdgeInsets.zero) + ..onActionClick((email) => controller.handleEmailAction( + context, + email, + currentMailbox?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam) + ) + ).build(); } Widget _quickCreatingRuleAction(BuildContext context, PresentationEmail email) { @@ -833,85 +844,76 @@ class EmailView extends GetWidget { } List _popupMenuEmailActionTile(BuildContext context, PresentationEmail email) { + final mailboxContain = controller.getMailboxContain(email); return [ - PopupMenuItem( - padding: EdgeInsets.zero, - child: _markAsEmailUnreadPopupItemAction(context, email)), - PopupMenuItem( - padding: EdgeInsets.zero, - child: _markAsEmailSpamOrUnSpamPopupItemAction(context, email)), - PopupMenuItem( - padding: EdgeInsets.zero, - child: _quickCreatingRulePopupItemAction(context, email)), + _markAsEmailUnreadPopupItemAction(context, email), + _markAsEmailSpamOrUnSpamPopupItemAction(context, email, mailboxContain), + _quickCreatingRulePopupItemAction(context, email) ]; } - Widget _markAsEmailUnreadPopupItemAction(BuildContext context, PresentationEmail email) { - return _popupMenuItem( - icon: imagePaths.icUnreadEmail, - title: AppLocalizations.of(context).mark_as_unread, - onTapCallback: () => controller.handleEmailAction(context, email, EmailActionType.markAsUnread) - ); - } - - Widget _markAsEmailSpamOrUnSpamPopupItemAction(BuildContext context, PresentationEmail email) { - final currentMailbox = controller.getMailboxContain(email); - - return _popupMenuItem( - icon: currentMailbox?.isSpam == true - ? imagePaths.icNotSpam - : imagePaths.icMailboxSpam, - title: currentMailbox?.isSpam == true - ? AppLocalizations.of(context).remove_from_spam - : AppLocalizations.of(context).mark_as_spam, - onTapCallback: () => controller.handleEmailAction( - context, - email, - currentMailbox?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam) - ); - } - - Widget _quickCreatingRulePopupItemAction(BuildContext context, PresentationEmail email) { - return _popupMenuItem( - icon: imagePaths.icQuickCreatingRule, - title: AppLocalizations.of(context).quickCreatingRule, - onTapCallback: () => controller.quickCreatingRule(context, email.from!.first) - ); - } - - Widget _popupMenuItem({ - required String icon, - required String title, - Function()? onTapCallback - }) { - return Material( - color: Colors.transparent, - child: InkWell( - onTap: onTapCallback, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: SizedBox( - width: 300, - height: 44, - child: Row(children: [ - SvgPicture.asset( - icon, - width: 20, - height: 20, - color: AppColor.colorTextButton, - fit: BoxFit.fill), - const SizedBox(width: 12), - Expanded(child: Text( - title, - style: const TextStyle( - fontSize: 15, - color: Colors.black, - fontWeight: FontWeight.normal) - )), - ]) - ), + PopupMenuEntry _markAsEmailUnreadPopupItemAction(BuildContext context, PresentationEmail email) { + return PopupMenuItem( + padding: EdgeInsets.zero, + child: popupItem( + imagePaths.icUnreadEmail, + AppLocalizations.of(context).mark_as_unread, + colorIcon: AppColor.colorTextButton, + styleName: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 16, + color: Colors.black + ), + onCallbackAction: () => controller.handleEmailAction( + context, + email, + EmailActionType.markAsUnread ) - ), + ) + ); + } + + PopupMenuEntry _markAsEmailSpamOrUnSpamPopupItemAction( + BuildContext context, + PresentationEmail email, + PresentationMailbox? mailboxContain + ) { + return PopupMenuItem( + padding: EdgeInsets.zero, + child: popupItem( + mailboxContain?.isSpam == true ? imagePaths.icNotSpam : imagePaths.icSpam, + mailboxContain?.isSpam == true + ? AppLocalizations.of(context).remove_from_spam + : AppLocalizations.of(context).mark_as_spam, + colorIcon: AppColor.colorTextButton, + styleName: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 16, + color: Colors.black + ), + onCallbackAction: () => controller.handleEmailAction( + context, + email, + mailboxContain?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam + ) + ) + ); + } + + PopupMenuEntry _quickCreatingRulePopupItemAction(BuildContext context, PresentationEmail email) { + return PopupMenuItem( + padding: EdgeInsets.zero, + child: popupItem( + imagePaths.icQuickCreatingRule, + AppLocalizations.of(context).quickCreatingRule, + colorIcon: AppColor.colorTextButton, + styleName: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 16, + color: Colors.black + ), + onCallbackAction: () => controller.quickCreatingRule(context, email.from!.first) + ) ); } } \ No newline at end of file diff --git a/lib/features/thread/presentation/mixin/email_action_controller.dart b/lib/features/thread/presentation/mixin/email_action_controller.dart index a6a60ea65..a0f7203c6 100644 --- a/lib/features/thread/presentation/mixin/email_action_controller.dart +++ b/lib/features/thread/presentation/mixin/email_action_controller.dart @@ -30,6 +30,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/routes/route_utils.dart'; +import 'package:url_launcher/url_launcher.dart'; mixin EmailActionController on ViewAsDialogActionMixin { @@ -288,4 +289,15 @@ mixin EmailActionController on ViewAsDialogActionMixin { mailboxCurrent: mailboxCurrent, onCancelSelectionEmail: onCancelSelectionEmail); } + + void openEmailInNewTabAction(BuildContext context, PresentationEmail email) { + launchLink(email.routeWebAsString); + } + + Future launchLink(String url, {bool isNewTab = true}) async { + await launchUrl( + Uri.parse(url), + webOnlyWindowName: isNewTab ? '_blank' : '_self', + ); + } } \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index a632bb79a..cad45e4db 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -803,6 +803,9 @@ class ThreadController extends BaseController with EmailActionController { popBack(); unSpam(selectedEmail); break; + case EmailActionType.openInNewTab: + openEmailInNewTabAction(context, selectedEmail); + break; default: break; } @@ -821,7 +824,7 @@ class ThreadController extends BaseController with EmailActionController { void calculateDragValue(PresentationEmail? currentPresentationEmail) { if(currentPresentationEmail != null) { - if(mailboxDashBoardController.listEmailSelected.contains(currentPresentationEmail)){ + if(mailboxDashBoardController.listEmailSelected.findEmail(currentPresentationEmail.id) != null){ listEmailDrag.clear(); listEmailDrag.addAll(mailboxDashBoardController.listEmailSelected); } else { diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index d281a729d..1bea91487 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; +import 'package:tmail_ui_user/features/base/mixin/popup_menu_widget_mixin.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; @@ -24,9 +25,12 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_bu if (dart.library.html) 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_web_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; -class ThreadView extends GetWidget with AppLoaderMixin, - FilterEmailPopupMenuMixin { +class ThreadView extends GetWidget + with AppLoaderMixin, + FilterEmailPopupMenuMixin, + PopupMenuWidgetMixin { final _responsiveUtils = Get.find(); final _imagePaths = Get.find(); @@ -496,46 +500,132 @@ class ThreadView extends GetWidget with AppLoaderMixin, } List _contextMenuActionTile(BuildContext context, PresentationEmail email) { + final mailboxContain = controller.getMailboxContain(email); + return [ - _markAsEmailSpamOrUnSpamAction(context, email), + _openInNewTabContextMenuItemAction(context, email), + if (mailboxContain?.isDrafts == false) + _markAsEmailSpamOrUnSpamContextMenuItemAction(context, email, mailboxContain), ]; } - Widget _markAsEmailSpamOrUnSpamAction(BuildContext context, PresentationEmail email) { - final mailboxContain = controller.getMailboxContain(email); + Widget _markAsEmailSpamOrUnSpamContextMenuItemAction( + BuildContext context, + PresentationEmail email, + PresentationMailbox? mailboxContain + ) { return (EmailActionCupertinoActionSheetActionBuilder( - const Key('mark_as_spam_or_un_spam_action'), - SvgPicture.asset( - mailboxContain?.isSpam == true ? _imagePaths.icNotSpam : _imagePaths.icSpam, - width: 28, - height: 28, - fit: BoxFit.fill, - color: AppColor.colorTextButton), - mailboxContain?.isSpam == true - ? AppLocalizations.of(context).remove_from_spam - : AppLocalizations.of(context).mark_as_spam, - email, - iconLeftPadding: _responsiveUtils.isMobile(context) - ? const EdgeInsets.only(left: 12, right: 16) - : const EdgeInsets.only(right: 12), - iconRightPadding: _responsiveUtils.isMobile(context) - ? const EdgeInsets.only(right: 12) - : EdgeInsets.zero) - ..onActionClick((email) => controller.pressEmailAction(context, - mailboxContain?.isSpam == true - ? EmailActionType.unSpam - : EmailActionType.moveToSpam, - email, - mailboxContain: mailboxContain))) - .build(); + const Key('mark_as_spam_or_un_spam_action'), + SvgPicture.asset( + mailboxContain?.isSpam == true ? _imagePaths.icNotSpam : _imagePaths.icSpam, + width: 24, + height: 24, + fit: BoxFit.fill, + color: AppColor.colorTextButton), + mailboxContain?.isSpam == true + ? AppLocalizations.of(context).remove_from_spam + : AppLocalizations.of(context).mark_as_spam, + email, + iconLeftPadding: _responsiveUtils.isMobile(context) + ? const EdgeInsets.only(left: 12, right: 16) + : const EdgeInsets.only(right: 12), + iconRightPadding: _responsiveUtils.isMobile(context) + ? const EdgeInsets.only(right: 12) + : EdgeInsets.zero) + ..onActionClick((email) => controller.pressEmailAction(context, + mailboxContain?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam, + email, + mailboxContain: mailboxContain) + ) + ).build(); + } + + Widget _openInNewTabContextMenuItemAction(BuildContext context, PresentationEmail email) { + return (EmailActionCupertinoActionSheetActionBuilder( + const Key('open_in_new_tab_action'), + SvgPicture.asset( + _imagePaths.icOpenInNewTab, + width: 24, + height: 24, + fit: BoxFit.fill, + color: AppColor.colorTextButton), + AppLocalizations.of(context).openInNewTab, + email, + iconLeftPadding: _responsiveUtils.isMobile(context) + ? const EdgeInsets.only(left: 12, right: 16) + : const EdgeInsets.only(right: 12), + iconRightPadding: _responsiveUtils.isMobile(context) + ? const EdgeInsets.only(right: 12) + : EdgeInsets.zero) + ..onActionClick((email) { + popBack(); + controller.openEmailInNewTabAction(context, email); + }) + ).build(); } List _popupMenuActionTile(BuildContext context, PresentationEmail email) { + final mailboxContain = controller.getMailboxContain(email); + return [ - PopupMenuItem(padding: const EdgeInsets.symmetric(horizontal: 8), child: _markAsEmailSpamOrUnSpamAction(context, email)), + _buildOpenInNewTabPopupMenuItem(context, email, mailboxContain), + if (mailboxContain?.isDrafts == false) + _buildMarkAsSpamPopupMenuItem(context, email, mailboxContain) ]; } + PopupMenuEntry _buildMarkAsSpamPopupMenuItem( + BuildContext context, + PresentationEmail email, + PresentationMailbox? mailboxContain + ) { + return PopupMenuItem( + padding: EdgeInsets.zero, + child: popupItem( + mailboxContain?.isSpam == true ? _imagePaths.icNotSpam : _imagePaths.icSpam, + mailboxContain?.isSpam == true + ? AppLocalizations.of(context).remove_from_spam + : AppLocalizations.of(context).mark_as_spam, + colorIcon: AppColor.colorTextButton, + styleName: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 16, + color: Colors.black + ), + onCallbackAction: () => controller.pressEmailAction( + context, + mailboxContain?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam, + email, + mailboxContain: mailboxContain + ) + ) + ); + } + + PopupMenuEntry _buildOpenInNewTabPopupMenuItem( + BuildContext context, + PresentationEmail email, + PresentationMailbox? mailboxContain + ) { + return PopupMenuItem( + padding: EdgeInsets.zero, + child: popupItem( + _imagePaths.icOpenInNewTab, + AppLocalizations.of(context).openInNewTab, + colorIcon: AppColor.colorTextButton, + styleName: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 16, + color: Colors.black + ), + onCallbackAction: () { + popBack(); + controller.openEmailInNewTabAction(context, email); + } + ) + ); + } + Widget _buildMarkAsMailboxReadLoading(BuildContext context) { return Obx(() { final viewState = controller.mailboxDashBoardController.viewStateMarkAsReadMailbox.value; 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 0ae23b2d3..c8d551f9a 100644 --- a/lib/features/thread/presentation/widgets/email_tile_web_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_web_builder.dart @@ -1,5 +1,6 @@ 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'; @@ -392,90 +393,84 @@ class EmailTileBuilder with BaseEmailItemTile { }); } + double _getIconSize() { + return responsiveUtils.isDesktop(_context) ? 18 : 16; + } + + EdgeInsets _getPaddingIcon() { + return responsiveUtils.isDesktop(_context) + ? const EdgeInsets.all(8) + : const EdgeInsets.all(5); + } + Widget _buildListActionButtonWhenHover() { return Row(children: [ + buildSVGIconButton( + icon: imagePaths.icOpenInNewTab, + iconColor: AppColor.colorActionButtonHover, + iconSize: _getIconSize(), + padding: _getPaddingIcon(), + tooltip: AppLocalizations.of(_context).openInNewTab, + onTap: () => _emailActionClick?.call( + EmailActionType.openInNewTab, + _presentationEmail + ) + ), if(!_presentationEmail.isDraft) - buildIconWeb( - minSize: 18, - iconSize: 18, - iconPadding: const EdgeInsets.all(5), - splashRadius: 10, - icon: SvgPicture.asset( - _presentationEmail.hasRead - ? imagePaths.icRead - : imagePaths.icUnread, - color: AppColor.colorActionButtonHover, - width: 16, - height: 16, - fit: BoxFit.fill), - 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)), - const SizedBox(width: 5), + buildSVGIconButton( + icon: _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 + ) + ), if (mailboxContain != null && mailboxContain?.isDrafts == false) ... [ - buildIconWeb( - minSize: 18, - iconSize: 18, - iconPadding: const EdgeInsets.all(5), - splashRadius: 10, - icon: SvgPicture.asset( - imagePaths.icMove, - width: 16, - height: 16, - color: AppColor.colorActionButtonHover, - fit: BoxFit.fill), - tooltip: AppLocalizations.of(_context).move, - onTap: () => _emailActionClick?.call( - EmailActionType.moveToMailbox, - _presentationEmail)), - const SizedBox(width: 5), + buildSVGIconButton( + icon: imagePaths.icMove, + iconColor: AppColor.colorActionButtonHover, + iconSize: _getIconSize(), + padding: _getPaddingIcon(), + tooltip: AppLocalizations.of(_context).move, + onTap: () => _emailActionClick?.call( + EmailActionType.moveToMailbox, + _presentationEmail + ) + ), ], - buildIconWeb( - minSize: 18, - iconSize: 18, - iconPadding: const EdgeInsets.all(5), - splashRadius: 10, - icon: SvgPicture.asset( - canDeletePermanently ? imagePaths.icDeleteComposer : imagePaths.icDelete, - width: canDeletePermanently ? 14 : 16, - height: canDeletePermanently ? 14 : 16, - color: AppColor.colorActionButtonHover, - fit: BoxFit.fill), - tooltip: canDeletePermanently - ? AppLocalizations.of(_context).delete_permanently - : AppLocalizations.of(_context).move_to_trash, - onTap: () => _emailActionClick?.call( - canDeletePermanently - ? EmailActionType.deletePermanently - : EmailActionType.moveToTrash, - _presentationEmail)), - const SizedBox(width: 5), - if (mailboxContain?.isDrafts == false) - buildIconWebHasPosition( - _context, - icon: SvgPicture.asset( - imagePaths.icMore, - width: 16, - height: 16, - color: AppColor.colorActionButtonHover, - fit: BoxFit.fill), - tooltip: AppLocalizations.of(_context).more, - onTap: () { - if (responsiveUtils.isMobile(_context)) { - _onMoreActionClick?.call(_presentationEmail, null); - } - }, - onTapDown: (position) { - if (!responsiveUtils.isMobile(_context)) { - _onMoreActionClick?.call(_presentationEmail, position); - } - }), + buildSVGIconButton( + icon: imagePaths.icDeleteComposer, + iconColor: AppColor.colorActionButtonHover, + iconSize: responsiveUtils.isDesktop(_context) ? 16 : 14, + padding: _getPaddingIcon(), + tooltip: canDeletePermanently + ? AppLocalizations.of(_context).delete_permanently + : AppLocalizations.of(_context).move_to_trash, + onTap: () => _emailActionClick?.call( + canDeletePermanently ? EmailActionType.deletePermanently : EmailActionType.moveToTrash, + _presentationEmail + ) + ), + buildSVGIconButton( + icon: imagePaths.icMore, + iconColor: AppColor.colorActionButtonHover, + iconSize: _getIconSize(), + padding: _getPaddingIcon(), + tooltip: AppLocalizations.of(_context).more, + onTapDown: (tapDetails) { + if (responsiveUtils.isScreenWithShortestSide(_context)) { + _onMoreActionClick?.call(_presentationEmail, null); + } else { + _onMoreActionClick?.call(_presentationEmail, tapDetails.getPosition(_context)); + } + } + ), if (responsiveUtils.isDesktop(_context)) const SizedBox(width: 16), ]); } @@ -510,7 +505,6 @@ class EmailTileBuilder with BaseEmailItemTile { padding: const EdgeInsets.only(right: 4, left: 8), child: buildDateTime(_context, _presentationEmail)), buildIconChevron() - ]); } diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index ec1f66177..2b68365d8 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-11-11T02:15:40.658946", + "@@last_modified": "2022-11-14T16:25:49.764077", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -2521,5 +2521,11 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "openInNewTab": "Open in New Tab", + "@openInNewTab": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 53f464af9..2437cc8dd 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -2587,4 +2587,11 @@ class AppLocalizations { 'Page 404', name: 'page404'); } + + String get openInNewTab { + return Intl.message( + 'Open in New Tab', + name: 'openInNewTab', + ); + } } \ No newline at end of file diff --git a/model/lib/email/email_action_type.dart b/model/lib/email/email_action_type.dart index 3e69876c8..161ee58fb 100644 --- a/model/lib/email/email_action_type.dart +++ b/model/lib/email/email_action_type.dart @@ -17,4 +17,5 @@ enum EmailActionType { selection, moveToSpam, unSpam, + openInNewTab } \ No newline at end of file diff --git a/model/lib/extensions/presentation_email_extension.dart b/model/lib/extensions/presentation_email_extension.dart index 462ada2ce..59fb398eb 100644 --- a/model/lib/extensions/presentation_email_extension.dart +++ b/model/lib/extensions/presentation_email_extension.dart @@ -155,7 +155,7 @@ extension PresentationEmailExtension on PresentationEmail { mailboxIds: mailboxIds, mailboxNames: listMailboxName, selectMode: selectMode, - routeWeb: routeWeb + routeWeb: routeWeb ); }