From 0b0e664a85ca16c124e6cc641ee8aa02ce940840 Mon Sep 17 00:00:00 2001 From: Vit Horacek <36083550+mountiny@users.noreply.github.com> Date: Tue, 19 Dec 2023 16:40:10 +0100 Subject: [PATCH] Revert "(perf) Debounce search in OptionList" --- src/CONST.ts | 2 +- src/components/CategoryPicker/index.js | 1 + .../MoneyRequestConfirmationList.js | 1 + ...oraryForRefactorRequestConfirmationList.js | 1 + src/components/OptionRow.js | 2 +- .../OptionsSelector/BaseOptionsSelector.js | 12 +-- .../optionsSelectorPropTypes.js | 3 + src/components/TagPicker/index.js | 1 + src/libs/actions/Report.ts | 5 +- src/pages/NewChatPage.js | 1 + src/pages/SearchPage.js | 13 ++- ...yForRefactorRequestParticipantsSelector.js | 1 + .../MoneyRequestParticipantsSelector.js | 93 ++++++++----------- src/pages/tasks/TaskAssigneeSelectorModal.js | 11 ++- .../TaskShareDestinationSelectorModal.js | 3 + 15 files changed, 84 insertions(+), 66 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 2e2f591f7117..b29456ba170b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -710,7 +710,7 @@ const CONST = { TOOLTIP_SENSE: 1000, TRIE_INITIALIZATION: 'trie_initialization', COMMENT_LENGTH_DEBOUNCE_TIME: 500, - SEARCH_OPTION_LIST_DEBOUNCE_TIME: 300, + SEARCH_FOR_REPORTS_DEBOUNCE_TIME: 300, }, PRIORITY_MODE: { GSD: 'gsd', diff --git a/src/components/CategoryPicker/index.js b/src/components/CategoryPicker/index.js index a957e31a9de4..d170def12276 100644 --- a/src/components/CategoryPicker/index.js +++ b/src/components/CategoryPicker/index.js @@ -62,6 +62,7 @@ function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedC sectionHeaderStyle={styles.mt5} sections={sections} selectedOptions={selectedOptions} + value={searchValue} // Focus the first option when searching focusedIndex={0} // Focus the selected option on first load diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 63181e4aea87..fc3e4b095873 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -565,6 +565,7 @@ function MoneyRequestConfirmationList(props) { return ( { // If we just toggled an option on a multi-selection page or cleared the search input, scroll to top - if (this.props.selectedOptions.length !== prevProps.selectedOptions.length || (!!prevState.value && !this.state.value)) { + if (this.props.selectedOptions.length !== prevProps.selectedOptions.length || (!!prevProps.value && !this.props.value)) { this.scrollToIndex(0); return; } @@ -249,7 +247,6 @@ class BaseOptionsSelector extends Component { this.setState({ paginationPage: 1, errorMessage: value.length > this.props.maxLength ? this.props.translate('common.error.characterLimitExceedCounter', {length: value.length, limit: this.props.maxLength}) : '', - value, }); this.props.onChangeText(value); @@ -418,7 +415,7 @@ class BaseOptionsSelector extends Component { this.relatedTarget = null; } if (this.textInput.isFocused()) { - setSelection(this.textInput, 0, this.state.value.length); + setSelection(this.textInput, 0, this.props.value.length); } } const selectedOption = this.props.onSelectRow(option); @@ -443,7 +440,7 @@ class BaseOptionsSelector extends Component { if (this.props.shouldShowTextInput && this.props.shouldPreventDefaultFocusOnSelectRow) { this.textInput.focus(); if (this.textInput.isFocused()) { - setSelection(this.textInput, 0, this.state.value.length); + setSelection(this.textInput, 0, this.props.value.length); } } this.props.onAddToSelection(option); @@ -471,10 +468,11 @@ class BaseOptionsSelector extends Component { const textInput = ( (this.textInput = el)} + value={this.props.value} label={this.props.textInputLabel} accessibilityLabel={this.props.textInputLabel} role={CONST.ROLE.PRESENTATION} - onChangeText={this.debouncedUpdateSearchValue} + onChangeText={this.updateSearchValue} errorText={this.state.errorMessage} onSubmitEditing={this.selectFocusedOption} placeholder={this.props.placeholderText} diff --git a/src/components/OptionsSelector/optionsSelectorPropTypes.js b/src/components/OptionsSelector/optionsSelectorPropTypes.js index e52187fa76d7..8593569dfafd 100644 --- a/src/components/OptionsSelector/optionsSelectorPropTypes.js +++ b/src/components/OptionsSelector/optionsSelectorPropTypes.js @@ -27,6 +27,9 @@ const propTypes = { }), ).isRequired, + /** Value in the search input field */ + value: PropTypes.string.isRequired, + /** Callback fired when text changes */ onChangeText: PropTypes.func, diff --git a/src/components/TagPicker/index.js b/src/components/TagPicker/index.js index e258472eae93..cb3d9bf260e6 100644 --- a/src/components/TagPicker/index.js +++ b/src/components/TagPicker/index.js @@ -70,6 +70,7 @@ function TagPicker({selectedTag, tag, policyTags, policyRecentlyUsedTags, should highlightSelectedOptions isRowMultilineSupported shouldShowTextInput={shouldShowTextInput} + value={searchValue} // Focus the first option when searching focusedIndex={0} // Focus the selected option on first load diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index c3b1532b4d87..135e616f7691 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1,6 +1,7 @@ import {format as timezoneFormat, utcToZonedTime} from 'date-fns-tz'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; +import lodashDebounce from 'lodash/debounce'; import isEmpty from 'lodash/isEmpty'; import {DeviceEventEmitter, InteractionManager} from 'react-native'; import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; @@ -2498,6 +2499,8 @@ function searchForReports(searchInput: string) { API.read('SearchForReports', parameters, {successData, failureData}); } +const debouncedSearchInServer = lodashDebounce(searchForReports, CONST.TIMING.SEARCH_FOR_REPORTS_DEBOUNCE_TIME, {leading: false}); + function searchInServer(searchInput: string) { if (isNetworkOffline || !searchInput.trim().length) { Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, false); @@ -2508,7 +2511,7 @@ function searchInServer(searchInput: string) { // we want to show the loading state right away. Otherwise, we will see a flashing UI where the client options are sorted and // tell the user there are no options, then we start searching, and tell them there are no options again. Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, true); - searchForReports(searchInput); + debouncedSearchInServer(searchInput); } function clearNewRoomFormError() { diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index d7abbab6e93f..6f45c0b33f5a 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -244,6 +244,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i onAddToSelection={(option) => toggleOption(option)} sections={sections} selectedOptions={selectedOptions} + value={searchTerm} onSelectRow={(option) => createChat(option)} onChangeText={setSearchTermAndSearchInServer} headerMessage={headerMessage} diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 28718d9aac1c..01fe83289d71 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -67,6 +67,7 @@ class SearchPage extends Component { this.selectReport = this.selectReport.bind(this); this.onChangeText = this.onChangeText.bind(this); this.updateOptions = this.updateOptions.bind(this); + this.debouncedUpdateOptions = _.debounce(this.updateOptions.bind(this), 75); this.state = { searchValue: '', recentReports: {}, @@ -84,7 +85,7 @@ class SearchPage extends Component { onChangeText(searchValue = '') { Report.searchInServer(searchValue); - this.setState({searchValue}, this.updateOptions); + this.setState({searchValue}, this.debouncedUpdateOptions); } /** @@ -155,7 +156,14 @@ class SearchPage extends Component { } if (option.reportID) { - Navigation.dismissModal(option.reportID); + this.setState( + { + searchValue: '', + }, + () => { + Navigation.dismissModal(option.reportID); + }, + ); } else { Report.navigateToAndOpenReport([option.login]); } @@ -182,6 +190,7 @@ class SearchPage extends Component { { - onAddParticipants( - [ - { - accountID: option.accountID, - login: option.login, - isPolicyExpenseChat: option.isPolicyExpenseChat, - reportID: option.reportID, - selected: true, - searchText: option.searchText, - }, - ], - false, - ); - navigateToRequest(); - }, - [onAddParticipants, navigateToRequest], - ); + const addSingleParticipant = (option) => { + onAddParticipants( + [{accountID: option.accountID, login: option.login, isPolicyExpenseChat: option.isPolicyExpenseChat, reportID: option.reportID, selected: true, searchText: option.searchText}], + false, + ); + navigateToRequest(); + }; /** * Removes a selected option from list if already selected. If not already selected add this option to the list. @@ -222,16 +215,12 @@ function MoneyRequestParticipantsSelector({ [participants, onAddParticipants], ); - const headerMessage = useMemo( - () => - OptionsListUtils.getHeaderMessage( - newChatOptions.personalDetails.length + newChatOptions.recentReports.length !== 0, - Boolean(newChatOptions.userToInvite), - searchTerm.trim(), - maxParticipantsReached, - _.some(participants, (participant) => participant.searchText.toLowerCase().includes(searchTerm.trim().toLowerCase())), - ), - [maxParticipantsReached, newChatOptions.personalDetails.length, newChatOptions.recentReports.length, newChatOptions.userToInvite, participants, searchTerm], + const headerMessage = OptionsListUtils.getHeaderMessage( + newChatOptions.personalDetails.length + newChatOptions.recentReports.length !== 0, + Boolean(newChatOptions.userToInvite), + searchTerm.trim(), + maxParticipantsReached, + _.some(participants, (participant) => participant.searchText.toLowerCase().includes(searchTerm.trim().toLowerCase())), ); const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails); @@ -289,27 +278,23 @@ function MoneyRequestParticipantsSelector({ navigateToSplit(); }, [shouldShowSplitBillErrorMessage, navigateToSplit]); - const footerContent = useMemo( - () => ( - - {shouldShowSplitBillErrorMessage && ( - - )} -