Skip to content

Commit

Permalink
optimize search in BaseOptionSelector and implement in money request
Browse files Browse the repository at this point in the history
  • Loading branch information
TMisiukiewicz committed Dec 15, 2023
1 parent 3a44f77 commit 9ad0872
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 53 deletions.
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ const CONST = {
TRIE_INITIALIZATION: 'trie_initialization',
COMMENT_LENGTH_DEBOUNCE_TIME: 500,
SEARCH_FOR_REPORTS_DEBOUNCE_TIME: 300,
SEARCH_OPTION_LIST_DEBOUNCE_TIME: 200,
},
PRIORITY_MODE: {
GSD: 'gsd',
Expand Down
2 changes: 1 addition & 1 deletion src/components/OptionRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ export default React.memo(
prevProps.showSelectedState === nextProps.showSelectedState &&
prevProps.highlightSelected === nextProps.highlightSelected &&
prevProps.showTitleTooltip === nextProps.showTitleTooltip &&
!_.isEqual(prevProps.option.icons, nextProps.option.icons) &&
_.isEqual(prevProps.option.icons, nextProps.option.icons) &&
prevProps.optionIsFocused === nextProps.optionIsFocused &&
prevProps.option.text === nextProps.option.text &&
prevProps.option.alternateText === nextProps.option.alternateText &&
Expand Down
12 changes: 7 additions & 5 deletions src/components/OptionsSelector/BaseOptionsSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class BaseOptionsSelector extends Component {
this.incrementPage = this.incrementPage.bind(this);
this.sliceSections = this.sliceSections.bind(this);
this.calculateAllVisibleOptionsCount = this.calculateAllVisibleOptionsCount.bind(this);
this.debouncedUpdateSearchValue = _.debounce(this.updateSearchValue, CONST.TIMING.SEARCH_OPTION_LIST_DEBOUNCE_TIME);
this.relatedTarget = null;

const allOptions = this.flattenSections();
Expand All @@ -94,6 +95,7 @@ class BaseOptionsSelector extends Component {
shouldShowReferralModal: false,
errorMessage: '',
paginationPage: 1,
value: '',
};
}

Expand Down Expand Up @@ -161,7 +163,7 @@ class BaseOptionsSelector extends Component {
},
() => {
// 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 || (!!prevProps.value && !this.state.value)) {
this.scrollToIndex(0);
return;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -468,11 +471,10 @@ class BaseOptionsSelector extends Component {
const textInput = (
<TextInput
ref={(el) => (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}
Expand Down
3 changes: 0 additions & 3 deletions src/components/OptionsSelector/optionsSelectorPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
5 changes: 1 addition & 4 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -2495,8 +2494,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);
Expand All @@ -2507,7 +2504,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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import Button from '@components/Button';
import FormHelpMessage from '@components/FormHelpMessage';
import {usePersonalDetails} from '@components/OnyxProvider';
import OptionsSelector from '@components/OptionsSelector';
import refPropTypes from '@components/refPropTypes';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
Expand All @@ -16,7 +17,6 @@ import * as Browser from '@libs/Browser';
import compose from '@libs/compose';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import personalDetailsPropType from '@pages/personalDetailsPropType';
import reportPropTypes from '@pages/reportPropTypes';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand Down Expand Up @@ -48,9 +48,6 @@ const propTypes = {
}),
),

/** All of the personal details for everyone */
personalDetails: PropTypes.objectOf(personalDetailsPropType),

/** All reports shared with the user */
reports: PropTypes.objectOf(reportPropTypes),

Expand All @@ -73,7 +70,6 @@ const defaultProps = {
participants: [],
forwardedRef: undefined,
safeAreaPaddingBottomStyle: {},
personalDetails: {},
reports: {},
betas: [],
isDistanceRequest: false,
Expand All @@ -84,7 +80,6 @@ function MoneyRequestParticipantsSelector({
forwardedRef,
betas,
participants,
personalDetails,
reports,
translate,
navigateToRequest,
Expand All @@ -103,7 +98,7 @@ function MoneyRequestParticipantsSelector({
userToInvite: null,
});
const {isOffline} = useNetwork();

const personalDetails = usePersonalDetails();
const maxParticipantsReached = participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS;

/**
Expand Down Expand Up @@ -160,20 +155,32 @@ function MoneyRequestParticipantsSelector({
}

return newSections;
}, [maxParticipantsReached, newChatOptions, participants, personalDetails, translate, searchTerm]);
}, [maxParticipantsReached, newChatOptions.personalDetails, newChatOptions.recentReports, newChatOptions.userToInvite, participants, personalDetails, searchTerm, translate]);

/**
* Adds a single participant to the request
*
* @param {Object} option
*/
const addSingleParticipant = (option) => {
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.
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -278,23 +289,26 @@ function MoneyRequestParticipantsSelector({
navigateToSplit();
}, [shouldShowSplitBillErrorMessage, navigateToSplit]);

const footerContent = (
<View>
{shouldShowSplitBillErrorMessage && (
<FormHelpMessage
style={[styles.ph1, styles.mb2]}
isError
message="iou.error.splitBillMultipleParticipantsErrorMessage"
const footerContent = useMemo(
() => (
<View>
{shouldShowSplitBillErrorMessage && (
<FormHelpMessage
style={[styles.ph1, styles.mb2]}
isError
message="iou.error.splitBillMultipleParticipantsErrorMessage"
/>
)}
<Button
success
text={translate('iou.addToSplit')}
onPress={handleConfirmSelection}
pressOnEnter
isDisabled={shouldShowSplitBillErrorMessage}
/>
)}
<Button
success
text={translate('iou.addToSplit')}
onPress={handleConfirmSelection}
pressOnEnter
isDisabled={shouldShowSplitBillErrorMessage}
/>
</View>
</View>
),
[handleConfirmSelection, shouldShowSplitBillErrorMessage, styles.mb2, styles.ph1, translate],
);

return (
Expand Down Expand Up @@ -346,9 +360,6 @@ MoneyRequestParticipantsSelectorWithRef.displayName = 'MoneyRequestParticipantsS
export default compose(
withLocalize,
withOnyx({
personalDetails: {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
},
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
},
Expand Down

0 comments on commit 9ad0872

Please sign in to comment.