diff --git a/src/CONST.ts b/src/CONST.ts index bc0a0c3216f0..1ad8aae1375b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -716,7 +716,7 @@ const CONST = { TOOLTIP_SENSE: 1000, TRIE_INITIALIZATION: 'trie_initialization', COMMENT_LENGTH_DEBOUNCE_TIME: 500, - SEARCH_FOR_REPORTS_DEBOUNCE_TIME: 300, + SEARCH_OPTION_LIST_DEBOUNCE_TIME: 300, }, PRIORITY_MODE: { GSD: 'gsd', diff --git a/src/components/CategoryPicker/index.js b/src/components/CategoryPicker/index.js index d170def12276..a957e31a9de4 100644 --- a/src/components/CategoryPicker/index.js +++ b/src/components/CategoryPicker/index.js @@ -62,7 +62,6 @@ 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 fc3e4b095873..63181e4aea87 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -565,7 +565,6 @@ 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 || (!!prevProps.value && !this.props.value)) { + if (this.props.selectedOptions.length !== prevProps.selectedOptions.length || (!!prevState.value && !this.state.value)) { this.scrollToIndex(0); return; } @@ -247,6 +249,7 @@ 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); @@ -415,7 +418,7 @@ class BaseOptionsSelector extends Component { this.relatedTarget = null; } if (this.textInput.isFocused()) { - setSelection(this.textInput, 0, this.props.value.length); + setSelection(this.textInput, 0, this.state.value.length); } } const selectedOption = this.props.onSelectRow(option); @@ -440,7 +443,7 @@ class BaseOptionsSelector extends Component { if (this.props.shouldShowTextInput && this.props.shouldPreventDefaultFocusOnSelectRow) { this.textInput.focus(); if (this.textInput.isFocused()) { - setSelection(this.textInput, 0, this.props.value.length); + setSelection(this.textInput, 0, this.state.value.length); } } this.props.onAddToSelection(option); @@ -468,11 +471,10 @@ 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.updateSearchValue} + onChangeText={this.debouncedUpdateSearchValue} 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 8593569dfafd..e52187fa76d7 100644 --- a/src/components/OptionsSelector/optionsSelectorPropTypes.js +++ b/src/components/OptionsSelector/optionsSelectorPropTypes.js @@ -27,9 +27,6 @@ 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 cb3d9bf260e6..e258472eae93 100644 --- a/src/components/TagPicker/index.js +++ b/src/components/TagPicker/index.js @@ -70,7 +70,6 @@ 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 1ed54c826e40..4ac46fcf212e 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1,7 +1,6 @@ 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'; @@ -2501,8 +2500,6 @@ 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); @@ -2513,7 +2510,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); - debouncedSearchInServer(searchInput); + searchForReports(searchInput); } function clearNewRoomFormError() { diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 6f45c0b33f5a..d7abbab6e93f 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -244,7 +244,6 @@ 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 8fa733903a43..061f43e73de8 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -1,8 +1,7 @@ import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -70,8 +69,6 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { }); }, [reports, personalDetails, searchValue, betas]); - const debouncedUpdateOptions = useMemo(() => _.debounce(updateOptions, 75), [updateOptions]); - useEffect(() => { Timing.start(CONST.TIMING.SEARCH_RENDER); Performance.markStart(CONST.TIMING.SEARCH_RENDER); @@ -87,7 +84,7 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { return; } - debouncedUpdateOptions(); + updateOptions(); // Ignoring the rule intentionally, we want to run the code only when search Value changes to prevent additional runs. // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchValue]); @@ -176,7 +173,6 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { { - onAddParticipants( - [{accountID: option.accountID, login: option.login, isPolicyExpenseChat: option.isPolicyExpenseChat, reportID: option.reportID, selected: true, searchText: option.searchText}], - false, - ); - navigateToRequest(); - }; + const addSingleParticipant = useCallback( + (option) => { + onAddParticipants( + [ + { + accountID: option.accountID, + login: option.login, + isPolicyExpenseChat: option.isPolicyExpenseChat, + reportID: option.reportID, + selected: true, + searchText: option.searchText, + }, + ], + false, + ); + navigateToRequest(); + }, + [onAddParticipants, navigateToRequest], + ); /** * Removes a selected option from list if already selected. If not already selected add this option to the list. @@ -215,12 +222,16 @@ function MoneyRequestParticipantsSelector({ [participants, onAddParticipants], ); - 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 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 isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails); @@ -278,23 +289,27 @@ function MoneyRequestParticipantsSelector({ navigateToSplit(); }, [shouldShowSplitBillErrorMessage, navigateToSplit]); - const footerContent = ( - - {shouldShowSplitBillErrorMessage && ( - ( + + {shouldShowSplitBillErrorMessage && ( + + )} +