TF-119 Filter messages with unread, attachments, starred

This commit is contained in:
dab246
2022-02-11 14:02:06 +07:00
committed by Dat H. Pham
parent c22bc94052
commit ca69bee4cd
22 changed files with 447 additions and 34 deletions
@@ -0,0 +1,67 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
enum FilterMessageOption {
all,
unread,
attachments,
starred,
}
extension FilterMessageOptionExtension on FilterMessageOption {
bool filterEmail(PresentationEmail email) {
switch(this) {
case FilterMessageOption.all:
return true;
case FilterMessageOption.unread:
return email.isUnReadEmail();
case FilterMessageOption.attachments:
return email.withAttachments;
case FilterMessageOption.starred:
return email.isFlaggedEmail();
}
}
String getIconToast(ImagePaths imagePaths) {
switch(this) {
case FilterMessageOption.all:
return imagePaths.icFilterMessageAll;
case FilterMessageOption.unread:
return imagePaths.icUnreadToast;
case FilterMessageOption.attachments:
return imagePaths.icFilterMessageAttachments;
case FilterMessageOption.starred:
return imagePaths.icStar;
}
}
String getMessageToast(BuildContext context) {
switch(this) {
case FilterMessageOption.all:
return AppLocalizations.of(context).disable_filter_message_toast;
case FilterMessageOption.unread:
return AppLocalizations.of(context).filter_message_toast(AppLocalizations.of(context).unread);
case FilterMessageOption.attachments:
return AppLocalizations.of(context).filter_message_toast(AppLocalizations.of(context).with_attachments);
case FilterMessageOption.starred:
return AppLocalizations.of(context).filter_message_toast(AppLocalizations.of(context).starred);
}
}
String getTitle(BuildContext context) {
switch(this) {
case FilterMessageOption.all:
return '';
case FilterMessageOption.unread:
return AppLocalizations.of(context).with_unread;
case FilterMessageOption.attachments:
return AppLocalizations.of(context).with_attachments.capitalizeFirstEach;
case FilterMessageOption.starred:
return AppLocalizations.of(context).with_starred;
}
}
}
@@ -39,6 +39,7 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multi
import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/search_email_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/search_more_email_interactor.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/filter_message_option.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
@@ -63,7 +64,9 @@ class ThreadController extends BaseController {
final emailList = <PresentationEmail>[].obs;
final emailListSearch = <PresentationEmail>[].obs;
final emailListFiltered = <PresentationEmail>[].obs;
final currentSelectMode = SelectMode.INACTIVE.obs;
final filterMessageOption = FilterMessageOption.all.obs;
final random = Random();
@@ -204,7 +207,8 @@ class ThreadController extends BaseController {
void _resetToOriginalValue() {
dispatchState(Right(LoadingState()));
emailList.value = <PresentationEmail>[];
emailList.clear();
emailListFiltered.clear();
canLoadMore = true;
disableSearch();
cancelSelectEmail();
@@ -216,6 +220,12 @@ class ThreadController extends BaseController {
.map((email) => email.asAvatarGradientColor(random))
.toList();
emailList.value = listEmailHaveAvatarGradientColor;
if (isFilterMessagesEnabled) {
final emailsFiltered = listEmailHaveAvatarGradientColor
.where((email) => filterMessageOption.value.filterEmail(email))
.toList();
emailListFiltered.addAll(emailsFiltered);
}
}
void _getAllEmailAction(AccountId accountId, {MailboxId? mailboxId}) {
@@ -283,6 +293,13 @@ class ThreadController extends BaseController {
.map((email) => email.asAvatarGradientColor(random))
.toList();
emailList.addAll(listEmailHaveAvatarGradientColor);
if (isFilterMessagesEnabled) {
final emailsFilteredMore = listEmailHaveAvatarGradientColor
.where((email) => filterMessageOption.value.filterEmail(email))
.toList();
emailListFiltered.addAll(emailsFilteredMore);
}
} else {
canLoadMore = false;
}
@@ -307,9 +324,15 @@ class ThreadController extends BaseController {
.map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email)
.toList();
} else {
emailList.value = emailList
.map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email)
.toList();
if (isFilterMessagesEnabled) {
emailListFiltered.value = emailListFiltered
.map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email)
.toList();
} else {
emailList.value = emailList
.map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email)
.toList();
}
}
if (_isUnSelectedAll()) {
@@ -329,7 +352,11 @@ class ThreadController extends BaseController {
if (isSearchActive()) {
return emailListSearch.where((email) => email.selectMode == SelectMode.ACTIVE).toList();
} else {
return emailList.where((email) => email.selectMode == SelectMode.ACTIVE).toList();
if (isFilterMessagesEnabled) {
return emailListFiltered.where((email) => email.selectMode == SelectMode.ACTIVE).toList();
} else {
return emailList.where((email) => email.selectMode == SelectMode.ACTIVE).toList();
}
}
}
@@ -337,7 +364,11 @@ class ThreadController extends BaseController {
if (isSearchActive()) {
return emailListSearch.every((email) => email.selectMode == SelectMode.INACTIVE);
} else {
return emailList.every((email) => email.selectMode == SelectMode.INACTIVE);
if (isFilterMessagesEnabled) {
return emailListFiltered.every((email) => email.selectMode == SelectMode.INACTIVE);
} else {
return emailList.every((email) => email.selectMode == SelectMode.INACTIVE);
}
}
}
@@ -349,7 +380,11 @@ class ThreadController extends BaseController {
if (isSearchActive()) {
emailListSearch.value = emailListSearch.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList();
} else {
emailList.value = emailList.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList();
if (isFilterMessagesEnabled) {
emailListFiltered.value = emailListFiltered.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList();
} else {
emailList.value = emailList.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList();
}
}
currentSelectMode.value = SelectMode.INACTIVE;
}
@@ -397,6 +432,35 @@ class ThreadController extends BaseController {
_appToast.showErrorToast(AppLocalizations.of(Get.context!).an_error_occurred);
}
void openFilterMessagesCupertinoActionSheet(BuildContext context, List<Widget> actionTiles, {Widget? cancelButton}) {
(CupertinoActionSheetBuilder(context)
..addTiles(actionTiles)
..addCancelButton(cancelButton))
.build();
}
void closeFilterMessageActionSheet() {
popBack();
}
bool get isFilterMessagesEnabled => filterMessageOption != FilterMessageOption.all;
void filterMessagesAction(BuildContext context, FilterMessageOption filterOption) {
popBack();
final newFilterOption = filterMessageOption.value == filterOption ? FilterMessageOption.all : filterOption;
final emailsFiltered = emailList.where((email) => newFilterOption.filterEmail(email)).toList();
emailListFiltered.value = emailsFiltered;
filterMessageOption.value = newFilterOption;
_appToast.showToastWithIcon(
Get.overlayContext!,
message: newFilterOption.getMessageToast(context),
icon: newFilterOption.getIconToast(_imagePaths));
}
void openContextMenuSelectedEmail(BuildContext context, List<Widget> actionTiles) {
(ContextMenuBuilder(context)
..addTiles(actionTiles))
@@ -573,7 +637,7 @@ class ThreadController extends BaseController {
void _searchEmailsSuccess(SearchEmailSuccess success) {
final resultEmailSearchList = success.emailList
.map((email) => email.toSearchPresentationEmail(mailboxDashBoardController.mapMailbox))
.map((email) => email.toSearchPresentationEmail(mailboxDashBoardController.mapMailbox, random))
.toList();
emailListSearch.value = resultEmailSearchList;
@@ -597,7 +661,7 @@ class ThreadController extends BaseController {
void _searchMoreEmailsSuccess(SearchMoreEmailSuccess success) {
if (success.emailList.isNotEmpty) {
final resultEmailSearchList = success.emailList
.map((email) => email.toSearchPresentationEmail(mailboxDashBoardController.mapMailbox))
.map((email) => email.toSearchPresentationEmail(mailboxDashBoardController.mapMailbox, random))
.where((email) => !emailListSearch.contains(email))
.toList();
emailListSearch.addAll(resultEmailSearchList);
@@ -1,14 +1,17 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
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/model/filter_message_option.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';
import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.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/search_app_bar_widget.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/search_bar_thread_view_widget.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/suggestion_box_widget.dart';
@@ -146,13 +149,72 @@ class ThreadView extends GetWidget<ThreadController> {
responsiveUtils,
controller.mailboxDashBoardController.selectedMailbox.value,
controller.getListEmailSelected(),
controller.currentSelectMode.value)
controller.currentSelectMode.value,
controller.filterMessageOption.value)
..addOpenListMailboxActionClick(() => controller.openMailboxLeftMenu())
..addOnEditThreadAction(() => controller.enableSelectionEmail())
..addOnFilterEmailAction((filterMessageOption) =>
controller.openFilterMessagesCupertinoActionSheet(
context,
_filterMessagesCupertinoActionTile(context, filterMessageOption),
cancelButton: _buildCupertinoActionCancelButton(context)))
..addOnCancelEditThread(() => controller.cancelSelectEmail()))
.build();
}
List<Widget> _filterMessagesCupertinoActionTile(BuildContext context, FilterMessageOption optionCurrent) {
return <Widget>[
_filterMessageWithAttachmentsAction(context, optionCurrent),
_filterMessagesUnreadAction(context, optionCurrent),
_filterMessageStarredAction(context, optionCurrent),
];
}
Widget _buildCupertinoActionCancelButton(BuildContext context) {
return CupertinoActionSheetAction(
child: Text(
AppLocalizations.of(context).cancel,
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20, color: AppColor.colorTextButton)),
onPressed: () => controller.closeFilterMessageActionSheet(),
);
}
Widget _filterMessageWithAttachmentsAction(BuildContext context, FilterMessageOption optionCurrent) {
return (FilterMessageCupertinoActionSheetActionBuilder(
Key('filter_attachment_action'),
SvgPicture.asset(imagePaths.icAttachment, width: 28, height: 28, fit: BoxFit.fill, color: AppColor.colorTextButton),
AppLocalizations.of(context).with_attachments,
FilterMessageOption.attachments,
optionCurrent: optionCurrent,
actionSelected: SvgPicture.asset(imagePaths.icFilterSelected, fit: BoxFit.fill))
..onActionClick((option) => controller.filterMessagesAction(context, option)))
.build();
}
Widget _filterMessagesUnreadAction(BuildContext context, FilterMessageOption optionCurrent) {
return (FilterMessageCupertinoActionSheetActionBuilder(
Key('filter_unread_action'),
SvgPicture.asset(imagePaths.icUnreadV2, width: 28, height: 28, fit: BoxFit.fill),
AppLocalizations.of(context).unread,
FilterMessageOption.unread,
optionCurrent: optionCurrent,
actionSelected: SvgPicture.asset(imagePaths.icFilterSelected, fit: BoxFit.fill))
..onActionClick((option) => controller.filterMessagesAction(context, option)))
.build();
}
Widget _filterMessageStarredAction(BuildContext context, FilterMessageOption optionCurrent) {
return (FilterMessageCupertinoActionSheetActionBuilder(
Key('filter_starred_action'),
SvgPicture.asset(imagePaths.icStar, width: 28, height: 28, fit: BoxFit.fill),
AppLocalizations.of(context).starred,
FilterMessageOption.starred,
optionCurrent: optionCurrent,
actionSelected: SvgPicture.asset(imagePaths.icFilterSelected, fit: BoxFit.fill))
..onActionClick((option) => controller.filterMessagesAction(context, option)))
.build();
}
// Widget _buildAppBarSelectModeActive(BuildContext context) {
// return (AppBarThreadSelectModeActiveBuilder(
// context,
@@ -318,7 +380,9 @@ class ThreadView extends GetWidget<ThreadController> {
return _buildResultSearchEmails(context, controller.emailListSearch);
}
} else {
return _buildResultListEmail(context, controller.emailList);
return _buildResultListEmail(
context,
controller.isFilterMessagesEnabled ? controller.emailListFiltered : controller.emailList);
}
})
);
@@ -4,9 +4,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/filter_message_option.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnFilterEmailAction = void Function();
typedef OnFilterEmailAction = void Function(FilterMessageOption);
typedef OnEditThreadAction = void Function();
typedef OnOpenListMailboxActionClick = void Function();
typedef OnCancelEditThread = void Function();
@@ -23,6 +24,7 @@ class AppBarThreadWidgetBuilder {
final PresentationMailbox? _presentationMailbox;
final List<PresentationEmail> _listSelectionEmail;
final SelectMode _selectMode;
final FilterMessageOption _filterMessageOption;
AppBarThreadWidgetBuilder(
this._context,
@@ -31,6 +33,7 @@ class AppBarThreadWidgetBuilder {
this._presentationMailbox,
this._listSelectionEmail,
this._selectMode,
this._filterMessageOption,
);
void addOnFilterEmailAction(OnFilterEmailAction onFilterEmailAction) {
@@ -69,9 +72,20 @@ class AppBarThreadWidgetBuilder {
if (_selectMode == SelectMode.ACTIVE)
Positioned(left: 40, child: _buildCountItemSelected()),
Positioned(right: 0, child: _buildFilterButton()),
Padding(
padding: EdgeInsets.only(left: 60, right: 40),
child: _buildContentCenterAppBar()),
_filterMessageOption.getTitle(_context).isNotEmpty
? Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Padding(
padding: EdgeInsets.only(left: 60, right: 40),
child: _buildContentCenterAppBar()),
Transform(
transform: Matrix4.translationValues(-2.0, -8.0, 0.0),
child: Text(
_filterMessageOption.getTitle(_context),
style: TextStyle(fontSize: 11, color: AppColor.colorContentEmail)))
])
: Padding(
padding: EdgeInsets.only(left: 60, right: 40),
child: _buildContentCenterAppBar()),
]
))
])
@@ -103,9 +117,13 @@ class AppBarThreadWidgetBuilder {
child: Padding(
padding: EdgeInsets.only(left: 16),
child: IconButton(
color: AppColor.baseTextColor,
icon: SvgPicture.asset(_imagePaths.icFilter, fit: BoxFit.fill),
onPressed: () => _onFilterEmailAction?.call()
icon: SvgPicture.asset(
_imagePaths.icFilter,
color: _filterMessageOption == FilterMessageOption.all
? AppColor.colorFilterMessageDisabled
: AppColor.colorFilterMessageEnabled,
fit: BoxFit.fill),
onPressed: () => _onFilterEmailAction?.call(_filterMessageOption)
)
)
);
@@ -0,0 +1,51 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/filter_message_option.dart';
class FilterMessageCupertinoActionSheetActionBuilder extends CupertinoActionSheetActionBuilder<FilterMessageOption> {
final FilterMessageOption option;
final FilterMessageOption? optionCurrent;
final SvgPicture? actionSelected;
final Color? bgColor;
FilterMessageCupertinoActionSheetActionBuilder(
Key key,
SvgPicture actionIcon,
String actionName,
this.option,
{
this.optionCurrent,
this.actionSelected,
this.bgColor,
}
) : super(key, actionIcon, actionName);
@override
Widget build() {
return Container(
color: bgColor ?? Colors.white,
child: CupertinoActionSheetAction(
key: key,
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Padding(
padding: EdgeInsets.only(left: 12, right: 16),
child: actionIcon),
Expanded(child: Text(actionName, textAlign: TextAlign.left, style: actionTextStyle())),
if (optionCurrent == option && actionSelected != null)
Padding(
padding: EdgeInsets.only(right: 12),
child: actionSelected!),
]),
onPressed: () {
if (onCupertinoActionSheetActionClick != null) {
onCupertinoActionSheetActionClick!(option);
}
},
),
);
}
}