import { useEffect, useRef, useState } from "preact/hooks"; import useDebouncedCallback from "@lib/hooks/useDebouncedCallback.ts"; import { IconGhost, IconLoader2, IconSearch } from "@components/icons.tsx"; import { useEventListener } from "@lib/hooks/useEventListener.ts"; import { SearchResult } from "@lib/types.ts"; import { resources } from "@lib/resources.ts"; import { isLocalImage } from "@lib/string.ts"; import { IS_BROWSER } from "$fresh/runtime.ts"; export const RedirectSearchHandler = () => { useEventListener("keydown", (e: KeyboardEvent) => { if (e?.target?.nodeName == "INPUT") return; if ( e.key === "?" && window.location.search === "" ) { window.location.href += "?q="; } }, IS_BROWSER ? document?.body : undefined); return <>; }; const SearchResultImage = ({ src }: { src: string }) => { const imageSrc = isLocalImage(src) ? `/api/images?image=${src}&width=50&height=50` : src; return ( preview image ); }; export const SearchResultItem = ( { item, showEmoji = false }: { item: NonNullable[number]; showEmoji?: boolean; }, ) => { const doc = item.document; const resourceType = resources[doc.type]; const href = (resourceType) ? `${resourceType.link}/${doc.id}` : ""; return ( {doc?.image && } {`${showEmoji && resourceType ? resourceType.emoji : ""}`} {doc?.name} ); }; export const SearchResultList = ( { result, showEmoji }: { result: SearchResult; showEmoji?: boolean }, ) => { return (
{result?.hits ? (
{result.hits.map((hit) => ( ))}
) :
No Results
}
); }; const SearchComponent = ( { q, type }: { q: string; type?: string }, ) => { const [searchQuery, setSearchQuery] = useState(q); const [data, setData] = useState(); const [isLoading, setIsLoading] = useState(false); const inputRef = useRef(null); if ("history" in globalThis) { const u = new URL(window.location.href); if (u.searchParams.get("q") !== searchQuery) { u.searchParams.set("q", searchQuery); window.history.replaceState({}, "", u); } } const fetchData = async (query: string) => { try { setIsLoading(true); const fetchUrl = new URL(window.location.href); fetchUrl.pathname = "/api/resources"; if (query) { fetchUrl.searchParams.set("q", encodeURIComponent(query)); } else { return; } if (type) { fetchUrl.searchParams.set("type", type); } const response = await fetch(fetchUrl); const jsonData = await response.json(); setData(jsonData); setIsLoading(false); } catch (error) { console.error("Error fetching data:", error); setIsLoading(false); } }; useEffect(() => { if (inputRef.current && searchQuery?.length === 0) { inputRef.current?.focus(); } }, [inputRef.current, searchQuery]); const debouncedFetchData = useDebouncedCallback(fetchData, 500); // Debounce the fetchData function with a delay of 500ms const handleInputChange = (event: Event) => { const target = event.target as HTMLInputElement; if (target.value !== searchQuery) { setSearchQuery(target.value); debouncedFetchData(target.value); // Call the debounced fetch function with the updated search query } }; useEffect(() => { debouncedFetchData(q); }, []); console.log({ data, isLoading }); return (
{isLoading && searchQuery ? : }
{data?.hits?.length && !isLoading ? : isLoading ?
: (
No Results
)}
); }; export default SearchComponent;