diff --git a/CHANGELOG.md b/CHANGELOG.md index c87353c9..76ded711 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -695,6 +695,14 @@ Foundational work: faster reviews, narrower diffs, and a safety net under the pa * Same scroll-margin bug as the one fixed by [#764](https://github.com/Psychotoxical/psysonic/pull/764) for the Album Detail "More by …" rail, on four more virtual lists: **Artists grid**, **Artists list**, **Composers list** and the **Tracks** virtual song browser. The virtual wrapper sat below the sticky page header but TanStack measured row positions from the scroll-element top — rows still on screen could unmount, and at larger header offsets the list refused to render at all. * The measurement is now a shared `useVirtualizerScrollMargin` hook used by every virtual-list call-site (including the existing `VirtualCardGrid` fix from #764). +### Album cards — per-artist click on multi-artist albums + +**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#767](https://github.com/Psychotoxical/psysonic/pull/767)** + +* The artist subtitle under an album card rendered a multi-artist string as a single link to the album's primary `artistId`. On an artist-detail page that id is the page's own artist, so the click resolved to the current URL and the router silently no-op'd — the cursor said clickable, nothing happened. +* Album cards now use the same `OpenArtistRefInline` component the album-detail header uses: each artist becomes its own ·-separated link. Behaviour on servers that don't expose the structured list is unchanged. +* Root cause was a stale field name: psysonic's internal type called the OpenSubsonic album-artist array `albumArtists`, but the spec (and Navidrome) returns it as `artists`, so the structured branch never fired and the song-level fallback was carrying the album-detail header on its own. + ## [1.45.0] - 2026-05-04 ## Added diff --git a/src/api/subsonicTypes.ts b/src/api/subsonicTypes.ts index 1c6f5b82..15e2258a 100644 --- a/src/api/subsonicTypes.ts +++ b/src/api/subsonicTypes.ts @@ -20,8 +20,10 @@ export interface SubsonicAlbum { isCompilation?: boolean; /** OpenSubsonic: release types from MusicBrainz tags (e.g. "Album", "EP", "Single", "Compilation", "Live"). */ releaseTypes?: string[]; - /** OpenSubsonic: album-level credits (Navidrome may attach on album and/or child songs). */ - albumArtists?: SubsonicOpenArtistRef[]; + /** OpenSubsonic: structured album-artist credits (e.g. featured guests on the album). */ + artists?: SubsonicOpenArtistRef[]; + /** OpenSubsonic: single-string album-artist for display (mirrors `artists` joined). */ + displayArtist?: string; /** OpenSubsonic: per-disc subtitles (e.g. "Sessions" on CD 3 of a deluxe edition). */ discTitles?: SubsonicDiscTitle[]; } diff --git a/src/components/AlbumCard.tsx b/src/components/AlbumCard.tsx index 6578d635..01ba050f 100644 --- a/src/components/AlbumCard.tsx +++ b/src/components/AlbumCard.tsx @@ -10,9 +10,11 @@ import { usePlayerStore } from '../store/playerStore'; import { useOfflineStore } from '../store/offlineStore'; import { useAuthStore } from '../store/authStore'; import CachedImage from './CachedImage'; +import { OpenArtistRefInline } from './OpenArtistRefInline'; import { playAlbum } from '../utils/playback/playAlbum'; import { useDragDrop } from '../contexts/DragDropContext'; import { isAlbumRecentlyAdded } from '../utils/albumRecency'; +import { deriveAlbumArtistRefs } from '../utils/album/deriveAlbumHeaderArtistRefs'; interface AlbumCardProps { album: SubsonicAlbum; @@ -56,6 +58,7 @@ function AlbumCard({ ); const psyDrag = useDragDrop(); const isNewAlbum = isAlbumRecentlyAdded(album.created); + const artistRefs = useMemo(() => deriveAlbumArtistRefs(album), [album]); const handleClick = (opts?: { shiftKey?: boolean }) => { if (selectionMode) { onToggleSelect?.(album.id, opts); return; } @@ -163,11 +166,16 @@ function AlbumCard({
{album.name}
-{ if (album.artistId) { e.stopPropagation(); navigate(`/artist/${album.artistId}`); } }} - >{album.artist}
+
+
{album.year}
} {showRating && (album.userRating ?? 0) > 0 && (