diff --git a/src/pages/search.jsx b/src/pages/search.jsx index 5b451e3b..24b52faa 100644 --- a/src/pages/search.jsx +++ b/src/pages/search.jsx @@ -1,7 +1,14 @@ import './search.css'; 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 AccountBlock from '../components/account-block'; @@ -13,6 +20,9 @@ import Status from '../components/status'; import { api } from '../utils/api'; import useTitle from '../utils/useTitle'; +const SHORT_LIMIT = 5; +const LIMIT = 40; + function Search(props) { const params = useParams(); const { masto, instance, authenticated } = api({ @@ -40,35 +50,78 @@ function Search(props) { `/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 [accountResults, setAccountResults] = 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(() => { // searchFieldRef.current?.focus?.(); // searchFormRef.current?.focus?.(); if (q) { // searchFieldRef.current.value = q; searchFormRef.current?.setValue?.(q); - - 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'); - })(); + loadResults(true); } }, [q, type, instance]); return ( -
+
No accounts found.
)} @@ -179,6 +236,10 @@ function Search(props) {
+
No hashtags found.
)} @@ -218,11 +279,48 @@ function Search(props) {
+
No posts found.
)} > )} + {!!type && + (uiState === 'default' ? ( + showMore ? ( +The end.
+ ) + ) : ( + !!( + hashtagResults.length || + accountResults.length || + statusResults.length + ) && ( +
+