mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-02 14:16:39 +01:00
Pagination for search results
This code is really hacky, may need to revisit one day
This commit is contained in:
parent
1a835c32ab
commit
86dd2f3f5c
1 changed files with 116 additions and 18 deletions
|
@ -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…
|
||||||
|
</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">
|
||||||
|
|
Loading…
Reference in a new issue