success pt. 6 runed
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user