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

Add pagination to Search page #41706

Merged
merged 15 commits into from
May 16, 2024
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4767,6 +4767,7 @@ const CONST = {
DISTANCE: 'distance',
},

SEARCH_RESULTS_PAGE_SIZE: 50,
SEARCH_BOTTOM_TAB_URL: '/Search_Bottom_Tab',

SEARCH_DATA_TYPES: {
Expand Down
28 changes: 24 additions & 4 deletions src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as SearchUtils from '@libs/SearchUtils';
import Navigation from '@navigation/Navigation';
import EmptySearchView from '@pages/Search/EmptySearchView';
import useCustomBackHandler from '@pages/Search/useCustomBackHandler';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
Expand All @@ -35,14 +36,15 @@ function Search({query, policyIDs}: SearchProps) {
return;
}

SearchActions.search(hash, query, policyIDs);
SearchActions.search(hash, query, 0, policyIDs);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hash, isOffline]);

const isLoading = (!isOffline && isLoadingOnyxValue(searchResultsMeta)) || searchResults?.data === undefined;
const shouldShowEmptyState = !isLoading && isEmptyObject(searchResults?.data);
const isLoadingInitialItems = (!isOffline && isLoadingOnyxValue(searchResultsMeta)) || searchResults?.data === undefined;
const isLoadingMoreItems = !isLoadingInitialItems && searchResults?.search?.isLoading;
const shouldShowEmptyState = !isLoadingInitialItems && isEmptyObject(searchResults?.data);

if (isLoading) {
if (isLoadingInitialItems) {
return <TableListItemSkeleton shouldAnimate />;
}

Expand All @@ -58,6 +60,14 @@ function Search({query, policyIDs}: SearchProps) {
Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(query, reportID));
};

const fetchMoreResults = () => {
if (!searchResults?.search?.hasMoreResults || isLoadingInitialItems || isLoadingMoreItems) {
return;
}
const currentOffset = searchResults?.search?.offset ?? 0;
SearchActions.search(hash, query, currentOffset + CONST.SEARCH_RESULTS_PAGE_SIZE);
};

const type = SearchUtils.getSearchType(searchResults?.search);

if (type === undefined) {
Expand All @@ -80,6 +90,16 @@ function Search({query, policyIDs}: SearchProps) {
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
containerStyle={[styles.pv0]}
showScrollIndicator={false}
onEndReachedThreshold={0.75}
onEndReached={fetchMoreResults}
listFooterContent={
isLoadingMoreItems ? (
<TableListItemSkeleton
shouldAnimate
fixedNumItems={5}
/>
) : undefined
}
/>
);
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ function BaseSelectionList<TItem extends ListItem>(
sectionTitleStyles,
textInputAutoFocus = true,
shouldTextInputInterceptSwipe = false,
onEndReached = () => {},
onEndReachedThreshold,
}: BaseSelectionListProps<TItem>,
ref: ForwardedRef<SelectionListHandle>,
) {
Expand Down Expand Up @@ -618,6 +620,8 @@ function BaseSelectionList<TItem extends ListItem>(
onLayout={onSectionListLayout}
style={(!maxToRenderPerBatch || (shouldHideListOnInitialRender && isInitialSectionListRender)) && styles.opacity0}
ListFooterComponent={listFooterContent ?? ShowMoreButtonInstance}
onEndReached={onEndReached}
onEndReachedThreshold={onEndReachedThreshold}
/>
{children}
</>
Expand Down
11 changes: 11 additions & 0 deletions src/components/SelectionList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,17 @@ type BaseSelectionListProps<TItem extends ListItem> = Partial<ChildrenProps> & {
* When false, the list will render immediately and scroll to the bottom which works great for small lists.
*/
shouldHideListOnInitialRender?: boolean;

/** Called once when the scroll position gets within onEndReachedThreshold of the rendered content. */
onEndReached?: () => void;

/**
* How far from the end (in units of visible length of the list) the bottom edge of the
* list must be from the end of the content to trigger the `onEndReached` callback.
* Thus a value of 0.5 will trigger `onEndReached` when the end of the content is
* within half the visible length of the list.
*/
onEndReachedThreshold?: number;
} & TRightHandSideComponent<TItem>;

type SelectionListHandle = {
Expand Down
9 changes: 7 additions & 2 deletions src/components/Skeletons/ItemListSkeletonView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import CONST from '@src/CONST';
type ListItemSkeletonProps = {
shouldAnimate?: boolean;
renderSkeletonItem: (args: {itemIndex: number}) => React.ReactNode;
fixedNumItems?: number;
};

function ItemListSkeletonView({shouldAnimate = true, renderSkeletonItem}: ListItemSkeletonProps) {
function ItemListSkeletonView({shouldAnimate = true, renderSkeletonItem, fixedNumItems}: ListItemSkeletonProps) {
const theme = useTheme();
const themeStyles = useThemeStyles();

const [numItems, setNumItems] = useState(0);
const [numItems, setNumItems] = useState(fixedNumItems ?? 0);
const skeletonViewItems = useMemo(() => {
const items = [];
for (let i = 0; i < numItems; i++) {
Expand All @@ -38,6 +39,10 @@ function ItemListSkeletonView({shouldAnimate = true, renderSkeletonItem}: ListIt
<View
style={[themeStyles.flex1, themeStyles.overflowHidden]}
onLayout={(event) => {
if (fixedNumItems) {
return;
}

const newNumItems = Math.ceil(event.nativeEvent.layout.height / CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT);
if (newNumItems === numItems) {
return;
Expand Down
4 changes: 3 additions & 1 deletion src/components/Skeletons/TableListItemSkeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ import ItemListSkeletonView from './ItemListSkeletonView';

type TableListItemSkeletonProps = {
shouldAnimate?: boolean;
fixedNumItems?: number;
};

const barHeight = '10';
const shortBarWidth = '40';
const longBarWidth = '120';

function TableListItemSkeleton({shouldAnimate = true}: TableListItemSkeletonProps) {
function TableListItemSkeleton({shouldAnimate = true, fixedNumItems}: TableListItemSkeletonProps) {
return (
<ItemListSkeletonView
shouldAnimate={shouldAnimate}
fixedNumItems={fixedNumItems}
renderSkeletonItem={() => (
<>
<Rect
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ type SearchParams = {
query: string;
policyIDs?: string;
hash: number;
offset: number;
};

export default SearchParams;
31 changes: 29 additions & 2 deletions src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,35 @@
import Onyx from 'react-native-onyx';
import type {OnyxUpdate} from 'react-native-onyx';
import * as API from '@libs/API';
import {READ_COMMANDS} from '@libs/API/types';
import ONYXKEYS from '@src/ONYXKEYS';

function search(hash: number, query: string, policyIDs?: string) {
API.read(READ_COMMANDS.SEARCH, {hash, query, policyIDs});
function search(hash: number, query: string, offset = 0, policyIDs?: string) {
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
value: {
search: {
isLoading: true,
},
},
},
];

const finallyData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
value: {
search: {
isLoading: false,
},
},
},
];

API.read(READ_COMMANDS.SEARCH, {hash, query, offset, policyIDs}, {optimisticData, finallyData});
}

export {
Expand Down
1 change: 1 addition & 0 deletions src/types/onyx/SearchResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type SearchResultsInfo = {
offset: number;
type: string;
hasMoreResults: boolean;
isLoading: boolean;
};

type SearchPersonalDetails = {
Expand Down
Loading