import { useEffect, useRef } from "preact/hooks"; import useDebouncedCallback from "@lib/hooks/useDebouncedCallback.ts"; import { IconLoader2, IconSearch } from "@components/icons.tsx"; import { useEventListener } from "@lib/hooks/useEventListener.ts"; import { GenericResource } from "@lib/types.ts"; import { resources } from "@lib/resources.ts"; import { getCookie } from "@lib/string.ts"; import { IS_BROWSER } from "$fresh/runtime.ts"; import Checkbox from "@components/Checkbox.tsx"; import { Rating } from "@components/Rating.tsx"; import { useSignal } from "@preact/signals"; import Image from "@components/Image.tsx"; import { Emoji } from "@components/Emoji.tsx"; export async function fetchQueryResource(url: URL, type = "") { const query = url.searchParams.get("q"); const status = url.searchParams.get("status"); try { url.pathname = "/api/query"; url.searchParams.set("q", encodeURIComponent(query || "")); if (status) { url.searchParams.set("status", "not-seen"); } if (type) { url.searchParams.set("types", type); } const response = await fetch(url); const jsonData = await response.json(); return jsonData; } catch (error) { console.error("Error fetching data:", error); } } export const RedirectSearchHandler = () => { if (getCookie("session_cookie")) { useEventListener("keydown", (e: KeyboardEvent) => { if (e?.target?.nodeName == "INPUT") return; if ( e.key === "?" && globalThis.location.search === "" ) { globalThis.location.href += "?q="; } }, IS_BROWSER ? document?.body : undefined); } return <>; }; const SearchResultImage = ({ src }: { src: string }) => { return ( preview image ); }; export const SearchResultItem = ( { item, showEmoji = false }: { item: GenericResource; showEmoji?: boolean; }, ) => { const resourceType = resources[item.type]; const href = resourceType ? `${resourceType.link}/${item.id}` : ""; return ( {showEmoji && resourceType ? : ""} {item.meta?.image && } {item?.name} ); }; export const SearchResultList = ( { result, showEmoji }: { result: GenericResource[]; showEmoji?: boolean }, ) => { return (
{result?.length ? (
{result.map((hit) => ( ))}
) :
No Results
}
); }; const Search = ( { q = "*", type, results }: { q: string; type?: string; results?: GenericResource[]; }, ) => { const searchQuery = useSignal(q); const data = useSignal(results); const isLoading = useSignal(false); const showSeenStatus = useSignal(false); const inputRef = useRef(null); if ("history" in globalThis) { const u = new URL(globalThis.location.href); if (u.searchParams.get("q") !== searchQuery.value) { u.searchParams.set("q", searchQuery.value); } if (showSeenStatus.value) { u.searchParams.set("rating", "0"); } else { u.searchParams.delete("rating"); } globalThis.history.replaceState({}, "", u); } const fetchData = async () => { try { isLoading.value = true; const jsonData = await fetchQueryResource( new URL(globalThis?.location.href), type, ); data.value = jsonData; isLoading.value = false; } catch (error) { console.error("Error fetching data:", error); isLoading.value = false; } }; const debouncedFetchData = useDebouncedCallback(fetchData, 500); // Debounce the fetchData function with a delay of 500ms useEffect(() => { if (inputRef.current && searchQuery?.value.length === 0) { inputRef.current?.focus(); } }, [inputRef.current, searchQuery]); const handleInputChange = (event: Event) => { const target = event.target as HTMLInputElement; if (target.value !== searchQuery.value) { searchQuery.value = target.value; } }; useEffect(() => { debouncedFetchData(); // Call the debounced fetch function with the updated search query }, [searchQuery.value, showSeenStatus.value]); useEffect(() => { debouncedFetchData(); }, []); return (
{isLoading.value && searchQuery.value ? : }
{data.value?.length && !isLoading.value ? : isLoading.value ?
: (
No Results
)}
); }; export default Search;