TF-1983 Add CreateFilters button in empty thread view widget

(cherry picked from commit 5eed73198c868448cfbc3c93600176aa15bf62f4)
This commit is contained in:
dab246
2023-08-09 19:03:01 +07:00
committed by Dat Vu
parent d7656a087c
commit 9b54b5d448
13 changed files with 294 additions and 116 deletions
-1
View File
@@ -65,7 +65,6 @@ export 'presentation/views/dialog/downloading_file_dialog_builder.dart';
export 'presentation/views/dialog/confirmation_dialog_builder.dart';
export 'presentation/views/dialog/edit_text_dialog_builder.dart';
export 'presentation/views/dialog/color_picker_dialog_builder.dart';
export 'presentation/views/background/background_widget_builder.dart';
export 'presentation/views/html_viewer/html_content_viewer_widget.dart';
export 'presentation/views/html_viewer/html_content_viewer_on_web_widget.dart';
export 'presentation/views/html_viewer/html_viewer_controller_for_web.dart';
@@ -201,6 +201,7 @@ extension AppColor on Color {
static const colorEventDescriptionBackground = Color(0x05000000);
static const colorLabelQuotas = Color(0xFF818C99);
static const colorLabelCancelButton = Color(0xFFAEB7C2);
static const colorCreateFiltersButton = Color(0xFFF3F3F7);
static const mapGradientColor = [
[Color(0xFF21D4FD), Color(0xFFB721FF)],
@@ -1,89 +0,0 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/views/responsive/responsive_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class BackgroundWidgetBuilder extends StatelessWidget {
final ResponsiveUtils responsiveUtils;
final String title;
final String? iconSVG;
final String? subTitle;
final double? maxWidth;
const BackgroundWidgetBuilder(
this.title,
this.responsiveUtils, {
Key? key,
this.iconSVG,
this.subTitle,
this.maxWidth
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
key: const Key('background_widget'),
child: SizedBox(
width: maxWidth ?? 360,
child: ResponsiveWidget(
mobile: CustomScrollView(slivers: [
SliverFillRemaining(child: _buildMessageBody(context))
]),
landscapeMobile: SingleChildScrollView(child: _buildMessageBody(context)),
responsiveUtils: responsiveUtils,
),
)
);
}
Widget _buildMessageBody(BuildContext context) {
return Container(
color: Colors.transparent,
padding: const EdgeInsets.symmetric(horizontal: 12),
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: responsiveUtils.isLandscapeMobile(context)
? MainAxisAlignment.start
: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (iconSVG != null)
SvgPicture.asset(
iconSVG!,
width: 212,
height: 212,
fit: BoxFit.fill
),
Padding(
padding: EdgeInsets.only(top: iconSVG != null ? 12 : 0),
child: Text(
title,
style: const TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.normal
),
textAlign: TextAlign.center,
),
),
if (subTitle != null)
Padding(
padding: const EdgeInsets.only(top: 12),
child: Text(
subTitle!,
style: const TextStyle(
color: AppColor.colorSubtitle,
fontSize: 15,
fontWeight: FontWeight.normal
),
textAlign: TextAlign.center,
),
)
],
),
);
}
}
@@ -74,19 +74,57 @@ class TMailButtonWidget extends StatelessWidget {
String? tooltipMessage,
Color? backgroundColor,
EdgeInsetsGeometry? padding,
String text = '',
bool verticalDirection = false,
double? iconSize,
Color? iconColor,
TextStyle? textStyle,
double iconSpace = 8,
String? trailingIcon,
double? trailingIconSize,
Color? trailingIconColor,
List<BoxShadow>? boxShadow,
EdgeInsetsGeometry? margin,
}) {
return TMailButtonWidget(
key: key,
text: '',
onTapActionCallback: onTapActionCallback,
onTapActionAtPositionCallback: onTapActionAtPositionCallback,
borderRadius: borderRadius,
width: width,
maxWidth : maxWidth,
maxHeight: maxHeight,
minWidth: minWidth,
tooltipMessage: tooltipMessage,
backgroundColor: backgroundColor,
padding: padding,
icon: icon,
iconSize: iconSize,
iconColor: iconColor,
iconSpace: iconSpace,
trailingIcon: trailingIcon,
trailingIconSize: trailingIconSize,
trailingIconColor: trailingIconColor,
boxShadow: boxShadow,
margin: margin,
);
}
factory TMailButtonWidget.fromText({
required String text,
final Key? key,
OnTapActionCallback? onTapActionCallback,
OnTapActionAtPositionCallback? onTapActionAtPositionCallback,
double borderRadius = 20,
double? width,
double maxWidth = double.infinity,
double maxHeight = double.infinity,
double minWidth = 0,
String? tooltipMessage,
Color? backgroundColor,
EdgeInsetsGeometry? padding,
TextStyle? textStyle,
List<BoxShadow>? boxShadow,
EdgeInsetsGeometry? margin,
TextAlign? textAlign,
bool flexibleText = false,
}) {
return TMailButtonWidget(
key: key,
@@ -101,19 +139,10 @@ class TMailButtonWidget extends StatelessWidget {
tooltipMessage: tooltipMessage,
backgroundColor: backgroundColor,
padding: padding,
verticalDirection: verticalDirection,
icon: icon,
iconSize: iconSize,
iconColor: iconColor,
textStyle: textStyle,
iconSpace: iconSpace,
trailingIcon: trailingIcon,
trailingIconSize: trailingIconSize,
trailingIconColor: trailingIconColor,
boxShadow: boxShadow,
margin: margin,
textAlign: textAlign,
flexibleText: flexibleText,
);
}
@@ -61,7 +61,6 @@ class TMailContainerWidget extends StatelessWidget {
? Tooltip(
message: tooltipMessage,
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: backgroundColor ?? AppColor.colorButtonHeaderThread,
borderRadius: BorderRadius.all(Radius.circular(borderRadius)),
@@ -78,7 +77,6 @@ class TMailContainerWidget extends StatelessWidget {
)
)
: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: backgroundColor ?? AppColor.colorButtonHeaderThread,
borderRadius: BorderRadius.all(Radius.circular(borderRadius)),
@@ -99,6 +99,8 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
final _activeScrollTop = RxBool(false);
final _activeScrollBottom = RxBool(true);
MailboxId? _newFolderId;
final _openMailboxEventController = StreamController<OpenMailboxViewEvent>();
final mailboxListScrollController = ScrollController();
@@ -180,6 +182,8 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
_createNewMailboxFailure(failure);
} else if (failure is DeleteMultipleMailboxFailure) {
_deleteMailboxFailure(failure);
} else if (failure is RefreshChangesAllMailboxFailure) {
_clearNewFolderId();
}
}
@@ -193,6 +197,10 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
} else if (success is RefreshChangesAllMailboxSuccess) {
_initialMailboxVariableStorage(isRefreshChange: true);
mailboxDashBoardController.refreshSpamReportBanner();
if (_newFolderId != null) {
_redirectToNewFolder();
}
}
});
}
@@ -307,6 +315,8 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
final session = mailboxDashBoardController.sessionCurrent;
if (accountId != null && session != null && newMailboxState != null) {
refreshMailboxChanges(session, accountId, newMailboxState);
} else {
_newFolderId = null;
}
}
@@ -504,9 +514,11 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
if (currentOverlayContext != null && currentContext != null) {
_appToast.showToastSuccessMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).new_mailbox_is_created(success.newMailbox.name?.name ?? ''),
AppLocalizations.of(currentContext!).createFolderSuccessfullyMessage(success.newMailbox.name?.name ?? ''),
leadingSVGIconColor: Colors.white,
leadingSVGIcon: _imagePaths.icFolderMailbox);
_newFolderId = success.newMailbox.id;
}
_refreshMailboxChanges(currentMailboxState: success.currentMailboxState);
@@ -1192,4 +1204,17 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
closeMailboxScreen(context);
mailboxDashBoardController.dispatchRoute(DashboardRoutes.sendingQueue);
}
void _clearNewFolderId() {
_newFolderId = null;
}
void _redirectToNewFolder() {
final newMailboxNode = findMailboxNodeById(_newFolderId!);
log('MailboxController::_redirectToNewFolder:newMailboxNode: $newMailboxNode');
if (newMailboxNode != null && currentContext != null) {
openMailbox(currentContext!, newMailboxNode.item);
}
_clearNewFolderId();
}
}
@@ -3,7 +3,6 @@ import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:core/presentation/views/background/background_widget_builder.dart';
import 'package:core/presentation/views/button/icon_button_web.dart';
import 'package:core/presentation/views/text/text_field_builder.dart';
import 'package:core/utils/platform_info.dart';
@@ -28,6 +27,7 @@ import 'package:tmail_ui_user/features/search/email/presentation/widgets/email_r
import 'package:tmail_ui_user/features/search/email/presentation/widgets/email_receive_time_cupertino_action_sheet_action_builder.dart';
import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart';
import 'package:tmail_ui_user/features/thread/domain/state/search_more_email_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/empty_emails_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.dart'
if (dart.library.html) 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_web_builder.dart';
@@ -392,9 +392,9 @@ class SearchEmailView extends GetWidget<SearchEmailController>
return Obx(() => controller.viewState.value.fold(
(failure) => const SizedBox.shrink(),
(success) => success is! SearchingState
? BackgroundWidgetBuilder(
AppLocalizations.of(context).no_emails_matching_your_search,
controller.responsiveUtils,
? EmptyEmailsWidget(
key: const Key('empty_search_email_view'),
title: AppLocalizations.of(context).no_emails_matching_your_search,
iconSVG: _imagePaths.icEmptyEmail
)
: const SizedBox.shrink())
@@ -0,0 +1,30 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:flutter/material.dart';
class EmptyEmailsWidgetStyles {
static const double mobileIconSize = 160;
static const double tabletIconSize = 180;
static const double desktopIconSize = 212;
static const double labelTextSize = 20;
static const double messageTextSize = 15;
static const double createFilterLabelTextSize = 22;
static const double createFilterButtonTextSize = 17;
static const double maxWidth = 490;
static const double createFilterButtonBorderRadius = 10;
static const FontWeight labelFontWeight = FontWeight.w400;
static const FontWeight messageFontWeight = FontWeight.w400;
static const FontWeight createFilterLabelFontWeight = FontWeight.w600;
static const FontWeight createFilterButtonFontWeight = FontWeight.w500;
static const Color labelTextColor = Colors.black;
static const Color messageTextColor = AppColor.colorSubtitle;
static const Color createFilterButtonTextColor = AppColor.primaryColor;
static const Color createFilterButtonBackgroundColor = AppColor.colorCreateFiltersButton;
static const EdgeInsetsGeometry padding = EdgeInsetsDirectional.all(16);
static const EdgeInsetsGeometry labelPadding = EdgeInsetsDirectional.symmetric(vertical: 12);
static const EdgeInsetsGeometry createFilterButtonPadding = EdgeInsetsDirectional.symmetric(vertical: 12, horizontal: 24);
static const EdgeInsetsGeometry createFilterButtonMargin = EdgeInsetsDirectional.only(top: 28);
}
@@ -1003,4 +1003,13 @@ class ThreadController extends BaseController with EmailActionController {
void onDragMailBox(bool isDrag) {
mailboxDashBoardController.onDragMailbox(isDrag);
}
bool get isNewFolderCreated {
final currentMailbox = mailboxDashBoardController.selectedMailbox.value;
return currentMailbox != null &&
currentMailbox.isPersonal &&
!currentMailbox.isDefault;
}
void goToCreateEmailRuleView() {}
}
@@ -28,6 +28,7 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/banner_empty_
import 'package:tmail_ui_user/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.dart'
if (dart.library.html) 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_web_builder.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/empty_emails_widget.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/spam_banner/spam_report_banner_widget.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/thread_view_bottom_loading_bar_widget.dart';
@@ -482,11 +483,14 @@ class ThreadView extends GetWidget<ThreadController>
return Obx(() => controller.viewState.value.fold(
(failure) => const SizedBox.shrink(),
(success) => success is! LoadingState && success is! SearchingState
? BackgroundWidgetBuilder(
_getMessageEmptyEmail(context),
controller.responsiveUtils,
? EmptyEmailsWidget(
key: const Key('empty_thread_view'),
title: _getMessageEmptyEmail(context),
iconSVG: _imagePaths.icEmptyEmail,
subTitle: _getSubMessageEmptyEmail(context),
onCreateFiltersActionCallback: controller.isNewFolderCreated
? controller.goToCreateEmailRuleView
: null,
)
: const SizedBox.shrink())
);
@@ -496,8 +500,9 @@ class ThreadView extends GetWidget<ThreadController>
if (controller.isSearchActive()) {
return AppLocalizations.of(context).no_emails_matching_your_search;
} else {
if (controller.mailboxDashBoardController.filterMessageOption.value == FilterMessageOption.all) {
return AppLocalizations.of(context).noEmailInYourCurrentMailbox;
if (controller.mailboxDashBoardController.filterMessageOption.value == FilterMessageOption.all &&
controller.isNewFolderCreated) {
return AppLocalizations.of(context).folderCreatedTitle;
} else {
return AppLocalizations.of(context).noEmailMatchYourCurrentFilter;
}
@@ -508,6 +513,9 @@ class ThreadView extends GetWidget<ThreadController>
if (!controller.isSearchActive()
&& controller.mailboxDashBoardController.filterMessageOption.value != FilterMessageOption.all) {
return AppLocalizations.of(context).reduceSomeFiltersAndTryAgain;
} else if (controller.mailboxDashBoardController.filterMessageOption.value == FilterMessageOption.all &&
controller.isNewFolderCreated) {
return AppLocalizations.of(context).folderCreatedMessage;
} else {
return null;
}
@@ -0,0 +1,113 @@
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/thread/presentation/styles/empty_emails_widget_styles.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnCreateFiltersActionCallback = Function();
class EmptyEmailsWidget extends StatelessWidget {
final String title;
final String? iconSVG;
final String? subTitle;
final OnCreateFiltersActionCallback? onCreateFiltersActionCallback;
final Color? titleColor;
const EmptyEmailsWidget({
Key? key,
required this.title,
this.iconSVG,
this.subTitle,
this.onCreateFiltersActionCallback,
this.titleColor,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final responsiveUtils = Get.find<ResponsiveUtils>();
final childWidget = Padding(
padding: EmptyEmailsWidgetStyles.padding,
child: Column(
mainAxisAlignment: responsiveUtils.isScreenWithShortestSide(context)
? MainAxisAlignment.start
: MainAxisAlignment.center,
children: [
if (iconSVG != null)
SvgPicture.asset(
iconSVG!,
width: _getIconSize(context, responsiveUtils),
height: _getIconSize(context, responsiveUtils),
fit: BoxFit.fill
),
Padding(
padding: EmptyEmailsWidgetStyles.labelPadding,
child: Text(
title,
style: TextStyle(
color: EmptyEmailsWidgetStyles.labelTextColor,
fontSize: onCreateFiltersActionCallback != null
? EmptyEmailsWidgetStyles.createFilterLabelTextSize
: EmptyEmailsWidgetStyles.labelTextSize,
fontWeight: onCreateFiltersActionCallback != null
? EmptyEmailsWidgetStyles.createFilterLabelFontWeight
: EmptyEmailsWidgetStyles.labelFontWeight
),
textAlign: TextAlign.center,
),
),
if (subTitle != null)
Text(
subTitle!,
style: const TextStyle(
color: EmptyEmailsWidgetStyles.messageTextColor,
fontSize: EmptyEmailsWidgetStyles.messageTextSize,
fontWeight: EmptyEmailsWidgetStyles.messageFontWeight
),
textAlign: TextAlign.center,
),
if (onCreateFiltersActionCallback != null)
TMailButtonWidget.fromText(
text: AppLocalizations.of(context).createFilters,
padding: EmptyEmailsWidgetStyles.createFilterButtonPadding,
margin: EmptyEmailsWidgetStyles.createFilterButtonMargin,
backgroundColor: EmptyEmailsWidgetStyles.createFilterButtonBackgroundColor,
borderRadius: EmptyEmailsWidgetStyles.createFilterButtonBorderRadius,
width: responsiveUtils.isPortraitMobile(context) ? double.infinity : null,
textAlign: TextAlign.center,
textStyle: const TextStyle(
fontSize: EmptyEmailsWidgetStyles.createFilterButtonTextSize,
fontWeight: EmptyEmailsWidgetStyles.createFilterButtonFontWeight,
color: EmptyEmailsWidgetStyles.createFilterButtonTextColor
),
onTapActionCallback: onCreateFiltersActionCallback,
)
],
),
);
return Container(
constraints: const BoxConstraints(maxWidth: EmptyEmailsWidgetStyles.maxWidth),
alignment: AlignmentDirectional.center,
child: responsiveUtils.isScreenWithShortestSide(context)
? SingleChildScrollView(child: childWidget)
: CustomScrollView(
slivers: [
SliverFillRemaining(child: childWidget)
]
)
);
}
double _getIconSize(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isMobile(context)) {
return EmptyEmailsWidgetStyles.mobileIconSize;
} else if (responsiveUtils.isDesktop(context)) {
return EmptyEmailsWidgetStyles.desktopIconSize;
} else {
return EmptyEmailsWidgetStyles.tabletIconSize;
}
}
}
+29 -1
View File
@@ -1,5 +1,5 @@
{
"@@last_modified": "2023-08-08T20:09:51.612589",
"@@last_modified": "2023-08-09T18:38:10.703509",
"initializing_data": "Initializing data...",
"@initializing_data": {
"type": "text",
@@ -3133,5 +3133,33 @@
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"createFolderSuccessfullyMessage": "You successfully created {folderName} folder",
"@createFolderSuccessfullyMessage": {
"type": "text",
"placeholders_order": [
"folderName"
],
"placeholders": {
"folderName": {}
}
},
"folderCreatedTitle": "Your folder is just created",
"@folderCreatedTitle": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"folderCreatedMessage": "To begin using this mailbox, you should add some rules to organize all of your mail in your own way.",
"@folderCreatedMessage": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"createFilters": "Create filters",
"@createFilters": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
}
}
@@ -3233,4 +3233,31 @@ class AppLocalizations {
name: 'quotaErrorBannerMessage'
);
}
String createFolderSuccessfullyMessage(String folderName) {
return Intl.message(
'You successfully created $folderName folder',
name: 'createFolderSuccessfullyMessage',
args: [folderName]
);
}
String get folderCreatedTitle {
return Intl.message(
'Your folder is just created',
name: 'folderCreatedTitle');
}
String get folderCreatedMessage {
return Intl.message(
'To begin using this mailbox, you should add some rules to organize all of your mail in your own way.',
name: 'folderCreatedMessage');
}
String get createFilters {
return Intl.message(
'Create filters',
name: 'createFilters');
}
}