feat(web): "You might like" Home row (#790 web client slice)
test-web / test (push) Successful in 32s

The web UI rendered a fixed set of Home sections and had no code for the
server's you_might_like_albums / you_might_like_artists (shipped in
v2026.06.11), so the row was absent in the web client. Adds it, mirroring
the Rediscover block, positioned directly under the system-playlists row.

- types.ts HomePayload: two new slices (server always emits them; web ships
  in lockstep with the server).
- +page.svelte: a "You might like" section (albums + artists scrollers) as the
  first section under the playlists row, with a "still learning your taste"
  empty state for the cold-start/gated case. Reuses existing AlbumCard /
  ArtistCard / HorizontalScrollRow.
- home.test.ts / page.test.ts: mock payloads gain the two fields.

Completes the You-might-like row across all three clients (server already
emits it; Android in v2026.06.11; web here).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 22:59:58 -04:00
parent 1cc9553d1f
commit 3f240bc777
4 changed files with 44 additions and 2 deletions
+3 -1
View File
@@ -18,7 +18,9 @@ describe('home client', () => {
rediscover_albums: [],
rediscover_artists: [],
most_played_tracks: [],
last_played_artists: []
last_played_artists: [],
you_might_like_albums: [],
you_might_like_artists: []
};
(api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce(fixture);
const got = await listHome();
+2
View File
@@ -342,4 +342,6 @@ export type HomePayload = {
rediscover_artists: ArtistRef[];
most_played_tracks: TrackRef[];
last_played_artists: ArtistRef[];
you_might_like_albums: AlbumRef[];
you_might_like_artists: ArtistRef[];
};
+36
View File
@@ -161,6 +161,42 @@
{:else if showSkeleton.value && !data}
<p class="text-text-secondary">Loading…</p>
{:else if data}
<!-- You might like: taste-predicted in-library albums + artists the user
doesn't actively spin (server section, daily-built, cold-start gated).
Sits directly under the system playlists row. Two scrollers like
Rediscover (square album vs circular artist cards). -->
<section class="space-y-3">
{#if data.you_might_like_albums.length === 0 && data.you_might_like_artists.length === 0}
<header>
<h2 class="font-display text-2xl font-medium text-text-primary">You might like</h2>
</header>
<p class="text-text-secondary">We're still learning your taste — keep listening and picks you might like will show up here.</p>
{:else}
{#if data.you_might_like_albums.length > 0}
<HorizontalScrollRow
rows={[data.you_might_like_albums]}
title="You might like"
ariaLabel="You might like albums"
>
{#snippet item(album: import('$lib/api/types').AlbumRef)}
<div class="w-40"><AlbumCard {album} /></div>
{/snippet}
</HorizontalScrollRow>
{/if}
{#if data.you_might_like_artists.length > 0}
<HorizontalScrollRow
rows={[data.you_might_like_artists]}
title={data.you_might_like_albums.length === 0 ? 'You might like' : undefined}
ariaLabel="You might like artists"
>
{#snippet item(artist: import('$lib/api/types').ArtistRef)}
<div class="w-36"><ArtistCard {artist} /></div>
{/snippet}
</HorizontalScrollRow>
{/if}
{/if}
</section>
<!-- Recently added: 50 albums in 2 rows of 25, scrolling together -->
<section class="space-y-3">
{#if data.recently_added_albums.length === 0}
+3 -1
View File
@@ -13,7 +13,9 @@ vi.mock('$lib/api/home', () => ({
most_played_tracks: [],
rediscover_albums: [],
rediscover_artists: [],
last_played_artists: []
last_played_artists: [],
you_might_like_albums: [],
you_might_like_artists: []
},
isPending: false,
isError: false,