TF-2666 Apply new UI for edit recipient card on mobile

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2025-07-25 12:59:09 +07:00
committed by Dat H. Pham
parent 7f7dba14fb
commit 84616f210c
17 changed files with 503 additions and 254 deletions
@@ -0,0 +1,63 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.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/composer/presentation/model/email_address_action_type.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnClickEmailAddressAction = void Function(EmailAddressActionType actionType);
class EmailAddressActionWidget extends StatelessWidget {
final ImagePaths imagePaths;
final EmailAddressActionType actionType;
final OnClickEmailAddressAction onClick;
const EmailAddressActionWidget({
super.key,
required this.imagePaths,
required this.actionType,
required this.onClick,
});
@override
Widget build(BuildContext context) {
return Material(
type: MaterialType.transparency,
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () => onClick(actionType),
child: Container(
width: double.infinity,
height: 36,
padding: const EdgeInsets.symmetric(horizontal: 14),
child: Row(
children: [
SvgPicture.asset(
actionType.getContextMenuIcon(imagePaths),
width: 20,
height: 20,
colorFilter: AppColor.steelGrayA540.asFilter(),
fit: BoxFit.fill,
),
const SizedBox(width: 22),
Expanded(
child: Text(
actionType.getContextMenuTitle(AppLocalizations.of(context)),
style: ThemeUtils.textStyleInter400.copyWith(
fontSize: 14,
height: 21.01 / 14,
letterSpacing: -0.15,
color: AppColor.gray424244.withOpacity(0.9),
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
),
);
}
}
@@ -12,6 +12,7 @@ import 'package:core/utils/platform_info.dart';
import 'package:core/utils/string_convert.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.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';
@@ -77,6 +78,7 @@ class RecipientComposerWidget extends StatefulWidget {
final bool isTestingForWeb;
final int minInputLengthAutocomplete;
final String? composerId;
final VoidCallback? onClearFocusAction;
const RecipientComposerWidget({
super.key,
@@ -110,6 +112,7 @@ class RecipientComposerWidget extends StatefulWidget {
this.onEnableAllRecipientsInputAction,
this.focusNodeKeyboard,
this.onEditRecipientAction,
this.onClearFocusAction,
});
@override
@@ -118,6 +121,8 @@ class RecipientComposerWidget extends StatefulWidget {
class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
final _responsiveUtils = Get.find<ResponsiveUtils>();
Timer? _gapBetweenTagChangedAndFindSuggestion;
bool _lastTagFocused = false;
bool _isDragging = false;
@@ -224,9 +229,11 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
isCollapsed: _isCollapse,
isLatestTagFocused: _lastTagFocused,
maxWidth: widget.maxWidth,
isMobile: _responsiveUtils.isMobile(context),
onDeleteTagAction: (emailAddress) => _handleDeleteTagAction.call(emailAddress, stateSetter),
onShowFullAction: widget.onShowFullListEmailAddressAction,
onEditRecipientAction: widget.onEditRecipientAction,
onClearFocusAction: widget.onClearFocusAction,
);
},
onTagChanged: (value) => _handleOnTagChangeAction.call(value, stateSetter),
@@ -317,9 +324,11 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
isCollapsed: _isCollapse,
isLatestTagFocused: _lastTagFocused,
maxWidth: widget.maxWidth,
isMobile: _responsiveUtils.isMobile(context),
onDeleteTagAction: (emailAddress) => _handleDeleteTagAction.call(emailAddress, stateSetter),
onShowFullAction: widget.onShowFullListEmailAddressAction,
onEditRecipientAction: widget.onEditRecipientAction,
onClearFocusAction: widget.onClearFocusAction,
);
},
onTagChanged: (value) => _handleOnTagChangeAction.call(value, stateSetter),
@@ -1,7 +1,6 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/text/rich_text_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
@@ -50,7 +49,6 @@ class RecipientSuggestionItemWidget extends StatelessWidget {
child: Padding(
padding: RecipientSuggestionItemWidgetStyle.labelPadding,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AvatarSuggestionItemWidget(emailAddress: emailAddress),
const SizedBox(width: 8),
@@ -58,18 +56,22 @@ class RecipientSuggestionItemWidget extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
RichTextWidget(
textOrigin: emailAddress.asString(),
wordSearched: suggestionValid ?? '',
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
if (emailAddress.displayName.isNotEmpty)
RichTextBuilder(
RichTextWidget(
textOrigin: emailAddress.emailAddress,
wordToStyle: suggestionValid ?? '',
styleOrigin: RecipientSuggestionItemWidgetStyle.labelTextStyle,
styleWord: RecipientSuggestionItemWidgetStyle.labelHighlightTextStyle,
wordSearched: suggestionValid ?? '',
overflow: TextOverflow.ellipsis,
maxLines: 1,
styleTextOrigin: RecipientSuggestionItemWidgetStyle.labelTextStyle,
styleWordSearched: RecipientSuggestionItemWidgetStyle.labelHighlightTextStyle,
),
],
),
@@ -98,7 +100,6 @@ class RecipientSuggestionItemWidget extends StatelessWidget {
child: Padding(
padding: RecipientSuggestionItemWidgetStyle.labelPadding,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AvatarSuggestionItemWidget(emailAddress: emailAddress),
const SizedBox(width: 8),
@@ -106,18 +107,22 @@ class RecipientSuggestionItemWidget extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
RichTextWidget(
textOrigin: emailAddress.asString(),
wordSearched: suggestionValid ?? '',
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
if (emailAddress.displayName.isNotEmpty)
RichTextBuilder(
RichTextWidget(
textOrigin: emailAddress.emailAddress,
wordToStyle: suggestionValid ?? '',
styleOrigin: RecipientSuggestionItemWidgetStyle.labelTextStyle,
styleWord: RecipientSuggestionItemWidgetStyle.labelHighlightTextStyle,
wordSearched: suggestionValid ?? '',
overflow: TextOverflow.ellipsis,
maxLines: 1,
styleTextOrigin: RecipientSuggestionItemWidgetStyle.labelTextStyle,
styleWordSearched: RecipientSuggestionItemWidgetStyle.labelHighlightTextStyle,
),
],
),
@@ -36,6 +36,8 @@ class RecipientTagItemWidget extends StatelessWidget {
final OnEditRecipientAction? onEditRecipientAction;
final bool isTestingForWeb;
final String? composerId;
final bool isMobile;
final VoidCallback? onClearFocusAction;
const RecipientTagItemWidget({
super.key,
@@ -49,28 +51,39 @@ class RecipientTagItemWidget extends StatelessWidget {
this.isCollapsed = false,
this.isLatestTagFocused = false,
this.isLatestEmail = false,
this.isMobile = false,
this.onShowFullAction,
this.onDeleteTagAction,
this.onEditRecipientAction,
this.maxWidth,
this.composerId,
this.onClearFocusAction,
});
@override
Widget build(BuildContext context) {
final overlayWidth = isMobile ? 227.0 : 361.0;
Widget tagWidget = CardWithSmartInteractionOverlayView(
overlayWidth: overlayWidth,
menuBuilder: (onClose) => EditRecipientsView(
emailAddress: currentEmailAddress,
imagePaths: imagePaths,
onCopyAction: () => _onEditRecipientAction(
context,
EmailAddressActionType.copy,
),
isMobile: isMobile,
width: overlayWidth,
onCopyAction: () {
if (isMobile) {
onClose();
}
_onEditRecipientAction(
context,
EmailAddressActionType.copy,
);
},
onEditAction: () {
onClose();
_onEditRecipientAction(
context,
EmailAddressActionType.modify,
EmailAddressActionType.edit,
);
},
onCreateRuleAction: () {
@@ -82,6 +95,7 @@ class RecipientTagItemWidget extends StatelessWidget {
},
onCloseAction: onClose,
),
onClearFocusAction: onClearFocusAction,
child: Chip(
labelPadding: EdgeInsetsDirectional.symmetric(
horizontal: 4,