feat(frontend): hide empty local/YouTube sections when searching in explore

This commit is contained in:
2026-07-16 17:00:27 +02:00
parent 1050372634
commit cfa5bdc6ff
+97 -90
View File
@@ -35,11 +35,20 @@
addedVideoIds = new Set(); addedVideoIds = new Set();
}); });
const showHeadings = $derived(!!data.query);
const tracks = $derived(data.tracks); const tracks = $derived(data.tracks);
const currentPage = $derived(Number(data.tracks.pageNumber)); const currentPage = $derived(Number(data.tracks.pageNumber));
const total = $derived(Number(data.tracks.totalItemCount)); const total = $derived(Number(data.tracks.totalItemCount));
const localVisible = $derived(!data.query || tracks.items.length > 0);
const youtubeVisible = $derived(
!!data.query && (data.needsAuth || data.ytError || ytItems.length > 0)
);
const localHeadingVisible = $derived(!!data.query && localVisible);
const youtubeHeadingVisible = $derived(!!data.query && youtubeVisible);
const nothingFound = $derived(
!!data.query && !localVisible && !youtubeVisible && !data.needsAuth && !data.ytError
);
function buildHref(page = 1, query = data.query) { function buildHref(page = 1, query = data.query) {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (query) params.set('q', query); if (query) params.set('q', query);
@@ -137,95 +146,98 @@
</p> </p>
{/if} {/if}
{#if showHeadings} {#if nothingFound}
<h2 class="mt-10 font-display text-lg font-bold tracking-tight">Mi música</h2> <div class="mt-10 rounded-2xl border border-white/10 bg-neutral-900/40 p-12 text-center backdrop-blur-md">
{/if} <Music class="mx-auto h-10 w-10 text-neutral-500" />
<p class="mt-4 text-neutral-300">No hay resultados para «{data.query}».</p>
<p class="{showHeadings ? 'mt-1' : 'mt-6'} text-sm text-neutral-500">
{#if data.query}
{total}
{total === 1 ? 'resultado' : 'resultados'} para «{data.query}»
{:else}
{total}
{total === 1 ? 'canción' : 'canciones'}
{/if}
</p>
{#if tracks.items.length === 0}
<div class="mt-6 rounded-2xl border border-white/10 bg-neutral-900/40 p-10 text-center backdrop-blur-md">
<Music class="mx-auto h-8 w-8 text-neutral-500" />
<p class="mt-3 text-sm text-neutral-300">
{#if data.query}
No hay canciones tuyas que coincidan con «{data.query}».
{:else}
Todavía no hay canciones. ¡Sé el primero en subir una!
{/if}
</p>
</div> </div>
{:else} {/if}
<ul class="mt-6 grid grid-cols-[repeat(auto-fill,minmax(160px,1fr))] gap-4">
{#each tracks.items as track, i (track.id)}
<li class="group animate-enter" style="animation-delay:{i * 40}ms">
<button type="button" onclick={() => togglePlayLocal(i)} class="block w-full text-left">
<Cover
trackId={track.id}
hue={hueFor(track.id)}
size="large"
alt={track.title}
class="aspect-square w-full rounded-xl shadow-[0_12px_28px_-10px_rgba(0,0,0,0.6)]"
>
<span
class="absolute right-2.5 bottom-2.5 grid h-11 w-11 translate-y-2 place-items-center rounded-full bg-[var(--mf-accent)] text-neutral-950 opacity-0 shadow-lg transition-all group-hover:translate-y-0 group-hover:opacity-100"
class:!opacity-100={player.current?.id === queueIdForTrack(track)}
class:!translate-y-0={player.current?.id === queueIdForTrack(track)}
>
{#if player.current?.id === queueIdForTrack(track) && player.isPlaying}
<svg viewBox="0 0 24 24" fill="currentColor" class="h-5 w-5">
<path d="M6 5h4v14H6zM14 5h4v14h-4z" />
</svg>
{:else}
<svg viewBox="0 0 24 24" fill="currentColor" class="h-5 w-5">
<path d="M8 5v14l11-7z" />
</svg>
{/if}
</span>
</Cover>
<div class="mt-2.5 truncate text-sm font-semibold text-[var(--mf-text)]" title={track.title}>
{track.title}
</div>
{#if track.artist}
<div class="mt-0.5 truncate text-xs text-neutral-500">{track.artist}</div>
{/if}
</button>
</li>
{/each}
</ul>
{#if tracks.hasPreviousPage || tracks.hasNextPage} {#if localVisible}
<nav class="mt-8 flex items-center justify-center gap-4"> {#if localHeadingVisible}
{#if tracks.hasPreviousPage} <h2 class="mt-10 font-display text-lg font-bold tracking-tight">Mi música</h2>
<a {/if}
href={buildHref(currentPage - 1)}
class="rounded-lg border border-white/10 px-5 py-2 text-sm text-white/65 transition hover:bg-white/2.5" <p class="{localHeadingVisible ? 'mt-1' : 'mt-6'} text-sm text-neutral-500">
> {#if data.query}
Anterior {total}
</a> {total === 1 ? 'resultado' : 'resultados'} para «{data.query}»
{/if} {:else}
<span class="text-sm text-neutral-500">Página {currentPage}</span> {total}
{#if tracks.hasNextPage} {total === 1 ? 'canción' : 'canciones'}
<a {/if}
href={buildHref(currentPage + 1)} </p>
class="rounded-lg border border-white/10 px-5 py-2 text-sm text-white/65 transition hover:bg-white/2.5"
> {#if tracks.items.length === 0}
Siguiente <div class="mt-6 rounded-2xl border border-white/10 bg-neutral-900/40 p-10 text-center backdrop-blur-md">
</a> <Music class="mx-auto h-8 w-8 text-neutral-500" />
{/if} <p class="mt-3 text-sm text-neutral-300">Todavía no hay canciones. ¡Sé el primero en subir una!</p>
</nav> </div>
{:else}
<ul class="mt-6 grid grid-cols-[repeat(auto-fill,minmax(160px,1fr))] gap-4">
{#each tracks.items as track, i (track.id)}
<li class="group animate-enter" style="animation-delay:{i * 40}ms">
<button type="button" onclick={() => togglePlayLocal(i)} class="block w-full text-left">
<Cover
trackId={track.id}
hue={hueFor(track.id)}
size="large"
alt={track.title}
class="aspect-square w-full rounded-xl shadow-[0_12px_28px_-10px_rgba(0,0,0,0.6)]"
>
<span
class="absolute right-2.5 bottom-2.5 grid h-11 w-11 translate-y-2 place-items-center rounded-full bg-[var(--mf-accent)] text-neutral-950 opacity-0 shadow-lg transition-all group-hover:translate-y-0 group-hover:opacity-100"
class:!opacity-100={player.current?.id === queueIdForTrack(track)}
class:!translate-y-0={player.current?.id === queueIdForTrack(track)}
>
{#if player.current?.id === queueIdForTrack(track) && player.isPlaying}
<svg viewBox="0 0 24 24" fill="currentColor" class="h-5 w-5">
<path d="M6 5h4v14H6zM14 5h4v14h-4z" />
</svg>
{:else}
<svg viewBox="0 0 24 24" fill="currentColor" class="h-5 w-5">
<path d="M8 5v14l11-7z" />
</svg>
{/if}
</span>
</Cover>
<div class="mt-2.5 truncate text-sm font-semibold text-[var(--mf-text)]" title={track.title}>
{track.title}
</div>
{#if track.artist}
<div class="mt-0.5 truncate text-xs text-neutral-500">{track.artist}</div>
{/if}
</button>
</li>
{/each}
</ul>
{#if tracks.hasPreviousPage || tracks.hasNextPage}
<nav class="mt-8 flex items-center justify-center gap-4">
{#if tracks.hasPreviousPage}
<a
href={buildHref(currentPage - 1)}
class="rounded-lg border border-white/10 px-5 py-2 text-sm text-white/65 transition hover:bg-white/2.5"
>
Anterior
</a>
{/if}
<span class="text-sm text-neutral-500">Página {currentPage}</span>
{#if tracks.hasNextPage}
<a
href={buildHref(currentPage + 1)}
class="rounded-lg border border-white/10 px-5 py-2 text-sm text-white/65 transition hover:bg-white/2.5"
>
Siguiente
</a>
{/if}
</nav>
{/if}
{/if} {/if}
{/if} {/if}
{#if data.query} {#if youtubeVisible}
{#if showHeadings} {#if youtubeHeadingVisible}
<h2 class="mt-12 font-display text-lg font-bold tracking-tight">YouTube Music</h2> <h2 class="mt-12 font-display text-lg font-bold tracking-tight">YouTube Music</h2>
{/if} {/if}
@@ -239,11 +251,6 @@
<Music class="mx-auto h-8 w-8 text-neutral-500" /> <Music class="mx-auto h-8 w-8 text-neutral-500" />
<p class="mt-3 text-sm text-neutral-300">YouTube Music no está disponible ahora mismo. Inténtalo de nuevo.</p> <p class="mt-3 text-sm text-neutral-300">YouTube Music no está disponible ahora mismo. Inténtalo de nuevo.</p>
</div> </div>
{:else if ytItems.length === 0}
<div class="mt-6 rounded-2xl border border-white/10 bg-neutral-900/40 p-10 text-center backdrop-blur-md">
<Music class="mx-auto h-8 w-8 text-neutral-500" />
<p class="mt-3 text-sm text-neutral-300">No hay resultados en YouTube Music para «{data.query}».</p>
</div>
{:else} {:else}
<ul class="mt-6 grid grid-cols-[repeat(auto-fill,minmax(160px,1fr))] gap-4"> <ul class="mt-6 grid grid-cols-[repeat(auto-fill,minmax(160px,1fr))] gap-4">
{#each ytItems as song, i (song.videoId)} {#each ytItems as song, i (song.videoId)}