import './search.css'; import { useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks'; import { useHotkeys } from 'react-hotkeys-hook'; import { InView } from 'react-intersection-observer'; import { useParams, useSearchParams } from 'react-router-dom'; import AccountBlock from '../components/account-block'; import Icon from '../components/icon'; import Link from '../components/link'; import Loader from '../components/loader'; import NavMenu from '../components/nav-menu'; import SearchForm from '../components/search-form'; 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({ instance: params.instance, }); const [uiState, setUIState] = useState('default'); const [searchParams] = useSearchParams(); const searchFormRef = useRef(); const q = props?.query || searchParams.get('q'); const type = props?.type || searchParams.get('type'); useTitle( q ? `Search: ${q}${ type ? ` (${ { statuses: 'Posts', accounts: 'Accounts', hashtags: 'Hashtags', }[type] })` : '' }` : 'Search', `/search`, ); const [showMore, setShowMore] = useState(false); const offsetRef = useRef(0); useEffect(() => { offsetRef.current = 0; }, [q, type]); const scrollableRef = useRef(); useLayoutEffect(() => { scrollableRef.current?.scrollTo?.(0, 0); }, [q, type]); const [statusResults, setStatusResults] = useState([]); const [accountResults, setAccountResults] = useState([]); const [hashtagResults, setHashtagResults] = useState([]); useEffect(() => { setStatusResults([]); setAccountResults([]); setHashtagResults([]); }, [q]); const setTypeResultsFunc = { statuses: setStatusResults, accounts: setAccountResults, hashtags: setHashtagResults, }; 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 (firstLoad) { setTypeResultsFunc[type](results[type]); const length = results[type]?.length; offsetRef.current = LIMIT; setShowMore(!!length); } else { setTypeResultsFunc[type]((prev) => [...prev, ...results[type]]); const length = results[type]?.length; offsetRef.current = offsetRef.current + LIMIT; setShowMore(!!length); } } else { setStatusResults(results.statuses); setAccountResults(results.accounts); setHashtagResults(results.hashtags); offsetRef.current = 0; setShowMore(false); } setUIState('default'); } catch (err) { console.error(err); setUIState('error'); } })(); } useEffect(() => { if (q) { searchFormRef.current?.setValue?.(q); loadResults(true); } else { searchFormRef.current?.focus?.(); } }, [q, type, instance]); useHotkeys( '/', (e) => { searchFormRef.current?.focus?.(); }, { preventDefault: true, }, ); return (
 
{!!q && (
{!!type && ( ‹ All )} {[ { label: 'Accounts', type: 'accounts', to: `/search?q=${encodeURIComponent(q)}&type=accounts`, }, { label: 'Hashtags', type: 'hashtags', to: `/search?q=${encodeURIComponent(q)}&type=hashtags`, }, { label: 'Posts', type: 'statuses', to: `/search?q=${encodeURIComponent(q)}&type=statuses`, }, ] .sort((a, b) => { if (a.type === type) return -1; if (b.type === type) return 1; return 0; }) .map((link) => ( {link.label} ))}
)} {!!q ? ( <> {(!type || type === 'accounts') && ( <> {type !== 'accounts' && (

Accounts

)} {accountResults.length > 0 ? ( <>
    {accountResults.map((account) => (
  • ))}
{type !== 'accounts' && (
See more accounts
)} ) : ( !type && (uiState === 'loading' ? (

) : (

No accounts found.

)) )} )} {(!type || type === 'hashtags') && ( <> {type !== 'hashtags' && (

Hashtags

)} {hashtagResults.length > 0 ? ( <> {type !== 'hashtags' && (
See more hashtags
)} ) : ( !type && (uiState === 'loading' ? (

) : (

No hashtags found.

)) )} )} {(!type || type === 'statuses') && ( <> {type !== 'statuses' && (

Posts

)} {statusResults.length > 0 ? ( <>
    {statusResults.map((status) => (
  • ))}
{type !== 'statuses' && (
See more posts
)} ) : ( !type && (uiState === 'loading' ? (

) : (

No posts found.

)) )} )} {!!type && (uiState === 'default' ? ( showMore ? ( { if (inView) { loadResults(); } }} > ) : (

The end.

) ) : ( uiState === 'loading' && (

) ))} ) : uiState === 'loading' ? (

) : (

Enter your search term or paste a URL above to get started.

)}
); } export default Search;