diff --git a/lib/features/mailbox/domain/state/search_mailbox_state.dart b/lib/features/mailbox/domain/state/search_mailbox_state.dart new file mode 100644 index 000000000..110764aff --- /dev/null +++ b/lib/features/mailbox/domain/state/search_mailbox_state.dart @@ -0,0 +1,21 @@ +import 'package:core/core.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; + +class SearchMailboxSuccess extends UIState { + + final List mailboxesSearched; + + SearchMailboxSuccess(this.mailboxesSearched); + + @override + List get props => [mailboxesSearched]; +} + +class SearchMailboxFailure extends FeatureFailure { + final exception; + + SearchMailboxFailure(this.exception); + + @override + List get props => [exception]; +} \ No newline at end of file diff --git a/lib/features/mailbox/domain/usecases/search_mailbox_interactor.dart b/lib/features/mailbox/domain/usecases/search_mailbox_interactor.dart new file mode 100644 index 000000000..60a838d5f --- /dev/null +++ b/lib/features/mailbox/domain/usecases/search_mailbox_interactor.dart @@ -0,0 +1,21 @@ + +import 'package:core/core.dart'; +import 'package:dartz/dartz.dart'; +import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/search_mailbox_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; + +class SearchMailboxInteractor { + + Stream> execute(List mailboxes, SearchQuery searchQuery) async* { + try { + final resultList = mailboxes + .where((mailbox) => mailbox.name?.name.toLowerCase().contains(searchQuery.value.toLowerCase()) == true) + .toList(); + + yield Right(SearchMailboxSuccess(resultList)); + } catch (exception) { + yield Left(SearchMailboxFailure(exception)); + } + } +} \ No newline at end of file diff --git a/lib/features/mailbox/presentation/mailbox_bindings.dart b/lib/features/mailbox/presentation/mailbox_bindings.dart index 223cddba9..67c129073 100644 --- a/lib/features/mailbox/presentation/mailbox_bindings.dart +++ b/lib/features/mailbox/presentation/mailbox_bindings.dart @@ -19,6 +19,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_reposit import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart'; import 'package:uuid/uuid.dart'; @@ -46,6 +47,7 @@ class MailboxBindings extends Bindings { Get.lazyPut(() => RefreshAllMailboxInteractor(Get.find())); Get.lazyPut(() => TreeBuilder()); Get.lazyPut(() => CreateNewMailboxInteractor(Get.find())); + Get.lazyPut(() => SearchMailboxInteractor()); Get.lazyPut(() => Uuid()); Get.lazyPut(() => TextEditingController()); Get.lazyPut(() => FocusNode()); @@ -54,13 +56,12 @@ class MailboxBindings extends Bindings { Get.find(), Get.find(), Get.find(), + Get.find(), Get.find(), Get.find(), Get.find(), Get.find(), Get.find(), - Get.find(), - Get.find(), Get.find())); } } \ No newline at end of file diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index ba1d37877..fc7b5d8f5 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -17,10 +17,13 @@ import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_i import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/create_new_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/search_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/list_mailbox_node_extension.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart'; @@ -45,22 +48,24 @@ class MailboxController extends BaseController { final DeleteCredentialInteractor _deleteCredentialInteractor; final RefreshAllMailboxInteractor _refreshAllMailboxInteractor; final CreateNewMailboxInteractor _createNewMailboxInteractor; + final SearchMailboxInteractor _searchMailboxInteractor; final TreeBuilder _treeBuilder; final Uuid _uuid; final AppToast _appToast; final ImagePaths _imagePaths; final ResponsiveUtils responsiveUtils; - final TextEditingController searchInputController; - final FocusNode searchFocus; final CachingManager _cachingManager; final defaultMailboxList = [].obs; final folderMailboxNodeList = [].obs; + final listMailboxSearched = [].obs; final searchState = SearchState.initial().obs; final searchQuery = SearchQuery.initial().obs; + MailboxTree folderMailboxTree = MailboxTree(MailboxNode.root()); List allMailboxes = []; - List listMailboxSearched = []; + TextEditingController searchInputController = TextEditingController(); + FocusNode searchFocus = FocusNode(); jmapState.State? currentMailboxState; @@ -69,13 +74,12 @@ class MailboxController extends BaseController { this._deleteCredentialInteractor, this._refreshAllMailboxInteractor, this._createNewMailboxInteractor, + this._searchMailboxInteractor, this._treeBuilder, this._uuid, this._appToast, this._imagePaths, this.responsiveUtils, - this.searchInputController, - this.searchFocus, this._cachingManager, ); @@ -111,6 +115,7 @@ class MailboxController extends BaseController { @override void onClose() { mailboxDashBoardController.accountId.close(); + searchInputController.dispose(); searchFocus.dispose(); super.onClose(); } @@ -135,11 +140,15 @@ class MailboxController extends BaseController { (failure) { if (failure is CreateNewMailboxFailure) { _createNewMailboxFailure(failure); + } else if (failure is SearchMailboxFailure) { + _searchMailboxFailure(failure); } }, (success) { if (success is CreateNewMailboxSuccess) { _createNewMailboxSuccess(success); + } else if (success is SearchMailboxSuccess) { + _searchMailboxSuccess(success); } } ); @@ -158,6 +167,8 @@ class MailboxController extends BaseController { if (accountId != null) { consumeState(_getAllMailboxInteractor.execute(accountId)); } + } else { + _searchMailboxAction(allMailboxes, searchQuery.value); } } @@ -169,8 +180,8 @@ class MailboxController extends BaseController { } void _buildTree(List folderMailboxList) async { - final _folderMailboxTree = await _treeBuilder.generateMailboxTree(folderMailboxList); - folderMailboxNodeList.value = _folderMailboxTree.root.childrenItems ?? []; + folderMailboxTree = await _treeBuilder.generateMailboxTree(folderMailboxList); + folderMailboxNodeList.value = folderMailboxTree.root.childrenItems ?? []; } void toggleMailboxFolder(MailboxNode mailboxNode) { @@ -240,6 +251,8 @@ class MailboxController extends BaseController { BuildContext context, PresentationMailbox presentationMailboxSelected ) { + FocusScope.of(context).unfocus(); + mailboxDashBoardController.setSelectedMailbox(presentationMailboxSelected); mailboxDashBoardController.clearSelectedEmail(); @@ -315,10 +328,48 @@ class MailboxController extends BaseController { void clearSearchText() { searchQuery.value = SearchQuery.initial(); searchFocus.requestFocus(); + listMailboxSearched.clear(); } void searchMailbox(String value) { searchQuery.value = SearchQuery(value); + _searchMailboxAction(allMailboxes, searchQuery.value); + } + + void _searchMailboxAction(List allMailboxes, SearchQuery searchQuery) { + if (searchQuery.value.isNotEmpty) { + consumeState(_searchMailboxInteractor.execute(allMailboxes, searchQuery)); + } else { + listMailboxSearched.clear(); + } + } + + void _searchMailboxSuccess(SearchMailboxSuccess success) { + final mailboxesSearchedWithPath = _findMailboxPath(success.mailboxesSearched); + listMailboxSearched.value = mailboxesSearchedWithPath; + } + + List _findMailboxPath(List mailboxes) { + return mailboxes.map((presentationMailbox) { + if (!presentationMailbox.hasParentId()) { + return presentationMailbox; + } else { + final mailboxNode = folderMailboxTree.findNode(presentationMailbox.id); + if (mailboxNode != null) { + String mailboxPath = mailboxNode.getPathMailboxNode(folderMailboxTree, defaultMailboxList); + if (mailboxPath.contains('/')) { + mailboxPath = mailboxPath.substring(0, mailboxPath.lastIndexOf('/')).replaceAll('/', ' / '); + } + return presentationMailbox.toPresentationMailboxWithMailboxPath(mailboxPath); + } else { + return presentationMailbox; + } + } + }).toList(); + } + + void _searchMailboxFailure(SearchMailboxFailure failure) { + listMailboxSearched.clear(); } void closeMailboxScreen(BuildContext context) { diff --git a/lib/features/mailbox/presentation/mailbox_view.dart b/lib/features/mailbox/presentation/mailbox_view.dart index c6a254e2c..ce5ffef7d 100644 --- a/lib/features/mailbox/presentation/mailbox_view.dart +++ b/lib/features/mailbox/presentation/mailbox_view.dart @@ -35,15 +35,15 @@ class MailboxView extends GetWidget { children: [ _buildHeaderMailbox(context), Obx(() => controller.isSearchActive() ? _buildInputSearchFormWidget(context) : SizedBox.shrink()), - Expanded(child: Container( + Expanded(child: Obx(() => Container( color: controller.isSearchActive() ? Colors.white : AppColor.colorBgMailbox, child: RefreshIndicator( color: AppColor.primaryColor, onRefresh: () async => controller.refreshAllMailbox(), - child: Obx(() => controller.isSearchActive() - ? _buildListMailboxSearched(context, controller.listMailboxSearched) - : _buildListMailbox(context))), - )) + child: controller.isSearchActive() + ? _buildListMailboxSearched(context, controller.listMailboxSearched) + : _buildListMailbox(context)), + ))) ] ), ) @@ -283,7 +283,7 @@ class MailboxView extends GetWidget { Widget _buildListMailboxSearched(BuildContext context, List listMailbox) { return ListView.builder( - padding: EdgeInsets.all(8), + padding: EdgeInsets.only(left: 16, right: 8, bottom: 30), key: Key('list_mailbox_searched'), itemCount: listMailbox.length, shrinkWrap: true, @@ -292,6 +292,7 @@ class MailboxView extends GetWidget { Obx(() => (MailboxTileBuilder( imagePaths, listMailbox[index], + isSearchActive: controller.isSearchActive(), isLastElement: index == listMailbox.length - 1) ..onOpenMailboxAction((mailbox) => controller.selectMailbox(context, mailbox))) .build())); diff --git a/lib/features/mailbox/presentation/widgets/mailbox_tile_builder.dart b/lib/features/mailbox/presentation/widgets/mailbox_tile_builder.dart index 814841409..55e6f5854 100644 --- a/lib/features/mailbox/presentation/widgets/mailbox_tile_builder.dart +++ b/lib/features/mailbox/presentation/widgets/mailbox_tile_builder.dart @@ -14,6 +14,7 @@ class MailboxTileBuilder { final ImagePaths _imagePaths; final MailboxDisplayed mailboxDisplayed; final bool isLastElement; + final bool isSearchActive; OnOpenMailboxActionClick? _onOpenMailboxActionClick; @@ -24,6 +25,7 @@ class MailboxTileBuilder { this.selectMode = SelectMode.INACTIVE, this.mailboxDisplayed = MailboxDisplayed.mailbox, this.isLastElement = false, + this.isSearchActive = false, } ); @@ -67,9 +69,9 @@ class MailboxTileBuilder { '${_presentationMailbox.name?.name ?? ''}', maxLines: 1, overflow:TextOverflow.ellipsis, - style: TextStyle(fontSize: 15, color: AppColor.colorNameEmail), + style: TextStyle(fontSize: isSearchActive ? 17 : 15, color: AppColor.colorNameEmail), )), - if (mailboxDisplayed == MailboxDisplayed.mailbox) + if (mailboxDisplayed == MailboxDisplayed.mailbox && !isSearchActive) Transform( transform: Matrix4.translationValues(40.0, 0.0, 0.0), child: Text( @@ -77,7 +79,18 @@ class MailboxTileBuilder { maxLines: 1, style: TextStyle(fontSize: 13, color: AppColor.colorNameEmail))) ])), - trailing: (mailboxDisplayed == MailboxDisplayed.mailbox) + subtitle: isSearchActive && _presentationMailbox.mailboxPath?.isNotEmpty == true + ? Transform( + transform: Matrix4.translationValues(-10.0, 0.0, 0.0), + child: Text( + _presentationMailbox.mailboxPath ?? '', + maxLines: 1, + overflow:TextOverflow.ellipsis, + style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail), + ), + ) + : null, + trailing: (mailboxDisplayed == MailboxDisplayed.mailbox && !isSearchActive) ? Transform( transform: Matrix4.translationValues(8.0, 0.0, 0.0), child: IconButton( diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart index a65c6b0aa..cb8cb90fe 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart @@ -1,5 +1,4 @@ import 'package:core/core.dart'; -import 'package:flutter/cupertino.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tmail_ui_user/features/base/base_bindings.dart'; diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart index 461689c43..b67befe67 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart @@ -28,8 +28,6 @@ class MailboxDashBoardController extends BaseController { final AppToast _appToast; final ImagePaths _imagePaths; final RemoveEmailDraftsInteractor _removeEmailDraftsInteractor; - final TextEditingController searchInputController; - final FocusNode searchFocus; final scaffoldKey = GlobalKey(); final selectedMailbox = Rxn(); @@ -43,13 +41,13 @@ class MailboxDashBoardController extends BaseController { Session? sessionCurrent; Map mapDefaultMailboxId = Map(); Map mapMailbox = Map(); + TextEditingController searchInputController = TextEditingController(); + FocusNode searchFocus = FocusNode(); MailboxDashBoardController( this._getUserProfileInteractor, this._appToast, this._imagePaths, - this.searchInputController, - this.searchFocus, this._removeEmailDraftsInteractor, ); @@ -230,6 +228,7 @@ class MailboxDashBoardController extends BaseController { @override void onClose() { searchInputController.dispose(); + searchFocus.dispose(); Get.delete(); super.onClose(); } diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 7165f682a..b03cc24db 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -136,6 +136,7 @@ class ThreadController extends BaseController { mailboxDashBoardController.clearState(); } else if (success is SearchEmailNewQuery){ _searchEmail(); + mailboxDashBoardController.clearState(); } else if (success is SaveEmailAsDraftsSuccess || success is RemoveEmailDraftsSuccess || success is SendEmailSuccess