Commit Graph

4 Commits

Author SHA1 Message Date
bvandeusen 98fdd9dcdf feat(flutter/cache): migrate playlist providers to drift-first (#357 plan C)
playlistsListProvider — StreamProvider over cached_playlists. Returns
all user-owned + others' public playlists. The 'kind' family arg only
affects the REST cold-cache fetch (server-side filter); drift can't
distinguish 'user' from 'system' kind because systemVariant isn't
persisted. v1 limitation: add-to-playlist sheet may briefly show
system playlists too. Follow-up: add systemVariant column + schema bump.

playlistDetailProvider — async* over the playlist watch stream + a
one-shot tracks query per emission (joins playlist_tracks → tracks →
artists → albums for snapshot fields). Cold-cache fallback inserts
playlist + playlist_tracks rows in one batch, then awaits re-emission.

Pull-to-refresh on these screens now triggers re-subscription rather
than forcing a REST fetch — drift is the source of truth and stays
fresh via SyncController's delta sync. Documented behaviour shift; if
operator wants force-refetch we can add it via a sync trigger later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 11:27:49 -04:00
bvandeusen 078520f504 feat(flutter): systemPlaylistsStatusProvider
Riverpod FutureProvider that reads the caller's system-playlists
build state. Consumed by the home Playlists row to decide between
real and placeholder cards.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:36:29 -04:00
bvandeusen 5613fec5ef fix(flutter): PlaylistsApi.list parses {owned, public} envelope
The server has always returned an enveloped response from GET
/api/playlists; the existing client decoded it as a flat List which
fails at runtime against the actual Map shape. Existing playlists
list screen would have been broken on any production instance.

list() now returns PlaylistsList { owned, public }. The existing
list screen consumes lists.all (owned + public flattened) — same
visible output as the previous flat-list assumption.

This unblocks the home parity slice which needs the same endpoint
for the new Playlists row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:35:34 -04:00
bvandeusen 5541171e94 feat(flutter): playlists list + detail screens
- models/playlist.dart (Playlist, PlaylistTrack, PlaylistDetail)
- api/endpoints/playlists.dart (list with kind=user|system|all, get detail)
- playlists/playlists_provider.dart (Riverpod family providers)
- playlists/playlists_list_screen.dart (with system-variant pill)
- playlists/playlist_detail_screen.dart (header + Play button + rows)
- /playlists + /playlists/:id routes wired
- Home AppBar: queue_music icon next to Search

Tracks with track_id=null render greyed-out + struck-through (matches
web's PlaylistTrackRow behavior). Tap a row plays from that position;
top-level Play button plays the full playable subset from track 0.
2026-05-08 14:33:53 -04:00