TF-2666 Edit one recipient in To/Cc/Bcc on web
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -0,0 +1,150 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:core/utils/platform_info.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart' hide OverlayEntry;
|
||||||
|
import 'package:universal_html/html.dart' as html;
|
||||||
|
|
||||||
|
typedef OnRightMouseClickAction = void Function(RelativeRect position);
|
||||||
|
typedef OnDoubleClickAction = void Function(RelativeRect position);
|
||||||
|
|
||||||
|
class SmartInteractionWidget extends StatefulWidget {
|
||||||
|
final Widget child;
|
||||||
|
final OnRightMouseClickAction onRightMouseClickAction;
|
||||||
|
final OnDoubleClickAction onDoubleClickAction;
|
||||||
|
|
||||||
|
const SmartInteractionWidget({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
required this.onRightMouseClickAction,
|
||||||
|
required this.onDoubleClickAction,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SmartInteractionWidget> createState() => _SmartInteractionWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SmartInteractionWidgetState extends State<SmartInteractionWidget> {
|
||||||
|
final GlobalKey _childKey = GlobalKey();
|
||||||
|
|
||||||
|
Offset? _lastPointerEventOffset;
|
||||||
|
StreamSubscription<html.MouseEvent>? _contextMenuSubscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (PlatformInfo.isWeb) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_addWebContextMenuListener();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addWebContextMenuListener() {
|
||||||
|
_contextMenuSubscription =
|
||||||
|
html.document.onContextMenu.listen(_onContextMenuListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onContextMenuListener(html.MouseEvent event) {
|
||||||
|
final renderBox =
|
||||||
|
_childKey.currentContext?.findRenderObject() as RenderBox?;
|
||||||
|
if (renderBox == null) return;
|
||||||
|
|
||||||
|
final localPosition = Offset(
|
||||||
|
event.page.x.toDouble(),
|
||||||
|
event.page.y.toDouble(),
|
||||||
|
);
|
||||||
|
final renderBoxPosition = renderBox.localToGlobal(Offset.zero);
|
||||||
|
final size = renderBox.size;
|
||||||
|
|
||||||
|
final rect = Rect.fromLTWH(
|
||||||
|
renderBoxPosition.dx,
|
||||||
|
renderBoxPosition.dy,
|
||||||
|
size.width,
|
||||||
|
size.height,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rect.contains(localPosition)) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
final relativeRect = _getRelativeRectFromOffset(context, localPosition);
|
||||||
|
widget.onRightMouseClickAction(relativeRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RelativeRect _getRelativeRectFromOffset(
|
||||||
|
BuildContext context,
|
||||||
|
Offset offset,
|
||||||
|
) {
|
||||||
|
final widgetBox =
|
||||||
|
_childKey.currentContext?.findRenderObject() as RenderBox?;
|
||||||
|
final overlayBox =
|
||||||
|
Overlay.maybeOf(context)?.context.findRenderObject() as RenderBox?;
|
||||||
|
|
||||||
|
if (widgetBox == null || overlayBox == null) {
|
||||||
|
final screenSize = MediaQuery.sizeOf(context);
|
||||||
|
return RelativeRect.fromLTRB(
|
||||||
|
offset.dx,
|
||||||
|
offset.dy,
|
||||||
|
screenSize.width - offset.dx,
|
||||||
|
screenSize.height - offset.dy,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final targetPosition = widgetBox.localToGlobal(
|
||||||
|
Offset.zero,
|
||||||
|
ancestor: overlayBox,
|
||||||
|
);
|
||||||
|
final overlaySize = overlayBox.size;
|
||||||
|
const topSpace = 4.0;
|
||||||
|
|
||||||
|
return RelativeRect.fromLTRB(
|
||||||
|
targetPosition.dx,
|
||||||
|
targetPosition.dy + widgetBox.size.height + topSpace,
|
||||||
|
overlaySize.width + targetPosition.dx,
|
||||||
|
overlaySize.height - targetPosition.dy - widgetBox.size.height,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleDoubleTap() {
|
||||||
|
if (_lastPointerEventOffset != null) {
|
||||||
|
final relativeRect = _getRelativeRectFromOffset(
|
||||||
|
context,
|
||||||
|
_lastPointerEventOffset!,
|
||||||
|
);
|
||||||
|
widget.onDoubleClickAction(relativeRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (PlatformInfo.isWeb) {
|
||||||
|
return Listener(
|
||||||
|
key: _childKey,
|
||||||
|
onPointerDown: (event) {
|
||||||
|
if (event.kind == PointerDeviceKind.mouse ||
|
||||||
|
event.kind == PointerDeviceKind.touch) {
|
||||||
|
_lastPointerEventOffset = event.position;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: GestureDetector(
|
||||||
|
onDoubleTap: _handleDoubleTap,
|
||||||
|
child: widget.child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return widget.child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_lastPointerEventOffset = null;
|
||||||
|
if (PlatformInfo.isWeb) {
|
||||||
|
_contextMenuSubscription?.cancel();
|
||||||
|
}
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import 'package:model/email/prefix_email_address.dart';
|
|||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/composer_print_draft_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/composer_print_draft_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_edit_recipient_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/mark_as_important_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/mark_as_important_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/remove_draggable_email_address_between_recipient_fields_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/remove_draggable_email_address_between_recipient_fields_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
|
||||||
@@ -120,6 +121,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
if (controller.ccRecipientState.value == PrefixRecipientState.enabled)
|
if (controller.ccRecipientState.value == PrefixRecipientState.enabled)
|
||||||
RecipientComposerWidget(
|
RecipientComposerWidget(
|
||||||
@@ -145,6 +147,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
if (controller.bccRecipientState.value == PrefixRecipientState.enabled)
|
if (controller.bccRecipientState.value == PrefixRecipientState.enabled)
|
||||||
RecipientComposerWidget(
|
RecipientComposerWidget(
|
||||||
@@ -170,6 +173,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
if (controller.replyToRecipientState.value == PrefixRecipientState.enabled)
|
if (controller.replyToRecipientState.value == PrefixRecipientState.enabled)
|
||||||
RecipientComposerWidget(
|
RecipientComposerWidget(
|
||||||
@@ -194,6 +198,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
@@ -389,6 +394,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
if (controller.ccRecipientState.value == PrefixRecipientState.enabled)
|
if (controller.ccRecipientState.value == PrefixRecipientState.enabled)
|
||||||
RecipientComposerWidget(
|
RecipientComposerWidget(
|
||||||
@@ -414,6 +420,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
if (controller.bccRecipientState.value == PrefixRecipientState.enabled)
|
if (controller.bccRecipientState.value == PrefixRecipientState.enabled)
|
||||||
RecipientComposerWidget(
|
RecipientComposerWidget(
|
||||||
@@ -439,6 +446,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
if (controller.replyToRecipientState.value == PrefixRecipientState.enabled)
|
if (controller.replyToRecipientState.value == PrefixRecipientState.enabled)
|
||||||
RecipientComposerWidget(
|
RecipientComposerWidget(
|
||||||
@@ -463,6 +471,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
@@ -701,6 +710,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
if (controller.ccRecipientState.value == PrefixRecipientState.enabled)
|
if (controller.ccRecipientState.value == PrefixRecipientState.enabled)
|
||||||
RecipientComposerWidget(
|
RecipientComposerWidget(
|
||||||
@@ -726,6 +736,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
if (controller.bccRecipientState.value == PrefixRecipientState.enabled)
|
if (controller.bccRecipientState.value == PrefixRecipientState.enabled)
|
||||||
RecipientComposerWidget(
|
RecipientComposerWidget(
|
||||||
@@ -751,6 +762,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
if (controller.replyToRecipientState.value == PrefixRecipientState.enabled)
|
if (controller.replyToRecipientState.value == PrefixRecipientState.enabled)
|
||||||
RecipientComposerWidget(
|
RecipientComposerWidget(
|
||||||
@@ -775,6 +787,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
|
||||||
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
|
||||||
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress,
|
||||||
|
onEditRecipientAction: controller.onEditRecipient,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
|
import 'package:model/email/prefix_email_address.dart';
|
||||||
|
import 'package:model/extensions/email_address_extension.dart';
|
||||||
|
import 'package:model/mailbox/expand_mode.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/email_address_action_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/popup_menu_item_email_address_action_type.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
|
extension HandleEditRecipientExtension on ComposerController {
|
||||||
|
void onEditRecipient(
|
||||||
|
BuildContext context,
|
||||||
|
PrefixEmailAddress prefix,
|
||||||
|
EmailAddress emailAddress,
|
||||||
|
RelativeRect position,
|
||||||
|
) {
|
||||||
|
final popupMenuItems = EmailAddressActionType.values.map((type) {
|
||||||
|
return PopupMenuItem(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: PopupMenuItemActionWidget(
|
||||||
|
menuAction: PopupMenuItemEmailAddressActionType(
|
||||||
|
type,
|
||||||
|
AppLocalizations.of(context),
|
||||||
|
imagePaths,
|
||||||
|
),
|
||||||
|
menuActionClick: (menuAction) {
|
||||||
|
popBack();
|
||||||
|
_handleEmailAddressActionTypeClick(
|
||||||
|
context,
|
||||||
|
menuAction.action,
|
||||||
|
prefix,
|
||||||
|
emailAddress,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
openPopupMenuAction(context, position, popupMenuItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleEmailAddressActionTypeClick(
|
||||||
|
BuildContext context,
|
||||||
|
EmailAddressActionType actionType,
|
||||||
|
PrefixEmailAddress prefix,
|
||||||
|
EmailAddress emailAddress,
|
||||||
|
) {
|
||||||
|
switch (actionType) {
|
||||||
|
case EmailAddressActionType.copy:
|
||||||
|
_copyEmailAddress(context, emailAddress);
|
||||||
|
break;
|
||||||
|
case EmailAddressActionType.modify:
|
||||||
|
_modifyEmailAddress(prefix, emailAddress);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _copyEmailAddress(BuildContext context, EmailAddress emailAddress) {
|
||||||
|
Clipboard.setData(ClipboardData(text: emailAddress.emailAddress));
|
||||||
|
appToast.showToastSuccessMessage(
|
||||||
|
context,
|
||||||
|
AppLocalizations.of(context).email_address_copied_to_clipboard,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _modifyEmailAddress(
|
||||||
|
PrefixEmailAddress prefix,
|
||||||
|
EmailAddress emailAddress,
|
||||||
|
) {
|
||||||
|
switch(prefix) {
|
||||||
|
case PrefixEmailAddress.to:
|
||||||
|
listToEmailAddress.remove(emailAddress);
|
||||||
|
toAddressExpandMode.value = ExpandMode.EXPAND;
|
||||||
|
toAddressExpandMode.refresh();
|
||||||
|
|
||||||
|
toEmailAddressController.text = emailAddress.emailAddress;
|
||||||
|
toEmailAddressController.value = toEmailAddressController.value.copyWith(
|
||||||
|
text: emailAddress.emailAddress,
|
||||||
|
selection: TextSelection(
|
||||||
|
baseOffset: emailAddress.emailAddress.length,
|
||||||
|
extentOffset: emailAddress.emailAddress.length,
|
||||||
|
),
|
||||||
|
composing: TextRange.empty,
|
||||||
|
);
|
||||||
|
toAddressFocusNode?.requestFocus();
|
||||||
|
break;
|
||||||
|
case PrefixEmailAddress.cc:
|
||||||
|
listCcEmailAddress.remove(emailAddress);
|
||||||
|
ccAddressExpandMode.value = ExpandMode.EXPAND;
|
||||||
|
ccAddressExpandMode.refresh();
|
||||||
|
|
||||||
|
ccEmailAddressController.text = emailAddress.emailAddress;
|
||||||
|
ccEmailAddressController.value = ccEmailAddressController.value.copyWith(
|
||||||
|
text: emailAddress.emailAddress,
|
||||||
|
selection: TextSelection(
|
||||||
|
baseOffset: emailAddress.emailAddress.length,
|
||||||
|
extentOffset: emailAddress.emailAddress.length,
|
||||||
|
),
|
||||||
|
composing: TextRange.empty,
|
||||||
|
);
|
||||||
|
ccAddressFocusNode?.requestFocus();
|
||||||
|
break;
|
||||||
|
case PrefixEmailAddress.bcc:
|
||||||
|
listBccEmailAddress.remove(emailAddress);
|
||||||
|
bccAddressExpandMode.value = ExpandMode.EXPAND;
|
||||||
|
bccAddressExpandMode.refresh();
|
||||||
|
|
||||||
|
bccEmailAddressController.text = emailAddress.emailAddress;
|
||||||
|
bccEmailAddressController.value = bccEmailAddressController.value.copyWith(
|
||||||
|
text: emailAddress.emailAddress,
|
||||||
|
selection: TextSelection(
|
||||||
|
baseOffset: emailAddress.emailAddress.length,
|
||||||
|
extentOffset: emailAddress.emailAddress.length,
|
||||||
|
),
|
||||||
|
composing: TextRange.empty,
|
||||||
|
);
|
||||||
|
bccAddressFocusNode?.requestFocus();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
|
enum EmailAddressActionType {
|
||||||
|
copy,
|
||||||
|
modify;
|
||||||
|
|
||||||
|
String getContextMenuTitle(AppLocalizations appLocalizations) {
|
||||||
|
switch(this) {
|
||||||
|
case EmailAddressActionType.copy:
|
||||||
|
return appLocalizations.copy;
|
||||||
|
case EmailAddressActionType.modify:
|
||||||
|
return appLocalizations.modifyEmailAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getContextMenuIcon(ImagePaths imagePaths) {
|
||||||
|
switch(this) {
|
||||||
|
case EmailAddressActionType.copy:
|
||||||
|
return imagePaths.icCopy;
|
||||||
|
case EmailAddressActionType.modify:
|
||||||
|
return imagePaths.icEditRule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/model/popup_menu_item_action.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/email_address_action_type.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
|
class PopupMenuItemEmailAddressActionType
|
||||||
|
extends PopupMenuItemActionRequiredIcon<EmailAddressActionType> {
|
||||||
|
final AppLocalizations appLocalizations;
|
||||||
|
final ImagePaths imagePaths;
|
||||||
|
|
||||||
|
PopupMenuItemEmailAddressActionType(
|
||||||
|
super.action,
|
||||||
|
this.appLocalizations,
|
||||||
|
this.imagePaths,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get actionIcon => action.getContextMenuIcon(imagePaths);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get actionName => action.getContextMenuTitle(appLocalizations);
|
||||||
|
}
|
||||||
@@ -37,6 +37,12 @@ typedef OnFocusEmailAddressChangeAction = void Function(PrefixEmailAddress prefi
|
|||||||
typedef OnRemoveDraggableEmailAddressAction = void Function(DraggableEmailAddress draggableEmailAddress);
|
typedef OnRemoveDraggableEmailAddressAction = void Function(DraggableEmailAddress draggableEmailAddress);
|
||||||
typedef OnDeleteTagAction = void Function(EmailAddress emailAddress);
|
typedef OnDeleteTagAction = void Function(EmailAddress emailAddress);
|
||||||
typedef OnEnableAllRecipientsInputAction = void Function(bool isEnabled);
|
typedef OnEnableAllRecipientsInputAction = void Function(bool isEnabled);
|
||||||
|
typedef OnEditRecipientAction = void Function(
|
||||||
|
BuildContext context,
|
||||||
|
PrefixEmailAddress prefix,
|
||||||
|
EmailAddress emailAddress,
|
||||||
|
RelativeRect position,
|
||||||
|
);
|
||||||
|
|
||||||
class RecipientComposerWidget extends StatefulWidget {
|
class RecipientComposerWidget extends StatefulWidget {
|
||||||
|
|
||||||
@@ -62,6 +68,7 @@ class RecipientComposerWidget extends StatefulWidget {
|
|||||||
final OnShowFullListEmailAddressAction? onShowFullListEmailAddressAction;
|
final OnShowFullListEmailAddressAction? onShowFullListEmailAddressAction;
|
||||||
final OnFocusEmailAddressChangeAction? onFocusEmailAddressChangeAction;
|
final OnFocusEmailAddressChangeAction? onFocusEmailAddressChangeAction;
|
||||||
final OnRemoveDraggableEmailAddressAction? onRemoveDraggableEmailAddressAction;
|
final OnRemoveDraggableEmailAddressAction? onRemoveDraggableEmailAddressAction;
|
||||||
|
final OnEditRecipientAction? onEditRecipientAction;
|
||||||
final VoidCallback? onFocusNextAddressAction;
|
final VoidCallback? onFocusNextAddressAction;
|
||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
final EdgeInsetsGeometry? margin;
|
final EdgeInsetsGeometry? margin;
|
||||||
@@ -101,6 +108,7 @@ class RecipientComposerWidget extends StatefulWidget {
|
|||||||
this.onRemoveDraggableEmailAddressAction,
|
this.onRemoveDraggableEmailAddressAction,
|
||||||
this.onEnableAllRecipientsInputAction,
|
this.onEnableAllRecipientsInputAction,
|
||||||
this.focusNodeKeyboard,
|
this.focusNodeKeyboard,
|
||||||
|
this.onEditRecipientAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -217,6 +225,7 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
|
|||||||
maxWidth: widget.maxWidth,
|
maxWidth: widget.maxWidth,
|
||||||
onDeleteTagAction: (emailAddress) => _handleDeleteTagAction.call(emailAddress, stateSetter),
|
onDeleteTagAction: (emailAddress) => _handleDeleteTagAction.call(emailAddress, stateSetter),
|
||||||
onShowFullAction: widget.onShowFullListEmailAddressAction,
|
onShowFullAction: widget.onShowFullListEmailAddressAction,
|
||||||
|
onEditRecipientAction: widget.onEditRecipientAction,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onTagChanged: (value) => _handleOnTagChangeAction.call(value, stateSetter),
|
onTagChanged: (value) => _handleOnTagChangeAction.call(value, stateSetter),
|
||||||
@@ -309,6 +318,7 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
|
|||||||
maxWidth: widget.maxWidth,
|
maxWidth: widget.maxWidth,
|
||||||
onDeleteTagAction: (emailAddress) => _handleDeleteTagAction.call(emailAddress, stateSetter),
|
onDeleteTagAction: (emailAddress) => _handleDeleteTagAction.call(emailAddress, stateSetter),
|
||||||
onShowFullAction: widget.onShowFullListEmailAddressAction,
|
onShowFullAction: widget.onShowFullListEmailAddressAction,
|
||||||
|
onEditRecipientAction: widget.onEditRecipientAction,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onTagChanged: (value) => _handleOnTagChangeAction.call(value, stateSetter),
|
onTagChanged: (value) => _handleOnTagChangeAction.call(value, stateSetter),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
import 'package:model/email/prefix_email_address.dart';
|
import 'package:model/email/prefix_email_address.dart';
|
||||||
import 'package:model/extensions/email_address_extension.dart';
|
import 'package:model/extensions/email_address_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/smart_interaction_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/styles/recipient_tag_item_widget_style.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/styles/recipient_tag_item_widget_style.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/draggable_recipient_tag_widget.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/draggable_recipient_tag_widget.dart';
|
||||||
@@ -30,6 +31,7 @@ class RecipientTagItemWidget extends StatelessWidget {
|
|||||||
final List<EmailAddress> collapsedListEmailAddress;
|
final List<EmailAddress> collapsedListEmailAddress;
|
||||||
final OnShowFullListEmailAddressAction? onShowFullAction;
|
final OnShowFullListEmailAddressAction? onShowFullAction;
|
||||||
final OnDeleteTagAction? onDeleteTagAction;
|
final OnDeleteTagAction? onDeleteTagAction;
|
||||||
|
final OnEditRecipientAction? onEditRecipientAction;
|
||||||
final bool isTestingForWeb;
|
final bool isTestingForWeb;
|
||||||
final String? composerId;
|
final String? composerId;
|
||||||
|
|
||||||
@@ -47,6 +49,7 @@ class RecipientTagItemWidget extends StatelessWidget {
|
|||||||
this.isLatestEmail = false,
|
this.isLatestEmail = false,
|
||||||
this.onShowFullAction,
|
this.onShowFullAction,
|
||||||
this.onDeleteTagAction,
|
this.onDeleteTagAction,
|
||||||
|
this.onEditRecipientAction,
|
||||||
this.maxWidth,
|
this.maxWidth,
|
||||||
this.composerId,
|
this.composerId,
|
||||||
});
|
});
|
||||||
@@ -100,7 +103,17 @@ class RecipientTagItemWidget extends StatelessWidget {
|
|||||||
childWhenDragging: DraggableRecipientTagWidget(emailAddress: currentEmailAddress),
|
childWhenDragging: DraggableRecipientTagWidget(emailAddress: currentEmailAddress),
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
cursor: SystemMouseCursors.grab,
|
cursor: SystemMouseCursors.grab,
|
||||||
child: tagWidget,
|
child: SmartInteractionWidget(
|
||||||
|
onRightMouseClickAction: (position) => _onEditRecipientAction(
|
||||||
|
context,
|
||||||
|
position,
|
||||||
|
),
|
||||||
|
onDoubleClickAction: (position) => _onEditRecipientAction(
|
||||||
|
context,
|
||||||
|
position,
|
||||||
|
),
|
||||||
|
child: tagWidget,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -171,4 +184,13 @@ class RecipientTagItemWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onEditRecipientAction(BuildContext context, RelativeRect position) {
|
||||||
|
onEditRecipientAction?.call(
|
||||||
|
context,
|
||||||
|
prefix,
|
||||||
|
currentEmailAddress,
|
||||||
|
position,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4601,5 +4601,17 @@
|
|||||||
"placeholders": {
|
"placeholders": {
|
||||||
"days": {}
|
"days": {}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"copy": "Copy",
|
||||||
|
"@copy": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"modifyEmailAddress": "Modify email address",
|
||||||
|
"@modifyEmailAddress": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4848,4 +4848,18 @@ class AppLocalizations {
|
|||||||
args: [days],
|
args: [days],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get copy {
|
||||||
|
return Intl.message(
|
||||||
|
'Copy',
|
||||||
|
name: 'copy',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get modifyEmailAddress {
|
||||||
|
return Intl.message(
|
||||||
|
'Modify email address',
|
||||||
|
name: 'modifyEmailAddress',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user