TF-524 Apply quick search for mobile and tablet

This commit is contained in:
dab246
2022-05-24 11:56:50 +07:00
committed by Dat H. Pham
parent 9a142a0e23
commit 11c9a8713a
30 changed files with 647 additions and 2429 deletions
+1 -1
View File
@@ -62,7 +62,7 @@ export 'presentation/views/modal_sheets/edit_text_modal_sheet_builder.dart';
export 'presentation/views/search/search_bar_view.dart';
export 'presentation/views/popup_menu/popup_menu_item_widget.dart';
export 'presentation/views/tab_bar/custom_tab_indicator.dart';
export 'presentation/views/typeahead/typeahead_for_quick_search.dart';
export 'presentation/views/quick_search/quick_search_input_form.dart';
// Resources
export 'presentation/resources/assets_paths.dart';
File diff suppressed because it is too large Load Diff
@@ -1,15 +0,0 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
typedef FutureOr<Iterable<T>> SuggestionsCallback<T>(String pattern);
typedef FutureOr<Iterable<R>> FetchRecentActionCallback<R>(String pattern);
typedef Widget ItemBuilder<T>(BuildContext context, T itemData);
typedef void SuggestionSelectionCallback<T>(T suggestion);
typedef void RecentSelectionCallback<R>(R recent);
typedef Widget ErrorBuilder(BuildContext context, Object? error);
typedef Widget ButtonActionBuilder(BuildContext context, dynamic action);
typedef void ButtonActionCallback(dynamic action);
typedef Widget AnimationTransitionBuilder(
BuildContext context, Widget child, AnimationController? controller);
@@ -1,5 +0,0 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
final supportedPlatform = (kIsWeb || Platform.isAndroid || Platform.isIOS);
@@ -7,6 +7,10 @@ class RecentSearch with EquatableMixin {
RecentSearch(this.value, this.creationDate);
factory RecentSearch.now(String word) {
return RecentSearch(word, DateTime.now());
}
@override
List<Object?> get props => [value, creationDate];
}
@@ -1,9 +1,9 @@
import 'package:flutter/cupertino.dart';
import 'package:model/email/email_action_type.dart';
import 'package:model/email/filter_message_option.dart';
import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/base/action/ui_action.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/main/routes/router_arguments.dart';
class DashBoardAction extends UIAction {
@@ -97,4 +97,12 @@ class OpenEmailDetailedAction extends DashBoardAction {
@override
List<Object?> get props => [presentationEmail];
}
class DisableSearchEmailAction extends DashBoardAction {
DisableSearchEmailAction();
@override
List<Object?> get props => [];
}
@@ -1,34 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
extension FilterMessageOptionExtension on FilterMessageOption {
String getName(BuildContext context) {
switch(this) {
case FilterMessageOption.all:
return '';
case FilterMessageOption.unread:
return AppLocalizations.of(context).unread;
case FilterMessageOption.attachments:
return AppLocalizations.of(context).with_attachments;
case FilterMessageOption.starred:
return AppLocalizations.of(context).starred;
}
}
String getIcon(ImagePaths imagePaths) {
switch(this) {
case FilterMessageOption.all:
return '';
case FilterMessageOption.unread:
return imagePaths.icUnread;
case FilterMessageOption.attachments:
return imagePaths.icAttachment;
case FilterMessageOption.starred:
return imagePaths.icStar;
}
}
}
@@ -50,6 +50,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/emai
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/quick_search_filter.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/manage_account_arguments.dart';
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_state.dart';
@@ -82,7 +83,6 @@ class MailboxDashBoardController extends ReloadableController {
final accountId = Rxn<AccountId>();
final userProfile = Rxn<UserProfile>();
final searchState = SearchState.initial().obs;
final suggestionSearch = <String>[].obs;
final dashBoardAction = Rxn<UIAction>();
final routePath = AppRoutes.MAILBOX_DASHBOARD.obs;
final appInformation = Rxn<PackageInfo>();
@@ -286,35 +286,40 @@ class MailboxDashBoardController extends ReloadableController {
void disableSearch() {
searchState.value = searchState.value.disableSearchState();
searchQuery = SearchQuery.initial();
clearSuggestionSearch();
searchInputController.clear();
listFilterQuickSearch.clear();
emailReceiveTimeType.value = null;
FocusManager.instance.primaryFocus?.unfocus();
}
void clearSearchText() {
void clearTextSearch() {
searchQuery = SearchQuery.initial();
clearSuggestionSearch();
searchInputController.clear();
searchFocus.requestFocus();
}
void clearSuggestionSearch() {
suggestionSearch.clear();
}
void addSuggestionSearch(String query) {
if (query.trim().isNotEmpty) {
suggestionSearch.value = [query];
} else {
clearSearchText();
}
void updateTextSearch(String value) {
searchInputController.text = value;
}
void searchEmail(BuildContext context, String value) {
searchQuery = SearchQuery(value);
dispatchState(Right(SearchEmailNewQuery(searchQuery ?? SearchQuery.initial())));
clearSuggestionSearch();
FocusScope.of(context).unfocus();
if (searchInsideEmailDetailedViewIsActive(context)) {
closeEmailDetailedView();
}
}
bool searchInsideEmailDetailedViewIsActive(BuildContext context) {
return BuildUtils.isWeb
&& _responsiveUtils.isDesktop(context)
&& routePath.value == AppRoutes.EMAIL;
}
void closeEmailDetailedView() {
dispatchRoute(AppRoutes.THREAD);
clearSelectedEmail();
}
void _saveEmailAsDraftsSuccess(SaveEmailAsDraftsSuccess success) {
@@ -472,6 +477,36 @@ class MailboxDashBoardController extends ReloadableController {
emailReceiveTimeType.value = receiveTimeType;
}
bool quickSearchFilterForLast7DaysIsActive () {
return listFilterQuickSearch.contains(QuickSearchFilter.last7Days)
&& emailReceiveTimeType.value != null;
}
bool quickSearchFilterForHasAttachmentIsActive () {
return listFilterQuickSearch.contains(QuickSearchFilter.hasAttachment)
|| filterMessageOption.value == FilterMessageOption.attachments;
}
bool quickSearchFilterForFromMeIsActive () {
return listFilterQuickSearch.contains(QuickSearchFilter.fromMe);
}
bool filterForHasAttachmentIsActive () {
return quickSearchFilterForHasAttachmentIsActive() || filterMessageWithAttachmentIsActive();
}
bool filterMessageWithAttachmentIsActive () {
return filterMessageOption.value == FilterMessageOption.attachments;
}
bool filterMessageUnreadIsActive () {
return filterMessageOption.value == FilterMessageOption.unread;
}
bool filterMessageStarredIsActive () {
return filterMessageOption.value == FilterMessageOption.starred;
}
void composeEmailAction() {
if (kIsWeb) {
if (dashBoardAction.value is! ComposeEmailAction) {
@@ -1,7 +1,6 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_view_web.dart';
@@ -15,7 +14,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/em
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/recent_search_item_tile_widget.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/app_setting.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/reading_pane.dart';
import 'package:tmail_ui_user/features/thread/presentation/extensions/filter_message_option_extension.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
@@ -396,17 +395,17 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
return Row(
children: [
buildIconWeb(
icon: SvgPicture.asset(imagePaths.icBack, color: AppColor.colorTextButton, fit: BoxFit.fill),
onTap: () {
controller.disableSearch();
controller.dispatchAction(CancelSelectionAllEmailAction());
}),
icon: SvgPicture.asset(
imagePaths.icBack,
color: AppColor.colorTextButton,
fit: BoxFit.fill),
onTap: () => controller.dispatchAction(DisableSearchEmailAction())),
Expanded(child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
color: AppColor.colorBgSearchBar),
height: 45,
child: TypeAheadFormFieldQuickSearch<PresentationEmail, RecentSearch>(
child: QuickSearchInputForm<PresentationEmail, RecentSearch>(
textFieldConfiguration: QuickSearchTextFieldConfiguration(
controller: controller.searchInputController,
autofocus: true,
@@ -414,7 +413,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
textInputAction: TextInputAction.done,
onSubmitted: (keyword) {
if (keyword.trim().isNotEmpty) {
controller.saveRecentSearch(RecentSearch(keyword, DateTime.now()));
controller.saveRecentSearch(RecentSearch.now(keyword));
}
controller.searchEmail(context, keyword);
},
@@ -424,30 +423,43 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
enabledBorder: InputBorder.none,
contentPadding: EdgeInsets.zero,
hintText: AppLocalizations.of(context).search_mail,
hintStyle: const TextStyle(color: AppColor.colorHintSearchBar, fontSize: 17.0),
labelStyle: const TextStyle(color: AppColor.colorHintSearchBar, fontSize: 17.0)
hintStyle: const TextStyle(
color: AppColor.colorHintSearchBar,
fontSize: 17.0),
labelStyle: const TextStyle(
color: AppColor.colorHintSearchBar,
fontSize: 17.0)
),
leftButton: buildIconWeb(
icon: SvgPicture.asset(imagePaths.icSearchBar, width: 16, height: 16, fit: BoxFit.fill),
icon: SvgPicture.asset(
imagePaths.icSearchBar,
width: 16,
height: 16,
fit: BoxFit.fill),
onTap: () {
final keyword = controller.searchInputController.text;
if (keyword.trim().isNotEmpty) {
controller.saveRecentSearch(RecentSearch(keyword, DateTime.now()));
controller.saveRecentSearch(RecentSearch.now(keyword));
}
controller.searchEmail(context, keyword);
}),
clearTextButton: buildIconWeb(
icon: SvgPicture.asset(imagePaths.icClearTextSearch, width: 16, height: 16, fit: BoxFit.fill),
icon: SvgPicture.asset(
imagePaths.icClearTextSearch,
width: 16,
height: 16,
fit: BoxFit.fill),
onTap: () {
controller.searchInputController.clear();
controller.clearSearchText();
controller.clearTextSearch();
})
),
suggestionsBoxDecoration: QuickSearchSuggestionsBoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(16)),
constraints: BoxConstraints(
maxWidth: responsiveUtils.isDesktop(context) ? 556 : double.infinity),
maxWidth: responsiveUtils.isDesktop(context)
? 556
: double.infinity),
),
debounceDuration: const Duration(milliseconds: 500),
listActionButton: const [
@@ -463,12 +475,18 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
},
buttonActionCallback: (filterAction) {
if (filterAction is QuickSearchFilter) {
controller.selectQuickSearchFilter(filterAction, fromSuggestionBox: true);
controller.selectQuickSearchFilter(
filterAction,
fromSuggestionBox: true);
}
},
listActionPadding: const EdgeInsets.only(left: 24, top: 24, right: 24, bottom: 16),
listActionPadding: const EdgeInsets.only(
left: 24,
top: 24,
right: 24,
bottom: 16),
titleHeaderRecent: Padding(
padding: const EdgeInsets.only(left: 24, right: 24, bottom: 8, top: 8),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
child: Text(AppLocalizations.of(context).recent,
style: const TextStyle(fontSize: 13.0,
color: AppColor.colorTextButtonHeaderThread,
@@ -480,12 +498,14 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
return InkWell(
onTap: () {
if (keyword.trim().isNotEmpty) {
controller.saveRecentSearch(RecentSearch(keyword, DateTime.now()));
controller.saveRecentSearch(RecentSearch.now(keyword));
}
controller.searchEmail(context, keyword);
},
child: Padding(
padding: const EdgeInsets.only(left: 24, right: 24, top: 14, bottom: 14),
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 14),
child: Row(children: [
Text(AppLocalizations.of(context).showingResultsFor,
style: const TextStyle(fontSize: 13.0,
@@ -523,10 +543,12 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
return controller.quickSearchEmailsAction(pattern);
},
itemBuilder: (context, email) {
return EmailQuickSearchItemTileWidget(email, controller.selectedMailbox.value);
return EmailQuickSearchItemTileWidget(
email, controller.selectedMailbox.value);
},
onSuggestionSelected: (presentationEmail) async {
controller.dispatchAction(OpenEmailDetailedAction(context, presentationEmail));
controller.dispatchAction(
OpenEmailDetailedAction(context, presentationEmail));
}),
)),
const SizedBox(width: 16),
@@ -3,8 +3,7 @@ import 'package:core/core.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/mailbox_dashboard/presentation/extensions/filter_message_option_extension.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
mixin FilterEmailPopupMenuMixin {
final _imagePaths = Get.find<ImagePaths>();
@@ -38,15 +37,24 @@ mixin FilterEmailPopupMenuMixin {
width: 20,
height: 20,
fit: BoxFit.fill,
color: option != FilterMessageOption.starred ? AppColor.colorTextButton : null),
color: option != FilterMessageOption.starred
? AppColor.colorTextButton
: null),
const SizedBox(width: 12),
Expanded(child: Text(
option.getName(context),
style: const TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500))),
style: const TextStyle(
fontSize: 15,
color: Colors.black,
fontWeight: FontWeight.w500))),
if (optionSelected == option)
const SizedBox(width: 12),
if (optionSelected == option)
SvgPicture.asset(_imagePaths.icFilterSelected, width: 16, height: 16, fit: BoxFit.fill),
... [
const SizedBox(width: 12),
SvgPicture.asset(_imagePaths.icFilterSelected,
width: 16,
height: 16,
fit: BoxFit.fill),
]
])
),
)
@@ -6,9 +6,9 @@ import 'package:jmap_dart_client/jmap/core/state.dart';
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
abstract class ThreadDataSource {
Future<EmailsResponse> getAllEmail(
@@ -6,11 +6,11 @@ import 'package:jmap_dart_client/jmap/core/state.dart';
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart';
import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart';
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
class LocalThreadDataSourceImpl extends ThreadDataSource {
@@ -6,11 +6,11 @@ import 'package:jmap_dart_client/jmap/core/state.dart';
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart';
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
class ThreadDataSourceImpl extends ThreadDataSource {
@@ -10,6 +10,7 @@ import 'package:tmail_ui_user/features/thread/data/extensions/email_extension.da
import 'package:tmail_ui_user/features/thread/data/extensions/list_email_cache_extension.dart';
import 'package:tmail_ui_user/features/thread/data/model/email_cache.dart';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
class EmailCacheManager {
@@ -21,7 +21,9 @@ import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart';
import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart';
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_filter.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart';
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart';
@@ -2,7 +2,7 @@
import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
class EmailFilter with EquatableMixin {
@@ -1,9 +1,17 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
enum FilterMessageOption {
all,
unread,
attachments,
starred,
}
extension FilterMessageOptionExtension on FilterMessageOption {
String getIconToast(ImagePaths imagePaths) {
@@ -44,4 +52,56 @@ extension FilterMessageOptionExtension on FilterMessageOption {
return AppLocalizations.of(context).with_starred;
}
}
bool filterPresentationEmail(PresentationEmail email) {
switch(this) {
case FilterMessageOption.all:
return true;
case FilterMessageOption.unread:
return !email.hasRead;
case FilterMessageOption.attachments:
return email.withAttachments;
case FilterMessageOption.starred:
return email.hasStarred;
}
}
bool filterEmail(Email email) {
switch(this) {
case FilterMessageOption.all:
return true;
case FilterMessageOption.unread:
return !email.hasRead;
case FilterMessageOption.attachments:
return email.withAttachments;
case FilterMessageOption.starred:
return email.hasStarred;
}
}
String getName(BuildContext context) {
switch(this) {
case FilterMessageOption.all:
return '';
case FilterMessageOption.unread:
return AppLocalizations.of(context).unread;
case FilterMessageOption.attachments:
return AppLocalizations.of(context).with_attachments;
case FilterMessageOption.starred:
return AppLocalizations.of(context).starred;
}
}
String getIcon(ImagePaths imagePaths) {
switch(this) {
case FilterMessageOption.all:
return '';
case FilterMessageOption.unread:
return imagePaths.icUnread;
case FilterMessageOption.attachments:
return imagePaths.icAttachment;
case FilterMessageOption.starred:
return imagePaths.icStar;
}
}
}
@@ -6,8 +6,9 @@ import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_filter.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart';
abstract class ThreadRepository {
@@ -4,6 +4,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_filter.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart';
import 'package:tmail_ui_user/features/thread/domain/state/get_all_email_state.dart';
@@ -5,6 +5,7 @@ import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart';
import 'package:model/model.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
@@ -42,6 +42,8 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_da
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/email_receive_time_type.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/quick_search_filter.dart';
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_filter.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
import 'package:tmail_ui_user/features/thread/domain/state/empty_trash_folder_state.dart';
@@ -62,7 +64,6 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_emai
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/extensions/filter_message_option_extension.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart';
@@ -92,7 +93,7 @@ class ThreadController extends BaseController {
final MoveToMailboxInteractor _moveToMailboxInteractor;
final emailList = <PresentationEmail>[].obs;
final emailListSearch = <PresentationEmail>[].obs;
final searchIsActive = RxBool(false);
bool canLoadMore = true;
bool canSearchMore = true;
@@ -113,6 +114,10 @@ class ThreadController extends BaseController {
PresentationMailbox? get currentMailbox => mailboxDashBoardController.selectedMailbox.value;
TextEditingController get searchController => mailboxDashBoardController.searchInputController;
String get currentTextSearch => mailboxDashBoardController.searchInputController.text;
ThreadController(
this._getEmailsInMailboxInteractor,
this._markAsMultipleEmailReadInteractor,
@@ -154,7 +159,7 @@ class ThreadController extends BaseController {
newState.fold(
(failure) {
if (failure is SearchEmailFailure) {
emailListSearch.clear();
emailList.clear();
} else if (failure is SearchMoreEmailFailure || failure is LoadMoreEmailsFailure) {
_isLoadingMore = false;
}
@@ -181,7 +186,6 @@ class ThreadController extends BaseController {
void onDone() {
viewState.value.fold(
(failure) {
log('ThreadController::onDone(): failure: $failure');
if (failure is MarkAsMultipleEmailReadAllFailure
|| failure is MarkAsMultipleEmailReadFailure) {
_markAsReadSelectedMultipleEmailFailure(failure);
@@ -197,7 +201,6 @@ class ThreadController extends BaseController {
(success) {
if (success is MarkAsMultipleEmailReadAllSuccess
|| success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
log('ThreadController::onDone(): success: $success');
_markAsReadSelectedMultipleEmailSuccess(success);
} else if (success is MoveMultipleEmailToMailboxAllSuccess
|| success is MoveMultipleEmailToMailboxHasSomeEmailFailure) {
@@ -239,9 +242,7 @@ class ThreadController extends BaseController {
searchWorker = ever(mailboxDashBoardController.searchState, (searchState) {
if (searchState is SearchState) {
if (searchState.searchStatus == SearchStatus.INACTIVE) {
emailListSearch.clear();
} else if (searchState.searchStatus == SearchStatus.ACTIVE) {
if (searchState.searchStatus == SearchStatus.ACTIVE) {
cancelSelectEmail();
}
}
@@ -270,6 +271,9 @@ class ThreadController extends BaseController {
} if (action is OpenEmailDetailedAction) {
pressEmailAction(action.context, EmailActionType.preview, action.presentationEmail);
mailboxDashBoardController.clearDashBoardAction();
} if (action is DisableSearchEmailAction) {
closeSearchEmailAction();
mailboxDashBoardController.clearDashBoardAction();
}
}
});
@@ -318,7 +322,6 @@ class ThreadController extends BaseController {
}
void _getAllEmailSuccess(GetAllEmailSuccess success) {
log('ThreadController::_getAllEmailSuccess(): ${success.emailList.length}');
_currentEmailState = success.currentEmailState;
emailList.value = success.emailList;
if (listEmailController.hasClients) {
@@ -327,7 +330,6 @@ class ThreadController extends BaseController {
}
void _refreshChangesAllEmailSuccess(RefreshChangesAllEmailSuccess success) {
log('ThreadController::_refreshChangesAllEmailSuccess(): ${success.emailList.length}');
_currentEmailState = success.currentEmailState;
final emailsBeforeChanges = emailList;
@@ -341,7 +343,6 @@ class ThreadController extends BaseController {
}
void _getAllEmailAction(AccountId accountId, {MailboxId? mailboxId}) {
log('ThreadController::_getAllEmailAction(): mailboxId = $mailboxId');
consumeState(_getEmailsInMailboxInteractor.execute(
accountId,
limit: ThreadConstants.defaultLimit,
@@ -394,37 +395,30 @@ class ThreadController extends BaseController {
}
void _refreshEmailChanges() {
log('ThreadController::_refreshEmailChanges(): ');
if (isSearchActive()) {
if (_accountId != null && searchQuery != null) {
final limit = emailListSearch.isNotEmpty ? UnsignedInt(emailListSearch.length) : ThreadConstants.defaultLimit;
consumeState(_searchEmailInteractor.execute(
_accountId!,
limit: limit,
sort: _sortOrder,
filter: EmailFilterCondition(
text: searchQuery!.value,
inMailbox: _currentMailboxId),
properties: ThreadConstants.propertiesDefault,
final limit = emailList.isNotEmpty
? UnsignedInt(emailList.length)
: ThreadConstants.defaultLimit;
_searchEmail(limit: limit);
}
} else {
if (_accountId != null && _currentEmailState != null) {
consumeState(_refreshChangesEmailsInMailboxInteractor.execute(
_accountId!,
_currentEmailState!,
sort: _sortOrder,
propertiesCreated: ThreadConstants.propertiesDefault,
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
inMailboxId: _currentMailboxId,
filterOption: mailboxDashBoardController.filterMessageOption.value
));
}
}
if (_accountId != null && _currentEmailState != null) {
consumeState(_refreshChangesEmailsInMailboxInteractor.execute(
_accountId!,
_currentEmailState!,
sort: _sortOrder,
propertiesCreated: ThreadConstants.propertiesDefault,
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
inMailboxId: _currentMailboxId,
filterOption: mailboxDashBoardController.filterMessageOption.value
));
}
}
void loadMoreEmails() {
if (canLoadMore && _accountId != null) {
log('ThreadController::loadMoreEmails(): latest: ${emailList.last.receivedAt}');
startFpsMeter();
consumeState(_loadMoreEmailsInMailboxInteractor.execute(
GetEmailRequest(
@@ -439,11 +433,8 @@ class ThreadController extends BaseController {
}
void _loadMoreEmailsSuccess(LoadMoreEmailsSuccess success) {
log('ThreadController::_loadMoreEmailsSuccess(): [BEFORE] totalEmailList = ${emailList.length}');
if (success.emailList.isNotEmpty) {
log('ThreadController::_loadMoreEmailsSuccess(): add success: ${success.emailList.length}');
emailList.addAll(success.emailList);
log('ThreadController::_loadMoreEmailsSuccess(): [AFTER] totalEmailList = ${emailList.length}');
} else {
canLoadMore = false;
}
@@ -466,15 +457,9 @@ class ThreadController extends BaseController {
}
void selectEmail(BuildContext context, PresentationEmail presentationEmailSelected) {
if (isSearchActive()) {
emailListSearch.value = emailListSearch
.map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email)
.toList();
} else {
emailList.value = emailList
.map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email)
.toList();
}
emailList.value = emailList
.map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email)
.toList();
if (_isUnSelectedAll()) {
mailboxDashBoardController.currentSelectMode.value = SelectMode.INACTIVE;
@@ -492,41 +477,27 @@ class ThreadController extends BaseController {
}
void setSelectAllEmailAction() {
if (isSearchActive()) {
emailListSearch.value = emailListSearch.map((email) => email.toSelectedEmail(selectMode: SelectMode.ACTIVE)).toList();
} else {
emailList.value = emailList.map((email) => email.toSelectedEmail(selectMode: SelectMode.ACTIVE)).toList();
}
emailList.value = emailList.map((email) => email.toSelectedEmail(selectMode: SelectMode.ACTIVE)).toList();
mailboxDashBoardController.currentSelectMode.value = SelectMode.ACTIVE;
mailboxDashBoardController.listEmailSelected.value = listEmailSelected;
}
List<PresentationEmail> get listEmailSelected {
if (isSearchActive()) {
return emailListSearch.where((email) => email.selectMode == SelectMode.ACTIVE).toList();
} else {
return emailList.where((email) => email.selectMode == SelectMode.ACTIVE).toList();
}
return emailList.where((email) => email.selectMode == SelectMode.ACTIVE).toList();
}
bool _isUnSelectedAll() {
if (isSearchActive()) {
return emailListSearch.every((email) => email.selectMode == SelectMode.INACTIVE);
} else {
return emailList.every((email) => email.selectMode == SelectMode.INACTIVE);
}
return emailList.every((email) => email.selectMode == SelectMode.INACTIVE);
}
void cancelSelectEmail() {
emailListSearch.value = emailListSearch.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList();
emailList.value = emailList.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList();
mailboxDashBoardController.currentSelectMode.value = SelectMode.INACTIVE;
mailboxDashBoardController.listEmailSelected.clear();
}
void markAsReadAllEmails() {
final listEmail = isSearchActive() ? emailListSearch.allEmailUnread : emailList.allEmailUnread;
log('ThreadController::markAsReadAllEmails(): ${listEmail.length}');
final listEmail = emailList.allEmailUnread;
if (listEmail.isNotEmpty) {
markAsReadSelectedMultipleEmail(listEmail);
}
@@ -534,12 +505,10 @@ class ThreadController extends BaseController {
void markAsReadSelectedMultipleEmail(List<PresentationEmail> listPresentationEmail) {
final listEmail = listPresentationEmail.map((presentationEmail) => presentationEmail.toEmail()).toList();
log('ThreadController::markAsReadSelectedMultipleEmail(): listPresentationEmail: ${listPresentationEmail.length}');
final readAction = listPresentationEmail.isAllEmailRead ? ReadActions.markAsUnread : ReadActions.markAsRead;
final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value;
if (_accountId != null && mailboxCurrent != null) {
cancelSelectEmail();
log('ThreadController::markAsReadSelectedMultipleEmail(): listEmail: ${listEmail.length}');
consumeState(_markAsMultipleEmailReadInteractor.execute(_accountId!, listEmail, readAction));
}
}
@@ -564,7 +533,6 @@ class ThreadController extends BaseController {
message: message,
icon: readActions == ReadActions.markAsUnread ? _imagePaths.icUnreadToast : _imagePaths.icReadToast);
}
log('ThreadController::_markAsReadSelectedMultipleEmailSuccess(): _refreshEmailChanges');
_refreshEmailChanges();
}
@@ -602,18 +570,18 @@ class ThreadController extends BaseController {
? FilterMessageOption.all
: filterOption;
log('ThreadController::filterMessagesAction(): newFilterOption: $newFilterOption');
mailboxDashBoardController.filterMessageOption.value = newFilterOption;
log('ThreadController::filterMessagesAction(): filterMessageOption: ${mailboxDashBoardController.filterMessageOption}');
_appToast.showToastWithIcon(
currentOverlayContext!,
message: newFilterOption.getMessageToast(context),
icon: newFilterOption.getIconToast(_imagePaths));
refreshAllEmail();
if (isSearchActive()) {
_searchEmail();
} else {
refreshAllEmail();
}
}
void moveSelectedMultipleEmailToMailbox(List<PresentationEmail> listEmail) async {
@@ -839,34 +807,62 @@ class ThreadController extends BaseController {
}
void disableSearch() {
emailListSearch.clear();
searchIsActive.value = false;
mailboxDashBoardController.disableSearch();
cancelSelectEmail();
}
void _searchEmail() {
emailListSearch.clear();
void closeSearchEmailAction() {
disableSearch();
cancelSelectEmail();
refreshAllEmail();
}
void clearTextSearch() {
mailboxDashBoardController.clearTextSearch();
}
void _searchEmail({UnsignedInt? limit}) {
if (_accountId != null && searchQuery != null) {
searchIsActive.value = true;
UTCDate? afterTime;
if (mailboxDashBoardController.listFilterQuickSearch.contains(QuickSearchFilter.last7Days)
&& mailboxDashBoardController.emailReceiveTimeType.value != null) {
bool? hasAttachment;
String? fromAddress;
String? keywordUnread;
String? keywordStarred;
if (mailboxDashBoardController.quickSearchFilterForLast7DaysIsActive()) {
afterTime = mailboxDashBoardController.emailReceiveTimeType.value!.toUTCDate();
}
if (mailboxDashBoardController.filterForHasAttachmentIsActive()) {
hasAttachment = true;
}
if (mailboxDashBoardController.quickSearchFilterForFromMeIsActive()) {
fromAddress = mailboxDashBoardController.userProfile.value?.email;
}
if (mailboxDashBoardController.filterMessageUnreadIsActive()) {
keywordUnread = KeyWordIdentifier.emailSeen.value;
}
if (mailboxDashBoardController.filterMessageStarredIsActive()) {
keywordStarred = KeyWordIdentifier.emailFlagged.value;
}
final emailFilterCondition = EmailFilterCondition(
text: searchQuery!.value,
hasAttachment: mailboxDashBoardController.listFilterQuickSearch.contains(QuickSearchFilter.hasAttachment)
? true : null,
from: mailboxDashBoardController.listFilterQuickSearch.contains(QuickSearchFilter.fromMe)
? mailboxDashBoardController.userProfile.value!.email
: null,
hasAttachment: hasAttachment,
from: fromAddress,
after: afterTime,
hasKeyword: keywordStarred,
notKeyword: keywordUnread,
inMailbox: _currentMailboxId);
consumeState(_searchEmailInteractor.execute(
_accountId!,
limit: ThreadConstants.defaultLimit,
limit: limit ?? ThreadConstants.defaultLimit,
sort: _sortOrder,
filter: emailFilterCondition,
properties: ThreadConstants.propertiesDefault,
@@ -879,10 +875,10 @@ class ThreadController extends BaseController {
.map((email) => email.toSearchPresentationEmail(mailboxDashBoardController.mapMailbox))
.toList();
final emailsSearchBeforeChanges = emailListSearch;
final emailsSearchBeforeChanges = emailList;
final emailsSearchAfterChanges = resultEmailSearchList;
final newListEmailSearch = emailsSearchAfterChanges.combine(emailsSearchBeforeChanges);
emailListSearch.value = newListEmailSearch;
emailList.value = newListEmailSearch;
}
void searchMoreEmails() {
@@ -893,9 +889,9 @@ class ThreadController extends BaseController {
sort: _sortOrder,
filter: EmailFilterCondition(
text: searchQuery!.value,
before: emailListSearch.last.receivedAt),
before: emailList.last.receivedAt),
properties: ThreadConstants.propertiesDefault,
lastEmailId: emailListSearch.last.id
lastEmailId: emailList.last.id
));
}
}
@@ -904,9 +900,9 @@ class ThreadController extends BaseController {
if (success.emailList.isNotEmpty) {
final resultEmailSearchList = success.emailList
.map((email) => email.toSearchPresentationEmail(mailboxDashBoardController.mapMailbox))
.where((email) => !emailListSearch.contains(email))
.where((email) => !emailList.contains(email))
.toList();
emailListSearch.addAll(resultEmailSearchList);
emailList.addAll(resultEmailSearchList);
} else {
canSearchMore = false;
}
@@ -916,7 +912,6 @@ class ThreadController extends BaseController {
bool isSelectionEnabled() => mailboxDashBoardController.isSelectionEnabled();
void pressEmailSelectionAction(BuildContext context, EmailActionType actionType, List<PresentationEmail> selectionEmail) {
log('ThreadController::pressEmailSelectionAction(): selectionEmail: ${selectionEmail.length}');
switch(actionType) {
case EmailActionType.markAsRead:
case EmailActionType.markAsUnread:
+322 -222
View File
@@ -5,8 +5,13 @@ import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/recent_search.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/filter_email_popup_menu_mixin.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/email_receive_time_type.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/quick_search_filter.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/email_quick_search_item_tile_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/recent_search_item_tile_widget.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.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/delete_action_type.dart';
@@ -16,11 +21,10 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/bottom_bar_th
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/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/suggestion_box_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin {
class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin,
FilterEmailPopupMenuMixin {
final _responsiveUtils = Get.find<ResponsiveUtils>();
final _imagePaths = Get.find<ImagePaths>();
@@ -46,50 +50,74 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Obx(() => !controller.isSearchActive()
&& ((!_responsiveUtils.isDesktop(context) && BuildUtils.isWeb) || !BuildUtils.isWeb)
? _buildAppBarNormal(context)
: const SizedBox.shrink()),
_buildSearchInputFormForMobile(context),
Container(
color: BuildUtils.isWeb ? AppColor.colorBgDesktop : Colors.white,
padding: EdgeInsets.zero,
child: _buildSearchButtonViewForMobile(context)),
Obx(() => controller.isMailboxTrash && controller.emailList.isNotEmpty && !controller.isSearchActive()
? _buildEmptyTrashButton(context)
: const SizedBox.shrink()),
Obx(() => controller.isSearchActive() && _responsiveUtils.isDesktop(context) && BuildUtils.isWeb
? _buildListButtonQuickSearchFilter(context)
: const SizedBox.shrink()),
if ((!_responsiveUtils.isDesktop(context) && BuildUtils.isWeb) || !BuildUtils.isWeb)
... [
_buildAppBarNormal(context),
Obx(() {
return Stack(children: [
if (!controller.isSearchActive())
Container(
color: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 16,
vertical: BuildUtils.isWeb && _responsiveUtils.isTabletLarge(context) ? 8 : 0),
margin: const EdgeInsets.only(
bottom: !BuildUtils.isWeb ? 16 : 0),
child: _buildSearchFormInActive(context))
else
Container(
color: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 8,
vertical: BuildUtils.isWeb && _responsiveUtils.isTabletLarge(context) ? 8 : 0),
margin: const EdgeInsets.only(
bottom: !BuildUtils.isWeb ? 16 : 0),
child: _buildSearchFormActive(context))
]);
})
]
else
const SizedBox.shrink(),
Obx(() {
if (controller.isMailboxTrash && controller.emailList.isNotEmpty
&& !controller.isSearchActive()) {
return _buildEmptyTrashButton(context);
} else {
return const SizedBox.shrink();
}
}),
Obx(() {
if (controller.isSearchActive()
&& controller.searchIsActive.isTrue
&& _responsiveUtils.isDesktop(context)
&& BuildUtils.isWeb) {
return _buildListButtonQuickSearchFilter(context);
} else {
return const SizedBox.shrink();
}
}),
Expanded(child: Container(
color: BuildUtils.isWeb ? AppColor.colorBgDesktop : Colors.white,
padding: BuildUtils.isWeb && _responsiveUtils.isDesktop(context)
alignment: Alignment.center,
margin: BuildUtils.isWeb && _responsiveUtils.isDesktop(context)
? const EdgeInsets.only(left: 32, right: 24, top: 16, bottom: 24)
: EdgeInsets.zero,
child: Stack(children: [
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BuildUtils.isWeb && _responsiveUtils.isDesktop(context)
? BorderRadius.circular(20)
: null,
border: BuildUtils.isWeb && _responsiveUtils.isDesktop(context)
? Border.all(color: AppColor.colorBorderBodyThread, width: 1)
: null,
color: Colors.white),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (_responsiveUtils.isMobile(context))
_buildStatusResultSearch(context),
_buildLoadingView(),
Expanded(child: _buildListEmail(context)),
_buildLoadingViewLoadMore(),
]
)
),
_buildSuggestionBox(context)
]))),
decoration: BoxDecoration(
borderRadius: BuildUtils.isWeb && _responsiveUtils.isDesktop(context)
? BorderRadius.circular(20)
: null,
border: BuildUtils.isWeb && _responsiveUtils.isDesktop(context)
? Border.all(color: AppColor.colorBorderBodyThread, width: 1)
: null,
color: Colors.white),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildLoadingView(),
Expanded(child: _buildListEmail(context)),
_buildLoadingViewLoadMore(),
]
)
)),
_buildListButtonSelectionForMobile(context),
]
)
@@ -125,62 +153,189 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin {
});
}
Widget _buildSearchButtonViewForMobile(BuildContext context) {
return Obx(() {
if (!controller.isSearchActive() &&
((!_responsiveUtils.isDesktop(context) && BuildUtils.isWeb) || !BuildUtils.isWeb)) {
return Container(
color: Colors.white,
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 10),
child: (SearchBarView(_imagePaths)
..hintTextSearch(BuildUtils.isWeb ? AppLocalizations.of(context).search_emails : AppLocalizations.of(context).hint_search_emails)
..addOnOpenSearchViewAction(() => controller.enableSearch(context)))
.build());
} else {
return const SizedBox.shrink();
}
});
Widget _buildSearchFormInActive(BuildContext context) {
return (SearchBarView(_imagePaths)
..hintTextSearch(AppLocalizations.of(context).search_emails)
..addOnOpenSearchViewAction(() => controller.enableSearch(context)))
.build();
}
Widget _buildSearchInputFormForMobile(BuildContext context) {
return Obx(() {
if (controller.isSearchActive() &&
((!_responsiveUtils.isDesktop(context) && BuildUtils.isWeb) || !BuildUtils.isWeb)) {
return Column(children: [
_buildSearchForm(context),
const Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2),
]);
} else {
return const SizedBox.shrink();
}
});
}
Widget _buildSearchForm(BuildContext context) {
return (SearchAppBarWidget(
context,
_imagePaths,
_responsiveUtils,
controller.searchQuery,
controller.mailboxDashBoardController.searchFocus,
controller.mailboxDashBoardController.searchInputController,
suggestionSearch: controller.mailboxDashBoardController.suggestionSearch,)
..addDecoration(const BoxDecoration(color: Colors.white))
..setMargin(const EdgeInsets.only(right: 10))
..setHintText(AppLocalizations.of(context).search_mail)
..addOnCancelSearchPressed(() => controller.disableSearch())
..addOnClearTextSearchAction(() => controller.mailboxDashBoardController.clearSearchText())
..addOnTextChangeSearchAction((query) => controller.mailboxDashBoardController.addSuggestionSearch(query))
..addOnSearchTextAction((query) => controller.mailboxDashBoardController.searchEmail(context, query)))
.build();
Widget _buildSearchFormActive(BuildContext context) {
return Row(
children: [
buildIconWeb(
icon: SvgPicture.asset(
_imagePaths.icBack,
color: AppColor.colorTextButton,
fit: BoxFit.fill),
onTap: () => controller.closeSearchEmailAction()),
Expanded(child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
color: AppColor.colorBgSearchBar),
height: 45,
child: QuickSearchInputForm<PresentationEmail, RecentSearch>(
textFieldConfiguration: QuickSearchTextFieldConfiguration(
controller: controller.searchController,
autofocus: true,
focusNode: controller.mailboxDashBoardController.searchFocus,
textInputAction: TextInputAction.done,
onSubmitted: (keyword) {
if (keyword.trim().isNotEmpty) {
controller.mailboxDashBoardController
.saveRecentSearch(RecentSearch.now(keyword));
}
controller.mailboxDashBoardController.searchEmail(context, keyword);
},
decoration: InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
contentPadding: EdgeInsets.zero,
hintText: AppLocalizations.of(context).search_mail,
hintStyle: const TextStyle(
color: AppColor.colorHintSearchBar,
fontSize: 17.0),
labelStyle: const TextStyle(
color: AppColor.colorHintSearchBar,
fontSize: 17.0)
),
leftButton: buildIconWeb(
icon: SvgPicture.asset(
_imagePaths.icSearchBar,
width: 16,
height: 16,
fit: BoxFit.fill),
onTap: () {
final keyword = controller.currentTextSearch;
if (keyword.trim().isNotEmpty) {
controller.mailboxDashBoardController
.saveRecentSearch(RecentSearch.now(keyword));
}
controller.mailboxDashBoardController.searchEmail(context, keyword);
}),
clearTextButton: buildIconWeb(
icon: SvgPicture.asset(
_imagePaths.icClearTextSearch,
width: 16,
height: 16,
fit: BoxFit.fill),
onTap: () {
controller.clearTextSearch();
})
),
suggestionsBoxDecoration: const QuickSearchSuggestionsBoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(16)),
),
debounceDuration: const Duration(milliseconds: 500),
hideSuggestionsBox: _responsiveUtils.isScreenWithShortestSide(context),
listActionButton: const [
QuickSearchFilter.hasAttachment,
QuickSearchFilter.last7Days,
QuickSearchFilter.fromMe,
],
actionButtonBuilder: (context, filterAction) {
if (filterAction is QuickSearchFilter) {
return _buildQuickSearchFilterButtonSuggestionBox(
context,
filterAction);
}
return const SizedBox.shrink();
},
buttonActionCallback: (filterAction) {
if (filterAction is QuickSearchFilter) {
controller.mailboxDashBoardController
.selectQuickSearchFilter(filterAction, fromSuggestionBox: true);
}
},
listActionPadding: const EdgeInsets.only(
left: 24,
top: 24,
right: 24,
bottom: 16),
titleHeaderRecent: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
child: Text(AppLocalizations.of(context).recent,
style: const TextStyle(fontSize: 13.0,
color: AppColor.colorTextButtonHeaderThread,
fontWeight: FontWeight.w500)
)
),
buttonShowAllResult: (context, keyword) {
if (keyword is String) {
return InkWell(
onTap: () {
if (keyword.trim().isNotEmpty) {
controller.mailboxDashBoardController
.saveRecentSearch(RecentSearch.now(keyword));
}
controller.mailboxDashBoardController.searchEmail(context, keyword);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
child: Row(children: [
Text(AppLocalizations.of(context).showingResultsFor,
style: const TextStyle(fontSize: 13.0,
color: AppColor.colorTextButtonHeaderThread,
fontWeight: FontWeight.w500)
),
const SizedBox(width: 4),
Expanded(child: Text('"$keyword"',
style: const TextStyle(
fontSize: 13.0,
color: Colors.black,
fontWeight: FontWeight.w500)))
])
),
);
} else {
return const SizedBox.shrink();
}
},
loadingBuilder: (context) => Padding(
padding: const EdgeInsets.only(bottom: 16),
child: loadingWidget,
),
fetchRecentActionCallback: (pattern) async {
return controller.mailboxDashBoardController
.getAllRecentSearchAction(pattern);
},
itemRecentBuilder: (context, recent) {
return RecentSearchItemTileWidget(recent);
},
onRecentSelected: (recent) {
controller.mailboxDashBoardController
.updateTextSearch(recent.value);
controller.mailboxDashBoardController
.searchEmail(context, recent.value);
},
suggestionsCallback: (pattern) async {
return controller.mailboxDashBoardController
.quickSearchEmailsAction(pattern);
},
itemBuilder: (context, email) {
return EmailQuickSearchItemTileWidget(
email,
controller.currentMailbox);
},
onSuggestionSelected: (presentationEmail) async {
controller.pressEmailAction(
context,
EmailActionType.preview,
presentationEmail);
}),
)),
const SizedBox(width: 16),
]
);
}
Widget _buildAppBarNormal(BuildContext context) {
return (AppBarThreadWidgetBuilder(
return Obx(() {
return (AppBarThreadWidgetBuilder(
context,
_imagePaths,
_responsiveUtils,
controller.mailboxDashBoardController.selectedMailbox.value,
controller.currentMailbox,
controller.listEmailSelected,
controller.mailboxDashBoardController.currentSelectMode.value,
controller.mailboxDashBoardController.filterMessageOption.value)
@@ -188,17 +343,24 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin {
..addOnEditThreadAction(() => controller.enableSelectionEmail())
..addOnEmailSelectionAction((actionType, selectionEmail) =>
controller.pressEmailSelectionAction(context, actionType, selectionEmail))
..addOnFilterEmailAction((filterMessageOption, position) =>
_responsiveUtils.isScreenWithShortestSide(context)
? controller.openContextMenuAction(
context,
_filterMessagesCupertinoActionTile(context, filterMessageOption))
: controller.openPopupMenuAction(
context,
position,
_popupMenuEmailActionTile(context, filterMessageOption)))
..addOnFilterEmailAction((filterMessageOption, position) {
if (_responsiveUtils.isScreenWithShortestSide(context)) {
controller.openContextMenuAction(
context,
_filterMessagesCupertinoActionTile(context, filterMessageOption));
} else {
controller.openPopupMenuAction(
context,
position,
popupMenuFilterEmailActionTile(
context,
filterMessageOption,
(option) => controller.filterMessagesAction(context, option)));
}
})
..addOnCancelEditThread(() => controller.cancelSelectEmail()))
.build();
});
}
Widget _buildFloatingButtonCompose(BuildContext context) {
@@ -232,75 +394,37 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin {
}
List<Widget> _filterMessagesCupertinoActionTile(BuildContext context, FilterMessageOption optionCurrent) {
return <Widget>[
_filterMessageWithAttachmentsAction(context, optionCurrent),
_filterMessagesUnreadAction(context, optionCurrent),
_filterMessageStarredAction(context, optionCurrent),
];
}
Widget _filterMessageWithAttachmentsAction(BuildContext context, FilterMessageOption optionCurrent) {
return (FilterMessageCupertinoActionSheetActionBuilder(
const 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,
iconLeftPadding: _responsiveUtils.isMobile(context)
? const EdgeInsets.only(left: 12, right: 16)
: const EdgeInsets.only(right: 12),
iconRightPadding: _responsiveUtils.isMobile(context)
? const EdgeInsets.only(right: 12)
: EdgeInsets.zero,
actionSelected: SvgPicture.asset(_imagePaths.icFilterSelected, fit: BoxFit.fill))
..onActionClick((option) => controller.filterMessagesAction(context, option)))
.build();
}
Widget _filterMessagesUnreadAction(BuildContext context, FilterMessageOption optionCurrent) {
return (FilterMessageCupertinoActionSheetActionBuilder(
const Key('filter_unread_action'),
SvgPicture.asset(_imagePaths.icUnread, width: 28, height: 28, fit: BoxFit.fill),
AppLocalizations.of(context).unread,
FilterMessageOption.unread,
optionCurrent: optionCurrent,
iconLeftPadding: _responsiveUtils.isMobile(context)
? const EdgeInsets.only(left: 12, right: 16)
: const EdgeInsets.only(right: 12),
iconRightPadding: _responsiveUtils.isMobile(context)
? const EdgeInsets.only(right: 12)
: EdgeInsets.zero,
actionSelected: SvgPicture.asset(_imagePaths.icFilterSelected, fit: BoxFit.fill))
..onActionClick((option) => controller.filterMessagesAction(context, option)))
.build();
}
Widget _filterMessageStarredAction(BuildContext context, FilterMessageOption optionCurrent) {
return (FilterMessageCupertinoActionSheetActionBuilder(
const Key('filter_starred_action'),
SvgPicture.asset(_imagePaths.icStar, width: 28, height: 28, fit: BoxFit.fill),
AppLocalizations.of(context).starred,
FilterMessageOption.starred,
optionCurrent: optionCurrent,
iconLeftPadding: _responsiveUtils.isMobile(context)
? const EdgeInsets.only(left: 12, right: 16)
: const EdgeInsets.only(right: 12),
iconRightPadding: _responsiveUtils.isMobile(context)
? const EdgeInsets.only(right: 12)
: EdgeInsets.zero,
actionSelected: SvgPicture.asset(_imagePaths.icFilterSelected, fit: BoxFit.fill))
..onActionClick((option) => controller.filterMessagesAction(context, option)))
.build();
}
List<PopupMenuEntry> _popupMenuEmailActionTile(BuildContext context, FilterMessageOption option) {
return [
PopupMenuItem(padding: const EdgeInsets.symmetric(horizontal: 8), child: _filterMessageWithAttachmentsAction(context, option)),
const PopupMenuDivider(height: 0.5),
PopupMenuItem(padding: const EdgeInsets.symmetric(horizontal: 8), child: _filterMessagesUnreadAction(context, option)),
const PopupMenuDivider(height: 0.5),
PopupMenuItem(padding: const EdgeInsets.symmetric(horizontal: 8), child: _filterMessageStarredAction(context, option)),
final listFilter = [
FilterMessageOption.attachments,
FilterMessageOption.unread,
FilterMessageOption.starred,
];
return listFilter.map((filter) => (FilterMessageCupertinoActionSheetActionBuilder(
Key('filter_email_${filter.name}'),
SvgPicture.asset(
filter.getIcon(_imagePaths),
width: 20,
height: 20,
fit: BoxFit.fill,
color: filter == FilterMessageOption.attachments
? AppColor.colorTextButton : null),
filter.getName(context),
filter,
optionCurrent: optionCurrent,
iconLeftPadding: _responsiveUtils.isMobile(context)
? const EdgeInsets.only(left: 12, right: 16)
: const EdgeInsets.only(right: 12),
iconRightPadding: _responsiveUtils.isMobile(context)
? const EdgeInsets.only(right: 12)
: EdgeInsets.zero,
actionSelected: SvgPicture.asset(
_imagePaths.icFilterSelected,
width: 20,
height: 20,
fit: BoxFit.fill))
..onActionClick((option) => controller.filterMessagesAction(context, option)))
.build()).toList();
}
Widget _buildLoadingView() {
@@ -344,25 +468,11 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin {
padding: EdgeInsets.zero,
color: Colors.white,
child: Obx(() {
if (controller.isSearchActive()) {
if (controller.mailboxDashBoardController.suggestionSearch.isNotEmpty) {
return const SizedBox.shrink();
} else {
return _buildResultSearchEmails(context, controller.emailListSearch);
}
} else {
return _buildResultListEmail(context, controller.emailList);
}
return _buildResultListEmail(context, controller.emailList);
})
);
}
Widget _buildResultSearchEmails(BuildContext context, List<PresentationEmail> listPresentationEmail) {
return listPresentationEmail.isNotEmpty
? _buildListEmailBody(context, listPresentationEmail)
: _buildEmptyEmail(context);
}
Widget _buildResultListEmail(BuildContext context, List<PresentationEmail> listPresentationEmail) {
if (controller.mailboxDashBoardController.currentSelectMode.value == SelectMode.INACTIVE) {
return listPresentationEmail.isNotEmpty
@@ -433,42 +543,6 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin {
);
}
Widget _buildSuggestionBox(BuildContext context) {
return Obx(() => controller.mailboxDashBoardController.suggestionSearch.isNotEmpty
? (SuggestionBoxWidget(
context,
_imagePaths,
controller.mailboxDashBoardController.suggestionSearch)
..addOnSelectedSuggestion((suggestion) =>
controller.mailboxDashBoardController.searchEmail(context, suggestion)))
.build()
: const SizedBox.shrink()
);
}
Widget _buildStatusResultSearch(BuildContext context) {
return Obx(() {
if (controller.isSearchActive()) {
return controller.emailListSearch.isNotEmpty
? Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
decoration: BoxDecoration(
borderRadius: BuildUtils.isWeb && _responsiveUtils.isDesktop(context)
? const BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20))
: null,
color: AppColor.bgStatusResultSearch),
child: Text(
AppLocalizations.of(context).results,
style: const TextStyle(color: AppColor.baseTextColor, fontSize: 14, fontWeight: FontWeight.w500),
))
: const SizedBox.shrink();
} else {
return const SizedBox.shrink();
}
});
}
Widget _buildEmptyTrashButton(BuildContext context) {
return Container(
decoration: BoxDecoration(
@@ -661,4 +735,30 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin {
)
);
}
Widget _buildQuickSearchFilterButtonSuggestionBox(
BuildContext context, QuickSearchFilter filter) {
return Obx(() {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: filter.getBackgroundColor(controller.mailboxDashBoardController.listFilterQuickSearch)),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
SvgPicture.asset(
filter.getIcon(_imagePaths, controller.mailboxDashBoardController.listFilterQuickSearch),
width: 16,
height: 16,
fit: BoxFit.fill),
const SizedBox(width: 4),
Text(
filter.getTitle(context,
receiveTimeType: controller.mailboxDashBoardController.emailReceiveTimeType.value),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: filter.getTextStyle(controller.mailboxDashBoardController.listFilterQuickSearch),
)
]));
});
}
}
@@ -5,7 +5,7 @@ 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/extensions/filter_message_option_extension.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnFilterEmailAction = void Function(FilterMessageOption, RelativeRect? position);
@@ -15,6 +15,9 @@ typedef OnCancelEditThread = void Function();
typedef OnEmailSelectionAction = void Function(EmailActionType actionType, List<PresentationEmail>);
class AppBarThreadWidgetBuilder {
final _imagePaths = Get.find<ImagePaths>();
final _responsiveUtils = Get.find<ResponsiveUtils>();
OnFilterEmailAction? _onFilterEmailAction;
OnOpenMailboxMenuActionClick? _onOpenMailboxMenuActionClick;
OnEditThreadAction? _onEditThreadAction;
@@ -22,8 +25,6 @@ class AppBarThreadWidgetBuilder {
OnEmailSelectionAction? _onEmailSelectionAction;
final BuildContext _context;
final ImagePaths _imagePaths;
final ResponsiveUtils _responsiveUtils;
final PresentationMailbox? _currentMailbox;
final List<PresentationEmail> _listSelectionEmail;
final SelectMode _selectMode;
@@ -31,8 +32,6 @@ class AppBarThreadWidgetBuilder {
AppBarThreadWidgetBuilder(
this._context,
this._imagePaths,
this._responsiveUtils,
this._currentMailbox,
this._listSelectionEmail,
this._selectMode,
@@ -65,8 +64,11 @@ class AppBarThreadWidgetBuilder {
alignment: Alignment.topCenter,
color: Colors.white,
margin: EdgeInsets.zero,
padding: EdgeInsets.only(left: 8, bottom: 8, right: 8,
top: (!BuildUtils.isWeb && _responsiveUtils.isPortraitMobile(_context)) ? 0 : 16),
padding: EdgeInsets.only(
left: 8,
bottom: !BuildUtils.isWeb ? 8 : 16,
right: 8,
top: !BuildUtils.isWeb && _responsiveUtils.isPortraitMobile(_context) ? 0 : 16),
child: MediaQuery(
data: const MediaQueryData(padding: EdgeInsets.zero),
child: kIsWeb
@@ -211,7 +213,8 @@ class AppBarThreadWidgetBuilder {
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.only(left: 16, right: 16),
child: GestureDetector(
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(10)),
onTap: () {
if (_onFilterEmailAction != null && _responsiveUtils.isScreenWithShortestSide(_context)) {
_onFilterEmailAction!.call(_filterMessageOption, null);
@@ -3,9 +3,10 @@ import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
class FilterMessageCupertinoActionSheetActionBuilder extends CupertinoActionSheetActionBuilder<FilterMessageOption> {
class FilterMessageCupertinoActionSheetActionBuilder
extends CupertinoActionSheetActionBuilder<FilterMessageOption> {
final FilterMessageOption option;
final FilterMessageOption? optionCurrent;
@@ -1,88 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnSelectedSuggestion = Function(String);
class SuggestionBoxWidget {
OnSelectedSuggestion? _onSelectedSuggestion;
final BuildContext _context;
final ImagePaths _imagePaths;
double? _suggestionHeight;
double? _elevation;
final List<String> _listData;
SuggestionBoxWidget(
this._context,
this._imagePaths,
this._listData,
);
void setSuggestionHeight(double suggestionHeight) {
_suggestionHeight = suggestionHeight;
}
void setElevation(double elevation) {
_elevation = elevation;
}
void addOnSelectedSuggestion(OnSelectedSuggestion onSelectedSuggestion) {
_onSelectedSuggestion = onSelectedSuggestion;
}
Widget build() {
return Material(
elevation: _elevation ?? 3,
color: Colors.white,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: _suggestionHeight ?? 60,
),
child: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
itemCount: _listData.length,
itemBuilder: (BuildContext context, int index) => ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 10),
onTap: () {
if (_onSelectedSuggestion != null) {
_onSelectedSuggestion!(_listData[index]);
}},
leading: IconButton(
color: AppColor.baseTextColor,
icon: SvgPicture.asset(_imagePaths.icSearch, color: AppColor.baseTextColor, fit: BoxFit.fill),
onPressed: () {
if (_onSelectedSuggestion != null) {
_onSelectedSuggestion!(_listData[index]);
}
}),
title: Transform(
transform: Matrix4.translationValues(-12.0, 0.0, 0.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'${AppLocalizations.of(_context).prefix_suggestion_search} ',
style: const TextStyle(fontSize: 15, color: AppColor.baseTextColor),
),
Expanded(child: Text(
'"${_listData[index]}"',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 15, color: AppColor.colorNameEmail),
))
],
)
)
),
),
),
);
}
}
@@ -1,40 +0,0 @@
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/email_extension.dart';
enum FilterMessageOption {
all,
unread,
attachments,
starred,
}
extension FilterMessageOptionExtension on FilterMessageOption {
bool filterPresentationEmail(PresentationEmail email) {
switch(this) {
case FilterMessageOption.all:
return true;
case FilterMessageOption.unread:
return !email.hasRead;
case FilterMessageOption.attachments:
return email.withAttachments;
case FilterMessageOption.starred:
return email.hasStarred;
}
}
bool filterEmail(Email email) {
switch(this) {
case FilterMessageOption.all:
return true;
case FilterMessageOption.unread:
return !email.hasRead;
case FilterMessageOption.attachments:
return email.withAttachments;
case FilterMessageOption.starred:
return email.hasStarred;
}
}
}
-2
View File
@@ -27,9 +27,7 @@ export 'email/read_actions.dart';
export 'email/attachment.dart';
export 'email/mark_star_action.dart';
export 'email/email_property.dart';
export 'email/email_filter.dart';
export 'email/email_content_type.dart';
export 'email/filter_message_option.dart';
// Extensions
export 'extensions/email_address_extension.dart';
@@ -11,6 +11,7 @@ import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_filter.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart';
@@ -38,7 +39,7 @@ void main() {
when(threadRepository.getAllEmail(
AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: Set()..add(EmailComparator(EmailComparatorProperty.sentAt)..setIsAscending(false)),
sort: {}..add(EmailComparator(EmailComparatorProperty.sentAt)..setIsAscending(false)),
emailFilter: EmailFilter(
filter: EmailFilterCondition(inMailbox: MailboxFixtures.inboxMailbox.id),
mailboxId: MailboxFixtures.inboxMailbox.id,
@@ -66,7 +67,7 @@ void main() {
final streamStates = getEmailsInMailboxInteractor.execute(
AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: Set()..add(EmailComparator(EmailComparatorProperty.sentAt)..setIsAscending(false)),
sort: {}..add(EmailComparator(EmailComparatorProperty.sentAt)..setIsAscending(false)),
emailFilter: EmailFilter(
filter: EmailFilterCondition(inMailbox: MailboxFixtures.inboxMailbox.id),
mailboxId: MailboxFixtures.inboxMailbox.id,
@@ -5,19 +5,22 @@
import 'dart:async' as _i3;
import 'package:jmap_dart_client/jmap/account_id.dart' as _i5;
import 'package:jmap_dart_client/jmap/core/filter/filter.dart' as _i14;
import 'package:jmap_dart_client/jmap/core/filter/filter.dart' as _i15;
import 'package:jmap_dart_client/jmap/core/properties/properties.dart' as _i9;
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart' as _i7;
import 'package:jmap_dart_client/jmap/core/state.dart' as _i10;
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart' as _i6;
import 'package:jmap_dart_client/jmap/mail/email/email.dart' as _i13;
import 'package:jmap_dart_client/jmap/mail/email/email.dart' as _i14;
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart' as _i11;
import 'package:mockito/mockito.dart' as _i1;
import 'package:model/model.dart' as _i8;
import 'package:tmail_ui_user/features/thread/domain/model/email_filter.dart'
as _i8;
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'
as _i4;
import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart'
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'
as _i12;
import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart'
as _i13;
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'
as _i2;
@@ -64,7 +67,7 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository {
_i9.Properties? propertiesCreated,
_i9.Properties? propertiesUpdated,
_i11.MailboxId? inMailboxId,
_i8.FilterMessageOption? filterOption}) =>
_i12.FilterMessageOption? filterOption}) =>
(super.noSuchMethod(
Invocation.method(#refreshChanges, [
accountId,
@@ -80,15 +83,15 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository {
as _i3.Stream<_i4.EmailsResponse>);
@override
_i3.Stream<_i4.EmailsResponse> loadMoreEmails(
_i12.GetEmailRequest? emailRequest) =>
_i13.GetEmailRequest? emailRequest) =>
(super.noSuchMethod(Invocation.method(#loadMoreEmails, [emailRequest]),
returnValue: Stream<_i4.EmailsResponse>.empty())
as _i3.Stream<_i4.EmailsResponse>);
@override
_i3.Future<List<_i13.Email>> searchEmails(_i5.AccountId? accountId,
_i3.Future<List<_i14.Email>> searchEmails(_i5.AccountId? accountId,
{_i6.UnsignedInt? limit,
Set<_i7.Comparator>? sort,
_i14.Filter? filter,
_i15.Filter? filter,
_i9.Properties? properties}) =>
(super.noSuchMethod(
Invocation.method(#searchEmails, [
@@ -99,8 +102,8 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository {
#filter: filter,
#properties: properties
}),
returnValue: Future<List<_i13.Email>>.value(<_i13.Email>[]))
as _i3.Future<List<_i13.Email>>);
returnValue: Future<List<_i14.Email>>.value(<_i14.Email>[]))
as _i3.Future<List<_i14.Email>>);
@override
_i3.Future<bool> emptyTrashFolder(
_i5.AccountId? accountId, _i11.MailboxId? trashMailboxId) =>
@@ -5,19 +5,22 @@
import 'dart:async' as _i3;
import 'package:jmap_dart_client/jmap/account_id.dart' as _i5;
import 'package:jmap_dart_client/jmap/core/filter/filter.dart' as _i14;
import 'package:jmap_dart_client/jmap/core/filter/filter.dart' as _i15;
import 'package:jmap_dart_client/jmap/core/properties/properties.dart' as _i9;
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart' as _i7;
import 'package:jmap_dart_client/jmap/core/state.dart' as _i10;
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart' as _i6;
import 'package:jmap_dart_client/jmap/mail/email/email.dart' as _i13;
import 'package:jmap_dart_client/jmap/mail/email/email.dart' as _i14;
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart' as _i11;
import 'package:mockito/mockito.dart' as _i1;
import 'package:model/model.dart' as _i8;
import 'package:tmail_ui_user/features/thread/domain/model/email_filter.dart'
as _i8;
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'
as _i4;
import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart'
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'
as _i12;
import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart'
as _i13;
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'
as _i2;
@@ -64,7 +67,7 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository {
_i9.Properties? propertiesCreated,
_i9.Properties? propertiesUpdated,
_i11.MailboxId? inMailboxId,
_i8.FilterMessageOption? filterOption}) =>
_i12.FilterMessageOption? filterOption}) =>
(super.noSuchMethod(
Invocation.method(#refreshChanges, [
accountId,
@@ -80,15 +83,15 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository {
as _i3.Stream<_i4.EmailsResponse>);
@override
_i3.Stream<_i4.EmailsResponse> loadMoreEmails(
_i12.GetEmailRequest? emailRequest) =>
_i13.GetEmailRequest? emailRequest) =>
(super.noSuchMethod(Invocation.method(#loadMoreEmails, [emailRequest]),
returnValue: Stream<_i4.EmailsResponse>.empty())
as _i3.Stream<_i4.EmailsResponse>);
@override
_i3.Future<List<_i13.Email>> searchEmails(_i5.AccountId? accountId,
_i3.Future<List<_i14.Email>> searchEmails(_i5.AccountId? accountId,
{_i6.UnsignedInt? limit,
Set<_i7.Comparator>? sort,
_i14.Filter? filter,
_i15.Filter? filter,
_i9.Properties? properties}) =>
(super.noSuchMethod(
Invocation.method(#searchEmails, [
@@ -99,8 +102,8 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository {
#filter: filter,
#properties: properties
}),
returnValue: Future<List<_i13.Email>>.value(<_i13.Email>[]))
as _i3.Future<List<_i13.Email>>);
returnValue: Future<List<_i14.Email>>.value(<_i14.Email>[]))
as _i3.Future<List<_i14.Email>>);
@override
_i3.Future<bool> emptyTrashFolder(
_i5.AccountId? accountId, _i11.MailboxId? trashMailboxId) =>