diff --git a/assets/images/ic_filter_message_all.svg b/assets/images/ic_filter_message_all.svg
new file mode 100644
index 000000000..d27976256
--- /dev/null
+++ b/assets/images/ic_filter_message_all.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/ic_filter_message_attachments.svg b/assets/images/ic_filter_message_attachments.svg
new file mode 100644
index 000000000..f1ee80599
--- /dev/null
+++ b/assets/images/ic_filter_message_attachments.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/ic_filter_selected.svg b/assets/images/ic_filter_selected.svg
new file mode 100644
index 000000000..2c8236226
--- /dev/null
+++ b/assets/images/ic_filter_selected.svg
@@ -0,0 +1,3 @@
+
diff --git a/core/lib/core.dart b/core/lib/core.dart
index bcbe072bb..94a42cf1c 100644
--- a/core/lib/core.dart
+++ b/core/lib/core.dart
@@ -42,6 +42,8 @@ export 'presentation/views/dialog/downloading_file_dialog_builder.dart';
export 'presentation/views/background/background_widget_builder.dart';
export 'presentation/views/html_viewer/html_content_viewer_widget.dart';
export 'presentation/views/floating_button/scrolling_floating_button_animated.dart';
+export 'presentation/views/bottom_popup/cupertino_action_sheet_action_builder.dart';
+export 'presentation/views/bottom_popup/cupertino_action_sheet_builder.dart';
// Resources
export 'presentation/resources/assets_paths.dart';
diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart
index 3f7d6ac31..c8253067e 100644
--- a/core/lib/presentation/extensions/color_extension.dart
+++ b/core/lib/presentation/extensions/color_extension.dart
@@ -71,6 +71,9 @@ extension AppColor on Color {
static const colorDividerMailbox = Color(0xFF99A2AD);
static const colorArrowUserMailbox = Color(0xFFB8C1CC);
static const colorBgMailbox = Color(0xFFF7F7F7);
+ static const colorFilterMessageDisabled = Color(0xFF99A2AD);
+ static const colorFilterMessageEnabled = Color(0xFF007AFF);
+ static const colorDefaultCupertinoActionSheet = Color(0x66000000);
static const mapGradientColor = [
[Color(0xFF21D4FD), Color(0xFFB721FF)],
diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart
index 5758855e8..e166628d6 100644
--- a/core/lib/presentation/resources/image_paths.dart
+++ b/core/lib/presentation/resources/image_paths.dart
@@ -75,6 +75,9 @@ class ImagePaths {
String get icMailboxArchivedV2 => _getImagePath('ic_mailbox_archived_v2.svg');
String get icMailboxSpamV2 => _getImagePath('ic_mailbox_spam_v2.svg');
String get icMailboxTrashV2 => _getImagePath('ic_mailbox_trash_v2.svg');
+ String get icFilterSelected => _getImagePath('ic_filter_selected.svg');
+ String get icFilterMessageAll => _getImagePath('ic_filter_message_all.svg');
+ String get icFilterMessageAttachments => _getImagePath('ic_filter_message_attachments.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
diff --git a/core/lib/presentation/utils/responsive_utils.dart b/core/lib/presentation/utils/responsive_utils.dart
index 2acb28039..80b2d97a2 100644
--- a/core/lib/presentation/utils/responsive_utils.dart
+++ b/core/lib/presentation/utils/responsive_utils.dart
@@ -40,7 +40,12 @@ class ResponsiveUtils {
double getWidthLoginButton() => _loginButtonWidth;
EdgeInsets getMarginDestinationPicker(BuildContext context) {
- if (isTablet(context)) {
+ if (isMobileDevice(context) && isLandscape(context)) {
+ return EdgeInsets.only(
+ left: _tabletHorizontalMargin,
+ right: _tabletHorizontalMargin,
+ top: 50.0);
+ } else if (isTablet(context)) {
return getSizeHeightScreen(context) <= _tabletVerticalMargin * 2
? EdgeInsets.symmetric(
horizontal: _tabletHorizontalMargin,
@@ -62,7 +67,9 @@ class ResponsiveUtils {
}
BorderRadius radiusDestinationPicker(BuildContext context, double radius) {
- if (isTablet(context)) {
+ if (isMobileDevice(context) && isLandscape(context)) {
+ return BorderRadius.only(topLeft: Radius.circular(radius), topRight: Radius.circular(radius));
+ } else if (isTablet(context)) {
return getSizeHeightScreen(context) <= _tabletVerticalMargin * 2
? BorderRadius.only(topLeft: Radius.circular(radius), topRight: Radius.circular(radius))
: BorderRadius.circular(radius);
diff --git a/core/lib/presentation/views/bottom_popup/cupertino_action_sheet_action_builder.dart b/core/lib/presentation/views/bottom_popup/cupertino_action_sheet_action_builder.dart
new file mode 100644
index 000000000..f2d143297
--- /dev/null
+++ b/core/lib/presentation/views/bottom_popup/cupertino_action_sheet_action_builder.dart
@@ -0,0 +1,29 @@
+import 'package:core/presentation/extensions/color_extension.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+typedef OnCupertinoActionSheetActionClick = void Function(T data);
+
+abstract class CupertinoActionSheetActionBuilder {
+ @protected final Key key;
+ @protected final SvgPicture actionIcon;
+ @protected final String actionName;
+ @protected OnCupertinoActionSheetActionClick? onCupertinoActionSheetActionClick;
+
+ CupertinoActionSheetActionBuilder(
+ this.key,
+ this.actionIcon,
+ this.actionName
+ );
+
+ void onActionClick(OnCupertinoActionSheetActionClick onCupertinoActionSheetActionClick) {
+ this.onCupertinoActionSheetActionClick = onCupertinoActionSheetActionClick;
+ }
+
+ TextStyle actionTextStyle() {
+ return TextStyle(fontSize: 17, color: AppColor.colorNameEmail);
+ }
+
+ Widget build();
+}
\ No newline at end of file
diff --git a/core/lib/presentation/views/bottom_popup/cupertino_action_sheet_builder.dart b/core/lib/presentation/views/bottom_popup/cupertino_action_sheet_builder.dart
new file mode 100644
index 000000000..83d3c829a
--- /dev/null
+++ b/core/lib/presentation/views/bottom_popup/cupertino_action_sheet_builder.dart
@@ -0,0 +1,44 @@
+
+import 'package:core/core.dart';
+import 'package:flutter/cupertino.dart';
+
+class CupertinoActionSheetBuilder {
+
+ final BuildContext _context;
+ final List _actionTiles = [];
+
+ Widget? _titleWidget;
+ Widget? _messageWidget;
+ Widget? _cancelWidget;
+
+ CupertinoActionSheetBuilder(this._context);
+
+ void title(Widget? titleWidget) {
+ _titleWidget = titleWidget;
+ }
+
+ void message(Widget? messageWidget) {
+ _messageWidget = messageWidget;
+ }
+
+ void addCancelButton(Widget? cancelWidget) {
+ _cancelWidget = cancelWidget;
+ }
+
+ void addTiles(List tiles) {
+ _actionTiles.addAll(tiles);
+ }
+
+ void build() {
+ showCupertinoModalPopup(
+ context: _context,
+ barrierColor: AppColor.colorDefaultCupertinoActionSheet,
+ builder: (context) => CupertinoActionSheet(
+ title: _titleWidget,
+ message: _messageWidget,
+ actions: _actionTiles,
+ cancelButton: _cancelWidget,
+ )
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/features/destination_picker/presentation/widgets/app_bar_destination_picker_builder.dart b/lib/features/destination_picker/presentation/widgets/app_bar_destination_picker_builder.dart
index 6315f5abf..77c9777d3 100644
--- a/lib/features/destination_picker/presentation/widgets/app_bar_destination_picker_builder.dart
+++ b/lib/features/destination_picker/presentation/widgets/app_bar_destination_picker_builder.dart
@@ -24,7 +24,9 @@ class AppBarDestinationPickerBuilder {
key: Key('app_bar_destination_picker'),
alignment: Alignment.center,
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
- color: Colors.white,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)),
+ color: Colors.white),
child: MediaQuery(
data: MediaQueryData(padding: EdgeInsets.zero),
child: Row(
diff --git a/lib/features/email/presentation/email_controller.dart b/lib/features/email/presentation/email_controller.dart
index b4b592a20..09115c1d4 100644
--- a/lib/features/email/presentation/email_controller.dart
+++ b/lib/features/email/presentation/email_controller.dart
@@ -48,6 +48,7 @@ class EmailController extends BaseController {
final emailContents = [].obs;
final attachments = [].obs;
EmailId? _currentEmailId;
+ List? _currentAvatarColor;
EmailController(
this._getEmailContentInteractor,
@@ -66,6 +67,7 @@ class EmailController extends BaseController {
mailboxDashBoardController.selectedEmail.listen((presentationEmail) {
if (_currentEmailId != presentationEmail?.id) {
_currentEmailId = presentationEmail?.id;
+ _currentAvatarColor = presentationEmail?.avatarColors;
_clearEmailContent();
final accountId = mailboxDashBoardController.accountId.value;
if (accountId != null && presentationEmail != null) {
@@ -151,7 +153,7 @@ class EmailController extends BaseController {
void _markAsEmailReadSuccess(Success success) {
if (success is MarkAsEmailReadSuccess) {
- mailboxDashBoardController.setSelectedEmail(success.updatedEmail.toPresentationEmail(selectMode: SelectMode.ACTIVE));
+ mailboxDashBoardController.setSelectedEmail(success.updatedEmail.toPresentationEmail(selectMode: SelectMode.ACTIVE, avatarColors: _currentAvatarColor));
}
mailboxDashBoardController.dispatchState(Right(success));
diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart
index 6dfd6e49e..a5f707aa8 100644
--- a/lib/features/email/presentation/email_view.dart
+++ b/lib/features/email/presentation/email_view.dart
@@ -83,10 +83,10 @@ class EmailView extends GetView {
width: double.infinity,
alignment: Alignment.center,
padding: EdgeInsets.only(
- left: responsiveUtils.isMobile(context) ? 16 : 24,
- right: responsiveUtils.isMobile(context) ? 16 : 24,
- bottom: responsiveUtils.isMobile(context) ? 16 : 24,
- top: responsiveUtils.isMobile(context) ? 10 : 16),
+ left: 16,
+ right: 16,
+ bottom: 16,
+ top: 16),
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@@ -120,9 +120,9 @@ class EmailView extends GetView {
Widget _buildEmailMessage(BuildContext context) {
return Container(
padding: EdgeInsets.only(
- bottom: responsiveUtils.isMobile(context) ? 16 : 24,
- left: responsiveUtils.isMobile(context) ? 16 : 24,
- right: responsiveUtils.isMobile(context) ? 16 : 24,
+ bottom: 16,
+ left: 16,
+ right: 16,
top: 10),
alignment: Alignment.center,
decoration: BoxDecoration(
diff --git a/lib/features/email/presentation/widgets/email_content_item_builder.dart b/lib/features/email/presentation/widgets/email_content_item_builder.dart
index ea595270d..c928e90bb 100644
--- a/lib/features/email/presentation/widgets/email_content_item_builder.dart
+++ b/lib/features/email/presentation/widgets/email_content_item_builder.dart
@@ -42,7 +42,7 @@ class EmailContentItemBuilder {
padding: EdgeInsets.zero,
child: Text(
_emailContent.content,
- style: TextStyle(fontSize: 14, color: AppColor.nameUserColor)));
+ style: TextStyle(fontSize: 14, color: AppColor.colorNameEmail)));
case EmailContentType.other:
return SizedBox.shrink();
}
diff --git a/lib/features/thread/presentation/model/filter_message_option.dart b/lib/features/thread/presentation/model/filter_message_option.dart
new file mode 100644
index 000000000..44e2ab09b
--- /dev/null
+++ b/lib/features/thread/presentation/model/filter_message_option.dart
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart
index 525f31074..310ae54ae 100644
--- a/lib/features/thread/presentation/thread_controller.dart
+++ b/lib/features/thread/presentation/thread_controller.dart
@@ -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 = [].obs;
final emailListSearch = [].obs;
+ final emailListFiltered = [].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 = [];
+ 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 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 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);
diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart
index d86b46dfd..11c911f12 100644
--- a/lib/features/thread/presentation/thread_view.dart
+++ b/lib/features/thread/presentation/thread_view.dart
@@ -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 {
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 _filterMessagesCupertinoActionTile(BuildContext context, FilterMessageOption optionCurrent) {
+ return [
+ _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 {
return _buildResultSearchEmails(context, controller.emailListSearch);
}
} else {
- return _buildResultListEmail(context, controller.emailList);
+ return _buildResultListEmail(
+ context,
+ controller.isFilterMessagesEnabled ? controller.emailListFiltered : controller.emailList);
}
})
);
diff --git a/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart b/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart
index 23c64ef46..52b6c280d 100644
--- a/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart
+++ b/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart
@@ -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 _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)
)
)
);
diff --git a/lib/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart b/lib/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart
new file mode 100644
index 000000000..ec6d5eba1
--- /dev/null
+++ b/lib/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart
@@ -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 {
+
+ 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);
+ }
+ },
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart
index 50c07ff02..f71d6b1b9 100644
--- a/lib/main/localizations/app_localizations.dart
+++ b/lib/main/localizations/app_localizations.dart
@@ -544,4 +544,45 @@ class AppLocalizations {
'Search for mailboxes',
name: 'hint_search_mailboxes');
}
+
+ String get with_attachments {
+ return Intl.message(
+ 'With attachments',
+ name: 'with_attachments'
+ );
+ }
+
+ String get starred {
+ return Intl.message(
+ 'Starred',
+ name: 'starred'
+ );
+ }
+
+ String filter_message_toast(String filterOption) {
+ return Intl.message(
+ 'You’ve filtered messages by "$filterOption"',
+ name: 'filter_message_toast',
+ args: [filterOption]
+ );
+ }
+
+ String get disable_filter_message_toast {
+ return Intl.message(
+ 'You’ve disabled filtered messages',
+ name: 'disable_filter_message_toast'
+ );
+ }
+
+ String get with_unread {
+ return Intl.message(
+ 'With Unread',
+ name: 'with_unread');
+ }
+
+ String get with_starred {
+ return Intl.message(
+ 'With Starred',
+ name: 'with_starred');
+ }
}
\ No newline at end of file
diff --git a/model/lib/email/presentation_email.dart b/model/lib/email/presentation_email.dart
index a84d6a3f1..a8222d35e 100644
--- a/model/lib/email/presentation_email.dart
+++ b/model/lib/email/presentation_email.dart
@@ -90,6 +90,8 @@ class PresentationEmail with EquatableMixin {
bool isFlaggedEmail() => keywords?.containsKey(KeyWordIdentifier.emailFlagged) == true;
+ bool get withAttachments => hasAttachment == true;
+
String get mailboxName => mailboxNames?.first?.name ?? '';
@override
diff --git a/model/lib/extensions/email_extension.dart b/model/lib/extensions/email_extension.dart
index 93cf44e8b..f6a52eb30 100644
--- a/model/lib/extensions/email_extension.dart
+++ b/model/lib/extensions/email_extension.dart
@@ -1,4 +1,6 @@
+import 'dart:ui';
+
import 'package:http_parser/http_parser.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
@@ -41,7 +43,7 @@ extension EmailExtension on Email {
);
}
- PresentationEmail toPresentationEmail({SelectMode selectMode = SelectMode.INACTIVE}) {
+ PresentationEmail toPresentationEmail({SelectMode selectMode = SelectMode.INACTIVE, List? avatarColors}) {
return PresentationEmail(
id,
keywords: keywords,
@@ -57,7 +59,8 @@ extension EmailExtension on Email {
bcc: bcc,
replyTo: replyTo,
mailboxIds: mailboxIds,
- selectMode: selectMode
+ selectMode: selectMode,
+ avatarColors: avatarColors,
);
}
diff --git a/model/lib/extensions/presentation_email_extension.dart b/model/lib/extensions/presentation_email_extension.dart
index 4d143ff4d..3264d6206 100644
--- a/model/lib/extensions/presentation_email_extension.dart
+++ b/model/lib/extensions/presentation_email_extension.dart
@@ -111,7 +111,7 @@ extension PresentationEmailExtension on PresentationEmail {
}
}
- PresentationEmail toSearchPresentationEmail(Map mapMailboxes) {
+ PresentationEmail toSearchPresentationEmail(Map mapMailboxes, Random random) {
mailboxIds?.removeWhere((key, value) => !value);
final listMailboxId = mailboxIds?.entries
@@ -124,6 +124,8 @@ extension PresentationEmailExtension on PresentationEmail {
.where((mailboxName) => mailboxName != null)
.toList();
+ final newAvatarColors = AppColor.mapGradientColor[random.nextInt(AppColor.mapGradientColor.length)];
+
return PresentationEmail(
this.id,
keywords: keywords,
@@ -141,7 +143,7 @@ extension PresentationEmailExtension on PresentationEmail {
mailboxIds: mailboxIds,
mailboxNames: listMailboxName,
selectMode: selectMode,
- avatarColors: avatarColors
+ avatarColors: avatarColors ?? newAvatarColors
);
}