Skip to content

Commit

Permalink
Merge pull request #41706 from Expensify/cmartins-addSearchPagination
Browse files Browse the repository at this point in the history
Add pagination to Search page
  • Loading branch information
srikarparsi authored May 16, 2024
2 parents 5900fea + 0677a8a commit e8ae3c5
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 9 deletions.
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

0 comments on commit e8ae3c5

Please sign in to comment.