diff --git a/lib/features/base/widget/report_message_banner.dart b/lib/features/base/widget/report_message_banner.dart new file mode 100644 index 000000000..ef6330b1b --- /dev/null +++ b/lib/features/base/widget/report_message_banner.dart @@ -0,0 +1,153 @@ +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:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class ReportMessageBanner extends StatelessWidget { + final ImagePaths imagePaths; + final String message; + final String positiveName; + final VoidCallback onPositiveAction; + final VoidCallback onNegativeAction; + final bool isDesktop; + final EdgeInsetsGeometry? margin; + + const ReportMessageBanner({ + super.key, + required this.imagePaths, + required this.message, + required this.positiveName, + required this.onNegativeAction, + required this.onPositiveAction, + this.isDesktop = true, + this.margin, + }); + + @override + Widget build(BuildContext context) { + final closeButton = TMailButtonWidget.fromIcon( + icon: imagePaths.icCloseDialog, + iconSize: 24, + iconColor: AppColor.m3Tertiary, + padding: const EdgeInsets.all(5), + backgroundColor: Colors.transparent, + onTapActionCallback: onNegativeAction, + ); + + late Widget bodyBanner; + + if (isDesktop) { + bodyBanner = Stack( + children: [ + Center( + child: Padding( + padding: const EdgeInsetsDirectional.symmetric(horizontal: 40), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset( + imagePaths.icInfoCircleOutline, + width: 20, + height: 20, + fit: BoxFit.fill, + colorFilter: AppColor.steelGray200.asFilter(), + ), + const SizedBox(width: 8), + Flexible( + child: Text( + message, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: AppColor.steelGray400, + ), + ), + ), + const SizedBox(width: 4), + TMailButtonWidget.fromText( + text: positiveName, + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + backgroundColor: Colors.transparent, + textStyle: ThemeUtils.textStyleInter700( + color: AppColor.blue700, + fontSize: 14, + ), + onTapActionCallback: onPositiveAction, + ), + ], + ), + ), + ), + Align( + alignment: AlignmentDirectional.centerEnd, + child: closeButton, + ), + ], + ); + } else { + bodyBanner = Row( + children: [ + SvgPicture.asset( + imagePaths.icInfoCircleOutline, + width: 20, + height: 20, + fit: BoxFit.fill, + colorFilter: AppColor.steelGray200.asFilter(), + ), + const SizedBox(width: 12), + Expanded( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + message, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: AppColor.steelGray400, + ), + ), + ), + const SizedBox(width: 4), + TMailButtonWidget.fromText( + text: positiveName, + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + backgroundColor: Colors.transparent, + textStyle: ThemeUtils.textStyleInter700( + color: AppColor.blue700, + fontSize: 14, + ), + onTapActionCallback: onPositiveAction, + ) + ], + ), + ), + const SizedBox(width: 12), + closeButton, + ], + ); + } + + return Container( + decoration: BoxDecoration( + color: _backgroundColor, + borderRadius: const BorderRadius.all(Radius.circular(8)), + ), + padding: const EdgeInsetsDirectional.only(start: 16, end: 12), + margin: margin, + height: _bannerHeight, + child: bodyBanner, + ); + } + + Color get _backgroundColor => isDesktop + ? AppColor.lightGrayEAEDF2 + : AppColor.m3LayerDarkOutline.withOpacity(0.08); + + double get _bannerHeight => isDesktop ? 44 : 52; +} diff --git a/lib/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart index 7f8283cc8..ca3807ab7 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart @@ -109,4 +109,8 @@ class SpamReportController extends BaseController { void setSpamPresentationMailbox(PresentationMailbox? spamMailbox) { presentationSpamMailbox.value = spamMailbox; } + + bool get showReportBanner => + spamReportState.value == SpamReportState.disabled || + presentationSpamMailbox.value == null; } diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart index 2fa4488c5..c35a16a5b 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -9,6 +9,7 @@ import 'package:model/extensions/presentation_mailbox_extension.dart'; import 'package:model/extensions/session_extension.dart'; import 'package:tmail_ui_user/features/base/widget/clean_messages_banner.dart'; import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart'; +import 'package:tmail_ui_user/features/base/widget/report_message_banner.dart'; import 'package:tmail_ui_user/features/base/widget/scrollbar_list_view.dart'; import 'package:tmail_ui_user/features/composer/presentation/view/web/composer_overlay_view.dart'; import 'package:tmail_ui_user/features/email/presentation/email_view.dart'; @@ -48,8 +49,8 @@ import 'package:tmail_ui_user/features/search/email/presentation/search_email_vi import 'package:tmail_ui_user/features/search/mailbox/presentation/search_mailbox_view.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/popup_menu_item_filter_message_action.dart'; +import 'package:tmail_ui_user/features/thread/presentation/styles/spam_banner/spam_report_banner_web_styles.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart'; -import 'package:tmail_ui_user/features/thread/presentation/widgets/spam_banner/spam_report_banner_web_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; @@ -172,7 +173,29 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { Obx(() => MarkMailboxAsReadLoadingBanner( viewState: controller.viewStateMailboxActionProgress.value, )), - const SpamReportBannerWebWidget(), + Obx(() { + final spamController = controller.spamReportController; + if (spamController.showReportBanner) { + return const SizedBox.shrink(); + } + + return ReportMessageBanner( + imagePaths: controller.imagePaths, + message: AppLocalizations + .of(context) + .countMessageInSpam( + spamController.numberOfUnreadSpamEmails, + ), + positiveName: AppLocalizations.of(context).view, + isDesktop: controller + .responsiveUtils + .isDesktop(context), + margin: SpamReportBannerWebStyles.bannerMargin, + onPositiveAction: spamController.openMailbox, + onNegativeAction: () => + spamController.dismissSpamReportAction(context), + ); + }), QuotasBannerWidget(), _buildVacationNotificationMessage(context), Obx(() { diff --git a/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_button_widget.dart b/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_button_widget.dart deleted file mode 100644 index 86912329e..000000000 --- a/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_button_widget.dart +++ /dev/null @@ -1,93 +0,0 @@ - -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/utils/style_utils.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/thread/presentation/styles/spam_banner/spam_report_banner_button_styles.dart'; - -class SpamReportBannerButtonWidget extends StatelessWidget { - - final String label; - final Color labelColor; - final VoidCallback onTap; - final String? icon; - final bool iconLeftAlignment; - final bool wrapContent; - - const SpamReportBannerButtonWidget({ - super.key, - required this.label, - required this.labelColor, - required this.onTap, - this.icon, - this.iconLeftAlignment = true, - this.wrapContent = false, - }); - - @override - Widget build(BuildContext context) { - return Material( - color: Colors.transparent, - child: InkWell( - onTap: onTap, - borderRadius: const BorderRadius.all(Radius.circular(SpamReportBannerButtonStyles.borderRadius)), - child: Container( - width: wrapContent ? null : double.infinity, - padding: const EdgeInsetsDirectional.all(SpamReportBannerButtonStyles.padding), - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(SpamReportBannerButtonStyles.borderRadius)), - color: SpamReportBannerButtonStyles.backgroundColor - ), - child: icon == null - ? Text( - label, - textAlign: TextAlign.center, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: SpamReportBannerButtonStyles.labelTextSize, - color: labelColor, - fontWeight: FontWeight.w400 - ) - ) - : Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (iconLeftAlignment) - Padding( - padding: const EdgeInsetsDirectional.only(end: SpamReportBannerButtonStyles.paddingIcon), - child: SvgPicture.asset( - icon!, - width: SpamReportBannerButtonStyles.iconSize, - height: SpamReportBannerButtonStyles.iconSize, - colorFilter: labelColor.asFilter(), - ), - ), - Text( - label, - textAlign: TextAlign.center, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: SpamReportBannerButtonStyles.labelTextSize, - color: labelColor, - fontWeight: FontWeight.w400 - ) - ), - if (!iconLeftAlignment) - Padding( - padding: const EdgeInsetsDirectional.only(start: SpamReportBannerButtonStyles.paddingIcon), - child: SvgPicture.asset( - icon!, - width: SpamReportBannerButtonStyles.iconSize, - height: SpamReportBannerButtonStyles.iconSize, - colorFilter: labelColor.asFilter(), - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_label_widget.dart b/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_label_widget.dart deleted file mode 100644 index a4b64e1f8..000000000 --- a/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_label_widget.dart +++ /dev/null @@ -1,60 +0,0 @@ - -import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/utils/responsive_utils.dart'; -import 'package:core/presentation/utils/theme_utils.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/thread/presentation/styles/spam_banner/spam_report_banner_label_styles.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; - -class SpamReportBannerLabelWidget extends StatelessWidget { - - final String countSpamEmailsAsString; - final Color labelColor; - - const SpamReportBannerLabelWidget({ - super.key, - required this.countSpamEmailsAsString, - this.labelColor = SpamReportBannerLabelStyles.labelTextColor - }); - - @override - Widget build(BuildContext context) { - final responsiveUtils = Get.find(); - final imagePaths = Get.find(); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset( - imagePaths.icInfoCircleOutline, - width: SpamReportBannerLabelStyles.iconSize, - height: SpamReportBannerLabelStyles.iconSize - ), - const SizedBox(width: SpamReportBannerLabelStyles.space), - if (responsiveUtils.isWebDesktop(context)) - Text( - AppLocalizations.of(context).countNewSpamEmails(countSpamEmailsAsString), - textAlign: TextAlign.center, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: SpamReportBannerLabelStyles.labelTextSize, - color: labelColor, - fontWeight: FontWeight.w500 - ), - ) - else - Flexible( - child: Text( - AppLocalizations.of(context).countNewSpamEmails(countSpamEmailsAsString), - textAlign: TextAlign.center, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: SpamReportBannerLabelStyles.labelTextSize, - color: labelColor, - fontWeight: FontWeight.w500 - ), - ), - ) - ], - ); - } -} diff --git a/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_web_widget.dart b/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_web_widget.dart deleted file mode 100644 index a6c691cf1..000000000 --- a/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_web_widget.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/utils/direction_utils.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/spam_report_state.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; -import 'package:tmail_ui_user/features/thread/presentation/styles/spam_banner/spam_report_banner_button_styles.dart'; -import 'package:tmail_ui_user/features/thread/presentation/styles/spam_banner/spam_report_banner_label_styles.dart'; -import 'package:tmail_ui_user/features/thread/presentation/styles/spam_banner/spam_report_banner_web_styles.dart'; -import 'package:tmail_ui_user/features/thread/presentation/widgets/spam_banner/spam_report_banner_button_widget.dart'; -import 'package:tmail_ui_user/features/thread/presentation/widgets/spam_banner/spam_report_banner_label_widget.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; - -class SpamReportBannerWebWidget extends StatelessWidget { - const SpamReportBannerWebWidget({ Key? key }) : super(key: key); - - @override - Widget build(BuildContext context){ - final spamReportController = Get.find(); - final imagePaths = Get.find(); - return Obx(() { - if (spamReportController.spamReportState.value == SpamReportState.disabled - || spamReportController.presentationSpamMailbox.value == null) { - return const SizedBox.shrink(); - } - return Container( - margin: SpamReportBannerWebStyles.bannerMargin, - width: double.infinity, - padding: SpamReportBannerWebStyles.bannerPadding, - decoration: ShapeDecoration( - color: SpamReportBannerWebStyles.backgroundColor, - shape: const RoundedRectangleBorder( - side: BorderSide( - width: 1, - color: SpamReportBannerWebStyles.strokeBorderColor, - ), - borderRadius: BorderRadius.all(Radius.circular(SpamReportBannerWebStyles.borderRadius)), - ), - ), - child: Stack( - alignment: AlignmentDirectional.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - SpamReportBannerLabelWidget( - countSpamEmailsAsString: spamReportController.numberOfUnreadSpamEmails, - labelColor: SpamReportBannerLabelStyles.highlightLabelTextColor - ), - const SizedBox(width: 32), - SpamReportBannerButtonWidget( - label: AppLocalizations.of(context).showDetails, - labelColor: SpamReportBannerButtonStyles.positiveButtonTextColor, - onTap: spamReportController.openMailbox, - icon: DirectionUtils.isDirectionRTLByLanguage(context) - ? imagePaths.icArrowLeft - : imagePaths.icArrowRight, - iconLeftAlignment: false, - wrapContent: true, - ), - ], - ), - PositionedDirectional( - end: 0, - child: SpamReportBannerButtonWidget( - label: AppLocalizations.of(context).dismiss, - labelColor: SpamReportBannerButtonStyles.negativeButtonTextColor, - onTap: () => spamReportController.dismissSpamReportAction(context), - icon: imagePaths.icClose, - wrapContent: true, - ), - ) - ], - ), - ); - }); - } -} \ No newline at end of file diff --git a/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_widget.dart b/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_widget.dart deleted file mode 100644 index 0fab3f0aa..000000000 --- a/lib/features/thread/presentation/widgets/spam_banner/spam_report_banner_widget.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/spam_report_state.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; -import 'package:tmail_ui_user/features/thread/presentation/styles/spam_banner/spam_report_banner_button_styles.dart'; -import 'package:tmail_ui_user/features/thread/presentation/styles/spam_banner/spam_report_banner_styles.dart'; -import 'package:tmail_ui_user/features/thread/presentation/widgets/spam_banner/spam_report_banner_button_widget.dart'; -import 'package:tmail_ui_user/features/thread/presentation/widgets/spam_banner/spam_report_banner_label_widget.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; - -class SpamReportBannerWidget extends StatelessWidget { - final EdgeInsetsGeometry? margin; - final SpamReportController spamReportController; - - const SpamReportBannerWidget({ - Key? key, - required this.spamReportController, - this.margin - }) : super(key: key); - - @override - Widget build(BuildContext context){ - return Obx(() { - if (spamReportController.spamReportState.value == SpamReportState.disabled - || spamReportController.presentationSpamMailbox.value == null) { - return const SizedBox.shrink(); - } - return Container( - margin: margin, - padding: const EdgeInsetsDirectional.all(SpamReportBannerStyles.padding), - decoration: ShapeDecoration( - color: SpamReportBannerStyles.backgroundColor, - shape: const RoundedRectangleBorder( - side: BorderSide( - width: 1, - color: SpamReportBannerStyles.strokeBorderColor, - ), - borderRadius: BorderRadius.all(Radius.circular(SpamReportBannerStyles.borderRadius)), - ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SpamReportBannerLabelWidget(countSpamEmailsAsString: spamReportController.numberOfUnreadSpamEmails), - const SizedBox(height: SpamReportBannerStyles.space), - Row( - children: [ - Expanded( - child: SpamReportBannerButtonWidget( - label: AppLocalizations.of(context).showDetails, - labelColor: SpamReportBannerButtonStyles.positiveButtonTextColor, - onTap: spamReportController.openMailbox - ), - ), - const SizedBox(width: SpamReportBannerStyles.space), - Expanded( - child: SpamReportBannerButtonWidget( - label: AppLocalizations.of(context).dismiss, - labelColor: SpamReportBannerButtonStyles.negativeButtonTextColor, - onTap: () => spamReportController.dismissSpamReportAction(context) - ), - ), - ], - ), - ], - ), - ); - }); - } -} \ No newline at end of file diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 46544c634..6ff76befb 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2025-06-20T15:26:10.574669", + "@@last_modified": "2025-07-08T10:52:37.280039", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -4516,7 +4516,7 @@ "placeholders_order": [], "placeholders": {} }, - "editLocalCopyInForwardFailed": "Failed to toggle local copy when transferring emails.", + "editLocalCopyInForwardFailed": "Edit local copy in forward failed.", "@editLocalCopyInForwardFailed": { "type": "text", "placeholders_order": [], @@ -4557,5 +4557,21 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "countMessageInSpam": "{count} message in spam", + "@countMessageInSpam": { + "type": "text", + "placeholders_order": [ + "count" + ], + "placeholders": { + "count": {} + } + }, + "view": "View", + "@view": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } -} +} \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 0adb03f1e..f021417e2 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -4801,4 +4801,19 @@ class AppLocalizations { name: 'youAreNotInvitedToThisEventPleaseContactTheOrganizer', ); } + + String countMessageInSpam(String count) { + return Intl.message( + '$count message in spam', + name: 'countMessageInSpam', + args: [count], + ); + } + + String get view { + return Intl.message( + 'View', + name: 'view', + ); + } }