TF-3986 Change style for alert quota

This commit is contained in:
dab246
2025-09-10 11:48:50 +07:00
committed by Dat H. Pham
parent 9a84f12690
commit 16f0eed730
10 changed files with 200 additions and 123 deletions
+21
View File
@@ -0,0 +1,21 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 7.99998C19.4933 7.99998 22.5067 10.48 23.1867 13.9066L23.5867 15.9066L25.6267 16.0533C27.7067 16.1866 29.3333 17.9333 29.3333 20C29.3333 22.2 27.5333 24 25.3333 24H8C5.05333 24 2.66667 21.6133 2.66667 18.6666C2.66667 15.9333 4.70667 13.6533 7.41333 13.3733L8.84 13.2266L9.50667 11.96C10.7733 9.51998 13.2533 7.99998 16 7.99998ZM16 5.33331C12.1467 5.33331 8.8 7.51998 7.13333 10.72C3.12 11.1466 0 14.5466 0 18.6666C0 23.08 3.58667 26.6666 8 26.6666H25.3333C29.0133 26.6666 32 23.68 32 20C32 16.48 29.2667 13.6266 25.8 13.3866C24.8933 8.78665 20.8533 5.33331 16 5.33331Z" fill="white"/>
<path d="M16 7.99998C19.4933 7.99998 22.5067 10.48 23.1867 13.9066L23.5867 15.9066L25.6267 16.0533C27.7067 16.1866 29.3333 17.9333 29.3333 20C29.3333 22.2 27.5333 24 25.3333 24H8C5.05333 24 2.66667 21.6133 2.66667 18.6666C2.66667 15.9333 4.70667 13.6533 7.41333 13.3733L8.84 13.2266L9.50667 11.96C10.7733 9.51998 13.2533 7.99998 16 7.99998ZM16 5.33331C12.1467 5.33331 8.8 7.51998 7.13333 10.72C3.12 11.1466 0 14.5466 0 18.6666C0 23.08 3.58667 26.6666 8 26.6666H25.3333C29.0133 26.6666 32 23.68 32 20C32 16.48 29.2667 13.6266 25.8 13.3866C24.8933 8.78665 20.8533 5.33331 16 5.33331Z" fill="url(#paint0_linear_6662_1277)"/>
<path d="M16 7.99998C19.4933 7.99998 22.5067 10.48 23.1867 13.9066L23.5867 15.9066L25.6267 16.0533C27.7067 16.1866 29.3333 17.9333 29.3333 20C29.3333 22.2 27.5333 24 25.3333 24H8C5.05333 24 2.66667 21.6133 2.66667 18.6666C2.66667 15.9333 4.70667 13.6533 7.41333 13.3733L8.84 13.2266L9.50667 11.96C10.7733 9.51998 13.2533 7.99998 16 7.99998ZM16 5.33331C12.1467 5.33331 8.8 7.51998 7.13333 10.72C3.12 11.1466 0 14.5466 0 18.6666C0 23.08 3.58667 26.6666 8 26.6666H25.3333C29.0133 26.6666 32 23.68 32 20C32 16.48 29.2667 13.6266 25.8 13.3866C24.8933 8.78665 20.8533 5.33331 16 5.33331Z" fill="url(#paint1_linear_6662_1277)"/>
<path d="M16 7.99998C19.4933 7.99998 22.5067 10.48 23.1867 13.9066L23.5867 15.9066L25.6267 16.0533C27.7067 16.1866 29.3333 17.9333 29.3333 20C29.3333 22.2 27.5333 24 25.3333 24H8C5.05333 24 2.66667 21.6133 2.66667 18.6666C2.66667 15.9333 4.70667 13.6533 7.41333 13.3733L8.84 13.2266L9.50667 11.96C10.7733 9.51998 13.2533 7.99998 16 7.99998ZM16 5.33331C12.1467 5.33331 8.8 7.51998 7.13333 10.72C3.12 11.1466 0 14.5466 0 18.6666C0 23.08 3.58667 26.6666 8 26.6666H25.3333C29.0133 26.6666 32 23.68 32 20C32 16.48 29.2667 13.6266 25.8 13.3866C24.8933 8.78665 20.8533 5.33331 16 5.33331Z" fill="url(#paint2_linear_6662_1277)"/>
<defs>
<linearGradient id="paint0_linear_6662_1277" x1="32" y1="17.7778" x2="-8.23842e-07" y2="17.7778" gradientUnits="userSpaceOnUse">
<stop offset="0.12998" stop-color="#A033FF"/>
<stop offset="0.610381" stop-color="#0094FF"/>
<stop offset="1" stop-color="#4FB500"/>
</linearGradient>
<linearGradient id="paint1_linear_6662_1277" x1="14.6667" y1="12" x2="9.66666" y2="27" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD600" stop-opacity="0"/>
<stop offset="0.563347" stop-color="#FFC700"/>
</linearGradient>
<linearGradient id="paint2_linear_6662_1277" x1="17.3333" y1="17.7778" x2="24.1578" y2="28.6167" gradientUnits="userSpaceOnUse">
<stop offset="0.0297327" stop-color="#FF3B30" stop-opacity="0"/>
<stop offset="0.842824" stop-color="#E73B2D"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

