efbbba6958
Closes 605 "Add pagination support to channel search"
## Previous Attempt
The previous attempt (69de63c4
) didn't work because the wunderbar is part of the list component, so it is unmounted when we switch between the Normal and Filtered list, causing it to lose focus while typing.
Also, creating another full-blown ClaimList* component is really redundant (we should be consolidating instead).
## Approach
ClaimListDiscover recently added a new `subSection`, so we can place the filtered `ClaimList` here without causing the wunderbar to unmount.
Wrapped the "lighthouse search with channel_id" into `searchResults.jsx` for now as a quick and isolated solution. When we refactor ClaimList*, we can then consider incorporating into `doSearch`.
92 lines
2.8 KiB
JavaScript
92 lines
2.8 KiB
JavaScript
// @flow
|
|
import React from 'react';
|
|
import ClaimList from 'component/claimList';
|
|
import { DEBOUNCE_WAIT_DURATION_MS, SEARCH_PAGE_SIZE } from 'constants/search';
|
|
import { lighthouse } from 'redux/actions/search';
|
|
|
|
type Props = {
|
|
searchQuery: string,
|
|
claimId: ?string,
|
|
showMature: ?boolean,
|
|
tileLayout: boolean,
|
|
onResults?: (results: ?Array<string>) => void,
|
|
doResolveUris: (Array<string>, boolean) => void,
|
|
};
|
|
|
|
export function SearchResults(props: Props) {
|
|
const { searchQuery, claimId, showMature, tileLayout, onResults, doResolveUris } = props;
|
|
|
|
const [page, setPage] = React.useState(1);
|
|
const [searchResults, setSearchResults] = React.useState(undefined);
|
|
const [isSearching, setIsSearching] = React.useState(false);
|
|
const noMoreResults = React.useRef(false);
|
|
|
|
React.useEffect(() => {
|
|
setPage(1);
|
|
}, [searchQuery]);
|
|
|
|
React.useEffect(() => {
|
|
if (onResults) {
|
|
onResults(searchResults);
|
|
}
|
|
}, [searchResults, onResults]);
|
|
|
|
React.useEffect(() => {
|
|
const timer = setTimeout(() => {
|
|
if (searchQuery.trim().length < 3 || !claimId) {
|
|
return setSearchResults(null);
|
|
}
|
|
|
|
setIsSearching(true);
|
|
|
|
lighthouse
|
|
.search(
|
|
`from=${SEARCH_PAGE_SIZE * (page - 1)}` +
|
|
`&s=${encodeURIComponent(searchQuery)}` +
|
|
`&channel_id=${encodeURIComponent(claimId)}` +
|
|
`&nsfw=${showMature ? 'true' : 'false'}` +
|
|
`&size=${SEARCH_PAGE_SIZE}`
|
|
)
|
|
.then(({ body: results }) => {
|
|
const urls = results.map(({ name, claimId }) => {
|
|
return `lbry://${name}#${claimId}`;
|
|
});
|
|
|
|
// Batch-resolve the urls before calling 'setSearchResults', as the
|
|
// latter will immediately cause the tiles to resolve, ending up
|
|
// calling doResolveUri one by one before the batched one.
|
|
doResolveUris(urls, true);
|
|
|
|
// De-dup (LH will return some duplicates) and concat results
|
|
setSearchResults((prev) => (page === 1 ? urls : Array.from(new Set((prev || []).concat(urls)))));
|
|
noMoreResults.current = !urls || urls.length < SEARCH_PAGE_SIZE;
|
|
})
|
|
.catch(() => {
|
|
setPage(1);
|
|
setSearchResults(null);
|
|
noMoreResults.current = false;
|
|
})
|
|
.finally(() => {
|
|
setIsSearching(false);
|
|
});
|
|
}, DEBOUNCE_WAIT_DURATION_MS);
|
|
|
|
return () => clearTimeout(timer);
|
|
}, [searchQuery, claimId, page, showMature, doResolveUris]);
|
|
|
|
if (!searchResults) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<ClaimList
|
|
uris={searchResults}
|
|
loading={isSearching}
|
|
onScrollBottom={() => setPage((prev) => (noMoreResults.current ? prev : prev + 1))}
|
|
page={page}
|
|
pageSize={SEARCH_PAGE_SIZE}
|
|
tileLayout={tileLayout}
|
|
useLoadingSpinner
|
|
/>
|
|
);
|
|
}
|