Pagination for search results

This code is really hacky, may need to revisit one day
This commit is contained in:
Lim Chee Aun 2023-06-28 23:36:37 +08:00
parent 1a835c32ab
commit 86dd2f3f5c

View file

@ -1,7 +1,14 @@
import './search.css'; import './search.css';
import { forwardRef } from 'preact/compat'; import { forwardRef } from 'preact/compat';
import { useEffect, useImperativeHandle, useRef, useState } from 'preact/hooks'; import {
useEffect,
useImperativeHandle,
useLayoutEffect,
useRef,
useState,
} from 'preact/hooks';
import { InView } from 'react-intersection-observer';
import { useParams, useSearchParams } from 'react-router-dom'; import { useParams, useSearchParams } from 'react-router-dom';
import AccountBlock from '../components/account-block'; import AccountBlock from '../components/account-block';
@ -13,6 +20,9 @@ import Status from '../components/status';
import { api } from '../utils/api'; import { api } from '../utils/api';
import useTitle from '../utils/useTitle'; import useTitle from '../utils/useTitle';
const SHORT_LIMIT = 5;
const LIMIT = 40;
function Search(props) { function Search(props) {
const params = useParams(); const params = useParams();
const { masto, instance, authenticated } = api({ const { masto, instance, authenticated } = api({
@ -40,35 +50,78 @@ function Search(props) {
`/search`, `/search`,
); );
const [showMore, setShowMore] = useState(false);
const offsetRef = useRef(0);
useEffect(() => {
offsetRef.current = 0;
}, [type]);
const scrollableRef = useRef();
useLayoutEffect(() => {
scrollableRef.current?.scrollTo?.(0, 0);
}, [q, type]);
const [statusResults, setStatusResults] = useState([]); const [statusResults, setStatusResults] = useState([]);
const [accountResults, setAccountResults] = useState([]); const [accountResults, setAccountResults] = useState([]);
const [hashtagResults, setHashtagResults] = useState([]); const [hashtagResults, setHashtagResults] = useState([]);
function loadResults(firstLoad) {
setUiState('loading');
if (firstLoad && !type) {
setStatusResults(statusResults.slice(0, SHORT_LIMIT));
setAccountResults(accountResults.slice(0, SHORT_LIMIT));
setHashtagResults(hashtagResults.slice(0, SHORT_LIMIT));
}
(async () => {
const params = {
q,
resolve: authenticated,
limit: SHORT_LIMIT,
};
if (type) {
params.limit = LIMIT;
params.type = type;
params.offset = offsetRef.current;
}
try {
const results = await masto.v2.search(params);
console.log(results);
if (type) {
if (type === 'statuses') {
setStatusResults((prev) => [...prev, ...results.statuses]);
} else if (type === 'accounts') {
setAccountResults((prev) => [...prev, ...results.accounts]);
} else if (type === 'hashtags') {
setHashtagResults((prev) => [...prev, ...results.hashtags]);
}
offsetRef.current = offsetRef.current + LIMIT;
setShowMore(!!results[type]?.length);
} else {
setStatusResults(results.statuses);
setAccountResults(results.accounts);
setHashtagResults(results.hashtags);
}
setUiState('default');
} catch (err) {
console.error(err);
setUiState('error');
}
})();
}
useEffect(() => { useEffect(() => {
// searchFieldRef.current?.focus?.(); // searchFieldRef.current?.focus?.();
// searchFormRef.current?.focus?.(); // searchFormRef.current?.focus?.();
if (q) { if (q) {
// searchFieldRef.current.value = q; // searchFieldRef.current.value = q;
searchFormRef.current?.setValue?.(q); searchFormRef.current?.setValue?.(q);
loadResults(true);
setUiState('loading');
(async () => {
const results = await masto.v2.search({
q,
limit: type ? 40 : 5,
resolve: authenticated,
type,
});
console.log(results);
setStatusResults(results.statuses);
setAccountResults(results.accounts);
setHashtagResults(results.hashtags);
setUiState('default');
})();
} }
}, [q, type, instance]); }, [q, type, instance]);
return ( return (
<div id="search-page" class="deck-container"> <div id="search-page" class="deck-container" ref={scrollableRef}>
<div class="timeline-deck deck"> <div class="timeline-deck deck">
<header> <header>
<div class="header-grid"> <div class="header-grid">
@ -110,7 +163,7 @@ function Search(props) {
))} ))}
</div> </div>
)} )}
{!!q && uiState !== 'loading' ? ( {!!q ? (
<> <>
{(!type || type === 'accounts') && ( {(!type || type === 'accounts') && (
<> <>
@ -140,6 +193,10 @@ function Search(props) {
</div> </div>
)} )}
</> </>
) : uiState === 'loading' ? (
<p class="ui-state">
<Loader abrupt />
</p>
) : ( ) : (
<p class="ui-state">No accounts found.</p> <p class="ui-state">No accounts found.</p>
)} )}
@ -179,6 +236,10 @@ function Search(props) {
</div> </div>
)} )}
</> </>
) : uiState === 'loading' ? (
<p class="ui-state">
<Loader abrupt />
</p>
) : ( ) : (
<p class="ui-state">No hashtags found.</p> <p class="ui-state">No hashtags found.</p>
)} )}
@ -218,11 +279,48 @@ function Search(props) {
</div> </div>
)} )}
</> </>
) : uiState === 'loading' ? (
<p class="ui-state">
<Loader abrupt />
</p>
) : ( ) : (
<p class="ui-state">No posts found.</p> <p class="ui-state">No posts found.</p>
)} )}
</> </>
)} )}
{!!type &&
(uiState === 'default' ? (
showMore ? (
<InView
onChange={(inView) => {
if (inView) {
loadResults();
}
}}
>
<button
type="button"
class="plain block"
onClick={() => loadResults()}
style={{ marginBlockEnd: '6em' }}
>
Show more&hellip;
</button>
</InView>
) : (
<p class="ui-state insignificant">The end.</p>
)
) : (
!!(
hashtagResults.length ||
accountResults.length ||
statusResults.length
) && (
<p class="ui-state">
<Loader abrupt />
</p>
)
))}
</> </>
) : uiState === 'loading' ? ( ) : uiState === 'loading' ? (
<p class="ui-state"> <p class="ui-state">