From 7dc8672b826aa9768eaf8b34a53a543283c37c18 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 22 Mar 2022 00:57:24 +0700 Subject: [PATCH] TF-381 Apply new UI for emails selection active --- .../extensions/color_extension.dart | 1 + .../views/image/icon_builder.dart | 1 + .../presentation/thread_controller.dart | 27 +++-- .../thread/presentation/thread_view.dart | 114 +++++++++++++----- .../widgets/email_tile_builder.dart | 38 +++--- lib/main/localizations/app_localizations.dart | 7 ++ .../list_presentation_email_extension.dart | 4 + 7 files changed, 138 insertions(+), 54 deletions(-) diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index 578d60e21..3646c1b42 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -102,6 +102,7 @@ extension AppColor on Color { static const colorButtonHeaderThread = Color(0x99EBEDF0); static const colorBorderBodyThread = Color(0x5CB8C1CC); static const colorBgDesktop = Color(0xFFF6F6F6); + static const colorItemEmailSelectedDesktop = Color(0x0F007AFF); static const mapGradientColor = [ [Color(0xFF21D4FD), Color(0xFFB721FF)], diff --git a/core/lib/presentation/views/image/icon_builder.dart b/core/lib/presentation/views/image/icon_builder.dart index 3f4066e8f..41a14b31f 100644 --- a/core/lib/presentation/views/image/icon_builder.dart +++ b/core/lib/presentation/views/image/icon_builder.dart @@ -41,6 +41,7 @@ class IconBuilder { child: IconButton( padding: EdgeInsets.zero, iconSize: _size ?? 40, + splashRadius: 20, icon: SvgPicture.asset(_icon!, width: _size ?? 40, height: _size ?? 40, fit: BoxFit.fill), onPressed: () => { if (_onPressIconActionClick != null) { diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 754bc7d09..6d5c2168b 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -131,7 +131,6 @@ class ThreadController extends BaseController { || success is MarkAsStarEmailSuccess) { cancelSelectEmail(); _refreshEmailChanges(); - mailboxDashBoardController.clearState(); } else if (success is SearchEmailNewQuery){ _searchEmail(); mailboxDashBoardController.clearState(); @@ -225,6 +224,7 @@ class ThreadController extends BaseController { canLoadMore = true; disableSearch(); cancelSelectEmail(); + mailboxDashBoardController.dispatchRoute(AppRoutes.THREAD); } void _getAllEmailSuccess(GetAllEmailSuccess success) { @@ -388,7 +388,7 @@ class ThreadController extends BaseController { currentSelectMode.value = SelectMode.ACTIVE; } - List getListEmailSelected() { + List get listEmailSelected { if (isSearchActive()) { return emailListSearch.where((email) => email.selectMode == SelectMode.ACTIVE).toList(); } else { @@ -404,10 +404,6 @@ class ThreadController extends BaseController { } } - bool isAllEmailRead(List listEmail) => listEmail.every((email) => email.isReadEmail()); - - bool isAllEmailMarkAsStar(List listEmail) => listEmail.every((email) => email.isFlaggedEmail()); - void cancelSelectEmail() { if (isSearchActive()) { emailListSearch.value = emailListSearch.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList(); @@ -422,7 +418,7 @@ class ThreadController extends BaseController { popBack(); } - final readAction = isAllEmailRead(listPresentationEmail) ? ReadActions.markAsUnread : ReadActions.markAsRead; + final readAction = listPresentationEmail.isAllEmailRead ? ReadActions.markAsUnread : ReadActions.markAsRead; final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value; if (_accountId != null && mailboxCurrent != null) { @@ -589,13 +585,20 @@ class ThreadController extends BaseController { _refreshEmailChanges(); } - void markAsStarSelectedMultipleEmail(List listPresentationEmail, MarkStarAction markStarAction) { - popBack(); + void markAsStarSelectedMultipleEmail(List listPresentationEmail, + {bool fromContextMenuAction = false, MarkStarAction? markStarAction}) { + if (fromContextMenuAction) { + popBack(); + } + + final starAction = markStarAction != null + ? markStarAction + : listPresentationEmail.isAllEmailStarred ? MarkStarAction.unMarkStar : MarkStarAction.markStar; final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value; if (_accountId != null && mailboxCurrent != null) { final listEmail = listPresentationEmail.map((presentationEmail) => presentationEmail.toEmail()).toList(); - consumeState(_markAsStarMultipleEmailInteractor.execute(_accountId!, listEmail, markStarAction)); + consumeState(_markAsStarMultipleEmailInteractor.execute(_accountId!, listEmail, starAction)); } } @@ -697,6 +700,10 @@ class ThreadController extends BaseController { case EmailActionType.markAsUnread: markAsSelectedEmailRead(selectionEmail); break; + case EmailActionType.markAsStar: + case EmailActionType.markAsUnStar: + markAsStarSelectedMultipleEmail(selectionEmail); + break; case EmailActionType.move: moveSelectedMultipleEmailToMailboxAction(selectionEmail); break; diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index e5eebcc0e..744126ef6 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -6,6 +6,7 @@ import 'package:get/get.dart'; import 'package:model/model.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/extensions/filter_message_option_extension.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart'; @@ -74,7 +75,7 @@ class ThreadView extends GetWidget { ], )) ), - Obx(() => controller.isSelectionEnabled() + Obx(() => controller.isSelectionEnabled() && !_responsiveUtils.isDesktop(context) ? Column(children: [ Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2), _buildOptionSelectionBottomBar(context) @@ -110,6 +111,37 @@ class ThreadView extends GetWidget { } Widget _buildHeader(BuildContext context) { + return Row(children: [ + Obx(() => !controller.isSelectionEnabled() && _responsiveUtils.isDesktop(context) + ? _buildOptionTopBar(context) + : SizedBox.shrink()), + Obx(() => controller.isSelectionEnabled() && _responsiveUtils.isDesktop(context) + ? _buildOptionSelectionTopBar(context) + : SizedBox.shrink()), + SizedBox(width: 16), + Spacer(), + Padding( + padding: EdgeInsets.only(right: 16), + child: (SearchBarView(_imagePaths) + ..hintTextSearch(AppLocalizations.of(context).search_emails) + ..maxSizeWidth(240) + ..addOnOpenSearchViewAction(() => controller.enableSearch(context))) + .build()), + Padding( + padding: EdgeInsets.only(right: 16), + child: (AvatarBuilder() + ..text(controller.mailboxDashBoardController.userProfile.value?.getAvatarText() ?? '') + ..backgroundColor(Colors.white) + ..textColor(Colors.black) + ..addBoxShadows([BoxShadow( + color: AppColor.colorShadowBgContentEmail, + spreadRadius: 1, blurRadius: 1, offset: Offset(0, 0.5))]) + ..size(48)) + .build()), + ]); + } + + Widget _buildOptionTopBar(BuildContext context) { return Row(children: [ (ButtonBuilder(_imagePaths.icRefresh) ..key(Key('button_reload_thread')) @@ -141,7 +173,15 @@ class ThreadView extends GetWidget { ..padding(EdgeInsets.symmetric(horizontal: 12, vertical: 8)) ..radiusSplash(10) ..textStyle(TextStyle(fontSize: 12, color: AppColor.colorTextButtonHeaderThread)) - ..onPressActionClick(() => {}) + ..onPressActionClick(() { + final listEmail = controller.isSearchActive() + ? controller.emailListSearch.allEmailUnread + : controller.emailList.allEmailUnread; + + if (listEmail.isNotEmpty) { + controller.markAsSelectedEmailRead(listEmail); + } + }) ..text(AppLocalizations.of(context).mark_all_as_read, isVertical: false)) .build(), SizedBox(width: 16), @@ -153,37 +193,53 @@ class ThreadView extends GetWidget { ..size(16) ..padding(EdgeInsets.symmetric(horizontal: 12, vertical: 8)) ..radiusSplash(10) - ..textStyle(TextStyle(fontSize: 12, color: AppColor.colorTextButtonHeaderThread)) + ..textStyle(TextStyle( + fontSize: 12, + color: controller.filterMessageOption.value == FilterMessageOption.all ? AppColor.colorTextButtonHeaderThread : AppColor.colorNameEmail, + fontWeight: controller.filterMessageOption.value == FilterMessageOption.all ? FontWeight.normal : FontWeight.w500)) ..addIconAction(Padding( padding: EdgeInsets.only(left: 8), child: SvgPicture.asset(_imagePaths.icArrowDown, fit: BoxFit.fill))) ..addOnPressActionWithPositionClick((position) => controller.openFilterMessagesForTablet( - context, - position, - _popupMenuEmailActionTile(context, controller.filterMessageOption.value))) - ..text(AppLocalizations.of(context).filter_messages, isVertical: false)) + context, + position, + _popupMenuEmailActionTile(context, controller.filterMessageOption.value))) + ..text(controller.filterMessageOption.value == FilterMessageOption.all + ? AppLocalizations.of(context).filter_messages + : controller.filterMessageOption.value.getTitle(context), isVertical: false)) .build(), - SizedBox(width: 16), - Spacer(), - Padding( - padding: EdgeInsets.only(right: 16), - child: (SearchBarView(_imagePaths) - ..hintTextSearch(AppLocalizations.of(context).search_emails) - ..maxSizeWidth(240) - ..addOnOpenSearchViewAction(() => controller.enableSearch(context))) - .build()), - Padding( - padding: EdgeInsets.only(right: 16), - child: (AvatarBuilder() - ..text(controller.mailboxDashBoardController.userProfile.value?.getAvatarText() ?? '') - ..backgroundColor(Colors.white) - ..textColor(Colors.black) - ..addBoxShadows([BoxShadow( - color: AppColor.colorShadowBgContentEmail, - spreadRadius: 1, blurRadius: 1, offset: Offset(0, 0.5))]) - ..size(48)) - .build()), + ]); + } + + Widget _buildOptionSelectionTopBar(BuildContext context) { + return Row(children: [ + buildIconWeb( + icon: SvgPicture.asset(_imagePaths.icCloseComposer, color: AppColor.colorTextButton, fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).cancel, + onTap: () => controller.cancelSelectEmail()), + Text( + AppLocalizations.of(context).count_email_selected(controller.listEmailSelected.length), + style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton),), + SizedBox(width: 30), + buildIconWeb( + icon: SvgPicture.asset(controller.listEmailSelected.isAllEmailRead ? _imagePaths.icUnread : _imagePaths.icRead, fit: BoxFit.fill), + tooltip: controller.listEmailSelected.isAllEmailRead ? AppLocalizations.of(context).unread : AppLocalizations.of(context).read, + onTap: () => controller.pressEmailSelectionAction( + context, + controller.listEmailSelected.isAllEmailRead ? EmailActionType.markAsUnread : EmailActionType.markAsRead, + controller.listEmailSelected)), + buildIconWeb( + icon: SvgPicture.asset(controller.listEmailSelected.isAllEmailStarred ? _imagePaths.icUnStar : _imagePaths.icStar, fit: BoxFit.fill), + tooltip: controller.listEmailSelected.isAllEmailStarred ? AppLocalizations.of(context).not_starred : AppLocalizations.of(context).starred, + onTap: () => controller.pressEmailSelectionAction( + context, + controller.listEmailSelected.isAllEmailStarred ? EmailActionType.markAsUnStar : EmailActionType.markAsStar, + controller.listEmailSelected)), + buildIconWeb( + icon: SvgPicture.asset(_imagePaths.icMove, fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).move, + onTap: () => controller.pressEmailSelectionAction(context, EmailActionType.move, controller.listEmailSelected)), ]); } @@ -194,7 +250,7 @@ class ThreadView extends GetWidget { context, _imagePaths, _responsiveUtils, - controller.getListEmailSelected()) + controller.listEmailSelected) ..addOnPressEmailSelectionActionClick((actionType, selectionEmail) => controller.pressEmailSelectionAction(context, actionType, selectionEmail))) .build()); @@ -223,7 +279,7 @@ class ThreadView extends GetWidget { _imagePaths, _responsiveUtils, controller.mailboxDashBoardController.selectedMailbox.value, - controller.getListEmailSelected(), + controller.listEmailSelected, controller.currentSelectMode.value, controller.filterMessageOption.value) ..addOpenListMailboxActionClick(() => controller.openMailboxLeftMenu()) diff --git a/lib/features/thread/presentation/widgets/email_tile_builder.dart b/lib/features/thread/presentation/widgets/email_tile_builder.dart index 8e22a4f20..0d94c29d3 100644 --- a/lib/features/thread/presentation/widgets/email_tile_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_builder.dart @@ -59,7 +59,16 @@ class EmailTileBuilder { child: ResponsiveWidget( responsiveUtils: _responsiveUtils, mobile: _buildListTile(), - desktop: _buildListTileForDesktop(), + desktop: Container( + margin: _selectModeAll == SelectMode.ACTIVE ? EdgeInsets.only(top: 3, left: 8, right: 8) : EdgeInsets.zero, + padding: _selectModeAll == SelectMode.ACTIVE ? EdgeInsets.symmetric(vertical: 10) : EdgeInsets.zero, + decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE + ? BoxDecoration( + borderRadius: BorderRadius.circular(14), + color: AppColor.colorItemEmailSelectedDesktop) + : null, + child: _buildListTileForDesktop(), + ), ) ); } @@ -212,13 +221,12 @@ class EmailTileBuilder { fit: BoxFit.fill), tooltip: _presentationEmail.isFlaggedEmail() ? AppLocalizations.of(_context).starred : AppLocalizations.of(_context).not_starred, onTap: () => _onMarkAsStarActionClick?.call(_presentationEmail)), - SizedBox(width: 8), + if (_selectModeAll == SelectMode.INACTIVE) SizedBox(width: 8), _buildAvatarIcon( iconSize: 32, - iconSizeSelect: 32, textStyle: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Colors.white), paddingIconSelect: EdgeInsets.all(5)), - SizedBox(width: 10), + if (_selectModeAll == SelectMode.INACTIVE) SizedBox(width: 10), Container(width: 180, child: _isSearchEnabled ? RichTextBuilder( _getInformationSender(), @@ -277,18 +285,18 @@ class EmailTileBuilder { child: Text('${_presentationEmail.getReceivedAt(Localizations.localeOf(_context).toLanguageTag())}', maxLines: 1, overflow: kIsWeb ? null: TextOverflow.ellipsis, - style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail))), + style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail, fontWeight: FontWeight.normal))), ]), - Padding( - padding: EdgeInsets.only(top: 10, bottom: 10, left: 70), - child: Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2)), + if (_selectModeAll == SelectMode.INACTIVE) + Padding( + padding: EdgeInsets.only(top: 10, bottom: 10, left: 70), + child: Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2)), ]), ); } Widget _buildAvatarIcon({ double? iconSize, - double? iconSizeSelect, EdgeInsets? paddingIconSelect, TextStyle? textStyle }) { @@ -296,14 +304,14 @@ class EmailTileBuilder { return AnimatedSwitcher( duration: Duration(milliseconds: 600), transitionBuilder: _transitionBuilder, - child: (IconBuilder(_presentationEmail.selectMode == SelectMode.ACTIVE ? _imagePaths.icSelected : _imagePaths.icUnSelected) - ..padding(paddingIconSelect ?? EdgeInsets.all(14)) - ..size(iconSize ?? (GetPlatform.isWeb ? 48 : 56)) - ..addOnTapActionClick(() { + child: buildIconWeb( + icon: SvgPicture.asset(_presentationEmail.selectMode == SelectMode.ACTIVE ? _imagePaths.icSelected : _imagePaths.icUnSelected, fit: BoxFit.fill), + iconPadding: paddingIconSelect ?? EdgeInsets.all(12), + tooltip: AppLocalizations.of(_context).select, + onTap: () { if (_selectModeAll == SelectMode.ACTIVE && _onSelectEmailActionClick != null) { _onSelectEmailActionClick!(_presentationEmail); - }})) - .build() + }}), ); } else { return (AvatarBuilder() diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 0fae87c33..397ef6f24 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -855,4 +855,11 @@ class AppLocalizations { name: 'not_starred' ); } + + String get select { + return Intl.message( + 'Select', + name: 'select', + ); + } } \ No newline at end of file diff --git a/model/lib/extensions/list_presentation_email_extension.dart b/model/lib/extensions/list_presentation_email_extension.dart index 8acb693de..2cf0b9f2b 100644 --- a/model/lib/extensions/list_presentation_email_extension.dart +++ b/model/lib/extensions/list_presentation_email_extension.dart @@ -3,4 +3,8 @@ import 'package:model/email/presentation_email.dart'; extension ListPresentationEmailExtension on List { bool get isAllEmailRead => every((email) => email.isReadEmail()); + + bool get isAllEmailStarred => every((email) => email.isFlaggedEmail()); + + List get allEmailUnread => where((email) => email.isUnReadEmail()).toList(); } \ No newline at end of file