feat: rework the home layout

Recently played keeps its shape. Mixes and playlists now share one row,
half and half on large screens with smaller cards, capped at six each
with a link to the full list. Albums are the ones the user listened to
most recently, which is a new query joining the listening history with
the album tracks. The endless track wall is now "Descubrir".
This commit is contained in:
2026-07-21 19:06:36 +02:00
parent 7e697e423b
commit 0d34a40931
6 changed files with 180 additions and 42 deletions
+14 -9
View File
@@ -3,7 +3,12 @@ import { createApiClient, requireUser } from '$lib/server/api';
import { fetchYoutubeFiller } from '$lib/server/youtube';
import { shuffle } from '$lib/collections';
import { addTrackAction, addYouTubeToPlaylistAction } from '$lib/server/playlistActions';
import { HOME_ALBUMS_PAGE_SIZE, HOME_LATEST_PAGE_SIZE, YOUTUBE_FILLER_LIMIT } from '$lib/config';
import {
HOME_ALBUMS_LIMIT,
HOME_LATEST_PAGE_SIZE,
HOME_SHELF_LIMIT,
YOUTUBE_FILLER_LIMIT
} from '$lib/config';
import { pickGreeting } from '$lib/server/greeting';
export const load: PageServerLoad = async ({ locals, url, fetch }) => {
@@ -14,14 +19,14 @@ export const load: PageServerLoad = async ({ locals, url, fetch }) => {
params: { query: { pageNumber: 1, pageSize: HOME_LATEST_PAGE_SIZE } }
});
const playlistsPromise = api.GET('/playlists/users/{userId}', {
params: { path: { userId: user.sub }, query: { pageNumber: 1, pageSize: 12 } }
});
const albumsPromise = api.GET('/albums/users/{userId}', {
params: {
path: { userId: user.sub },
query: { pageNumber: 1, pageSize: HOME_ALBUMS_PAGE_SIZE }
query: { pageNumber: 1, pageSize: HOME_SHELF_LIMIT + 1 }
}
});
const albumsPromise = api.GET('/albums/recent', {
params: { query: { limit: HOME_ALBUMS_LIMIT } }
});
const mixesPromise = api.GET('/mixes');
const recentlyPlayedPromise = api.GET('/users/{id}/listening-history', {
params: { path: { id: user.sub } }
@@ -47,10 +52,10 @@ export const load: PageServerLoad = async ({ locals, url, fetch }) => {
undefined,
user.sub
),
playlists: playlistItems,
albums: albums.data?.items ?? [],
albumsHasMore: Boolean(albums.data?.hasNextPage),
mixes: mixes.data ?? [],
playlists: playlistItems.slice(0, HOME_SHELF_LIMIT),
playlistsHasMore: playlistItems.length > HOME_SHELF_LIMIT,
albums: albums.data ?? [],
mixes: (mixes.data ?? []).slice(0, HOME_SHELF_LIMIT),
recentlyPlayed: recentlyPlayed.data ?? []
};
};
+40 -32
View File
@@ -227,44 +227,52 @@
</section>
{/if}
{#if mixes.length > 0}
<section class="page-x pt-5 pb-3 sm:pt-6">
<SectionHeading title="Hechas para ti" />
<MediaGrid>
{#each mixes as mix, i (mix.id)}
<MixCard {mix} index={i} />
{/each}
</MediaGrid>
</section>
{/if}
{#if mixes.length > 0 || playlists.length > 0}
<section class="grid gap-x-8 gap-y-5 page-x pt-5 pb-3 sm:pt-6 lg:grid-cols-2">
{#if mixes.length > 0}
<div class="min-w-0">
<SectionHeading title="Hechas para ti" />
<MediaGrid min="140px" minMobile="130px">
{#each mixes as mix, i (mix.id)}
<MixCard {mix} index={i} />
{/each}
</MediaGrid>
</div>
{/if}
{#if playlists.length > 0}
<section class="page-x pt-5 pb-3 sm:pt-6">
<SectionHeading title="Mis listas" />
<MediaGrid>
{#each playlists as playlist, i (playlist.id)}
<PlaylistCard
id={playlist.id}
name={playlist.name}
description={playlist.description}
trackIds={playlist.coverTrackIds}
updatedAt={playlist.updatedAt}
index={i}
/>
{/each}
</MediaGrid>
{#if playlists.length > 0}
<div class="min-w-0">
<SectionHeading title="Mis listas">
{#snippet actions()}
{#if data.playlistsHasMore}
<a href="/playlists" class="text-sm text-fg-3 transition hover:text-accent-soft">
Ver todas
</a>
{/if}
{/snippet}
</SectionHeading>
<MediaGrid min="140px" minMobile="130px">
{#each playlists as playlist, i (playlist.id)}
<PlaylistCard
id={playlist.id}
name={playlist.name}
description={playlist.description}
trackIds={playlist.coverTrackIds}
updatedAt={playlist.updatedAt}
index={i}
/>
{/each}
</MediaGrid>
</div>
{/if}
</section>
{/if}
{#if albums.length > 0}
<section class="page-x pt-5 pb-3 sm:pt-6">
<SectionHeading title="Tus álbumes">
<SectionHeading title="Álbumes escuchados recientemente">
{#snippet actions()}
{#if data.albumsHasMore}
<a href="/albums" class="text-sm text-fg-3 transition hover:text-accent-soft">
Ver todos
</a>
{/if}
<a href="/albums" class="text-sm text-fg-3 transition hover:text-accent-soft">Ver todos</a>
{/snippet}
</SectionHeading>
<MediaGrid>
@@ -283,7 +291,7 @@
{/if}
<section class="page-x pt-5 pb-10 sm:pt-6">
<SectionHeading title="Novedades" />
<SectionHeading title="Descubrir" />
{#if latest.length > 0}
<MediaGrid as="ul">
{#each latest as item, i (targetId(item))}