@@ -240,6 +240,7 @@ class ImagePaths {
String get animLottieTmail => _getAnimationPath('lottie-tmail.json'); String get animLottieTmail => _getAnimationPath('lottie-tmail.json');
String get icExpandArrows => _getImagePath('ic_expand_arrows.svg'); String get icExpandArrows => _getImagePath('ic_expand_arrows.svg');
String get icPremium => _getImagePath('ic_premium.svg'); String get icPremium => _getImagePath('ic_premium.svg');
String get icCloud => _getImagePath('ic_cloud.svg');
String _getImagePath(String imageName) { String _getImagePath(String imageName) {
return AssetsPaths.images + imageName; return AssetsPaths.images + imageName;
@@ -1,6 +1,5 @@
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:filesize/filesize.dart'; import 'package:filesize/filesize.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
@@ -79,56 +78,16 @@ extension QuotasExtensions on Quota {
} }
} }
Color getQuotaBannerBackgroundColor() {
if (isHardLimitReached) {
return AppColor.colorQuotaError.withValues(alpha: 0.12);
} else if (isWarnLimitReached) {
return AppColor.colorBackgroundQuotasWarning.withValues(alpha: 0.12);
} else {
return AppColor.colorNetworkConnectionBannerBackground;
}
}
String getQuotaBannerIcon(ImagePaths imagePaths) {
if (isHardLimitReached) {
return imagePaths.icQuotasOutOfStorage;
} else if (isWarnLimitReached) {
return imagePaths.icQuotasWarning;
} else {
return '';
}
}
String getQuotaBannerTitle(BuildContext context) { String getQuotaBannerTitle(BuildContext context) {
if (isHardLimitReached) { if (isHardLimitReached) {
return AppLocalizations.of(context).quotaErrorBannerTitle; return AppLocalizations.of(context).quotaErrorBannerTitle;
} else if (isWarnLimitReached) { } else if (isWarnLimitReached) {
return AppLocalizations.of(context).quotaWarningBannerTitle; return AppLocalizations.of(context).quotaBannerWarningTitle;
} else { } else {
return ''; return '';
} }
} }
String getQuotaBannerMessage(BuildContext context) {
if (isHardLimitReached) {
return AppLocalizations.of(context).quotaErrorBannerMessage;
} else if (isWarnLimitReached) {
return AppLocalizations.of(context).quotaWarningBannerMessage;
} else {
return '';
}
}
Color getQuotaBannerTitleColor() {
if (isHardLimitReached) {
return AppColor.colorQuotaError;
} else if (isWarnLimitReached) {
return AppColor.colorQuotaWarning;
} else {
return Colors.black;
}
}
Color getQuotaBannerMessageColor() { Color getQuotaBannerMessageColor() {
if (isHardLimitReached) { if (isHardLimitReached) {
return AppColor.colorQuotaError; return AppColor.colorQuotaError;
@@ -1,10 +1,13 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/state/success.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
import 'package:jmap_dart_client/jmap/quotas/quota.dart'; import 'package:jmap_dart_client/jmap/quotas/quota.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/validate_saas_premium_available_extension.dart';
import 'package:tmail_ui_user/features/quotas/domain/extensions/list_quotas_extensions.dart'; import 'package:tmail_ui_user/features/quotas/domain/extensions/list_quotas_extensions.dart';
import 'package:tmail_ui_user/features/quotas/domain/state/get_quotas_state.dart'; import 'package:tmail_ui_user/features/quotas/domain/state/get_quotas_state.dart';
import 'package:tmail_ui_user/features/quotas/domain/use_case/get_quotas_interactor.dart'; import 'package:tmail_ui_user/features/quotas/domain/use_case/get_quotas_interactor.dart';
@@ -17,6 +20,7 @@ class QuotasController extends BaseController {
final GetQuotasInteractor _getQuotasInteractor; final GetQuotasInteractor _getQuotasInteractor;
final octetsQuota = Rxn<Quota>(); final octetsQuota = Rxn<Quota>();
final isBannerEnabled = RxBool(false);
late Worker accountIdListener; late Worker accountIdListener;
@@ -28,6 +32,7 @@ class QuotasController extends BaseController {
void _handleGetQuotasSuccess(GetQuotasSuccess success) { void _handleGetQuotasSuccess(GetQuotasSuccess success) {
octetsQuota.value = success.quotas.octetsQuota; octetsQuota.value = success.quotas.octetsQuota;
isBannerEnabled.value = true;
} }
void _initWorker() { void _initWorker() {
@@ -62,9 +67,32 @@ class QuotasController extends BaseController {
@override @override
void handleSuccessViewState(Success success) { void handleSuccessViewState(Success success) {
super.handleSuccessViewState(success);
if (success is GetQuotasSuccess) { if (success is GetQuotasSuccess) {
_handleGetQuotasSuccess(success); _handleGetQuotasSuccess(success);
} else {
super.handleSuccessViewState(success);
} }
} }
@override
void handleFailureViewState(Failure failure) {
if (failure is GetQuotasFailure) {
isBannerEnabled.value = true;
} else {
super.handleFailureViewState(failure);
}
}
bool get isManageMyStorageIsEnabled {
return mailboxDashBoardController.isPremiumAvailable &&
!mailboxDashBoardController.isAlreadyHighestSubscription;
}
void handleManageMyStorage(BuildContext context) {
mailboxDashBoardController.navigateToPaywall(context);
}
void closeBanner() {
isBannerEnabled.value = false;
}
} }
@@ -1,25 +1,45 @@
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/utils/platform_info.dart'; import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class QuotasBannerStyles { class QuotasBannerStyles {
static const double iconPadding = 16; static const double iconPadding = 16;
static const double iconSize = 32; static const double iconSize = 32;
static const double titleTextSize = 17; static const double titleTextSize = 15;
static const double messageTextSize = 15; static const double borderRadius = 8;
static const double space = 4;
static const double borderRadius = 12;
static const Color messageTextColor = AppColor.steelGray400; static const Color messageTextColor = AppColor.steelGray400;
static const Color backgroundColor = AppColor.lightGrayEAEDF2;
static const FontWeight titleFontWeight = FontWeight.w700;
static const FontWeight messageFontWeight = FontWeight.w400; static const FontWeight messageFontWeight = FontWeight.w400;
static const EdgeInsetsGeometry bannerPadding = EdgeInsetsDirectional.symmetric( static TextStyle titleTextStyle = ThemeUtils.textStyleInter500().copyWith(
horizontal: 16, fontSize: 15,
vertical: 8, height: 20 / 15,
letterSpacing: 0.0,
color: Colors.black,
);
static TextStyle subTitleTextStyle = ThemeUtils.textStyleInter400.copyWith(
fontSize: 13,
height: 16 / 13,
letterSpacing: 0.0,
color: AppColor.steelGray400,
);
static TextStyle manageStorageButtonTextStyle = ThemeUtils.textStyleInter700().copyWith(
fontSize: 14,
height: 20 / 14,
letterSpacing: 0.0,
color: AppColor.blue700,
);
static const EdgeInsetsGeometry bannerPadding = EdgeInsetsDirectional.only(
start: 16,
end: 40,
top: 12,
bottom: 12,
); );
static EdgeInsetsGeometry getBannerMargin( static EdgeInsetsGeometry getBannerMargin(
BuildContext context, BuildContext context,
@@ -1,10 +1,13 @@
import 'package:core/presentation/utils/theme_utils.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/default_close_button_widget.dart';
import 'package:tmail_ui_user/features/quotas/domain/extensions/quota_extensions.dart'; import 'package:tmail_ui_user/features/quotas/domain/extensions/quota_extensions.dart';
import 'package:tmail_ui_user/features/quotas/presentation/quotas_controller.dart'; import 'package:tmail_ui_user/features/quotas/presentation/quotas_controller.dart';
import 'package:tmail_ui_user/features/quotas/presentation/styles/quotas_banner_styles.dart'; import 'package:tmail_ui_user/features/quotas/presentation/styles/quotas_banner_styles.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class QuotasBannerWidget extends StatelessWidget { class QuotasBannerWidget extends StatelessWidget {
@@ -16,49 +19,79 @@ class QuotasBannerWidget extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Obx(() { return Obx(() {
final octetQuota = _quotasController.octetsQuota.value; final octetQuota = _quotasController.octetsQuota.value;
if (octetQuota != null && octetQuota.allowedDisplayToQuotaBanner) {
if (octetQuota != null &&
octetQuota.allowedDisplayToQuotaBanner &&
_quotasController.isBannerEnabled.isTrue) {
final appLocalizations = AppLocalizations.of(context);
return Container( return Container(
decoration: BoxDecoration( decoration: const BoxDecoration(
color: octetQuota.getQuotaBannerBackgroundColor(), color: QuotasBannerStyles.backgroundColor,
borderRadius: const BorderRadius.all(Radius.circular(QuotasBannerStyles.borderRadius)), borderRadius: BorderRadius.all(
Radius.circular(QuotasBannerStyles.borderRadius),
),
), ),
margin: QuotasBannerStyles.getBannerMargin( margin: QuotasBannerStyles.getBannerMargin(
context, context,
_quotasController.responsiveUtils), _quotasController.responsiveUtils,
padding: QuotasBannerStyles.bannerPadding, ),
child: Row( child: Stack(
children: [ children: [
SvgPicture.asset( Padding(
octetQuota.getQuotaBannerIcon(_quotasController.imagePaths), padding: QuotasBannerStyles.bannerPadding,
width: QuotasBannerStyles.iconSize, child: Row(
height: QuotasBannerStyles.iconSize,
fit: BoxFit.fill,
),
const SizedBox(width: QuotasBannerStyles.iconPadding),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( SvgPicture.asset(
octetQuota.getQuotaBannerTitle(context), _quotasController.imagePaths.icCloud,
style: ThemeUtils.defaultTextStyleInterFont.copyWith( width: QuotasBannerStyles.iconSize,
fontSize: QuotasBannerStyles.titleTextSize, height: QuotasBannerStyles.iconSize,
fontWeight: QuotasBannerStyles.titleFontWeight, fit: BoxFit.fill,
color: octetQuota.getQuotaBannerTitleColor(),
),
), ),
const SizedBox(height: QuotasBannerStyles.space), const SizedBox(width: QuotasBannerStyles.iconPadding),
Text( Expanded(
octetQuota.getQuotaBannerMessage(context), child: Column(
style: ThemeUtils.defaultTextStyleInterFont.copyWith( crossAxisAlignment: CrossAxisAlignment.start,
fontSize: QuotasBannerStyles.messageTextSize, children: [
fontWeight: QuotasBannerStyles.messageFontWeight, Text(
color: QuotasBannerStyles.messageTextColor, octetQuota.getQuotaBannerTitle(context),
style: QuotasBannerStyles.titleTextStyle,
),
const SizedBox(height: 8),
if (!PlatformInfo.isWeb ||
!_quotasController.isManageMyStorageIsEnabled)
Text(
appLocalizations.quotaBannerWarningSubtitleWithoutPremium,
style: QuotasBannerStyles.subTitleTextStyle,
)
else
Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(
'${appLocalizations.quotaBannerWarningSubtitleWithPremium} ',
style: QuotasBannerStyles.subTitleTextStyle,
),
TMailButtonWidget.fromText(
text: appLocalizations.manageMyStorage,
backgroundColor: QuotasBannerStyles.backgroundColor,
textStyle: QuotasBannerStyles.manageStorageButtonTextStyle,
padding: EdgeInsets.zero,
onTapActionCallback: () =>
_quotasController.handleManageMyStorage(context),
),
],
),
],
), ),
), ),
], ],
), ),
), ),
DefaultCloseButtonWidget(
iconClose: _quotasController.imagePaths.icCloseDialog,
onTapActionCallback: _quotasController.closeBanner,
),
], ],
), ),
); );
+25 -19
View File
@@ -1,5 +1,5 @@
{ {
"@@last_modified": "2025-09-09T15:28:33.596868", "@@last_modified": "2025-09-10T11:45:56.814190",
"initializing_data": "Initializing data...", "initializing_data": "Initializing data...",
"@initializing_data": { "@initializing_data": {
"type": "text", "type": "text",
@@ -3176,24 +3176,6 @@
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"quotaWarningBannerTitle": "You are running out of storage (90%).",
"@quotaWarningBannerTitle": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"quotaWarningBannerMessage": "Soon you won't be able to email in Tmail. Please clean your storage or upgrade your storage to get full features in Tmail.",
"@quotaWarningBannerMessage": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"quotaErrorBannerMessage": "Soon you won't be able to email in Tmail. Please clean your storage or upgrade your storage to get full features in Tmail.",
"@quotaErrorBannerMessage": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"createFolderSuccessfullyMessage": "You successfully created {folderName} folder", "createFolderSuccessfullyMessage": "You successfully created {folderName} folder",
"@createFolderSuccessfullyMessage": { "@createFolderSuccessfullyMessage": {
"type": "text", "type": "text",
@@ -4693,5 +4675,29 @@
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
},
"quotaBannerWarningTitle": "You are running low on storage (90%)",
"@quotaBannerWarningTitle": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"quotaBannerWarningSubtitleWithPremium": "To keep sending messages and enjoying all Twake Mail features, please consider cleaning up or upgrading your storage.",
"@quotaBannerWarningSubtitleWithPremium": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"quotaBannerWarningSubtitleWithoutPremium": "To keep sending messages and enjoying all Twake Mail features, please consider cleaning up.",
"@quotaBannerWarningSubtitleWithoutPremium": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"manageMyStorage": "Manage my storage",
"@manageMyStorage": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
} }
} }
+28 -21
View File
@@ -3261,27 +3261,6 @@ class AppLocalizations {
); );
} }
String get quotaWarningBannerTitle {
return Intl.message(
'You are running out of storage (90%).',
name: 'quotaWarningBannerTitle'
);
}
String get quotaWarningBannerMessage {
return Intl.message(
'Soon you won\'t be able to email in Tmail. Please clean your storage or upgrade your storage to get full features in Tmail.',
name: 'quotaWarningBannerMessage'
);
}
String get quotaErrorBannerMessage {
return Intl.message(
'Soon you won\'t be able to email in Tmail. Please clean your storage or upgrade your storage to get full features in Tmail.',
name: 'quotaErrorBannerMessage'
);
}
String createFolderSuccessfullyMessage(String folderName) { String createFolderSuccessfullyMessage(String folderName) {
return Intl.message( return Intl.message(
'You successfully created $folderName folder', 'You successfully created $folderName folder',
@@ -4950,4 +4929,32 @@ class AppLocalizations {
name: 'paywallUrlNotAvailable', name: 'paywallUrlNotAvailable',
); );
} }
String get quotaBannerWarningTitle {
return Intl.message(
'You are running low on storage (90%)',
name: 'quotaBannerWarningTitle',
);
}
String get quotaBannerWarningSubtitleWithPremium {
return Intl.message(
'To keep sending messages and enjoying all Twake Mail features, please consider cleaning up or upgrading your storage.',
name: 'quotaBannerWarningSubtitleWithPremium',
);
}
String get quotaBannerWarningSubtitleWithoutPremium {
return Intl.message(
'To keep sending messages and enjoying all Twake Mail features, please consider cleaning up.',
name: 'quotaBannerWarningSubtitleWithoutPremium',
);
}
String get manageMyStorage {
return Intl.message(
'Manage my storage',
name: 'manageMyStorage',
);
}
} }
@@ -21,6 +21,8 @@ class SaaSAccountCapability extends CapabilityProperties {
bool get isPremiumAvailable => canUpgrade; bool get isPremiumAvailable => canUpgrade;
bool get isAlreadyHighestSubscription => isPaying && !canUpgrade;
@override @override
List<Object?> get props => [isPaying, canUpgrade]; List<Object?> get props => [isPaying, canUpgrade];
} }