Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix double click navigating twice on lists #25604

Merged
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
ae5a99f
Added debounce row selection prop to selection list
huzaifa-99 Aug 21, 2023
9213d0d
Updated pages to use debounced onSelectRow on selection list for side…
huzaifa-99 Aug 21, 2023
e2ea627
Added debounce row selection prop to options list
huzaifa-99 Aug 21, 2023
4754aa7
Updated pages to use debounced onSelectRow on options list for side e…
huzaifa-99 Aug 21, 2023
c289d5a
Added debounce row selection prop to options selector
huzaifa-99 Aug 21, 2023
54e0fe0
Updated pages to use debounced onSelectRow on options selector for si…
huzaifa-99 Aug 21, 2023
fc64f73
Updated debounce callback in option row
huzaifa-99 Aug 21, 2023
6e3c7ac
Fix lint
huzaifa-99 Aug 21, 2023
e50bb84
Fix lint
huzaifa-99 Aug 21, 2023
4adf263
Fix debounced onSelectRow implementation in OptionRow
huzaifa-99 Aug 21, 2023
d375d2d
Removed extra call to onSelectRow
huzaifa-99 Aug 21, 2023
8edf17f
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Aug 22, 2023
7be2135
Updated new selection list to conditionally debounce onSelectRow
huzaifa-99 Aug 22, 2023
3f6942d
Merge branch 'Expensify:main' into 24074-fix-double-click-navigating-…
huzaifa-99 Aug 24, 2023
c57dbba
Added debounced row select to ThemePage
huzaifa-99 Aug 28, 2023
9d0d697
Added debounced row select to CategoryPicker
huzaifa-99 Aug 28, 2023
3712746
Updated early return syntax
huzaifa-99 Aug 28, 2023
995f33d
Added comment explaining updateOnSelectRow in OptionRow
huzaifa-99 Aug 28, 2023
0abd5d2
Cancel debounced function version when props change
huzaifa-99 Aug 28, 2023
5cfe674
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Aug 28, 2023
329767c
Removed unnecessary callback
huzaifa-99 Aug 28, 2023
b2b25fa
Updated comments lettercase
huzaifa-99 Aug 28, 2023
44c5f32
Fixed comment syntax
huzaifa-99 Aug 28, 2023
1f2d67a
Remove eslint disable rule
huzaifa-99 Aug 28, 2023
2f67ee0
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Sep 12, 2023
27f5aca
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Sep 13, 2023
9cb54b1
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Sep 27, 2023
f5aaccc
Added debounce for arrow selection on selection list
huzaifa-99 Sep 27, 2023
57696cc
Added row select debounce on tag picker
huzaifa-99 Sep 27, 2023
dec4563
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Oct 2, 2023
e93c2ab
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Oct 5, 2023
21ce3df
Updated debounce function
huzaifa-99 Oct 9, 2023
bee6f1a
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Oct 9, 2023
2b81ebf
Use seperate callback for debounced func
huzaifa-99 Oct 11, 2023
0434a68
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Oct 11, 2023
e987f14
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Dec 12, 2023
ce8e6e6
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Dec 21, 2023
4c9668e
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Jan 23, 2024
fe8f185
Added single execution logic on web
huzaifa-99 Jan 24, 2024
4e71b31
Removed debounced onRowSelect from OptionsList, OptionsSelector and O…
huzaifa-99 Jan 24, 2024
f410673
Added shouldDebounceRowSelect on all SelectionLists with single item …
huzaifa-99 Jan 24, 2024
f668b79
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Jan 24, 2024
d0ea9f3
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Jan 24, 2024
3940e32
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Jan 29, 2024
019e706
Fix type export
huzaifa-99 Jan 29, 2024
855a7f3
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Feb 7, 2024
1a69275
Fix type
huzaifa-99 Feb 7, 2024
2d46d87
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Feb 29, 2024
dfe0c26
Revert useSingleExecution on web
huzaifa-99 Mar 1, 2024
403d15f
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Mar 1, 2024
bfd87a2
Lint
huzaifa-99 Mar 1, 2024
2f8f685
Added shouldDebounceRowSelect to new pages
huzaifa-99 Mar 1, 2024
1aff45b
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 Apr 2, 2024
dda25b6
Added debounce to new components
huzaifa-99 Apr 2, 2024
762c3b4
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 May 17, 2024
f626b5e
Added `shouldDebounceRowSelect` to newer components
huzaifa-99 May 17, 2024
1e5c77f
Merge branch 'main' into 24074-fix-double-click-navigating-twice
huzaifa-99 May 26, 2024
7c9240b
Removed unnecessary logical check
huzaifa-99 May 26, 2024
8dcd282
Updated comment
huzaifa-99 May 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/components/MoneyRequestConfirmationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ function MoneyRequestConfirmationList(props) {
sections={optionSelectorSections}
value=""
onSelectRow={canModifyParticipants ? selectParticipant : navigateToReportOrUserDetail}
shouldDebounceRowSelect={!canModifyParticipants}
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
onConfirmSelection={confirm}
selectedOptions={selectedOptions}
canSelectMultipleOptions={canModifyParticipants}
Expand Down
28 changes: 23 additions & 5 deletions src/components/OptionRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ const propTypes = {
/** A function that is called when an option is selected. Selected option is passed as a param */
onSelectRow: PropTypes.func,

/** Boolean to indicate if onSelectRow should be debounced */
shouldDebounceRowSelect: PropTypes.bool,
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved

/** Whether we should show the selected state */
showSelectedState: PropTypes.bool,

Expand Down Expand Up @@ -69,6 +72,7 @@ const defaultProps = {
boldStyle: false,
showTitleTooltip: false,
onSelectRow: undefined,
shouldDebounceRowSelect: false,
isDisabled: false,
optionIsFocused: false,
style: null,
Expand All @@ -82,6 +86,8 @@ class OptionRow extends Component {
this.state = {
isDisabled: this.props.isDisabled,
};

this.updateOnSelectRow();
}

// It is very important to use shouldComponentUpdate here so SectionList items will not unnecessarily re-render
Expand All @@ -103,16 +109,28 @@ class OptionRow extends Component {
this.props.option.ownerAccountID !== nextProps.option.ownerAccountID ||
this.props.option.subtitle !== nextProps.option.subtitle ||
this.props.option.pendingAction !== nextProps.option.pendingAction ||
this.props.option.customIcon !== nextProps.option.customIcon
this.props.option.customIcon !== nextProps.option.customIcon ||
this.props.shouldDebounceRowSelect !== nextProps.shouldDebounceRowSelect
);
}

componentDidUpdate(prevProps) {
if (this.props.isDisabled === prevProps.isDisabled) {
return;
if (this.props.isDisabled !== prevProps.isDisabled) {
this.setState({isDisabled: this.props.isDisabled});
}

if (prevProps.onSelectRow !== this.props.onSelectRow || prevProps.shouldDebounceRowSelect !== this.props.shouldDebounceRowSelect) {
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
this.updateOnSelectRow();
}
}

componentWillUnmount() {
if (!(this.props.shouldDebounceRowSelect && this.onSelectRow.cancel)) return;
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
this.onSelectRow.cancel();
}

this.setState({isDisabled: this.props.isDisabled});
updateOnSelectRow() {
this.onSelectRow = this.props.shouldDebounceRowSelect && this.props.onSelectRow ? _.debounce(this.props.onSelectRow, 1000, {leading: true}) : this.props.onSelectRow;
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
}

render() {
Expand Down Expand Up @@ -164,7 +182,7 @@ class OptionRow extends Component {
if (e) {
e.preventDefault();
}
let result = this.props.onSelectRow(this.props.option, pressableRef);
let result = this.onSelectRow(this.props.option, pressableRef);
if (!(result instanceof Promise)) {
result = Promise.resolve();
}
Expand Down
1 change: 1 addition & 0 deletions src/components/OptionsList/BaseOptionsList.js
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ class BaseOptionsList extends Component {
hoverStyle={this.props.optionHoveredStyle}
optionIsFocused={!this.props.disableFocusOptions && !isDisabled && this.props.focusedIndex === index + section.indexOffset}
onSelectRow={this.props.onSelectRow}
shouldDebounceRowSelect={this.props.shouldDebounceRowSelect}
isSelected={Boolean(_.find(this.props.selectedOptions, (option) => option.accountID === item.accountID))}
showSelectedState={this.props.canSelectMultipleOptions}
boldStyle={this.props.boldStyle}
Expand Down
4 changes: 4 additions & 0 deletions src/components/OptionsList/optionsListPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ const propTypes = {
/** Callback to fire when a row is selected */
onSelectRow: PropTypes.func,

/** Boolean to indicate if onSelectRow should be debounced */
shouldDebounceRowSelect: PropTypes.bool,

/** Optional header message */
headerMessage: PropTypes.string,

Expand Down Expand Up @@ -92,6 +95,7 @@ const defaultProps = {
disableFocusOptions: false,
boldStyle: false,
onSelectRow: undefined,
shouldDebounceRowSelect: false,
headerMessage: '',
innerRef: null,
showTitleTooltip: false,
Expand Down
1 change: 1 addition & 0 deletions src/components/OptionsSelector/BaseOptionsSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ class BaseOptionsSelector extends Component {
ref={(el) => (this.list = el)}
optionHoveredStyle={this.props.optionHoveredStyle}
onSelectRow={this.props.onSelectRow ? this.selectRow : undefined}
shouldDebounceRowSelect={this.props.shouldDebounceRowSelect}
sections={this.props.sections}
focusedIndex={this.state.focusedIndex}
selectedOptions={this.props.selectedOptions}
Expand Down
4 changes: 4 additions & 0 deletions src/components/OptionsSelector/optionsSelectorPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ const propTypes = {
/** Callback to fire when a row is tapped */
onSelectRow: PropTypes.func,

/** Boolean to indicate if onSelectRow should be debounced */
shouldDebounceRowSelect: PropTypes.bool,

/** Sections for the section list */
sections: PropTypes.arrayOf(
PropTypes.shape({
Expand Down Expand Up @@ -110,6 +113,7 @@ const propTypes = {

const defaultProps = {
onSelectRow: undefined,
shouldDebounceRowSelect: false,
textInputLabel: '',
placeholderText: '',
keyboardType: 'default',
Expand Down
19 changes: 17 additions & 2 deletions src/components/SelectionList/BaseSelectionList.js
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useEffect, useMemo, useRef, useState} from 'react';
import React, {useEffect, useMemo, useRef, useState, useCallback} from 'react';
import {View} from 'react-native';
import _ from 'underscore';
import lodashGet from 'lodash/get';
Expand Down Expand Up @@ -32,6 +32,7 @@ function BaseSelectionList({
sections,
canSelectMultiple = false,
onSelectRow,
shouldDebounceRowSelect = false,
onSelectAll,
onDismissError,
textInputLabel = '',
Expand Down Expand Up @@ -145,6 +146,12 @@ function BaseSelectionList({
return defaultIndex;
});

// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedOnSelectRow = useCallback(shouldDebounceRowSelect ? _.debounce((item) => onSelectRow(item), 1000, {leading: true}) : (item) => onSelectRow(item), [
shouldDebounceRowSelect,
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am still skeptical about this way of switching between callback states. But I don't have better ideas so let me ask this on Slack.

Copy link
Contributor Author

@huzaifa-99 huzaifa-99 Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@parasharrajat Coming from this discussion in slack. I added separate callback for the debounced func in 2b81ebf

onSelectRow,
]);

/**
* Scrolls to the desired item index in the section list
*
Expand Down Expand Up @@ -188,7 +195,7 @@ function BaseSelectionList({
}
}

onSelectRow(item);
debouncedOnSelectRow(item);
};

const selectFocusedOption = () => {
Expand Down Expand Up @@ -268,6 +275,14 @@ function BaseSelectionList({
);
};

// eslint-disable-next-line arrow-body-style
useEffect(() => {
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
return () => {
if (!(shouldDebounceRowSelect && debouncedOnSelectRow.cancel)) return;
debouncedOnSelectRow.cancel();
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
};
}, [debouncedOnSelectRow, shouldDebounceRowSelect]);

/** Focuses the text input when the component mounts. If `props.shouldDelayFocus` is true, we wait for the animation to finish */
useEffect(() => {
if (shouldShowTextInput) {
Expand Down
3 changes: 3 additions & 0 deletions src/components/SelectionList/selectionListPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ const propTypes = {
/** Callback to fire when a row is pressed */
onSelectRow: PropTypes.func.isRequired,

/** Boolean to indicate if onSelectRow should be debounced */
shouldDebounceRowSelect: PropTypes.bool,
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved

/** Callback to fire when "Select All" checkbox is pressed. Only use along with `canSelectMultiple` */
onSelectAll: PropTypes.func,

Expand Down
1 change: 1 addition & 0 deletions src/pages/NewChatPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ function NewChatPage(props) {
selectedOptions={selectedOptions}
value={searchTerm}
onSelectRow={(option) => (props.isGroupChat ? toggleOption(option) : createChat(option))}
shouldDebounceRowSelect={!props.isGroupChat}
onChangeText={setSearchTerm}
headerMessage={headerMessage}
boldStyle
Expand Down
1 change: 1 addition & 0 deletions src/pages/ReportParticipantsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ function ReportParticipantsPage(props) {
onSelectRow={(option) => {
Navigation.navigate(ROUTES.getProfileRoute(option.accountID));
}}
shouldDebounceRowSelect
hideSectionHeaders
showTitleTooltip
showScrollIndicator
Expand Down
1 change: 1 addition & 0 deletions src/pages/SearchPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ class SearchPage extends Component {
sections={sections}
value={this.state.searchValue}
onSelectRow={this.selectReport}
shouldDebounceRowSelect
onChangeText={this.onChangeText}
headerMessage={headerMessage}
hideSectionHeaders
Expand Down
1 change: 1 addition & 0 deletions src/pages/home/report/ReactionList/BaseReactionList.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ function BaseReactionList(props) {
props.onClose();
Navigation.navigate(ROUTES.getProfileRoute(item.accountID));
}}
shouldDebounceRowSelect
option={{
text: Str.removeSMSDomain(item.displayName),
alternateText: Str.removeSMSDomain(item.login || ''),
Expand Down
1 change: 1 addition & 0 deletions src/pages/iou/IOUCurrencySelection.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ function IOUCurrencySelection(props) {
<OptionsSelector
sections={sections}
onSelectRow={confirmCurrencySelection}
shouldDebounceRowSelect
value={searchValue}
onChangeText={setSearchValue}
textInputLabel={translate('common.search')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ class MoneyRequestParticipantsSelector extends Component {
sections={this.getSections()}
value={this.state.searchTerm}
onSelectRow={this.addSingleParticipant}
shouldDebounceRowSelect
onChangeText={this.updateOptionsWithSearchTerm}
headerMessage={headerMessage}
textInputLabel={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')}
Expand Down
1 change: 1 addition & 0 deletions src/pages/settings/Preferences/LanguagePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function LanguagePage(props) {
<SelectionList
sections={[{data: localesToLanguages}]}
onSelectRow={(language) => App.setLocaleAndNavigate(language.value)}
shouldDebounceRowSelect
initiallyFocusedOptionKey={_.find(localesToLanguages, (locale) => locale.isSelected).keyForList}
/>
</ScreenWrapper>
Expand Down
1 change: 1 addition & 0 deletions src/pages/settings/Preferences/PriorityModePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function PriorityModePage(props) {
<SelectionList
sections={[{data: priorityModes}]}
onSelectRow={updateMode}
shouldDebounceRowSelect
initiallyFocusedOptionKey={_.find(priorityModes, (mode) => mode.isSelected).keyForList}
/>
</ScreenWrapper>
Expand Down
10 changes: 7 additions & 3 deletions src/pages/settings/Profile/PronounsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,12 @@ function PronounsPage(props) {
/**
* @param {Object} selectedPronouns
*/
const updatePronouns = (selectedPronouns) => {
PersonalDetails.updatePronouns(selectedPronouns.keyForList === initiallyFocusedOption.keyForList ? '' : lodashGet(selectedPronouns, 'value', ''));
};
const updatePronouns = useCallback(
(selectedPronouns) => {
PersonalDetails.updatePronouns(selectedPronouns.keyForList === initiallyFocusedOption.keyForList ? '' : lodashGet(selectedPronouns, 'value', ''));
},
[initiallyFocusedOption],
huzaifa-99 marked this conversation as resolved.
Show resolved Hide resolved
);

/**
* Pronouns list filtered by searchValue needed for the OptionsSelector.
Expand Down Expand Up @@ -111,6 +114,7 @@ function PronounsPage(props) {
textInputValue={searchValue}
sections={[{data: filteredPronounsList, indexOffset: 0}]}
onSelectRow={updatePronouns}
shouldDebounceRowSelect
onChangeText={onChangeText}
initiallyFocusedOptionKey={initiallyFocusedOption.keyForList}
/>
Expand Down
1 change: 1 addition & 0 deletions src/pages/settings/Profile/TimezoneSelectPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ function TimezoneSelectPage(props) {
textInputValue={timezoneInputText}
onChangeText={filterShownTimezones}
onSelectRow={saveSelectedTimezone}
shouldDebounceRowSelect
sections={[{data: timezoneOptions, indexOffset: 0, isDisabled: timezone.automatic}]}
initiallyFocusedOptionKey={_.get(_.filter(timezoneOptions, (tz) => tz.text === timezone.selected)[0], 'keyForList')}
/>
Expand Down
1 change: 1 addition & 0 deletions src/pages/settings/Report/NotificationPreferencePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ function NotificationPreferencePage(props) {
<OptionsList
sections={[{data: notificationPreferenceOptions}]}
onSelectRow={(option) => Report.updateNotificationPreferenceAndNavigate(props.report.reportID, props.report.notificationPreference, option.value)}
shouldDebounceRowSelect
hideSectionHeaders
optionHoveredStyle={{
...styles.hoveredComponentBG,
Expand Down
1 change: 1 addition & 0 deletions src/pages/settings/Report/WriteCapabilityPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ function WriteCapabilityPage(props) {
<OptionsList
sections={[{data: writeCapabilityOptions}]}
onSelectRow={(option) => Report.updateWriteCapabilityAndNavigate(props.report, option.value)}
shouldDebounceRowSelect
hideSectionHeaders
optionHoveredStyle={{
...styles.hoveredComponentBG,
Expand Down
1 change: 1 addition & 0 deletions src/pages/tasks/TaskAssigneeSelectorModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ function TaskAssigneeSelectorModal(props) {
sections={sections}
value={searchValue}
onSelectRow={selectReport}
shouldDebounceRowSelect
onChangeText={onChangeText}
headerMessage={headerMessage}
showTitleTooltip
Expand Down
1 change: 1 addition & 0 deletions src/pages/tasks/TaskShareDestinationSelectorModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ function TaskShareDestinationSelectorModal(props) {
sections={sections}
value={searchValue}
onSelectRow={selectReport}
shouldDebounceRowSelect
onChangeText={onChangeText}
headerMessage={headerMessage}
hideSection
Expand Down
Loading