success pt. 6 runed

This commit is contained in:
2026-02-05 04:00:11 -08:00
parent e69f228172
commit 3786873f62
4 changed files with 65 additions and 27 deletions

View File

@@ -1,4 +1,6 @@
<script lang="ts">
import { Debounced } from "runed";
import { useSearchParams } from "runed/kit";
import { onMount } from "svelte";
import {
db,
@@ -6,23 +8,48 @@
getAnimeList,
searchAnimeByName,
} from "$lib/db/client-db";
import { AmqBrowseSearchSchema } from "$lib/types/search/amq-browse";
const params = useSearchParams(AmqBrowseSearchSchema, {
debounce: 250,
pushHistory: false,
showDefaults: false,
});
let status = $state<"idle" | "loading" | "ready" | "error">("idle");
let error = $state<string | null>(null);
let query = $state("");
let isSearching = $state(false);
// Debounce the actual DB query updates (separate from URL debounce)
const debouncedQuery = new Debounced(() => params.q, 250);
type AnimeItem = Awaited<ReturnType<typeof getAnimeList>>[number];
let anime = $state<AnimeItem[]>([]);
async function loadListFor(query: string) {
const q = query.trim();
try {
isSearching = true;
if (!q) {
anime = await getAnimeList(db, 20);
} else {
anime = await searchAnimeByName(db, q, 20);
}
} finally {
isSearching = false;
}
}
async function loadInitial() {
status = "loading";
error = null;
try {
await ensureSeeded();
anime = await getAnimeList(db, 20);
await loadListFor(params.q);
status = "ready";
} catch (e) {
error = e instanceof Error ? e.message : String(e);
@@ -34,35 +61,22 @@
void loadInitial();
});
// Debounced search
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
// React to debounced query changes (URL updates + user typing)
$effect(() => {
if (status !== "ready") return;
function scheduleSearch(nextQuery: string) {
query = nextQuery;
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
if (status !== "ready") return;
const q = query.trim();
// Track debounced query to avoid hammering the DB while typing
const q = debouncedQuery.current;
void (async () => {
try {
isSearching = true;
if (!q) {
anime = await getAnimeList(db, 20);
} else {
anime = await searchAnimeByName(db, q, 20);
}
await loadListFor(q);
} catch (e) {
error = e instanceof Error ? e.message : String(e);
status = "error";
} finally {
isSearching = false;
}
}, 200);
}
})();
});
function seasonName(seasonId: number) {
switch (seasonId) {
@@ -95,9 +109,8 @@
id="anime-search"
class="rounded border px-3 py-2 text-sm"
placeholder="Type to search by name…"
value={query}
oninput={(e) =>
scheduleSearch((e.currentTarget as HTMLInputElement).value)}
value={params.q}
oninput={(e) => (params.q = (e.currentTarget as HTMLInputElement).value)}
autocomplete="off"
spellcheck={false}
/>