79 lines
2.8 KiB
TypeScript
79 lines
2.8 KiB
TypeScript
|
|
import { useEffect, useState } from 'react'
|
||
|
|
import type { BackendArt } from '../../functions/BackendTypes'
|
||
|
|
import { BackendDebounce, BackendFetch } from '../../functions/Backend'
|
||
|
|
import { routeBackURI, setTitle } from '../../functions/Route'
|
||
|
|
import { useScrollRoot } from '../../functions/Context'
|
||
|
|
|
||
|
|
import HeaderMessage from '../layout/HeaderMessage'
|
||
|
|
import HeaderLoading from '../layout/HeaderLoading'
|
||
|
|
import LayoutBrowser from '../layout/LayoutBrowser'
|
||
|
|
import FooterLoading from '../layout/FooterLoading'
|
||
|
|
import FooterError from '../layout/FooterError'
|
||
|
|
import FooterText from '../layout/FooterText'
|
||
|
|
import InputBack from '../inputs/Back'
|
||
|
|
|
||
|
|
export default function ViewSearch() {
|
||
|
|
const scrollRoot = useScrollRoot()
|
||
|
|
const [error, setError] = useState<string>()
|
||
|
|
const [isFetching, setFetching] = useState(false)
|
||
|
|
const [isLoading, setLoading] = useState(false)
|
||
|
|
const [isBottom, setBottom] = useState(false)
|
||
|
|
const [items, setItems] = useState<BackendArt[]>([])
|
||
|
|
|
||
|
|
const params = new URLSearchParams(window.location.search)
|
||
|
|
const query = params.getAll('tag')
|
||
|
|
const limit = 30
|
||
|
|
|
||
|
|
async function loadMore() {
|
||
|
|
if (query.length === 0) {
|
||
|
|
setError('Enter tags to begin search.')
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isFetching || isBottom) return
|
||
|
|
setFetching(true)
|
||
|
|
setLoading(true)
|
||
|
|
try {
|
||
|
|
const resp = await BackendFetch<BackendArt[]>(
|
||
|
|
`/art/search?limit=${limit}&after=${items.at(-1)?.id}` + query.map((q) => `&tag=${q}`).join(''),
|
||
|
|
)
|
||
|
|
if (!resp.success) {
|
||
|
|
const d = BackendDebounce(resp, 1)
|
||
|
|
if (!d.ratelimit) {
|
||
|
|
setError(resp.error)
|
||
|
|
setLoading(false)
|
||
|
|
}
|
||
|
|
await d.sleep
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if (resp.json.length < limit) setBottom(true)
|
||
|
|
setItems((prev) => [...prev, ...resp.json])
|
||
|
|
setError(undefined)
|
||
|
|
setLoading(false)
|
||
|
|
} finally {
|
||
|
|
setFetching(false)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (!scrollRoot) return
|
||
|
|
if (scrollRoot.scrollHeight <= scrollRoot.clientHeight) {
|
||
|
|
loadMore()
|
||
|
|
}
|
||
|
|
}, [items, scrollRoot])
|
||
|
|
|
||
|
|
setTitle(`Search (${query.join(', ')})`)
|
||
|
|
return (
|
||
|
|
<>
|
||
|
|
<HeaderMessage label={`View: Search <Sort: Latest> <TAGS: ${query.length ? query.join(', ') : 'NONE'}>`} />
|
||
|
|
{routeBackURI()?.startsWith('/art/') && <InputBack />}
|
||
|
|
<LayoutBrowser position={0} items={items} onEndReached={loadMore} />
|
||
|
|
|
||
|
|
{error && <FooterError reason={error} />}
|
||
|
|
{isBottom && <FooterText label="- No More Results -" />}
|
||
|
|
{isLoading && (items.length ? <FooterLoading reason={undefined} /> : <HeaderLoading reason={undefined} />)}
|
||
|
|
</>
|
||
|
|
)
|
||
|
|
}
|