TF-3759 Update style popup context menu for email more action on thread view
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -64,24 +64,23 @@ mixin PopupContextMenuActionMixin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void openPopupMenuAction(
|
Future<void> openPopupMenuAction(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
RelativeRect? position,
|
RelativeRect? position,
|
||||||
List<PopupMenuEntry> popupMenuItems,
|
List<PopupMenuEntry> popupMenuItems,
|
||||||
{
|
|
||||||
double? radius,
|
|
||||||
}
|
|
||||||
) async {
|
) async {
|
||||||
await showMenu(
|
await showMenu(
|
||||||
context: context,
|
context: context,
|
||||||
position: position ?? const RelativeRect.fromLTRB(16, 40, 16, 16),
|
position: position ?? const RelativeRect.fromLTRB(16, 40, 16, 16),
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
surfaceTintColor: Colors.white,
|
surfaceTintColor: Colors.white,
|
||||||
elevation: 4,
|
menuPadding: const EdgeInsets.only(top: 8, bottom: 8),
|
||||||
shape: RoundedRectangleBorder(
|
elevation: 8,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(radius ?? 16))
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(6)),
|
||||||
),
|
),
|
||||||
items: popupMenuItems
|
constraints: const BoxConstraints(maxWidth: 220),
|
||||||
|
items: popupMenuItems,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
typedef OnPopupMenuActionClick = void Function(PopupMenuItemAction action);
|
||||||
|
|
||||||
|
abstract class PopupMenuItemAction<T> with EquatableMixin {
|
||||||
|
final T action;
|
||||||
|
|
||||||
|
PopupMenuItemAction(this.action);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [action];
|
||||||
|
|
||||||
|
String get actionName;
|
||||||
|
|
||||||
|
String getActionNameWithLimitation({int limitCharacters = 22}) =>
|
||||||
|
actionName.length > limitCharacters
|
||||||
|
? '${actionName.substring(0, limitCharacters)}...'
|
||||||
|
: actionName;
|
||||||
|
|
||||||
|
Color get actionNameColor => Colors.black;
|
||||||
|
|
||||||
|
void onClick(OnPopupMenuActionClick callback) => callback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
mixin OptionalPopupIcon {
|
||||||
|
String get actionIcon;
|
||||||
|
|
||||||
|
Color get actionIconColor => AppColor.steelGrayA540;
|
||||||
|
|
||||||
|
double get actionIconSize => 20.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class PopupMenuItemActionRequiredIcon<T>
|
||||||
|
extends PopupMenuItemAction<T> with OptionalPopupIcon {
|
||||||
|
PopupMenuItemActionRequiredIcon(super.action);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/presentation/utils/theme_utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/model/popup_menu_item_action.dart';
|
||||||
|
|
||||||
|
class PopupMenuItemActionWidget extends StatelessWidget {
|
||||||
|
final PopupMenuItemAction menuAction;
|
||||||
|
final OnPopupMenuActionClick menuActionClick;
|
||||||
|
|
||||||
|
const PopupMenuItemActionWidget({
|
||||||
|
super.key,
|
||||||
|
required this.menuAction,
|
||||||
|
required this.menuActionClick,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget? iconWidget;
|
||||||
|
|
||||||
|
if (menuAction is PopupMenuItemActionRequiredIcon) {
|
||||||
|
final specificMenuAction = menuAction as PopupMenuItemActionRequiredIcon;
|
||||||
|
iconWidget = Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.only(end: 16),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
specificMenuAction.actionIcon,
|
||||||
|
width: specificMenuAction.actionIconSize,
|
||||||
|
height: specificMenuAction.actionIconSize,
|
||||||
|
colorFilter: specificMenuAction.actionIconColor.asFilter(),
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Material(
|
||||||
|
type: MaterialType.transparency,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => menuAction.onClick(menuActionClick),
|
||||||
|
child: Container(
|
||||||
|
height: 48,
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
if (iconWidget != null) iconWidget,
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
menuAction.getActionNameWithLimitation(),
|
||||||
|
style: ThemeUtils.textStyleBodyBody3(color: Colors.black),
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,7 +61,6 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
context,
|
context,
|
||||||
position,
|
position,
|
||||||
_createMoreOptionPopupItems(context),
|
_createMoreOptionPopupItems(context),
|
||||||
radius: ComposerStyle.popupMenuRadius
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
isNetworkConnectionAvailable: controller.isNetworkConnectionAvailable,
|
isNetworkConnectionAvailable: controller.isNetworkConnectionAvailable,
|
||||||
@@ -87,7 +86,6 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
context,
|
context,
|
||||||
position,
|
position,
|
||||||
_createMoreOptionPopupItems(context),
|
_createMoreOptionPopupItems(context),
|
||||||
radius: ComposerStyle.popupMenuRadius
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
isNetworkConnectionAvailable: controller.isNetworkConnectionAvailable,
|
isNetworkConnectionAvailable: controller.isNetworkConnectionAvailable,
|
||||||
|
|||||||
@@ -150,6 +150,8 @@ extension EmailActionTypeExtension on EmailActionType {
|
|||||||
return imagePaths.icDownloadAttachment;
|
return imagePaths.icDownloadAttachment;
|
||||||
case EmailActionType.editAsNewEmail:
|
case EmailActionType.editAsNewEmail:
|
||||||
return imagePaths.icEdit;
|
return imagePaths.icEdit;
|
||||||
|
case EmailActionType.openInNewTab:
|
||||||
|
return imagePaths.icOpenInNewTab;
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -173,6 +175,8 @@ extension EmailActionTypeExtension on EmailActionType {
|
|||||||
return appLocalizations.downloadMessageAsEML;
|
return appLocalizations.downloadMessageAsEML;
|
||||||
case EmailActionType.editAsNewEmail:
|
case EmailActionType.editAsNewEmail:
|
||||||
return appLocalizations.editAsNewEmail;
|
return appLocalizations.editAsNewEmail;
|
||||||
|
case EmailActionType.openInNewTab:
|
||||||
|
return appLocalizations.openInNewTab;
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import 'package:flutter/material.dart';
|
|||||||
class ComposerStyle {
|
class ComposerStyle {
|
||||||
static const double radius = 28;
|
static const double radius = 28;
|
||||||
static const double keyboardToolBarHeight = 200;
|
static const double keyboardToolBarHeight = 200;
|
||||||
static const double popupMenuRadius = 8;
|
|
||||||
static const double suggestionItemHeight = 60;
|
static const double suggestionItemHeight = 60;
|
||||||
static const double normalWidth = 600;
|
static const double normalWidth = 600;
|
||||||
static const double normalHeight = 525;
|
static const double normalHeight = 525;
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:model/email/email_action_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/model/popup_menu_item_action.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
|
class PopupMenuItemEmailAction
|
||||||
|
extends PopupMenuItemActionRequiredIcon<EmailActionType> {
|
||||||
|
final AppLocalizations appLocalizations;
|
||||||
|
final ImagePaths imagePaths;
|
||||||
|
|
||||||
|
PopupMenuItemEmailAction(
|
||||||
|
super.action,
|
||||||
|
this.appLocalizations,
|
||||||
|
this.imagePaths,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get actionIcon => action.getIcon(imagePaths);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get actionName => action.getTitle(appLocalizations);
|
||||||
|
}
|
||||||
@@ -10,8 +10,10 @@ 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/base/mixin/popup_menu_widget_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/base/widget/clean_messages_banner.dart';
|
import 'package:tmail_ui_user/features/base/widget/clean_messages_banner.dart';
|
||||||
import 'package:tmail_ui_user/features/base/widget/compose_floating_button.dart';
|
import 'package:tmail_ui_user/features/base/widget/compose_floating_button.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/composer_arguments.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/model/context_item_email_action.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/context_item_email_action.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_email_action.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.dart';
|
import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart';
|
import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart';
|
||||||
@@ -622,23 +624,25 @@ class ThreadView extends GetWidget<ThreadController>
|
|||||||
PresentationEmail presentationEmail,
|
PresentationEmail presentationEmail,
|
||||||
RelativeRect? position
|
RelativeRect? position
|
||||||
) {
|
) {
|
||||||
|
final mailboxContain = presentationEmail.mailboxContain;
|
||||||
|
final isDrafts = mailboxContain?.isDrafts ?? false;
|
||||||
|
final isChildOfTeamMailboxes =
|
||||||
|
mailboxContain?.isChildOfTeamMailboxes ?? false;
|
||||||
|
final isSpam = mailboxContain?.isSpam ?? false;
|
||||||
|
final isArchive = mailboxContain?.isArchive ?? false;
|
||||||
|
final isTemplates = mailboxContain?.isTemplates ?? false;
|
||||||
|
|
||||||
|
final listEmailActions = [
|
||||||
|
EmailActionType.openInNewTab,
|
||||||
|
if (!isDrafts && !isChildOfTeamMailboxes)
|
||||||
|
isSpam ? EmailActionType.unSpam : EmailActionType.moveToSpam,
|
||||||
|
if (!isArchive) EmailActionType.archiveMessage,
|
||||||
|
if (!isDrafts && !isTemplates) EmailActionType.editAsNewEmail,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (listEmailActions.isEmpty) return;
|
||||||
|
|
||||||
if (controller.responsiveUtils.isScreenWithShortestSide(context)) {
|
if (controller.responsiveUtils.isScreenWithShortestSide(context)) {
|
||||||
final mailboxContain = presentationEmail.mailboxContain;
|
|
||||||
|
|
||||||
final listEmailActions = [
|
|
||||||
EmailActionType.openInNewTab,
|
|
||||||
if (mailboxContain?.isDrafts == false && mailboxContain?.isChildOfTeamMailboxes == false)
|
|
||||||
mailboxContain?.isSpam == true
|
|
||||||
? EmailActionType.unSpam
|
|
||||||
: EmailActionType.moveToSpam,
|
|
||||||
if (mailboxContain?.isArchive == false)
|
|
||||||
EmailActionType.archiveMessage,
|
|
||||||
if (mailboxContain?.isDrafts == false && mailboxContain?.isTemplates == false)
|
|
||||||
EmailActionType.editAsNewEmail,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (listEmailActions.isEmpty) return;
|
|
||||||
|
|
||||||
final contextMenuActions = listEmailActions
|
final contextMenuActions = listEmailActions
|
||||||
.map((action) => ContextItemEmailAction(
|
.map((action) => ContextItemEmailAction(
|
||||||
action,
|
action,
|
||||||
@@ -659,10 +663,31 @@ class ThreadView extends GetWidget<ThreadController>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
final popupMenuActions = listEmailActions.map((actionType) {
|
||||||
|
return PopupMenuItem(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: PopupMenuItemActionWidget(
|
||||||
|
menuAction: PopupMenuItemEmailAction(
|
||||||
|
actionType,
|
||||||
|
AppLocalizations.of(context),
|
||||||
|
controller.imagePaths,
|
||||||
|
),
|
||||||
|
menuActionClick: (menuAction) {
|
||||||
|
popBack();
|
||||||
|
controller.handleEmailActionType(
|
||||||
|
menuAction.action,
|
||||||
|
presentationEmail,
|
||||||
|
mailboxContain: mailboxContain,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
controller.openPopupMenuAction(
|
controller.openPopupMenuAction(
|
||||||
context,
|
context,
|
||||||
position,
|
position,
|
||||||
_popupMenuActionTile(context, presentationEmail)
|
popupMenuActions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -737,117 +762,6 @@ class ThreadView extends GetWidget<ThreadController>
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PopupMenuEntry> _popupMenuActionTile(BuildContext context, PresentationEmail email) {
|
|
||||||
final mailboxContain = email.mailboxContain;
|
|
||||||
|
|
||||||
return [
|
|
||||||
_buildOpenInNewTabPopupMenuItem(context, email, mailboxContain),
|
|
||||||
if (mailboxContain?.isDrafts == false && mailboxContain?.isChildOfTeamMailboxes == false)
|
|
||||||
_buildMarkAsSpamPopupMenuItem(context, email, mailboxContain),
|
|
||||||
if (mailboxContain?.isArchive == false)
|
|
||||||
_buildArchiveMessagePopupMenuItem(context, email),
|
|
||||||
if (mailboxContain?.isDrafts == false && mailboxContain?.isTemplates == false)
|
|
||||||
_buildEditAsNewEmailPopupMenuItem(AppLocalizations.of(context), email),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
PopupMenuEntry _buildMarkAsSpamPopupMenuItem(
|
|
||||||
BuildContext context,
|
|
||||||
PresentationEmail email,
|
|
||||||
PresentationMailbox? mailboxContain
|
|
||||||
) {
|
|
||||||
return PopupMenuItem(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
child: popupItem(
|
|
||||||
mailboxContain?.isSpam == true ? controller.imagePaths.icNotSpam : controller.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.handleEmailActionType(
|
|
||||||
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(
|
|
||||||
controller.imagePaths.icOpenInNewTab,
|
|
||||||
AppLocalizations.of(context).openInNewTab,
|
|
||||||
colorIcon: AppColor.colorTextButton,
|
|
||||||
styleName: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.black
|
|
||||||
),
|
|
||||||
onCallbackAction: () {
|
|
||||||
popBack();
|
|
||||||
controller.openEmailInNewTabAction(email);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
PopupMenuEntry _buildArchiveMessagePopupMenuItem(
|
|
||||||
BuildContext context,
|
|
||||||
PresentationEmail email
|
|
||||||
) {
|
|
||||||
return PopupMenuItem(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
child: popupItem(
|
|
||||||
controller.imagePaths.icMailboxArchived,
|
|
||||||
AppLocalizations.of(context).archiveMessage,
|
|
||||||
colorIcon: AppColor.colorTextButton,
|
|
||||||
styleName: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.black
|
|
||||||
),
|
|
||||||
onCallbackAction: () {
|
|
||||||
popBack();
|
|
||||||
controller.archiveMessage(context, email);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
PopupMenuEntry _buildEditAsNewEmailPopupMenuItem(
|
|
||||||
AppLocalizations appLocalizations,
|
|
||||||
PresentationEmail email,
|
|
||||||
) {
|
|
||||||
return PopupMenuItem(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
child: popupItem(
|
|
||||||
controller.imagePaths.icEdit,
|
|
||||||
appLocalizations.editAsNewEmail,
|
|
||||||
colorIcon: AppColor.colorTextButton,
|
|
||||||
styleName: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.black
|
|
||||||
),
|
|
||||||
onCallbackAction: () {
|
|
||||||
popBack();
|
|
||||||
controller.editAsNewEmail(email);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildMailboxActionProgressBanner(BuildContext context) {
|
Widget _buildMailboxActionProgressBanner(BuildContext context) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
return _MailboxActionProgressBanner(
|
return _MailboxActionProgressBanner(
|
||||||
|
|||||||
Reference in New Issue
Block a user