first success!
This commit is contained in:
@@ -1,2 +1,117 @@
|
||||
<h1>Welcome to SvelteKit</h1>
|
||||
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
|
||||
<script lang="ts">
|
||||
import { desc } from "drizzle-orm";
|
||||
import { onMount } from "svelte";
|
||||
import { db, overwriteDatabaseFile } from "$lib/db/client-db";
|
||||
import { animeTable } from "$lib/db/schema";
|
||||
|
||||
type AnimeListItem = {
|
||||
annId: number;
|
||||
mainName: string;
|
||||
year: number;
|
||||
seasonId: number;
|
||||
};
|
||||
|
||||
const SEED_URL = "/data/amq.sqlite";
|
||||
const SEED_VERSION = "amq.sqlite.v1"; // bump when static/data/amq.sqlite changes
|
||||
const SEED_KEY = "amq.seed.version";
|
||||
|
||||
let status = $state<"idle" | "loading" | "ready" | "error">("idle");
|
||||
let error = $state<string | null>(null);
|
||||
let anime = $state<AnimeListItem[]>([]);
|
||||
|
||||
async function ensureSeeded() {
|
||||
const current = localStorage.getItem(SEED_KEY);
|
||||
if (current === SEED_VERSION) return;
|
||||
|
||||
const res = await fetch(SEED_URL, { cache: "no-cache" });
|
||||
if (!res.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch seed DB: ${res.status} ${res.statusText}`,
|
||||
);
|
||||
}
|
||||
|
||||
const stream = res.body;
|
||||
if (stream) {
|
||||
await overwriteDatabaseFile(stream);
|
||||
} else {
|
||||
const blob = await res.blob();
|
||||
await overwriteDatabaseFile(blob);
|
||||
}
|
||||
|
||||
localStorage.setItem(SEED_KEY, SEED_VERSION);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
(async () => {
|
||||
status = "loading";
|
||||
error = null;
|
||||
|
||||
try {
|
||||
await ensureSeeded();
|
||||
|
||||
const rows = await db
|
||||
.select({
|
||||
annId: animeTable.annId,
|
||||
mainName: animeTable.mainName,
|
||||
year: animeTable.year,
|
||||
seasonId: animeTable.seasonId,
|
||||
})
|
||||
.from(animeTable)
|
||||
.orderBy(
|
||||
desc(animeTable.year),
|
||||
desc(animeTable.seasonId),
|
||||
desc(animeTable.annId),
|
||||
)
|
||||
.limit(20);
|
||||
|
||||
anime = rows;
|
||||
status = "ready";
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : String(e);
|
||||
status = "error";
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
function seasonName(seasonId: number) {
|
||||
// matches your Season enum mapping (0..3)
|
||||
switch (seasonId) {
|
||||
case 0:
|
||||
return "Winter";
|
||||
case 1:
|
||||
return "Spring";
|
||||
case 2:
|
||||
return "Summer";
|
||||
case 3:
|
||||
return "Fall";
|
||||
default:
|
||||
return `Season ${seasonId}`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1 class="text-2xl font-semibold">AMQ Browser</h1>
|
||||
|
||||
{#if status === "loading"}
|
||||
<p class="mt-3 text-sm text-muted-foreground">Loading client database…</p>
|
||||
{:else if status === "error"}
|
||||
<p class="mt-3 text-sm text-red-600">
|
||||
Error: {error}
|
||||
</p>
|
||||
{:else if status === "ready"}
|
||||
<p class="mt-3 text-sm text-muted-foreground">
|
||||
Showing {anime.length} anime
|
||||
</p>
|
||||
|
||||
<ul class="mt-4 space-y-2">
|
||||
{#each anime as a (a.annId)}
|
||||
<li class="rounded border px-3 py-2">
|
||||
<div class="font-medium">{a.mainName}</div>
|
||||
<div class="text-sm text-muted-foreground">
|
||||
{a.year}
|
||||
{seasonName(a.seasonId)} • ANN {a.annId}
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user