feat(web/playlists): For-You tile play button refreshes and plays

Click on the For-You tile's play button now triggers a synchronous
refresh, then enqueues + auto-plays the freshly-built playlist.
Two-click-target tile pattern: tile BODY click still navigates to
the playlist detail page (existing behavior); the play button is
the new generate-and-play action.

Behavior is gated on playlist.system_variant === 'for_you' — every
other playlist (user, Discover, Songs-like-X) keeps the existing
"fetch detail, enqueue, play" flow. The atomic-replace BuildSystem-
Playlists creates a new playlist_id, so the play handler uses the
refresh response's id for the follow-up getPlaylist call rather
than the stale tile prop's id.

Empty-library response (playlist_id: null) is a no-op — clicking
play in a degenerate-empty library doesn't error, it just doesn't
start playback. Caches are invalidated post-refresh so the home
view re-fetches and the tile re-renders with the new id, avoiding
the corner case of a click-then-tile-body 404.

Tests cover the refresh-and-play happy path, the empty-library
no-op, and that non-For-You playlists keep the existing flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-07 10:40:23 -04:00
parent 54c40c18be
commit e48d84cde1
4 changed files with 161 additions and 3 deletions
+21
View File
@@ -108,3 +108,24 @@ export async function refreshDiscover(): Promise<RefreshDiscoverResponse> {
body: JSON.stringify({})
})) as RefreshDiscoverResponse;
}
// For-You refresh ------------------------------------------------------------
export type RefreshForYouResponse = {
playlist_id: string | null;
track_count: number;
track_ids: string[];
};
// POST /api/playlists/system/for-you/refresh — synchronously
// rebuilds the calling user's For-You playlist and returns the new
// id, track count, and track id list.
//
// Used by the home Playlists row's For-You tile play button: click
// triggers refresh, the frontend then calls getPlaylist(new_id) to
// get full TrackRef snapshots and enqueues for playback.
export async function refreshForYou(): Promise<RefreshForYouResponse> {
return (await apiFetch('/api/playlists/system/for-you/refresh', {
method: 'POST'
})) as RefreshForYouResponse;
}