app/components/admin/use-media-search.ts
60 lines and 7 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| useMediaSearch | 20 to 66 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:27> | 27 to 63 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:36> | 36 to 61 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:41> | 41 to 47 | 4 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:48> | 48 to 54 | 3 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:55> | 55 to 60 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:62> | 62 to 62 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
Imports
It imports app/lib/error-message.mjs. Imported by app/components/admin/media-palette.tsx.
Source
import { useEffect, useRef, useState } from "react";
import { errorMessage } from "~/lib/error-message.mjs";
type PalettePayload = { results: PaletteResult[]; hasMore: boolean };
export type PaletteResult = {
key: string;
url: string;
name: string;
dir: string;
size: number;
viewable: boolean;
};
/**
* The palette's search: debounced, and sequenced, because a slow response to a shorter query can
* land after a fast one and replace the right answer. `setCursor(0)` runs when fresh results land.
*/
export function useMediaSearch(query: string, setCursor: (cursor: number) => void) {
const [results, setResults] = useState<PaletteResult[]>([]);
const [hasMore, setHasMore] = useState(false);
/* A failed search is said as one; an empty list would read as "nothing matches". */
const [searchError, setSearchError] = useState<string | null>(null);
const seq = useRef(0);
useEffect(() => {
const trimmed = query.trim();
if (!trimmed) {
setResults([]);
setHasMore(false);
setSearchError(null);
return;
}
const id = (seq.current += 1);
const timer = window.setTimeout(() => {
// Answered as JSON by the route's middleware; anything else (an expired session's page) is a failure.
fetch(`/admin/media?palette=1&q=${encodeURIComponent(trimmed)}`, {
headers: { accept: "application/json" },
})
.then((r) => {
if (!r.ok) throw new Error(`HTTP ${r.status}`);
if (!(r.headers.get("content-type") ?? "").includes("application/json")) {
throw new Error("the server did not answer with results");
}
return r.json() as Promise<PalettePayload>;
})
.then((data) => {
if (id !== seq.current) return;
setSearchError(null);
setResults(data.results ?? []);
setHasMore(Boolean(data.hasMore));
setCursor(0);
})
.catch((error: unknown) => {
if (id !== seq.current) return;
setResults([]);
setHasMore(false);
setSearchError(errorMessage(error));
});
}, 130);
return () => window.clearTimeout(timer);
}, [query, setCursor]);
return { results, hasMore, searchError };
}