diff --git a/ui/src/routes/catalogue/+page.server.ts b/ui/src/routes/catalogue/+page.server.ts index a727892..90c3afd 100644 --- a/ui/src/routes/catalogue/+page.server.ts +++ b/ui/src/routes/catalogue/+page.server.ts @@ -16,6 +16,7 @@ export const load: PageServerLoad = async ({ url, locals }) => { const sort = url.searchParams.get('sort') ?? 'popular'; const status = url.searchParams.get('status') ?? 'all'; const q = url.searchParams.get('q') ?? ''; + const audioOnly = url.searchParams.get('audio') === '1'; const params = new URLSearchParams({ page, genre, sort, status }); if (q.trim().length >= 2) { @@ -64,7 +65,8 @@ export const load: PageServerLoad = async ({ url, locals }) => { isAdmin: locals.user?.role === 'admin', searchQuery: q.trim().length >= 2 ? q.trim() : '', searchLocalCount: 0, - searchRemoteCount: 0 + searchRemoteCount: 0, + audioOnly }; }; diff --git a/ui/src/routes/catalogue/+page.svelte b/ui/src/routes/catalogue/+page.svelte index 4533cb0..f4affb1 100644 --- a/ui/src/routes/catalogue/+page.svelte +++ b/ui/src/routes/catalogue/+page.svelte @@ -21,11 +21,13 @@ }); function applyFilters() { + filtersOpen = false; const params = new URLSearchParams(); params.set('sort', filterSort); params.set('genre', filterGenre); params.set('status', filterStatus); params.set('page', '1'); + if (filterAudioOnly) params.set('audio', '1'); goto(`/catalogue?${params.toString()}`); } @@ -215,7 +217,7 @@ // ── Audio-available set ─────────────────────────────────────────────────── let audioSlugs = $state>(new Set()); - let filterAudioOnly = $state(false); + let filterAudioOnly = $state(untrack(() => data.audioOnly)); $effect(() => { fetch('/api/audio/slugs') @@ -224,6 +226,17 @@ .catch(() => { /* non-critical */ }); }); + function toggleAudio() { + filterAudioOnly = !filterAudioOnly; + const u = new URL(window.location.href); + if (filterAudioOnly) { + u.searchParams.set('audio', '1'); + } else { + u.searchParams.delete('audio'); + } + history.replaceState({}, '', u.toString()); + } + const displayedNovels = $derived( filterAudioOnly ? novels.filter((n) => audioSlugs.has(n.slug)) : novels ); @@ -249,7 +262,7 @@ {m.catalogue_rank_no_data_body()} {/if} {:else} - {m.catalogue_browse_source()} + {m.catalogue_browse_source()}{#if data.total > 0} {data.total.toLocaleString()} novels{/if} {/if}

@@ -349,7 +362,7 @@ {#if audioSlugs.size > 0} + {:else} {m.catalogue_no_results_filters()} {/if} @@ -531,11 +544,8 @@ loading="lazy" /> {:else} -
- - - +
+ {novel.title.charAt(0).toUpperCase()}
{/if} {#if novel.rank} @@ -622,11 +632,8 @@ {#if novel.cover} {novel.title} {:else} -
- - - +
+ {novel.title.charAt(0).toUpperCase()}
{/if} {#if isLoading} @@ -688,6 +695,8 @@ {m.catalogue_scrape_queued_badge()} {:else if scrapeResult[novel.slug] === 'busy'} {m.catalogue_scrape_busy_list()} + {:else if scrapeResult[novel.slug] === 'forbidden'} + {m.catalogue_scrape_forbidden_badge()} {:else if scrapeResult[novel.slug] === 'error'} {m.common_error()} {:else}