From 497b6325a8afb36191427ca9a115d0534c0bc054 Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 3 Jul 2025 01:28:57 +0700 Subject: [PATCH] Apply new style modal profile UI for mobile Signed-off-by: dab246 --- .../controller/single_email_controller.dart | 43 ++- .../email_address_bottom_sheet_builder.dart | 265 ++++-------------- .../widgets/email_address_detail_widget.dart | 119 ++++++++ .../widgets/email_address_dialog_builder.dart | 137 ++------- 4 files changed, 237 insertions(+), 327 deletions(-) create mode 100644 lib/features/email/presentation/widgets/email_address_detail_widget.dart diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 66e3961a4..e742dfc89 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:io'; -import 'dart:typed_data'; import 'package:better_open_file/better_open_file.dart' as open_file; import 'package:core/core.dart'; @@ -1516,23 +1515,43 @@ class SingleEmailController extends BaseController with AppLoaderMixin { void openEmailAddressDialog(BuildContext context, EmailAddress emailAddress) { if (responsiveUtils.isScreenWithShortestSide(context)) { - (EmailAddressBottomSheetBuilder(context, imagePaths, emailAddress) - ..addOnCloseContextMenuAction(() => popBack()) - ..addOnCopyEmailAddressAction((emailAddress) => copyEmailAddress(context, emailAddress)) - ..addOnComposeEmailAction((emailAddress) => composeEmailFromEmailAddress(emailAddress)) - ..addOnQuickCreatingRuleEmailBottomSheetAction((emailAddress) => quickCreatingRule(context, emailAddress)) - ).show(); + Get.bottomSheet( + PointerInterceptor( + child: EmailAddressBottomSheetBuilder( + imagePaths: imagePaths, + emailAddress: emailAddress, + onCloseDialogAction: popBack, + onCopyEmailAddressAction: (emailAddress) => + copyEmailAddress(context, emailAddress), + onComposeEmailAction: composeEmailFromEmailAddress, + onQuickCreatingRuleEmailDialogAction: (emailAddress) => + quickCreatingRule(context, emailAddress), + ), + ), + useRootNavigator: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadiusDirectional.only( + topStart: Radius.circular(16.0), + topEnd: Radius.circular(16.0), + ), + ), + isScrollControlled: true, + enableDrag: false, + backgroundColor: Colors.transparent, + ); } else { Get.dialog( PointerInterceptor( child: EmailAddressDialogBuilder( imagePaths: imagePaths, emailAddress: emailAddress, - onCloseDialogAction: () => popBack(), - onCopyEmailAddressAction: (emailAddress) => copyEmailAddress(context, emailAddress), - onComposeEmailAction: (emailAddress) => composeEmailFromEmailAddress(emailAddress), - onQuickCreatingRuleEmailDialogAction: (emailAddress) => quickCreatingRule(context, emailAddress) - ) + onCloseDialogAction: popBack, + onCopyEmailAddressAction: (emailAddress) => + copyEmailAddress(context, emailAddress), + onComposeEmailAction: composeEmailFromEmailAddress, + onQuickCreatingRuleEmailDialogAction: (emailAddress) => + quickCreatingRule(context, emailAddress), + ), ), barrierColor: AppColor.colorDefaultCupertinoActionSheet, ); diff --git a/lib/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart b/lib/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart index 5ed4ffe2a..7877ab786 100644 --- a/lib/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart +++ b/lib/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart @@ -1,216 +1,65 @@ - -import 'package:core/core.dart'; +import 'package:core/presentation/resources/image_paths.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; -import 'package:model/model.dart'; -import 'package:pointer_interceptor/pointer_interceptor.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/utils/app_utils.dart'; +import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_detail_widget.dart'; -typedef OnCloseBottomSheetAction = void Function(); -typedef OnCopyEmailAddressBottomSheetAction = void Function(EmailAddress); -typedef OnComposeEmailBottomSheetAction = void Function(EmailAddress); -typedef OnQuickCreatingRuleEmailBottomSheetAction = void Function(EmailAddress); +class EmailAddressBottomSheetBuilder extends StatelessWidget { + final ImagePaths imagePaths; + final EmailAddress emailAddress; + final OnCloseDialogAction? onCloseDialogAction; + final OnCopyEmailAddressDialogAction? onCopyEmailAddressAction; + final OnComposeEmailDialogAction? onComposeEmailAction; + final OnQuickCreatingRuleEmailDialogAction? + onQuickCreatingRuleEmailDialogAction; -class EmailAddressBottomSheetBuilder { + const EmailAddressBottomSheetBuilder({ + Key? key, + required this.imagePaths, + required this.emailAddress, + this.onCloseDialogAction, + this.onCopyEmailAddressAction, + this.onComposeEmailAction, + this.onQuickCreatingRuleEmailDialogAction, + }) : super(key: key); - final BuildContext _context; - final ImagePaths _imagePaths; - final EmailAddress _emailAddress; - - OnCloseBottomSheetAction? _onCloseBottomSheetAction; - OnCopyEmailAddressBottomSheetAction? _onCopyEmailAddressAction; - OnComposeEmailBottomSheetAction? _onComposeEmailAction; - OnQuickCreatingRuleEmailBottomSheetAction? _creatingRuleEmailBottomSheetAction; - - EmailAddressBottomSheetBuilder( - this._context, - this._imagePaths, - this._emailAddress - ); - - void addOnCloseContextMenuAction(OnCloseBottomSheetAction onCloseBottomSheetAction) { - _onCloseBottomSheetAction = onCloseBottomSheetAction; - } - - void addOnCopyEmailAddressAction(OnCopyEmailAddressBottomSheetAction onCopyEmailAddressAction) { - _onCopyEmailAddressAction = onCopyEmailAddressAction; - } - - void addOnComposeEmailAction(OnComposeEmailBottomSheetAction onComposeEmailAction) { - _onComposeEmailAction = onComposeEmailAction; - } - - void addOnQuickCreatingRuleEmailBottomSheetAction(OnQuickCreatingRuleEmailBottomSheetAction creatingRuleEmailBottomSheetAction) { - _creatingRuleEmailBottomSheetAction = creatingRuleEmailBottomSheetAction; - } - - RoundedRectangleBorder _shape() { - return const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20.0), - topRight: Radius.circular(20.0))); - } - - BoxDecoration _decoration(BuildContext context) { - return const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20.0), - topRight: Radius.circular(20.0))); - } - - void show() { - Get.bottomSheet( - PointerInterceptor(child: GestureDetector( - onTap: () => _onCloseBottomSheetAction?.call(), - child: SingleChildScrollView( - child: Container( - margin: EdgeInsets.zero, - decoration: _decoration(_context), - child: SafeArea(child: GestureDetector( - onTap: () => {}, - child: Column( - children: [ - Align( - alignment: Alignment.centerRight, - child: IconButton( - padding: const EdgeInsets.only(top: 16, right: 16), - onPressed: () => _onCloseBottomSheetAction?.call(), - icon: SvgPicture.asset( - _imagePaths.icCircleClose, - width: 24, - height: 24, - fit: BoxFit.fill))), - (AvatarBuilder() - ..text(_emailAddress.asString().firstLetterToUpperCase) - ..size(64) - ..addTextStyle(const TextStyle( - fontWeight: FontWeight.w600, - fontSize: 23, - color: Colors.white)) - ..avatarColor(_emailAddress.avatarColors)) - .build(), - if (_emailAddress.displayName.isNotEmpty) - Padding( - padding: const EdgeInsets.only( - left: 16, - right: 16, - top: 16), - child: SelectableText( - _emailAddress.displayName, - textAlign: TextAlign.center, - maxLines: 2, - style: const TextStyle( - overflow: CommonTextStyle.defaultTextOverFlow, - fontSize: 20, - fontWeight: FontWeight.w600, - color: AppColor.colorNameEmail), - )), - Material( - color: Colors.transparent, - child: InkWell( - onTap: () {}, - onLongPress: () { - AppUtils.copyEmailAddressToClipboard(_context, _emailAddress.emailAddress); - }, - borderRadius: const BorderRadius.all(Radius.circular(8)), - child: Padding( - padding: const EdgeInsetsDirectional.symmetric(horizontal: 16, vertical: 8), - child: Text( - _emailAddress.emailAddress, - textAlign: TextAlign.center, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - maxLines: 2, - style: const TextStyle( - fontSize: 17, - fontWeight: FontWeight.normal, - color: AppColor.colorMessageConfirmDialog), - ) - ), - ), - ), - Material( - borderRadius: BorderRadius.circular(20), - color: Colors.transparent, - child: TextButton( - child: Text( - AppLocalizations.of(_context).copy_email_address, - style: const TextStyle( - fontSize: 13, - color: AppColor.colorTextButton, - fontWeight: FontWeight.normal), - ), - onPressed: () => _onCopyEmailAddressAction?.call(_emailAddress) - ) - ), - const SizedBox(height: 24), - Padding( - padding: const EdgeInsets.only(left: 24, right: 24, bottom: 12), - child: SizedBox( - key: const Key('quick_creating_rule_email_button'), - width: double.infinity, - height: 44, - child: TextButton( - style: ButtonStyle( - foregroundColor: WidgetStateProperty.resolveWith( - (Set states) => AppColor.colorTextButton), - backgroundColor: WidgetStateProperty.resolveWith( - (Set states) => AppColor.colorItemEmailSelectedDesktop), - shape: WidgetStateProperty.all(RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: const BorderSide( - width: 0, - color: AppColor.colorItemEmailSelectedDesktop)))), - child: Text( - AppLocalizations.of(_context).quickCreatingRule, - style: const TextStyle( - fontSize: 17, - color: AppColor.colorTextButton, - fontWeight: FontWeight.w500)), - onPressed: () => _creatingRuleEmailBottomSheetAction?.call(_emailAddress)), - ) - ), - Padding( - padding: const EdgeInsets.only(left: 24, right: 24, bottom: 24), - child: SizedBox( - key: const Key('compose_email_button'), - width: double.infinity, - height: 44, - child: TextButton( - style: ButtonStyle( - foregroundColor: WidgetStateProperty.resolveWith( - (Set states) => Colors.white), - backgroundColor: WidgetStateProperty.resolveWith( - (Set states) => AppColor.colorTextButton), - shape: WidgetStateProperty.all(RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: const BorderSide( - width: 0, - color: AppColor.colorTextButton)))), - child: Text( - AppLocalizations.of(_context).compose_email, - style: const TextStyle( - fontSize: 17, - color: Colors.white, - fontWeight: FontWeight.w500)), - onPressed: () => _onComposeEmailAction?.call(_emailAddress)), - ) - ) - ], - ), - )), - ), + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadiusDirectional.only( + topStart: Radius.circular(16.0), + topEnd: Radius.circular(16.0), ), - )), - useRootNavigator: true, - shape: _shape(), - isScrollControlled: true, - enableDrag: false, - backgroundColor: Colors.transparent + 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, + spreadRadius: 0, + offset: const Offset(0, 1), + ), + ], + ), + child: SafeArea( + top: false, + left: false, + right: false, + child: EmailAddressDetailWidget( + imagePaths: imagePaths, + emailAddress: emailAddress, + onCloseDialogAction: onCloseDialogAction, + onCopyEmailAddressAction: onCopyEmailAddressAction, + onComposeEmailAction: onComposeEmailAction, + onQuickCreatingRuleEmailDialogAction: + onQuickCreatingRuleEmailDialogAction, + ), + ), ); } -} \ No newline at end of file +} diff --git a/lib/features/email/presentation/widgets/email_address_detail_widget.dart b/lib/features/email/presentation/widgets/email_address_detail_widget.dart new file mode 100644 index 000000000..2707ebdb2 --- /dev/null +++ b/lib/features/email/presentation/widgets/email_address_detail_widget.dart @@ -0,0 +1,119 @@ +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: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'; + +typedef OnCloseDialogAction = void Function(); +typedef OnCopyEmailAddressDialogAction = void Function(EmailAddress); +typedef OnComposeEmailDialogAction = void Function(EmailAddress); +typedef OnQuickCreatingRuleEmailDialogAction = void Function(EmailAddress); + +class EmailAddressDetailWidget extends StatelessWidget { + final ImagePaths imagePaths; + final EmailAddress emailAddress; + final OnCloseDialogAction? onCloseDialogAction; + final OnCopyEmailAddressDialogAction? onCopyEmailAddressAction; + final OnComposeEmailDialogAction? onComposeEmailAction; + final OnQuickCreatingRuleEmailDialogAction? + onQuickCreatingRuleEmailDialogAction; + + const EmailAddressDetailWidget({ + Key? key, + required this.imagePaths, + required this.emailAddress, + this.onCloseDialogAction, + this.onCopyEmailAddressAction, + this.onComposeEmailAction, + this.onQuickCreatingRuleEmailDialogAction, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 32), + child: Column( + mainAxisSize: MainAxisSize.min, + 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, + ), + ), + const SizedBox(height: 24), + if (emailAddress.displayName.isNotEmpty) + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: SelectableText( + emailAddress.displayName, + textAlign: TextAlign.center, + maxLines: 1, + style: ThemeUtils.textStyleM3HeadlineSmall.copyWith( + color: AppColor.textPrimary, + ), + ), + ), + EmailAddressWithCopyWidget( + label: emailAddress.emailAddress, + copyLabelIcon: imagePaths.icCopy, + onCopyButtonAction: () => + onCopyEmailAddressAction?.call(emailAddress), + ), + const SizedBox(height: 24), + SizedBox( + width: double.infinity, + height: 48, + child: ConfirmDialogButton( + label: AppLocalizations.of(context).quickCreatingRule, + backgroundColor: Colors.white, + textColor: AppColor.primaryMain, + borderColor: AppColor.primaryMain, + onTapAction: () => + onQuickCreatingRuleEmailDialogAction?.call(emailAddress), + ), + ), + const SizedBox(height: 14), + SizedBox( + width: double.infinity, + height: 48, + child: ConfirmDialogButton( + label: AppLocalizations.of(context).compose_email, + backgroundColor: AppColor.primaryMain, + textColor: Colors.white, + onTapAction: () => onComposeEmailAction?.call(emailAddress), + ), + ), + ], + ), + ), + 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: onCloseDialogAction, + ), + ), + ], + ); + } +} diff --git a/lib/features/email/presentation/widgets/email_address_dialog_builder.dart b/lib/features/email/presentation/widgets/email_address_dialog_builder.dart index b2638dbba..c6600ee76 100644 --- a/lib/features/email/presentation/widgets/email_address_dialog_builder.dart +++ b/lib/features/email/presentation/widgets/email_address_dialog_builder.dart @@ -1,15 +1,7 @@ -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: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/email/presentation/widgets/email_address_detail_widget.dart'; typedef OnCloseDialogAction = void Function(); typedef OnCopyEmailAddressDialogAction = void Function(EmailAddress); @@ -42,104 +34,35 @@ class EmailAddressDialogBuilder extends StatelessWidget { borderRadius: BorderRadius.all(Radius.circular(16)), ), child: Container( - width: 383, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(16)), - 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, - spreadRadius: 0, - offset: const Offset(0, 1), - ), - ], - ), - child: Stack( - children: [ - Padding( - padding: - const EdgeInsets.symmetric(vertical: 24, horizontal: 32), - child: Column( - mainAxisSize: MainAxisSize.min, - 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, - ), - ), - const SizedBox(height: 24), - if (emailAddress.displayName.isNotEmpty) - Padding( - padding: const EdgeInsets.only(bottom: 8), - child: SelectableText( - emailAddress.displayName, - textAlign: TextAlign.center, - style: ThemeUtils.textStyleM3HeadlineSmall.copyWith( - color: AppColor.textPrimary, - ), - ), - ), - EmailAddressWithCopyWidget( - label: emailAddress.emailAddress, - copyLabelIcon: imagePaths.icCopy, - onCopyButtonAction: () => - onCopyEmailAddressAction?.call(emailAddress), - ), - const SizedBox(height: 24), - SizedBox( - width: double.infinity, - height: 48, - child: ConfirmDialogButton( - label: AppLocalizations.of(context).quickCreatingRule, - backgroundColor: Colors.white, - textColor: AppColor.primaryMain, - borderColor: AppColor.primaryMain, - onTapAction: () => - onQuickCreatingRuleEmailDialogAction?.call(emailAddress), - ), - ), - const SizedBox(height: 14), - SizedBox( - width: double.infinity, - height: 48, - child: ConfirmDialogButton( - label: AppLocalizations.of(context).compose_email, - backgroundColor: AppColor.primaryMain, - textColor: Colors.white, - onTapAction: () => - onComposeEmailAction?.call(emailAddress), - ), - ), - ], - ), - ), - 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: onCloseDialogAction, - ), - ) - ], - )), + width: 383, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(16)), + 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, + spreadRadius: 0, + offset: const Offset(0, 1), + ), + ], + ), + child: EmailAddressDetailWidget( + imagePaths: imagePaths, + emailAddress: emailAddress, + onCloseDialogAction: onCloseDialogAction, + onCopyEmailAddressAction: onCopyEmailAddressAction, + onComposeEmailAction: onComposeEmailAction, + onQuickCreatingRuleEmailDialogAction: + onQuickCreatingRuleEmailDialogAction, + ), + ), ); } }