3df5e5cb3c
android / Build + lint + test (push) Successful in 3m48s
Two independent bugs surfaced together: Bug 1: like button on tracks in playlist/album detail screens didn't reflect actual liked state. LikesRepository.observeLikedTracks() does a mapNotNull join against trackDao - a liked track whose row isn't in the local cache yet (e.g. liked via web/notification, cache not sync'd) gets DROPPED from the returned list. PlaylistDetailViewModel + AlbumDetailViewModel both used that as their like-set source, so those rows showed as not-liked. Adds LikesRepository.observeLikedTrackIds(): Flow<Set<String>> that hits the DAO directly via observeLikedIdsOfType - no trackDao join, no drops. The two ViewModels switch to it. LikedTab continues to use observeLikedTracks because it needs the full TrackRef to render. Bug 2: playlist cover art didn't render on the playlist detail header. Server's derivePlaylistView returns CoverURL as the relative path "/api/playlists/<id>/cover". PlaylistsRepository's two domain mappers (CachedPlaylistEntity.toDomain + PlaylistDetailWire .toPlaylistRef) stored it verbatim - Coil's AsyncImage can't fetch a relative URL with no base, so the image silently failed. Wraps the coverPath/coverUrl through resolveServerUrl so the placeholder.invalid host triggers BaseUrlInterceptor's live-server rewrite, same idiom every other cover surface (album / artist / track / playlist track rows) already uses. System-playlist 24h refresh investigation pending - need to know how you verified (server logs, DB state, or client-visible content) before I can dig into the right layer.