Apply new design for spam alert on desktop

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2025-07-08 11:02:53 +07:00
committed by Dat H. Pham
parent da71890120
commit 290372cb75
9 changed files with 216 additions and 307 deletions
@@ -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;
}
@@ -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;
}
@@ -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(() {
@@ -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(),
),
),
],
),
),
),
);
}
}
@@ -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<ResponsiveUtils>();
final imagePaths = Get.find<ImagePaths>();
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
),
),
)
],
);
}
}
@@ -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<SpamReportController>();
final imagePaths = Get.find<ImagePaths>();
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,
),
)
],
),
);
});
}
}
@@ -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)
),
),
],
),
],
),
);
});
}
}
+19 -3
View File
@@ -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": {}
}
}
}
@@ -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',
);
}
}