list page pt. 6 remove hydration variables
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user