From 5f0803e98abbb3b49860ba7dd2976fa194719bdd Mon Sep 17 00:00:00 2001 From: cucadmuh <49571317+cucadmuh@users.noreply.github.com> Date: Thu, 14 May 2026 23:12:55 +0300 Subject: [PATCH] fix(ui): stable React list keys on Now Playing cards (#703) * fix(ui): use stable list keys on Now Playing dashboard cards Subsonic payloads can repeat the same id in similar artists, album track rows, and top songs. Keys now combine id with list index so React reconciliation stays stable and duplicate-key warnings stop. * docs: changelog and credits for Now Playing list keys (PR #703) Document the dashboard list key fix in CHANGELOG and Settings contributors. --- CHANGELOG.md | 6 ++++++ src/components/nowPlaying/AlbumCard.tsx | 4 ++-- src/components/nowPlaying/ArtistCard.tsx | 4 ++-- src/components/nowPlaying/TopSongsCard.tsx | 2 +- src/config/settingsCredits.ts | 1 + 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ac99738..7acd06e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -289,6 +289,12 @@ Foundational work: faster reviews, narrower diffs, and a safety net under the pa * **Add Station** / **Edit** now mount **`RadioEditModal`** with **`createPortal(..., document.body)`** (same layering approach as **Search Directory**). Previously the overlay lived under nested **`.content-body`** with **`contain: paint`**, so on an **empty** station list the fixed overlay was **painted inside a short box** and looked cropped; after the first station the layout was tall enough that the bug was easy to miss. +### Now Playing — stable list keys on dashboard cards (duplicate React `key` warnings) + +**By [@cucadmuh](https://github.com/cucadmuh), PR [#703](https://github.com/Psychotoxical/psysonic/pull/703)** + +* **Similar artists**, the **track list inside the in-player album card**, and **Top songs** can all include **more than one row with the same Subsonic `id`** (server payloads are not always strictly deduped). Those lists used **`key={id}`**, which triggered **duplicate `key`** warnings in React devtools and made reconciliation depend on row order. Keys now combine **`id` with the list index** — rendering only; **no queue or playback change**. + ### Search — hide duplicate artist hits with zero albums **By [@cucadmuh](https://github.com/cucadmuh), thanks to zunoz for the report on the Psysonic Discord, PR [#697](https://github.com/Psychotoxical/psysonic/pull/697)** diff --git a/src/components/nowPlaying/AlbumCard.tsx b/src/components/nowPlaying/AlbumCard.tsx index 57144abe..544397ce 100644 --- a/src/components/nowPlaying/AlbumCard.tsx +++ b/src/components/nowPlaying/AlbumCard.tsx @@ -69,10 +69,10 @@ const AlbumCard = memo(function AlbumCard({ album, songs, currentTrackId, albumN
- {visibleSongs.map(track => { + {visibleSongs.map((track, idx) => { const isActive = track.id === currentTrackId; return ( -
{isActive diff --git a/src/components/nowPlaying/ArtistCard.tsx b/src/components/nowPlaying/ArtistCard.tsx index 59c5a6ec..21725706 100644 --- a/src/components/nowPlaying/ArtistCard.tsx +++ b/src/components/nowPlaying/ArtistCard.tsx @@ -81,8 +81,8 @@ const ArtistCard = memo(function ArtistCard({ artistName, artistId, artistInfo, {similar.length > 0 && (
- {similar.slice(0, 12).map(a => ( - ( + a.id && onNavigate(`/artist/${a.id}`)} data-tooltip={t('nowPlaying.goToArtist')}> {a.name} diff --git a/src/components/nowPlaying/TopSongsCard.tsx b/src/components/nowPlaying/TopSongsCard.tsx index 239c781a..6975af3f 100644 --- a/src/components/nowPlaying/TopSongsCard.tsx +++ b/src/components/nowPlaying/TopSongsCard.tsx @@ -35,7 +35,7 @@ const TopSongsCard = memo(function TopSongsCard({ artistName, artistId, songs, c {top.map((s, idx) => { const isActive = s.id === currentTrackId; return ( -
onPlay(s)} data-tooltip={t('contextMenu.playNow')}> diff --git a/src/config/settingsCredits.ts b/src/config/settingsCredits.ts index 89f052f9..0b338ad1 100644 --- a/src/config/settingsCredits.ts +++ b/src/config/settingsCredits.ts @@ -110,6 +110,7 @@ const CONTRIBUTOR_ENTRIES = [ 'OpenSubsonic albumArtists in album header; track artists in player bar, mobile, mini + songToTrack (#552) (PR #696)', 'Search: filter search3 artist rows with zero albums (report: zunoz on Psysonic Discord) (PR #697)', 'Internet Radio: portal add/edit station modal to document.body — fixes clipped modal on empty library (report: voidboywannabe on Psysonic Discord) (PR #699)', + 'Now Playing: composite list keys on similar artists, album-card tracklist, and top songs — avoids duplicate React keys when Subsonic repeats ids (PR #703)', ], }, {