list page pt. 6 remove hydration variables

This commit is contained in:
2026-02-05 22:18:10 -08:00
parent 61dcd2e173
commit 6c03916c2b

View File

@@ -1,7 +1,9 @@
<script lang="ts"> <script lang="ts">
import { useSearchParams } from "runed/kit"; import { useSearchParams } from "runed/kit";
import { onMount } from "svelte";
import { z } from "zod"; import { z } from "zod";
import { db, ensureSeeded, getSongsForMalAnimeIds } from "$lib/db/client-db"; import { invalidate } from "$app/navigation";
import { db as clientDb } from "$lib/db/client-db";
import { import {
MalAnimeListQuerySchema, MalAnimeListQuerySchema,
MalAnimeListStatusEnum, MalAnimeListStatusEnum,
@@ -27,38 +29,40 @@
// Local username field that does NOT update the URL as you type. // Local username field that does NOT update the URL as you type.
let formMal = $state<string>(""); let formMal = $state<string>("");
// Songs displayed in the UI: start with server-provided `songRows`,
// then hydrate client-side if needed.
let hydratedSongRows = $state<PageData["songRows"]>([]);
// Keep form input synced with navigation (back/forward) and initial load. // Keep form input synced with navigation (back/forward) and initial load.
$effect(() => { $effect(() => {
formMal = data.username || params.mal || ""; formMal = data.username || params.mal || "";
}); });
// Keep displayed songs synced to the latest `data` (avoid stale capture). // If SSR returned no songRows (because client DB wasn't available),
$effect(() => { // re-run load on the client once the DB is ready by invalidating.
hydratedSongRows = data.songRows; onMount(() => {
}); if (data.songRows.length > 0) return;
if (!data.username || !data.malResponse) return;
// If SSR returned no songRows (because client DB wasn't available), hydrate in browser. if (clientDb) {
$effect(() => { void invalidate("clientdb:songs");
void (async () => { return;
// If server already provided songs, we are done. }
if (data.songRows.length > 0) return;
// If we don't have an active username or MAL response, nothing to hydrate. let cancelled = false;
if (!data.username) return;
if (!data.malResponse) return;
// If client DB isn't ready (SSR) we'll re-run once it exists (module init). const waitForDbAndInvalidate = async () => {
if (!db) return; for (let i = 0; i < 50; i++) {
if (cancelled) return;
if (clientDb) {
await invalidate("clientdb:songs");
return;
}
await new Promise((r) => setTimeout(r, 50));
}
};
await ensureSeeded(); void waitForDbAndInvalidate();
const malIds = data.malResponse.data.map((e) => e.node.id); return () => {
hydratedSongRows = await getSongsForMalAnimeIds(db, malIds); cancelled = true;
})(); };
}); });
function onSearch() { function onSearch() {
@@ -66,7 +70,7 @@
params.mal = formMal; params.mal = formMal;
} }
function songArtistLabel(r: (typeof hydratedSongRows)[number]) { function songArtistLabel(r: (typeof data.songRows)[number]) {
return r.artistName ?? r.groupName ?? null; return r.artistName ?? r.groupName ?? null;
} }
@@ -141,7 +145,7 @@
Waiting for username… Waiting for username…
{:else} {:else}
MAL entries: {data.malResponse?.data.length ?? 0} (limited to {LIST_QUERY_LIMIT}) MAL entries: {data.malResponse?.data.length ?? 0} (limited to {LIST_QUERY_LIMIT})
• Songs found: {hydratedSongRows.length} • Songs found: {data.songRows.length}
{/if} {/if}
</div> </div>
@@ -165,18 +169,18 @@
</p> </p>
{/if} {/if}
{#if (formMal ?? "").trim() && data.username && (data.malResponse?.data.length ?? 0) > 0 && hydratedSongRows.length === 0} {#if (formMal ?? "").trim() && data.username && (data.malResponse?.data.length ?? 0) > 0 && data.songRows.length === 0}
<p class="mt-4 text-sm text-muted-foreground"> <p class="mt-4 text-sm text-muted-foreground">
No songs matched in the local database. This likely means none of the MAL No songs matched in the local database. This likely means none of the MAL
anime IDs exist in the AMQ DB. anime IDs exist in the AMQ DB.
</p> </p>
{/if} {/if}
{#if hydratedSongRows.length > 0} {#if data.songRows.length > 0}
<h2 class="mt-6 text-lg font-semibold">Songs</h2> <h2 class="mt-6 text-lg font-semibold">Songs</h2>
<ul class="mt-3 space-y-2"> <ul class="mt-3 space-y-2">
{#each hydratedSongRows as r (String(r.annId) + ":" + String(r.annSongId))} {#each data.songRows as r (String(r.annId) + ":" + String(r.annSongId))}
<li class="rounded border px-3 py-2"> <li class="rounded border px-3 py-2">
<div class="font-medium"> <div class="font-medium">
{r.songName} {r.songName}