From 20393eacf0a122da5c46cae16f5e427689246b6f Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 8 Mar 2023 12:14:14 +0700 Subject: [PATCH] TF-1561 Fix focus is lost on overlay --- .../quick_search/quick_search_input_form.dart | 4 +- .../advanced_filter_controller.dart | 1 + .../controller/search_controller.dart | 1 - .../mailbox_dashboard_view_web.dart | 46 +-- .../widgets/search_input_form_widget.dart | 278 +++++++++--------- .../presentation/thread_controller.dart | 2 - 6 files changed, 151 insertions(+), 181 deletions(-) diff --git a/core/lib/presentation/views/quick_search/quick_search_input_form.dart b/core/lib/presentation/views/quick_search/quick_search_input_form.dart index f8f78b7b4..e5245e31a 100644 --- a/core/lib/presentation/views/quick_search/quick_search_input_form.dart +++ b/core/lib/presentation/views/quick_search/quick_search_input_form.dart @@ -779,11 +779,13 @@ class _TypeAheadFieldQuickSearchState extends State(); final simpleSearchIsActivated = RxBool(false); final advancedSearchIsActivated = RxBool(false); - final autoFocus = RxBool(true); SearchQuery? get searchQuery => searchEmailFilter.value.text; diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart index 28369ca6e..d6f4a0cf2 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/base/widget/popup_item_no_icon_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_view_web.dart'; import 'package:tmail_ui_user/features/email/presentation/email_view.dart'; @@ -20,8 +19,6 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/comp import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_filter_overlay.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/icon_open_advanced_search_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/download/download_task_item_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/spam_report_banner_web_widget.dart'; @@ -262,43 +259,12 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { width: constraint.maxWidth / 2, height: 52, color: Colors.transparent, - child: Obx(() { - if (searchController.isSearchActive()) { - return SearchInputFormWidget( - maxWidth: constraint.maxWidth / 2, - dashBoardController: controller, - imagePaths: imagePaths); - } else { - return PortalTarget( - visible: searchController.isAdvancedSearchViewOpen.isTrue, - portalFollower: PointerInterceptor( - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => searchController.selectOpenAdvanceSearch()), - ), - child: PortalTarget( - visible: searchController.isAdvancedSearchViewOpen.isTrue, - anchor: const Aligned( - follower: Alignment.topRight, - target: Alignment.bottomRight, - widthFactor: 1, - backup: Aligned( - follower: Alignment.topRight, - target: Alignment.bottomRight, - widthFactor: 1, - ), - ), - portalFollower: AdvancedSearchFilterOverlay(maxWidth: constraint.maxWidth / 2), - child: SearchBarView(imagePaths, - hintTextSearch: AppLocalizations.of(context).search_emails, - onOpenSearchViewAction: controller.searchController.enableSearch, - heightSearchBar: 52, - radius: 12, - rightButton: IconOpenAdvancedSearchWidget(context)), - ), - ); - } - })), + child: SearchInputFormWidget( + maxWidth: constraint.maxWidth / 2, + dashBoardController: controller, + imagePaths: imagePaths + ) + ), const Spacer(), AppConfig.appGridDashboardAvailable ? Obx(() => PortalTarget( diff --git a/lib/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart index 0057af269..1e14d0a6c 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart @@ -1,5 +1,9 @@ -import 'package:core/core.dart'; +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/style_utils.dart'; +import 'package:core/presentation/views/button/icon_button_web.dart'; +import 'package:core/presentation/views/quick_search/quick_search_input_form.dart'; import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -33,90 +37,37 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin { Widget build(BuildContext context) { final controller = dashBoardController.searchController; - return PortalTarget( - visible: controller.isAdvancedSearchViewOpen.isTrue, - portalFollower: PointerInterceptor( - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => controller.selectOpenAdvanceSearch()), - ), - child: PortalTarget( + return Obx(() { + return PortalTarget( visible: controller.isAdvancedSearchViewOpen.isTrue, - anchor: const Aligned( - follower: Alignment.topRight, - target: Alignment.bottomRight, - widthFactor: 1, - backup: Aligned( + portalFollower: PointerInterceptor( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => controller.selectOpenAdvanceSearch() + ), + ), + child: PortalTarget( + visible: controller.isAdvancedSearchViewOpen.isTrue, + anchor: const Aligned( follower: Alignment.topRight, target: Alignment.bottomRight, widthFactor: 1, + backup: Aligned( + follower: Alignment.topRight, + target: Alignment.bottomRight, + widthFactor: 1, + ), ), - ), - portalFollower: AdvancedSearchFilterOverlay(maxWidth: maxWidth), - child: QuickSearchInputForm( + portalFollower: AdvancedSearchFilterOverlay(maxWidth: maxWidth), + child: QuickSearchInputForm( maxHeight: 52, suggestionsBoxVerticalOffset: 0.0, - textFieldConfiguration: QuickSearchTextFieldConfiguration( - controller: controller.searchInputController, - autofocus: controller.autoFocus.value, - enabled: controller.isAdvancedSearchViewOpen.isFalse, - focusNode: controller.searchFocus, - textInputAction: TextInputAction.done, - onSubmitted: (keyword) { - final query = keyword.trim(); - if (query.isNotEmpty) { - controller.saveRecentSearch(RecentSearch.now(query)); - dashBoardController.searchEmail(context, query); - } else { - dashBoardController.clearSearchEmail(); - } - }, - onChanged: controller.onChangeTextSearch, - decoration: InputDecoration( - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - contentPadding: EdgeInsets.zero, - hintText: AppLocalizations.of(context).search_emails, - hintStyle: const TextStyle( - color: AppColor.colorHintSearchBar, - fontSize: 16.0), - labelStyle: const TextStyle( - color: Colors.black, - fontSize: 16.0) - ), - leftButton: Padding( - padding: const EdgeInsets.only(left: 8), - child: buildIconWeb( - minSize: 40, - iconPadding: EdgeInsets.zero, - icon: SvgPicture.asset( - imagePaths.icSearchBar, - fit: BoxFit.fill), - onTap: () { - final keyword = controller.searchInputController.text.trim(); - if (keyword.isNotEmpty) { - controller.saveRecentSearch(RecentSearch.now(keyword)); - dashBoardController.searchEmail(context, keyword); - } else { - dashBoardController.clearSearchEmail(); - } - } - ) - ), - clearTextButton: buildIconWeb( - icon: SvgPicture.asset( - imagePaths.icClearTextSearch, - width: 16, - height: 16, - fit: BoxFit.fill), - onTap: controller.clearTextSearch), - rightButton: IconOpenAdvancedSearchWidget(context) - ), + textFieldConfiguration: _createConfiguration(context), suggestionsBoxDecoration: QuickSearchSuggestionsBoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(16)), - constraints: BoxConstraints(maxWidth: maxWidth)), + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(16)), + constraints: BoxConstraints(maxWidth: maxWidth) + ), debounceDuration: const Duration(milliseconds: 500), listActionButton: QuickSearchFilter.values, actionButtonBuilder: (context, filterAction) { @@ -131,81 +82,134 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin { dashBoardController.addFilterToSuggestionForm(filterAction); } }, - listActionPadding: const EdgeInsets.only( - left: 12, - right: 12, - top: 12, - bottom: 6), + listActionPadding: const EdgeInsets.only(left: 12, right: 12, top: 12, bottom: 6), titleHeaderRecent: Padding( - padding: const EdgeInsets.only( - left: 12, - right: 12, - bottom: 8, - top: 12), - child: Text(AppLocalizations.of(context).recent, - style: const TextStyle( - fontSize: 13.0, - color: AppColor.colorTextButtonHeaderThread, - fontWeight: FontWeight.w500) + padding: const EdgeInsets.only(left: 12, right: 12, bottom: 8, top: 12), + 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: () { - final query = keyword.trim(); - if (query.isNotEmpty) { - controller.saveRecentSearch(RecentSearch.now(query)); - dashBoardController.searchEmail(context, query); - } else { - dashBoardController.clearSearchEmail(); - } - - }, - child: Padding( - padding: const EdgeInsets.all(12), - 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))) - ]) - ), - ); + return _buildShowAllResultButton(context, keyword); } else { return const SizedBox.shrink(); } }, loadingBuilder: (context) => Padding( - padding: const EdgeInsets.only(bottom: 16), - child: loadingWidget), + padding: const EdgeInsets.only(bottom: 16), + child: loadingWidget + ), fetchRecentActionCallback: controller.getAllRecentSearchAction, - itemRecentBuilder: (context, recent) => - RecentSearchItemTileWidget(recent), - onRecentSelected: (recent) { - controller.searchInputController.text = recent.value; - dashBoardController.searchEmail(context, recent.value); - }, - suggestionsCallback: (pattern) => - dashBoardController.quickSearchEmails(), - itemBuilder: (context, email) => - EmailQuickSearchItemTileWidget(email, dashBoardController.selectedMailbox.value), - onSuggestionSelected: (presentationEmail) => - dashBoardController.dispatchAction( - OpenEmailDetailedFromSuggestionQuickSearchAction( - context, - presentationEmail))), + itemRecentBuilder: (context, recent) => RecentSearchItemTileWidget(recent), + onRecentSelected: (recent) => _invokeSelectRecentItem(context, recent), + suggestionsCallback: (pattern) => dashBoardController.quickSearchEmails(), + itemBuilder: (context, email) => EmailQuickSearchItemTileWidget(email, dashBoardController.selectedMailbox.value), + onSuggestionSelected: (presentationEmail) => _invokeSelectSuggestionItem(context, presentationEmail)) + ), + ); + }); + } + + void _invokeSearchEmailAction(BuildContext context, String query) { + dashBoardController.searchController.enableSearch(); + + if (query.isNotEmpty) { + dashBoardController.searchController.saveRecentSearch(RecentSearch.now(query)); + dashBoardController.searchEmail(context, query); + } else { + dashBoardController.clearSearchEmail(); + } + } + + void _invokeSelectSuggestionItem(BuildContext context, PresentationEmail presentationEmail) { + dashBoardController.dispatchAction( + OpenEmailDetailedFromSuggestionQuickSearchAction( + context, + presentationEmail + ) + ); + } + + void _invokeSelectRecentItem(BuildContext context, RecentSearch recent) { + dashBoardController.searchController.enableSearch(); + dashBoardController.searchController.searchInputController.text = recent.value; + dashBoardController.searchEmail(context, recent.value); + } + + Widget _buildShowAllResultButton(BuildContext context, String keyword) { + return InkWell( + onTap: () => _invokeSearchEmailAction(context, keyword.trim()), + child: Padding( + padding: const EdgeInsets.all(12), + 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 + ) + )) + ]) ), ); } + QuickSearchTextFieldConfiguration _createConfiguration(BuildContext context) { + final controller = dashBoardController.searchController; + + return QuickSearchTextFieldConfiguration( + controller: controller.searchInputController, + focusNode: controller.searchFocus, + textInputAction: TextInputAction.done, + onSubmitted: (keyword) => _invokeSearchEmailAction(context, keyword.trim()), + onChanged: controller.onChangeTextSearch, + decoration: InputDecoration( + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + contentPadding: EdgeInsets.zero, + hintText: AppLocalizations.of(context).search_emails, + hintStyle: const TextStyle(color: AppColor.colorHintSearchBar, fontSize: 16.0), + labelStyle: const TextStyle(color: Colors.black, fontSize: 16.0) + ), + leftButton: Padding( + padding: const EdgeInsets.only(left: 8), + child: buildIconWeb( + minSize: 40, + iconPadding: EdgeInsets.zero, + icon: SvgPicture.asset(imagePaths.icSearchBar, fit: BoxFit.fill), + onTap: () => _invokeSearchEmailAction(context, controller.searchInputController.text.trim()) + ) + ), + clearTextButton: buildIconWeb( + icon: SvgPicture.asset( + imagePaths.icClearTextSearch, + width: 16, + height: 16, + fit: BoxFit.fill + ), + onTap: controller.clearTextSearch + ), + rightButton: IconOpenAdvancedSearchWidget(context) + ); + } + Widget buildListButtonForQuickSearchForm(BuildContext context, QuickSearchFilter filter) { return Obx(() { final isFilterSelected = filter.isApplied(dashBoardController.searchController.listFilterOnSuggestionForm); diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 9e4e47c94..531036ba0 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -327,7 +327,6 @@ class ThreadController extends BaseController with EmailActionController { } void _activateSearchFromRouter() { - searchController.autoFocus.value = false; searchController.enableSearch(); searchController.updateTextSearch(_navigationRouter!.searchQuery!.value); searchController.updateFilterEmail(text: _navigationRouter!.searchQuery!); @@ -716,7 +715,6 @@ class ThreadController extends BaseController with EmailActionController { isSearchEmailRunning: searchController.isSearchEmailRunning ); mailboxDashBoardController.updateEmailList(newEmailListSynced); - searchController.autoFocus.value = true; } void searchMoreEmails() {