mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
fix(album-card): per-artist click on multi-artist albums (#767)
* refactor(album): rename SubsonicAlbum.albumArtists to artists per OpenSubsonic spec
The OpenSubsonic AlbumID3 object exposes structured album-artist credits as
`artists` (not `albumArtists`); psysonic's internal type used the wrong name,
so `album.albumArtists` was always undefined on the Subsonic responses
Navidrome returns. `deriveAlbumHeaderArtistRefs` therefore never hit its
album-level branch — only the song-level fallback was carrying the multi-
artist split on the album-detail header.
- Rename the field on `SubsonicAlbum` and add the spec-defined `displayArtist`
string alongside it.
- Extract `deriveAlbumArtistRefs(album)` for the common case where only the
album object is available (cards, rails); `deriveAlbumHeaderArtistRefs`
now uses it after the song-level fallback. The song-level `albumArtists`
field is unchanged (the spec does name it that way on child songs).
- Tests cover both helpers and the legacy `artist` + `artistId` fallback.
* fix(album-card): per-artist click on multi-artist albums
The artist subtitle under an album card rendered the full `album.artist`
string ("Melvins • Napalm Death") as a single link that always navigated to
`album.artistId`. On the artist-detail page that id is the page's own
artist — so the click resolved to the URL the user was already on and the
router silently no-op'd, with no visible effect.
Render through the existing `OpenArtistRefInline` component instead, fed
from the structured `artists` array via `deriveAlbumArtistRefs`. Each
artist becomes its own ·-separated link; the single-artist fallback is the
same legacy `artist` + `artistId` pair as before, so the behaviour on
servers that don't expose the structured field is unchanged.
* docs(changelog): note PR #767 under Fixed in v1.46.0
This commit is contained in:
committed by
GitHub
parent
48a69754d6
commit
6e0f076f43
@@ -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({
|
||||
</div>
|
||||
<div className="album-card-info">
|
||||
<p className="album-card-title truncate">{album.name}</p>
|
||||
<p
|
||||
className={`album-card-artist truncate${album.artistId ? ' track-artist-link' : ''}`}
|
||||
style={{ cursor: album.artistId ? 'pointer' : 'default' }}
|
||||
onClick={e => { if (album.artistId) { e.stopPropagation(); navigate(`/artist/${album.artistId}`); } }}
|
||||
>{album.artist}</p>
|
||||
<p className="album-card-artist truncate">
|
||||
<OpenArtistRefInline
|
||||
refs={artistRefs}
|
||||
fallbackName={album.artist}
|
||||
onGoArtist={id => navigate(`/artist/${id}`)}
|
||||
as="none"
|
||||
linkTag="span"
|
||||
linkClassName="track-artist-link"
|
||||
/>
|
||||
</p>
|
||||
{album.year && <p className="album-card-year">{album.year}</p>}
|
||||
{showRating && (album.userRating ?? 0) > 0 && (
|
||||
<div className="album-card-rating-row">
|
||||
|
||||
Reference in New Issue
Block a user