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
@@ -1,20 +1,14 @@
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';
import 'package:tmail_ui_user/features/composer/presentation/view/mobile/mobile_edit_recipients_view.dart';
import 'package:tmail_ui_user/features/composer/presentation/view/web/desktop_edit_recipients_view.dart';
class EditRecipientsView extends StatelessWidget {
final EmailAddress emailAddress;
final ImagePaths imagePaths;
final bool isMobile;
final double width;
final VoidCallback onCopyAction;
final VoidCallback onEditAction;
final VoidCallback onCreateRuleAction;
@@ -24,6 +18,8 @@ class EditRecipientsView extends StatelessWidget {
super.key,
required this.emailAddress,
required this.imagePaths,
required this.isMobile,
required this.width,
required this.onCopyAction,
required this.onEditAction,
required this.onCreateRuleAction,
@@ -32,135 +28,25 @@ class EditRecipientsView extends StatelessWidget {
@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: 16,
top: 21,
bottom: 21,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
UserAvatarBuilder(
username: emailAddress.asString().firstLetterToUpperCase,
size: 42,
textStyle: ThemeUtils.textStyleInter600().copyWith(
fontSize: 16,
height: 22 / 16,
letterSpacing: -0.41,
color: Colors.white,
),
padding: const EdgeInsetsDirectional.only(end: 16),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (emailAddress.displayName.isNotEmpty)
Padding(
padding: const EdgeInsetsDirectional.only(end: 24),
child: Text(
emailAddress.displayName,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: AppColor.textPrimary,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
EmailAddressWithCopyWidget(
label: emailAddress.emailAddress,
copyLabelIcon: imagePaths.icCopy,
textStyle: ThemeUtils.textStyleBodyBody2(
color: AppColor.steelGray400,
),
copyIconMargin: const EdgeInsetsDirectional.only(
start: 7,
),
copyIconColor: AppColor.steelGray400,
onCopyButtonAction: onCopyAction,
),
],
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Flexible(
child: Container(
constraints: const BoxConstraints(minWidth: 127),
margin: const EdgeInsetsDirectional.only(end: 8),
height: 36,
child: ConfirmDialogButton(
label: AppLocalizations.of(context).editEmail,
backgroundColor: AppColor.primaryMain,
textColor: Colors.white,
onTapAction: onEditAction,
),
),
),
Flexible(
child: Container(
constraints: const BoxConstraints(minWidth: 134),
height: 36,
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,
),
),
],
),
),
);
if (isMobile) {
return MobileEditRecipientsView(
emailAddress: emailAddress,
imagePaths: imagePaths,
width: width,
onCopyAction: onCopyAction,
onEditAction: onEditAction,
onCreateRuleAction: onCreateRuleAction,
);
} else {
return DesktopEditRecipientsView(
emailAddress: emailAddress,
imagePaths: imagePaths,
width: width,
onCopyAction: onCopyAction,
onEditAction: onEditAction,
onCreateRuleAction: onCreateRuleAction,
onCloseAction: onCloseAction,
);
}
}
}
@@ -0,0 +1,153 @@
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: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/user_avatar_builder.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/email_address_action_type.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_action_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class MobileEditRecipientsView extends StatelessWidget {
final EmailAddress emailAddress;
final ImagePaths imagePaths;
final double width;
final VoidCallback onCopyAction;
final VoidCallback onEditAction;
final VoidCallback onCreateRuleAction;
const MobileEditRecipientsView({
super.key,
required this.emailAddress,
required this.imagePaths,
required this.width,
required this.onCopyAction,
required this.onEditAction,
required this.onCreateRuleAction,
});
@override
Widget build(BuildContext context) {
return PointerInterceptor(
child: Container(
width: width,
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),
),
],
),
padding: const EdgeInsetsDirectional.only(bottom: 6),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsetsDirectional.symmetric(
horizontal: 14,
vertical: 12,
),
child: Row(
children: [
UserAvatarBuilder(
username: emailAddress.asString().firstLetterToUpperCase,
size: 28,
textStyle: ThemeUtils.textStyleInter600().copyWith(
fontSize: 11,
height: 22 / 11,
letterSpacing: -0.41,
color: Colors.white,
),
padding: const EdgeInsetsDirectional.only(end: 16),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (emailAddress.displayName.isNotEmpty)
Padding(
padding: const EdgeInsetsDirectional.only(end: 24),
child: Text(
emailAddress.displayName,
style: Theme.of(context)
.textTheme
.titleSmall
?.copyWith(
color: AppColor.textPrimary,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
Text(
emailAddress.emailAddress,
style: ThemeUtils.textStyleInter400.copyWith(
color: AppColor.steelGray400,
fontSize: 11,
height: 14 / 11,
letterSpacing: 0.0,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
],
),
),
],
),
),
..._buildActions(AppLocalizations.of(context)),
],
),
),
);
}
List<Widget> _buildActions(AppLocalizations appLocalizations) {
return EmailAddressActionType.values.map((type) {
return Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: AppColor.gray424244.withOpacity(0.12),
width: 1,
),
),
),
child: EmailAddressActionWidget(
imagePaths: imagePaths,
actionType: type,
onClick: _handleEmailAddressActionTypeClick,
),
);
}).toList();
}
void _handleEmailAddressActionTypeClick(EmailAddressActionType actionType) {
switch (actionType) {
case EmailAddressActionType.copy:
onCopyAction();
break;
case EmailAddressActionType.edit:
onEditAction();
break;
case EmailAddressActionType.createRule:
onCreateRuleAction();
break;
}
}
}
@@ -0,0 +1,168 @@
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 DesktopEditRecipientsView extends StatelessWidget {
final EmailAddress emailAddress;
final ImagePaths imagePaths;
final double width;
final VoidCallback onCopyAction;
final VoidCallback onEditAction;
final VoidCallback onCreateRuleAction;
final VoidCallback onCloseAction;
const DesktopEditRecipientsView({
super.key,
required this.emailAddress,
required this.imagePaths,
required this.width,
required this.onCopyAction,
required this.onEditAction,
required this.onCreateRuleAction,
required this.onCloseAction,
});
@override
Widget build(BuildContext context) {
return PointerInterceptor(
child: Container(
width: width,
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: 16,
top: 21,
bottom: 21,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
UserAvatarBuilder(
username: emailAddress.asString().firstLetterToUpperCase,
size: 42,
textStyle: ThemeUtils.textStyleInter600().copyWith(
fontSize: 16,
height: 22 / 16,
letterSpacing: -0.41,
color: Colors.white,
),
padding: const EdgeInsetsDirectional.only(end: 16),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (emailAddress.displayName.isNotEmpty)
Padding(
padding: const EdgeInsetsDirectional.only(end: 24),
child: Text(
emailAddress.displayName,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: AppColor.textPrimary,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
EmailAddressWithCopyWidget(
label: emailAddress.emailAddress,
copyLabelIcon: imagePaths.icCopy,
textStyle: ThemeUtils.textStyleBodyBody2(
color: AppColor.steelGray400,
),
copyIconMargin: const EdgeInsetsDirectional.only(
start: 7,
),
copyIconColor: AppColor.steelGray400,
onCopyButtonAction: onCopyAction,
),
],
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Flexible(
child: Container(
constraints: const BoxConstraints(minWidth: 127),
margin: const EdgeInsetsDirectional.only(end: 8),
height: 36,
child: ConfirmDialogButton(
label: AppLocalizations.of(context).editEmail,
backgroundColor: AppColor.primaryMain,
textColor: Colors.white,
onTapAction: onEditAction,
),
),
),
Flexible(
child: Container(
constraints: const BoxConstraints(minWidth: 134),
height: 36,
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,
),
),
],
),
),
);
}
}