TF-2666 Apply new design for edit recipient card on web
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -75,7 +75,9 @@ class ThemeUtils {
|
|||||||
headlineSmall: TextStyle(
|
headlineSmall: TextStyle(
|
||||||
fontFamily: ConstantsUI.fontApp,
|
fontFamily: ConstantsUI.fontApp,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
letterSpacing: 0.4,
|
fontSize: 24,
|
||||||
|
height: 32 / 24,
|
||||||
|
letterSpacing: 0.0,
|
||||||
),
|
),
|
||||||
headlineLarge: TextStyle(
|
headlineLarge: TextStyle(
|
||||||
fontFamily: ConstantsUI.fontApp,
|
fontFamily: ConstantsUI.fontApp,
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/smart_interaction_widget.dart';
|
||||||
|
|
||||||
|
class CardWithSmartInteractionOverlayView extends StatefulWidget {
|
||||||
|
final Widget child;
|
||||||
|
final double overlayWidth;
|
||||||
|
final Widget Function(VoidCallback onClose) menuBuilder;
|
||||||
|
|
||||||
|
const CardWithSmartInteractionOverlayView({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
required this.menuBuilder,
|
||||||
|
this.overlayWidth = 361,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CardWithSmartInteractionOverlayView> createState() => _CardWithSmartInteractionOverlayViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CardWithSmartInteractionOverlayViewState
|
||||||
|
extends State<CardWithSmartInteractionOverlayView>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
final LayerLink _layerLink = LayerLink();
|
||||||
|
OverlayEntry? _overlayEntry;
|
||||||
|
late AnimationController _controller;
|
||||||
|
bool _isDisposed = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
reverseDuration: const Duration(milliseconds: 100),
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showPopup() {
|
||||||
|
_overlayEntry = OverlayEntry(
|
||||||
|
builder: (context) => Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fill(
|
||||||
|
child: PointerInterceptor(
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: _removePopup,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
width: widget.overlayWidth,
|
||||||
|
child: CompositedTransformFollower(
|
||||||
|
link: _layerLink,
|
||||||
|
offset: const Offset(0, 40),
|
||||||
|
showWhenUnlinked: false,
|
||||||
|
child: FadeTransition(
|
||||||
|
opacity: CurvedAnimation(
|
||||||
|
parent: _controller,
|
||||||
|
curve: Curves.easeOut,
|
||||||
|
),
|
||||||
|
child: ScaleTransition(
|
||||||
|
scale: CurvedAnimation(
|
||||||
|
parent: _controller,
|
||||||
|
curve: Curves.easeOutBack,
|
||||||
|
),
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: widget.menuBuilder(_removePopup),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Overlay.maybeOf(context)?.insert(_overlayEntry!);
|
||||||
|
_controller.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _removePopup() async {
|
||||||
|
if (_overlayEntry == null) return;
|
||||||
|
|
||||||
|
if (!_isDisposed && _controller.isAnimating == false) {
|
||||||
|
await _controller.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
_overlayEntry?.remove();
|
||||||
|
_overlayEntry = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_isDisposed = true;
|
||||||
|
_controller.dispose();
|
||||||
|
_overlayEntry?.remove();
|
||||||
|
_overlayEntry = null;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return CompositedTransformTarget(
|
||||||
|
link: _layerLink,
|
||||||
|
child: SmartInteractionWidget(
|
||||||
|
onRightMouseClickAction: ({RelativeRect? position}) => _togglePopup(),
|
||||||
|
onDoubleClickAction: ({RelativeRect? position}) => _togglePopup(),
|
||||||
|
onLongPressAction: _togglePopup,
|
||||||
|
child: widget.child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _togglePopup() {
|
||||||
|
if (_overlayEntry == null) {
|
||||||
|
_showPopup();
|
||||||
|
} else {
|
||||||
|
_removePopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,13 @@ import 'package:flutter/gestures.dart';
|
|||||||
import 'package:flutter/material.dart' hide OverlayEntry;
|
import 'package:flutter/material.dart' hide OverlayEntry;
|
||||||
import 'package:universal_html/html.dart' as html;
|
import 'package:universal_html/html.dart' as html;
|
||||||
|
|
||||||
typedef OnRightMouseClickAction = void Function(RelativeRect position);
|
typedef OnRightMouseClickAction = void Function({RelativeRect? position});
|
||||||
typedef OnDoubleClickAction = void Function(RelativeRect position);
|
typedef OnDoubleClickAction = void Function({RelativeRect? position});
|
||||||
typedef OnLongPressAction = void Function();
|
typedef OnLongPressAction = void Function();
|
||||||
|
|
||||||
class SmartInteractionWidget extends StatefulWidget {
|
class SmartInteractionWidget extends StatefulWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
final bool usePosition;
|
||||||
final OnRightMouseClickAction onRightMouseClickAction;
|
final OnRightMouseClickAction onRightMouseClickAction;
|
||||||
final OnDoubleClickAction onDoubleClickAction;
|
final OnDoubleClickAction onDoubleClickAction;
|
||||||
final OnLongPressAction onLongPressAction;
|
final OnLongPressAction onLongPressAction;
|
||||||
@@ -21,6 +22,7 @@ class SmartInteractionWidget extends StatefulWidget {
|
|||||||
required this.onRightMouseClickAction,
|
required this.onRightMouseClickAction,
|
||||||
required this.onDoubleClickAction,
|
required this.onDoubleClickAction,
|
||||||
required this.onLongPressAction,
|
required this.onLongPressAction,
|
||||||
|
this.usePosition = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -73,8 +75,12 @@ class _SmartInteractionWidgetState extends State<SmartInteractionWidget> {
|
|||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
final relativeRect = _getRelativeRectFromOffset(context, localPosition);
|
if (widget.usePosition) {
|
||||||
widget.onRightMouseClickAction(relativeRect);
|
final relativeRect = _getRelativeRectFromOffset(context, localPosition);
|
||||||
|
widget.onRightMouseClickAction(position: relativeRect);
|
||||||
|
} else {
|
||||||
|
widget.onRightMouseClickAction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,12 +119,14 @@ class _SmartInteractionWidgetState extends State<SmartInteractionWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleDoubleTap() {
|
void _handleDoubleTap() {
|
||||||
if (_lastPointerEventOffset != null) {
|
if (widget.usePosition && _lastPointerEventOffset != null) {
|
||||||
final relativeRect = _getRelativeRectFromOffset(
|
final relativeRect = _getRelativeRectFromOffset(
|
||||||
context,
|
context,
|
||||||
_lastPointerEventOffset!,
|
_lastPointerEventOffset!,
|
||||||
);
|
);
|
||||||
widget.onDoubleClickAction(relativeRect);
|
widget.onDoubleClickAction(position: relativeRect);
|
||||||
|
} else {
|
||||||
|
widget.onDoubleClickAction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,19 +135,28 @@ class _SmartInteractionWidgetState extends State<SmartInteractionWidget> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (PlatformInfo.isWeb) {
|
if (PlatformInfo.isWeb) {
|
||||||
return Listener(
|
if (widget.usePosition) {
|
||||||
key: _childKey,
|
return Listener(
|
||||||
onPointerDown: (event) {
|
key: _childKey,
|
||||||
if (event.kind == PointerDeviceKind.mouse ||
|
onPointerDown: (event) {
|
||||||
event.kind == PointerDeviceKind.touch) {
|
if (event.kind == PointerDeviceKind.mouse ||
|
||||||
_lastPointerEventOffset = event.position;
|
event.kind == PointerDeviceKind.touch) {
|
||||||
}
|
_lastPointerEventOffset = event.position;
|
||||||
},
|
}
|
||||||
child: GestureDetector(
|
},
|
||||||
|
child: GestureDetector(
|
||||||
|
onDoubleTap: _handleDoubleTap,
|
||||||
|
child: widget.child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return GestureDetector(
|
||||||
|
key: _childKey,
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
onDoubleTap: _handleDoubleTap,
|
onDoubleTap: _handleDoubleTap,
|
||||||
child: widget.child,
|
child: widget.child,
|
||||||
),
|
);
|
||||||
);
|
}
|
||||||
} else {
|
} else {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onLongPress: _handleLongPress,
|
onLongPress: _handleLongPress,
|
||||||
|
|||||||
@@ -1497,36 +1497,28 @@ class ComposerController extends BaseController
|
|||||||
} else {
|
} else {
|
||||||
switch(prefixEmailAddress) {
|
switch(prefixEmailAddress) {
|
||||||
case PrefixEmailAddress.to:
|
case PrefixEmailAddress.to:
|
||||||
if (mailboxDashBoardController.isPopupMenuOpened.isFalse) {
|
toAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
toAddressExpandMode.value = ExpandMode.COLLAPSE;
|
|
||||||
}
|
|
||||||
final inputToEmail = toEmailAddressController.text;
|
final inputToEmail = toEmailAddressController.text;
|
||||||
if (inputToEmail.trim().isNotEmpty) {
|
if (inputToEmail.trim().isNotEmpty) {
|
||||||
autoCreateEmailTagForType(PrefixEmailAddress.to, inputToEmail);
|
autoCreateEmailTagForType(PrefixEmailAddress.to, inputToEmail);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PrefixEmailAddress.cc:
|
case PrefixEmailAddress.cc:
|
||||||
if (mailboxDashBoardController.isPopupMenuOpened.isFalse) {
|
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
|
||||||
}
|
|
||||||
final inputCcEmail = ccEmailAddressController.text;
|
final inputCcEmail = ccEmailAddressController.text;
|
||||||
if (inputCcEmail.trim().isNotEmpty) {
|
if (inputCcEmail.trim().isNotEmpty) {
|
||||||
autoCreateEmailTagForType(PrefixEmailAddress.cc, inputCcEmail);
|
autoCreateEmailTagForType(PrefixEmailAddress.cc, inputCcEmail);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PrefixEmailAddress.bcc:
|
case PrefixEmailAddress.bcc:
|
||||||
if (mailboxDashBoardController.isPopupMenuOpened.isFalse) {
|
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
|
||||||
}
|
|
||||||
final inputBccEmail = bccEmailAddressController.text;
|
final inputBccEmail = bccEmailAddressController.text;
|
||||||
if (inputBccEmail.trim().isNotEmpty) {
|
if (inputBccEmail.trim().isNotEmpty) {
|
||||||
autoCreateEmailTagForType(PrefixEmailAddress.bcc, inputBccEmail);
|
autoCreateEmailTagForType(PrefixEmailAddress.bcc, inputBccEmail);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PrefixEmailAddress.replyTo:
|
case PrefixEmailAddress.replyTo:
|
||||||
if (mailboxDashBoardController.isPopupMenuOpened.isFalse) {
|
replyToAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
replyToAddressExpandMode.value = ExpandMode.COLLAPSE;
|
|
||||||
}
|
|
||||||
final inputReplyToEmail = replyToEmailAddressController.text;
|
final inputReplyToEmail = replyToEmailAddressController.text;
|
||||||
if (inputReplyToEmail.trim().isNotEmpty) {
|
if (inputReplyToEmail.trim().isNotEmpty) {
|
||||||
autoCreateEmailTagForType(PrefixEmailAddress.replyTo, inputReplyToEmail);
|
autoCreateEmailTagForType(PrefixEmailAddress.replyTo, inputReplyToEmail);
|
||||||
|
|||||||
+17
-33
@@ -1,17 +1,15 @@
|
|||||||
|
|
||||||
|
import 'package:core/utils/platform_info.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.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:model/mailbox/expand_mode.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/composer_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/context_item_email_address_action_type.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/context_item_email_address_action_type.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/email_address_action_type.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/features/mailbox_dashboard/presentation/extensions/handle_create_new_rule_filter.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_open_context_menu_extension.dart';
|
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
@@ -20,9 +18,9 @@ extension HandleEditRecipientExtension on ComposerController {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
PrefixEmailAddress prefix,
|
PrefixEmailAddress prefix,
|
||||||
EmailAddress emailAddress,
|
EmailAddress emailAddress,
|
||||||
RelativeRect? position,
|
EmailAddressActionType emailAddressActionType,
|
||||||
) {
|
) {
|
||||||
if (position == null) {
|
if (PlatformInfo.isMobile) {
|
||||||
clearFocus(context);
|
clearFocus(context);
|
||||||
|
|
||||||
final contextMenuActions = EmailAddressActionType.values
|
final contextMenuActions = EmailAddressActionType.values
|
||||||
@@ -47,34 +45,11 @@ extension HandleEditRecipientExtension on ComposerController {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (mailboxDashBoardController.isPopupMenuOpened.isTrue) return;
|
_handleEmailAddressActionTypeClick(
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
mailboxDashBoardController.openPopupMenu(
|
|
||||||
context,
|
context,
|
||||||
position,
|
emailAddressActionType,
|
||||||
popupMenuItems,
|
prefix,
|
||||||
|
emailAddress,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,6 +67,9 @@ extension HandleEditRecipientExtension on ComposerController {
|
|||||||
case EmailAddressActionType.modify:
|
case EmailAddressActionType.modify:
|
||||||
_modifyEmailAddress(prefix, emailAddress);
|
_modifyEmailAddress(prefix, emailAddress);
|
||||||
break;
|
break;
|
||||||
|
case EmailAddressActionType.createRule:
|
||||||
|
_createRuleFromEmailAddress(emailAddress);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,4 +154,10 @@ extension HandleEditRecipientExtension on ComposerController {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _createRuleFromEmailAddress(EmailAddress emailAddress) async {
|
||||||
|
await mailboxDashBoardController.openCreateEmailRuleView(
|
||||||
|
emailAddress: emailAddress,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,8 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
|||||||
|
|
||||||
enum EmailAddressActionType {
|
enum EmailAddressActionType {
|
||||||
copy,
|
copy,
|
||||||
modify;
|
modify,
|
||||||
|
createRule;
|
||||||
|
|
||||||
String getContextMenuTitle(AppLocalizations appLocalizations) {
|
String getContextMenuTitle(AppLocalizations appLocalizations) {
|
||||||
switch(this) {
|
switch(this) {
|
||||||
@@ -12,6 +13,8 @@ enum EmailAddressActionType {
|
|||||||
return appLocalizations.copy;
|
return appLocalizations.copy;
|
||||||
case EmailAddressActionType.modify:
|
case EmailAddressActionType.modify:
|
||||||
return appLocalizations.modifyEmailAddress;
|
return appLocalizations.modifyEmailAddress;
|
||||||
|
case EmailAddressActionType.createRule:
|
||||||
|
return appLocalizations.createARule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +24,8 @@ enum EmailAddressActionType {
|
|||||||
return imagePaths.icCopy;
|
return imagePaths.icCopy;
|
||||||
case EmailAddressActionType.modify:
|
case EmailAddressActionType.modify:
|
||||||
return imagePaths.icEditRule;
|
return imagePaths.icEditRule;
|
||||||
|
case EmailAddressActionType.createRule:
|
||||||
|
return imagePaths.icQuickCreatingRule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/presentation/extensions/string_extension.dart';
|
||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/presentation/utils/theme_utils.dart';
|
||||||
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
|
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
|
import 'package:model/extensions/email_address_extension.dart';
|
||||||
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/email_address_with_copy_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/user_avatar_builder.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
|
class EditRecipientsView extends StatelessWidget {
|
||||||
|
final EmailAddress emailAddress;
|
||||||
|
final ImagePaths imagePaths;
|
||||||
|
final VoidCallback onCopyAction;
|
||||||
|
final VoidCallback onEditAction;
|
||||||
|
final VoidCallback onCreateRuleAction;
|
||||||
|
final VoidCallback onCloseAction;
|
||||||
|
|
||||||
|
const EditRecipientsView({
|
||||||
|
super.key,
|
||||||
|
required this.emailAddress,
|
||||||
|
required this.imagePaths,
|
||||||
|
required this.onCopyAction,
|
||||||
|
required this.onEditAction,
|
||||||
|
required this.onCreateRuleAction,
|
||||||
|
required this.onCloseAction,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return PointerInterceptor(
|
||||||
|
child: Container(
|
||||||
|
width: 361,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.15),
|
||||||
|
blurRadius: 8,
|
||||||
|
spreadRadius: 3,
|
||||||
|
offset: const Offset(0, 4),
|
||||||
|
),
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.3),
|
||||||
|
blurRadius: 3,
|
||||||
|
offset: const Offset(0, 1),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.only(
|
||||||
|
start: 16,
|
||||||
|
end: 24,
|
||||||
|
top: 16,
|
||||||
|
bottom: 16,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
UserAvatarBuilder(
|
||||||
|
username: emailAddress.asString().firstLetterToUpperCase,
|
||||||
|
size: 67,
|
||||||
|
textStyle: ThemeUtils.textStyleInter500().copyWith(
|
||||||
|
fontSize: 33.5,
|
||||||
|
height: 50.25 / 33.5,
|
||||||
|
letterSpacing: 0.31,
|
||||||
|
color: AppColor.secondaryContrastText,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsetsDirectional.only(end: 21),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (emailAddress.displayName.isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.only(end: 12),
|
||||||
|
child: Text(
|
||||||
|
emailAddress.displayName,
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||||
|
color: AppColor.textPrimary,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
EmailAddressWithCopyWidget(
|
||||||
|
label: emailAddress.emailAddress,
|
||||||
|
copyLabelIcon: imagePaths.icCopy,
|
||||||
|
onCopyButtonAction: onCopyAction,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 13),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 48,
|
||||||
|
child: ConfirmDialogButton(
|
||||||
|
label: AppLocalizations.of(context).editEmail,
|
||||||
|
backgroundColor: AppColor.primaryMain,
|
||||||
|
textColor: Colors.white,
|
||||||
|
onTapAction: onEditAction,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 48,
|
||||||
|
child: ConfirmDialogButton(
|
||||||
|
label: AppLocalizations.of(context).createARule,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
textColor: AppColor.primaryMain,
|
||||||
|
borderColor: AppColor.primaryMain,
|
||||||
|
onTapAction: onCreateRuleAction,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PositionedDirectional(
|
||||||
|
top: 0,
|
||||||
|
end: 0,
|
||||||
|
child: TMailButtonWidget.fromIcon(
|
||||||
|
icon: imagePaths.icCloseDialog,
|
||||||
|
iconSize: 24,
|
||||||
|
iconColor: AppColor.m3Tertiary,
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
borderRadius: 24,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
onTapActionCallback: onCloseAction,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import 'package:super_tag_editor/tag_editor.dart';
|
|||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_email_address_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_email_address_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/mail_address_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/mail_address_extension.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/model/email_address_action_type.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';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/suggestion_email_address.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/suggestion_email_address.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/styles/recipient_composer_widget_style.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/styles/recipient_composer_widget_style.dart';
|
||||||
@@ -41,7 +42,7 @@ typedef OnEditRecipientAction = void Function(
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
PrefixEmailAddress prefix,
|
PrefixEmailAddress prefix,
|
||||||
EmailAddress emailAddress,
|
EmailAddress emailAddress,
|
||||||
RelativeRect? position,
|
EmailAddressActionType emailAddressActionType,
|
||||||
);
|
);
|
||||||
|
|
||||||
class RecipientComposerWidget extends StatefulWidget {
|
class RecipientComposerWidget extends StatefulWidget {
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ 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/base/widget/card_with_smart_interaction_overlay_view.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/model/email_address_action_type.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/view/edit_recipients_view.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';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
|
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
|
||||||
@@ -56,16 +58,30 @@ class RecipientTagItemWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget tagWidget = SmartInteractionWidget(
|
Widget tagWidget = CardWithSmartInteractionOverlayView(
|
||||||
onRightMouseClickAction: (position) => _onEditRecipientAction(
|
menuBuilder: (onClose) => EditRecipientsView(
|
||||||
context,
|
emailAddress: currentEmailAddress,
|
||||||
position: position,
|
imagePaths: imagePaths,
|
||||||
|
onCopyAction: () => _onEditRecipientAction(
|
||||||
|
context,
|
||||||
|
EmailAddressActionType.copy,
|
||||||
|
),
|
||||||
|
onEditAction: () {
|
||||||
|
onClose();
|
||||||
|
_onEditRecipientAction(
|
||||||
|
context,
|
||||||
|
EmailAddressActionType.modify,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onCreateRuleAction: () {
|
||||||
|
onClose();
|
||||||
|
_onEditRecipientAction(
|
||||||
|
context,
|
||||||
|
EmailAddressActionType.createRule,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onCloseAction: onClose,
|
||||||
),
|
),
|
||||||
onDoubleClickAction: (position) => _onEditRecipientAction(
|
|
||||||
context,
|
|
||||||
position: position,
|
|
||||||
),
|
|
||||||
onLongPressAction: () => _onEditRecipientAction(context),
|
|
||||||
child: Chip(
|
child: Chip(
|
||||||
labelPadding: EdgeInsetsDirectional.symmetric(
|
labelPadding: EdgeInsetsDirectional.symmetric(
|
||||||
horizontal: 4,
|
horizontal: 4,
|
||||||
@@ -186,12 +202,15 @@ class RecipientTagItemWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onEditRecipientAction(BuildContext context, {RelativeRect? position}) {
|
void _onEditRecipientAction(
|
||||||
|
BuildContext context,
|
||||||
|
EmailAddressActionType emailAddressActionType,
|
||||||
|
) {
|
||||||
onEditRecipientAction?.call(
|
onEditRecipientAction?.call(
|
||||||
context,
|
context,
|
||||||
prefix,
|
prefix,
|
||||||
currentEmailAddress,
|
currentEmailAddress,
|
||||||
position,
|
emailAddressActionType,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/new_ma
|
|||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_create_filter_for_folder.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_create_new_rule_filter.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';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart';
|
||||||
import 'package:tmail_ui_user/features/push_notification/presentation/websocket/web_socket_message.dart';
|
import 'package:tmail_ui_user/features/push_notification/presentation/websocket/web_socket_message.dart';
|
||||||
@@ -1275,7 +1275,9 @@ class MailboxController extends BaseMailboxController
|
|||||||
goToCreateNewMailboxView(context, parentMailbox: mailbox);
|
goToCreateNewMailboxView(context, parentMailbox: mailbox);
|
||||||
break;
|
break;
|
||||||
case MailboxActions.createFilter:
|
case MailboxActions.createFilter:
|
||||||
mailboxDashBoardController.openCreateEmailRuleView(mailbox);
|
mailboxDashBoardController.openCreateEmailRuleView(
|
||||||
|
presentationMailbox: mailbox,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case MailboxActions.recoverDeletedMessages:
|
case MailboxActions.recoverDeletedMessages:
|
||||||
mailboxDashBoardController.gotoEmailRecovery();
|
mailboxDashBoardController.gotoEmailRecovery();
|
||||||
|
|||||||
+1
-1
@@ -111,7 +111,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller
|
|||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/cleanup_recent_search_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/cleanup_recent_search_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/delete_emails_in_mailbox_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/delete_emails_in_mailbox_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_clear_mailbox_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_clear_mailbox_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_create_filter_for_folder.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_create_new_rule_filter.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preferences_setting_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preferences_setting_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_save_email_as_draft_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_save_email_as_draft_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/initialize_app_language.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/initialize_app_language.dart';
|
||||||
|
|||||||
+7
-4
@@ -2,6 +2,7 @@
|
|||||||
import 'package:core/utils/platform_info.dart';
|
import 'package:core/utils/platform_info.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
import 'package:model/mailbox/presentation_mailbox.dart';
|
import 'package:model/mailbox/presentation_mailbox.dart';
|
||||||
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
||||||
@@ -14,11 +15,12 @@ import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
|||||||
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
extension HandleCreateFilterForFolder on MailboxDashBoardController {
|
extension HandleCreateNewRuleFilter on MailboxDashBoardController {
|
||||||
|
|
||||||
Future<void> openCreateEmailRuleView(
|
Future<void> openCreateEmailRuleView({
|
||||||
PresentationMailbox presentationMailbox,
|
PresentationMailbox? presentationMailbox,
|
||||||
) async {
|
EmailAddress? emailAddress,
|
||||||
|
}) async {
|
||||||
final accountId = this.accountId.value;
|
final accountId = this.accountId.value;
|
||||||
final session = sessionCurrent;
|
final session = sessionCurrent;
|
||||||
|
|
||||||
@@ -38,6 +40,7 @@ extension HandleCreateFilterForFolder on MailboxDashBoardController {
|
|||||||
accountId,
|
accountId,
|
||||||
session,
|
session,
|
||||||
mailboxDestination: presentationMailbox,
|
mailboxDestination: presentationMailbox,
|
||||||
|
emailAddress: emailAddress,
|
||||||
);
|
);
|
||||||
|
|
||||||
final newRuleFilterRequest = PlatformInfo.isWeb
|
final newRuleFilterRequest = PlatformInfo.isWeb
|
||||||
@@ -64,7 +64,7 @@ import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_na
|
|||||||
import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/new_mailbox_arguments.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/new_mailbox_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_create_filter_for_folder.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_create_new_rule_filter.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart';
|
||||||
import 'package:tmail_ui_user/features/search/mailbox/presentation/search_mailbox_bindings.dart';
|
import 'package:tmail_ui_user/features/search/mailbox/presentation/search_mailbox_bindings.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
|
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
|
||||||
@@ -349,7 +349,9 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa
|
|||||||
goToCreateNewMailboxView(context, parentMailbox: mailbox);
|
goToCreateNewMailboxView(context, parentMailbox: mailbox);
|
||||||
break;
|
break;
|
||||||
case MailboxActions.createFilter:
|
case MailboxActions.createFilter:
|
||||||
dashboardController.openCreateEmailRuleView(mailbox);
|
dashboardController.openCreateEmailRuleView(
|
||||||
|
presentationMailbox: mailbox,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case MailboxActions.recoverDeletedMessages:
|
case MailboxActions.recoverDeletedMessages:
|
||||||
dashboardController.gotoEmailRecovery();
|
dashboardController.gotoEmailRecovery();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"@@last_modified": "2025-07-23T15:51:45.812311",
|
"@@last_modified": "2025-07-24T16:57:08.573149",
|
||||||
"initializing_data": "Initializing data...",
|
"initializing_data": "Initializing data...",
|
||||||
"@initializing_data": {
|
"@initializing_data": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -4613,5 +4613,17 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"editEmail": "Edit email",
|
||||||
|
"@editEmail": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"createARule": "Create a rule",
|
||||||
|
"@createARule": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4862,4 +4862,18 @@ class AppLocalizations {
|
|||||||
name: 'modifyEmailAddress',
|
name: 'modifyEmailAddress',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get editEmail {
|
||||||
|
return Intl.message(
|
||||||
|
'Edit email',
|
||||||
|
name: 'editEmail',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get createARule {
|
||||||
|
return Intl.message(
|
||||||
|
'Create a rule',
|
||||||
|
name: 'createARule',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user