-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
[TS migration] Migrate SearchPage #34655
Changes from 21 commits
5ef38a6
a1bf42a
0311d75
043d7a6
3160959
4b868f6
0b6deab
4885b59
000eb11
221bed7
be14da6
751c974
db8c05f
0b0ba7a
33a370f
5802e70
fe38639
8ef09aa
15ba310
1532321
2cd3918
dbaabaf
efa4b99
4430458
5c88c77
333700b
75f3770
2b01f41
74061d7
dcec860
44ddd40
7ba0847
f6e6552
57fcb34
bef7c71
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -14,7 +14,7 @@ import useTackInputFocus from '@hooks/useTackInputFocus'; | |||||
import useThemeStyles from '@hooks/useThemeStyles'; | ||||||
import useWindowDimensions from '@hooks/useWindowDimensions'; | ||||||
import * as Browser from '@libs/Browser'; | ||||||
import type {RootStackParamList} from '@libs/Navigation/types'; | ||||||
import type {RootStackParamList, SearchNavigatorParamList} from '@libs/Navigation/types'; | ||||||
import toggleTestToolsModal from '@userActions/TestTool'; | ||||||
import CONST from '@src/CONST'; | ||||||
import CustomDevMenu from './CustomDevMenu'; | ||||||
|
@@ -89,7 +89,7 @@ type ScreenWrapperProps = { | |||||
* | ||||||
* This is required because transitionEnd event doesn't trigger in the testing environment. | ||||||
*/ | ||||||
navigation?: StackNavigationProp<RootStackParamList>; | ||||||
navigation?: StackNavigationProp<RootStackParamList> | StackNavigationProp<SearchNavigatorParamList>; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
/** Whether to show offline indicator on wide screens */ | ||||||
shouldShowOfflineIndicatorInWideScreen?: boolean; | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -314,7 +314,7 @@ | |||||
|
||||||
const onSectionListLayout = useCallback( | ||||||
(nativeEvent: LayoutChangeEvent) => { | ||||||
onLayout?.(nativeEvent); | ||||||
scrollToFocusedIndexOnFirstRender(nativeEvent); | ||||||
}, | ||||||
[onLayout, scrollToFocusedIndexOnFirstRender], | ||||||
|
@@ -457,7 +457,7 @@ | |||||
getItemLayout={getItemLayout} | ||||||
onScroll={onScroll} | ||||||
onScrollBeginDrag={onScrollBeginDrag} | ||||||
keyExtractor={(item) => item.keyForList} | ||||||
keyExtractor={(item) => item.keyForList ?? ''} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
How about falling back to using the index, like React does? |
||||||
extraData={focusedIndex} | ||||||
indicatorStyle="white" | ||||||
keyboardShouldPersistTaps="always" | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import type {ReactElement, ReactNode} from 'react'; | ||
import type {GestureResponderEvent, InputModeOptions, LayoutChangeEvent, SectionListData, StyleProp, TextStyle, ViewStyle} from 'react-native'; | ||
import type {GestureResponderEvent, InputModeOptions, SectionListData, StyleProp, TextStyle, ViewStyle} from 'react-native'; | ||
import type CONST from '@src/CONST'; | ||
import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; | ||
import type ChildrenProps from '@src/types/utils/ChildrenProps'; | ||
|
||
|
@@ -14,7 +15,7 @@ type CommonListItemProps<TItem> = { | |
alternateTextStyles?: StyleProp<TextStyle>; | ||
|
||
/** Whether this item is disabled */ | ||
isDisabled?: boolean; | ||
isDisabled?: boolean | null; | ||
|
||
/** Whether this item should show Tooltip */ | ||
showTooltip: boolean; | ||
|
@@ -34,25 +35,25 @@ type CommonListItemProps<TItem> = { | |
|
||
type User = { | ||
/** Text to display */ | ||
text: string; | ||
text?: string; | ||
|
||
/** Alternate text to display */ | ||
alternateText?: string; | ||
alternateText?: string | null; | ||
|
||
/** Key used internally by React */ | ||
keyForList: string; | ||
keyForList?: string | null; | ||
|
||
/** Whether this option is selected */ | ||
isSelected?: boolean; | ||
|
||
/** Whether this option is disabled for selection */ | ||
isDisabled?: boolean; | ||
isDisabled?: boolean | null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi, my vacation has just ended. I can review this PR again tomorrow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, I've reverted all the changes made on SelectionList and updated SearchPage. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, I think you're right. In addition, I'm not sure if using
If removing the BTW, maybe we can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ntdiary I'm afraid with this changed we can't avoid the assertion usage. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@ruben-rebelo, thank you very much for your attempt, then let's revert to the previous revision and let |
||
|
||
/** User accountID */ | ||
accountID?: number; | ||
accountID?: number | null; | ||
|
||
/** User login */ | ||
login?: string; | ||
login?: string | null; | ||
|
||
/** Element to show on the right side of the item */ | ||
rightElement?: ReactNode; | ||
|
@@ -73,6 +74,9 @@ type User = { | |
|
||
/** Represents the index of the option within the section it came from */ | ||
index?: number; | ||
|
||
/** ID of the report */ | ||
reportID?: string; | ||
}; | ||
|
||
type UserListItemProps = CommonListItemProps<User> & { | ||
|
@@ -85,19 +89,19 @@ type UserListItemProps = CommonListItemProps<User> & { | |
|
||
type RadioItem = { | ||
/** Text to display */ | ||
text: string; | ||
text?: string; | ||
|
||
/** Alternate text to display */ | ||
alternateText?: string; | ||
alternateText?: string | null; | ||
|
||
/** Key used internally by React */ | ||
keyForList: string; | ||
keyForList?: string | null; | ||
|
||
/** Whether this option is selected */ | ||
isSelected?: boolean; | ||
|
||
/** Whether this option is disabled for selection */ | ||
isDisabled?: boolean; | ||
isDisabled?: boolean | null; | ||
|
||
/** Represents the index of the section it came from */ | ||
sectionIndex?: number; | ||
|
@@ -114,7 +118,7 @@ type RadioListItemProps = CommonListItemProps<RadioItem> & { | |
type BaseListItemProps<TItem extends User | RadioItem> = CommonListItemProps<TItem> & { | ||
item: TItem; | ||
shouldPreventDefaultFocusOnSelectRow?: boolean; | ||
keyForList?: string; | ||
keyForList?: string | null; | ||
}; | ||
|
||
type Section<TItem extends User | RadioItem> = { | ||
|
@@ -136,7 +140,7 @@ type Section<TItem extends User | RadioItem> = { | |
|
||
type BaseSelectionListProps<TItem extends User | RadioItem> = Partial<ChildrenProps> & { | ||
/** Sections for the section list */ | ||
sections: Array<SectionListData<TItem, Section<TItem>>>; | ||
sections: Array<SectionListData<TItem, Section<TItem>>> | typeof CONST.EMPTY_ARRAY; | ||
|
||
/** Whether this is a multi-select list */ | ||
canSelectMultiple?: boolean; | ||
|
@@ -237,8 +241,11 @@ type BaseSelectionListProps<TItem extends User | RadioItem> = Partial<ChildrenPr | |
/** Whether to show the loading indicator for new options */ | ||
isLoadingNewOptions?: boolean; | ||
|
||
/** Fired when the list is displayed with the items */ | ||
onLayout?: (event: LayoutChangeEvent) => void; | ||
/** Custom callback when Selection List layout changes */ | ||
onLayout?: () => void; | ||
|
||
/** Whether to auto focus the Search Input */ | ||
autoFocus?: boolean; | ||
}; | ||
|
||
type ItemLayout = { | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,6 +1,7 @@ | ||||||
import PropTypes from 'prop-types'; | ||||||
import type {StackScreenProps} from '@react-navigation/stack'; | ||||||
import React, {useEffect, useMemo, useState} from 'react'; | ||||||
import {View} from 'react-native'; | ||||||
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; | ||||||
import {withOnyx} from 'react-native-onyx'; | ||||||
import HeaderWithBackButton from '@components/HeaderWithBackButton'; | ||||||
import {usePersonalDetails} from '@components/OnyxProvider'; | ||||||
|
@@ -11,43 +12,47 @@ | |||||
import useNetwork from '@hooks/useNetwork'; | ||||||
import useThemeStyles from '@hooks/useThemeStyles'; | ||||||
import Navigation from '@libs/Navigation/Navigation'; | ||||||
import type {SearchNavigatorParamList} from '@libs/Navigation/types'; | ||||||
import * as OptionsListUtils from '@libs/OptionsListUtils'; | ||||||
import Performance from '@libs/Performance'; | ||||||
import * as ReportUtils from '@libs/ReportUtils'; | ||||||
import reportPropTypes from '@pages/reportPropTypes'; | ||||||
import * as Report from '@userActions/Report'; | ||||||
import Timing from '@userActions/Timing'; | ||||||
import CONST from '@src/CONST'; | ||||||
import ONYXKEYS from '@src/ONYXKEYS'; | ||||||
import type SCREENS from '@src/SCREENS'; | ||||||
import type * as OnyxTypes from '@src/types/onyx'; | ||||||
import SearchPageFooter from './SearchPageFooter'; | ||||||
|
||||||
const propTypes = { | ||||||
/* Onyx Props */ | ||||||
|
||||||
type SearchPageOnyxProps = { | ||||||
/** Beta features list */ | ||||||
betas: PropTypes.arrayOf(PropTypes.string), | ||||||
betas: OnyxEntry<OnyxTypes.Beta[]>; | ||||||
|
||||||
/** All reports shared with the user */ | ||||||
reports: PropTypes.objectOf(reportPropTypes), | ||||||
reports: OnyxCollection<OnyxTypes.Report>; | ||||||
|
||||||
/** Whether or not we are searching for reports on the server */ | ||||||
isSearchingForReports: PropTypes.bool, | ||||||
isSearchingForReports: OnyxEntry<boolean>; | ||||||
}; | ||||||
|
||||||
const defaultProps = { | ||||||
betas: [], | ||||||
reports: {}, | ||||||
isSearchingForReports: false, | ||||||
type SearchPageProps = SearchPageOnyxProps & StackScreenProps<SearchNavigatorParamList, typeof SCREENS.SEARCH_ROOT>; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
type SearchPageSectionItem = { | ||||||
data: ReportUtils.OptionData[]; | ||||||
shouldShow: boolean; | ||||||
indexOffset: number; | ||||||
}; | ||||||
|
||||||
type SearchPageSectionList = SearchPageSectionItem[]; | ||||||
|
||||||
const setPerformanceTimersEnd = () => { | ||||||
Timing.end(CONST.TIMING.SEARCH_RENDER); | ||||||
Performance.markEnd(CONST.TIMING.SEARCH_RENDER); | ||||||
}; | ||||||
|
||||||
const SearchPageFooterInstance = <SearchPageFooter />; | ||||||
|
||||||
function SearchPage({betas, reports, isSearchingForReports}) { | ||||||
function SearchPage({betas, reports, isSearchingForReports}: SearchPageProps) { | ||||||
const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); | ||||||
const {translate} = useLocalize(); | ||||||
const {isOffline} = useNetwork(); | ||||||
|
@@ -75,28 +80,28 @@ | |||||
} = useMemo(() => { | ||||||
if (!isScreenTransitionEnd) { | ||||||
return { | ||||||
recentReports: {}, | ||||||
personalDetails: {}, | ||||||
userToInvite: {}, | ||||||
recentReports: [], | ||||||
personalDetails: [], | ||||||
userToInvite: null, | ||||||
headerMessage: '', | ||||||
}; | ||||||
} | ||||||
const options = OptionsListUtils.getSearchOptions(reports, personalDetails, debouncedSearchValue.trim(), betas); | ||||||
const options = OptionsListUtils.getSearchOptions(reports, personalDetails, debouncedSearchValue.trim(), betas ?? []); | ||||||
const header = OptionsListUtils.getHeaderMessage(options.recentReports.length + options.personalDetails.length !== 0, Boolean(options.userToInvite), debouncedSearchValue); | ||||||
return {...options, headerMessage: header}; | ||||||
}, [debouncedSearchValue, reports, personalDetails, betas, isScreenTransitionEnd]); | ||||||
|
||||||
const sections = useMemo(() => { | ||||||
const newSections = []; | ||||||
const sections = useMemo((): SearchPageSectionList => { | ||||||
const newSections: SearchPageSectionList = []; | ||||||
let indexOffset = 0; | ||||||
|
||||||
if (recentReports.length > 0) { | ||||||
if (recentReports?.length > 0) { | ||||||
newSections.push({ | ||||||
data: recentReports, | ||||||
shouldShow: true, | ||||||
indexOffset, | ||||||
}); | ||||||
indexOffset += recentReports.length; | ||||||
indexOffset += recentReports?.length; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
if (localPersonalDetails.length > 0) { | ||||||
|
@@ -119,7 +124,7 @@ | |||||
return newSections; | ||||||
}, [localPersonalDetails, recentReports, userToInvite]); | ||||||
|
||||||
const selectReport = (option) => { | ||||||
const selectReport = (option: ReportUtils.OptionData) => { | ||||||
if (!option) { | ||||||
return; | ||||||
} | ||||||
|
@@ -128,7 +133,7 @@ | |||||
setSearchValue(''); | ||||||
Navigation.dismissModal(option.reportID); | ||||||
} else { | ||||||
Report.navigateToAndOpenReport([option.login]); | ||||||
Report.navigateToAndOpenReport(option.login ? [option.login] : []); | ||||||
} | ||||||
}; | ||||||
|
||||||
|
@@ -151,11 +156,11 @@ | |||||
onBackButtonPress={Navigation.goBack} | ||||||
/> | ||||||
<View style={[themeStyles.flex1, themeStyles.w100, safeAreaPaddingBottomStyle]}> | ||||||
<SelectionList | ||||||
<SelectionList<ReportUtils.OptionData> | ||||||
sections={didScreenTransitionEnd && isOptionsDataReady ? sections : CONST.EMPTY_ARRAY} | ||||||
textInputValue={searchValue} | ||||||
textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} | ||||||
textInputHint={offlineMessage} | ||||||
onChangeText={setSearchValue} | ||||||
headerMessage={headerMessage} | ||||||
onLayout={setPerformanceTimersEnd} | ||||||
|
@@ -163,7 +168,7 @@ | |||||
onSelectRow={selectReport} | ||||||
showLoadingPlaceholder={!didScreenTransitionEnd || !isOptionsDataReady} | ||||||
footerContent={SearchPageFooterInstance} | ||||||
isLoadingNewOptions={isSearchingForReports} | ||||||
isLoadingNewOptions={isSearchingForReports ?? undefined} | ||||||
/> | ||||||
</View> | ||||||
</> | ||||||
|
@@ -172,11 +177,9 @@ | |||||
); | ||||||
} | ||||||
|
||||||
SearchPage.propTypes = propTypes; | ||||||
SearchPage.defaultProps = defaultProps; | ||||||
SearchPage.displayName = 'SearchPage'; | ||||||
|
||||||
export default withOnyx({ | ||||||
export default withOnyx<SearchPageProps, SearchPageOnyxProps>({ | ||||||
reports: { | ||||||
key: ONYXKEYS.COLLECTION.REPORT, | ||||||
}, | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SearchPage doesn't pass
navigation
toScreenWrapper
, so we can revert this change.