Mirrors the Android change from commit bf61d6cf. Web's Home Playlists
row gains the 5 secondary system kinds (deep_cuts, rediscover,
new_for_you, on_this_day, first_listens) after the Songs-like slots,
in server-registry order, when they exist. No placeholders for these
— they depend on library shape (Deep cuts needs deep albums, On this
day needs prior history, etc.) so a missing one means "not enough
data," not "still building."
Test coverage: new `renders secondary system kinds` test creates a
mocked owned set with for_you + deep_cuts + new_for_you and asserts
all three card names render. Existing tests (5 placeholders, building
variant, For-You + 3 placeholders) unchanged since none seed
secondary kinds.
Most Played multi-row is NOT touched — web already chunks the 75
tracks into 3 rows of 25 via the existing HorizontalScrollRow rows=
{chunk(...,25)} pattern. My initial task description was wrong on
that point.
Naming collision (`rediscover` playlist vs Rediscover recommendations
section both read "Rediscover" on the same Home) deferred — operator
follow-up. Same on Android.
Scribe 531, local task #61.
This commit is contained in:
@@ -49,6 +49,30 @@
|
||||
.slice(0, 3)
|
||||
);
|
||||
|
||||
// Secondary system kinds the server generates that don't get pinned
|
||||
// to fixed Home slots: surfaced after the Songs-like slots when they
|
||||
// exist, in server-registry order (internal/playlists/system.go).
|
||||
// No placeholders for these — they depend on library shape (Deep
|
||||
// cuts needs deep albums, On this day needs prior history, etc.) so
|
||||
// a missing one means "not enough data," not "still building."
|
||||
// Mirrors Android's SECONDARY_SYSTEM_VARIANTS (HomeScreen.kt). Operator
|
||||
// request 2026-06-01 for web parity.
|
||||
const SECONDARY_SYSTEM_VARIANTS = [
|
||||
'deep_cuts',
|
||||
'rediscover',
|
||||
'new_for_you',
|
||||
'on_this_day',
|
||||
'first_listens'
|
||||
] as const;
|
||||
|
||||
const secondarySystemPlaylists = $derived(
|
||||
SECONDARY_SYSTEM_VARIANTS
|
||||
.map((variant) =>
|
||||
(systemPlaylistsQ.data?.owned ?? []).find((p) => p.system_variant === variant)
|
||||
)
|
||||
.filter((p): p is Playlist => p != null)
|
||||
);
|
||||
|
||||
const userPlaylists = $derived(userPlaylistsQ.data?.owned ?? []);
|
||||
|
||||
type PlaceholderVariant = 'building' | 'failed' | 'pending' | 'seed-needed';
|
||||
@@ -93,6 +117,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Secondary system kinds in server-registry order, when generated.
|
||||
for (const p of secondarySystemPlaylists) {
|
||||
out.push({ kind: 'real', playlist: p });
|
||||
}
|
||||
|
||||
// User playlists trail (server returns most-recently-updated first).
|
||||
for (const p of userPlaylists) {
|
||||
out.push({ kind: 'real', playlist: p });
|
||||
|
||||
@@ -109,4 +109,42 @@ describe('home Playlists section', () => {
|
||||
// real For-You tile.
|
||||
expect(placeholders).toHaveLength(4);
|
||||
});
|
||||
|
||||
test('renders secondary system kinds (deep_cuts / new_for_you) after Songs-like slots', () => {
|
||||
// Operator backflow 2026-06-01: web Home surfaces the 5 secondary
|
||||
// system kinds when generated. No placeholders for them — they
|
||||
// depend on library shape, so missing means "not enough data."
|
||||
const makePlaylist = (id: string, variant: string, name: string): Playlist => ({
|
||||
id,
|
||||
user_id: 'u1',
|
||||
owner_username: 'u1',
|
||||
name,
|
||||
description: '',
|
||||
is_public: false,
|
||||
kind: 'system',
|
||||
system_variant: variant,
|
||||
refreshable: variant !== 'songs_like_artist',
|
||||
seed_artist_id: null,
|
||||
cover_url: '',
|
||||
track_count: 25,
|
||||
duration_sec: 1500,
|
||||
created_at: '2026-05-04T00:00:00Z',
|
||||
updated_at: '2026-05-04T00:00:00Z'
|
||||
});
|
||||
const owned = [
|
||||
makePlaylist('fy', 'for_you', 'For You'),
|
||||
makePlaylist('dc', 'deep_cuts', 'Deep cuts'),
|
||||
makePlaylist('nfy', 'new_for_you', 'New for you')
|
||||
];
|
||||
(createPlaylistsQuery as unknown as ReturnType<typeof vi.fn>).mockImplementation(
|
||||
(kind?: string) =>
|
||||
kind === 'system'
|
||||
? readable({ data: { owned, public: [] }, isPending: false, isError: false })
|
||||
: readable({ data: emptyPlaylistsResponse, isPending: false, isError: false })
|
||||
);
|
||||
render(Page);
|
||||
expect(screen.getByText('For You')).toBeInTheDocument();
|
||||
expect(screen.getByText('Deep cuts')).toBeInTheDocument();
|
||||
expect(screen.getByText('New for you')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user