fix(player): show live track metadata, not the stale queue snapshot
The queue slice stores denormalized display fields captured at play-time, so the player and queue panel kept showing pre-enrichment title/artist after a track's metadata was updated — the library (RTKQ cache) and the player disagreed. Add useResolvedQueueEntry: read through to the RTKQ Track cache and prefer its fresh values, keeping the snapshot only as instant/offline fallback. Wire it into PersistentPlayer (now-playing + cover) and QueuePanel (now-playing + up-next rows), so enrichment updates reach the player through the same Track tags that refresh the library. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -7,8 +7,10 @@ import {
|
||||
goToIndex,
|
||||
removeFromQueue,
|
||||
clearQueue,
|
||||
type QueueEntry,
|
||||
} from '../../store/slices/queue';
|
||||
import { toggleQueue } from '../../store/slices/player';
|
||||
import { useResolvedQueueEntry } from '../../hooks/useResolvedQueueEntry';
|
||||
|
||||
export function QueuePanel() {
|
||||
const { t } = useTranslation();
|
||||
@@ -16,8 +18,9 @@ export function QueuePanel() {
|
||||
const queue = useAppSelector((s) => s.queue);
|
||||
const isOpen = useAppSelector((s) => s.player.isQueueOpen);
|
||||
|
||||
const now =
|
||||
const nowEntry =
|
||||
queue.currentIndex >= 0 ? queue.entries[queue.currentIndex] : undefined;
|
||||
const now = useResolvedQueueEntry(nowEntry);
|
||||
const upNext = queue.entries
|
||||
.map((entry, index) => ({ entry, index }))
|
||||
.filter(({ index }) => index > queue.currentIndex);
|
||||
@@ -120,33 +123,12 @@ export function QueuePanel() {
|
||||
<div className="qd-empty">{t('queue.nothingNext')}</div>
|
||||
) : (
|
||||
upNext.map(({ entry, index }) => (
|
||||
<div
|
||||
<QueueRow
|
||||
key={`${entry.trackId}-${index}`}
|
||||
className="qrow"
|
||||
onDoubleClick={() => dispatch(goToIndex(index))}
|
||||
title={t('queue.doubleClickPlay')}
|
||||
>
|
||||
<span className="grip">
|
||||
<Icon name="dots-six-vertical" />
|
||||
</span>
|
||||
<ArtTile
|
||||
seed={entry.albumTitle}
|
||||
size={36}
|
||||
label={entry.albumTitle}
|
||||
/>
|
||||
<div className="qt">
|
||||
<div className="t">{entry.title}</div>
|
||||
<div className="r">{entry.artistName}</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="iconbtn sm"
|
||||
onClick={() => dispatch(removeFromQueue(index))}
|
||||
title={t('queue.removeFromQueue')}
|
||||
>
|
||||
<Icon name="x" />
|
||||
</button>
|
||||
</div>
|
||||
entry={entry}
|
||||
onPlay={() => dispatch(goToIndex(index))}
|
||||
onRemove={() => dispatch(removeFromQueue(index))}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
|
||||
@@ -162,3 +144,40 @@ export function QueuePanel() {
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
/** An "up next" row, resolving its display fields against the live Track cache
|
||||
* (same read-through as the now-playing entry) so enrichment updates show. */
|
||||
function QueueRow({
|
||||
entry,
|
||||
onPlay,
|
||||
onRemove,
|
||||
}: {
|
||||
entry: QueueEntry;
|
||||
onPlay: () => void;
|
||||
onRemove: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const resolved = useResolvedQueueEntry(entry);
|
||||
const albumTitle = resolved?.albumTitle ?? entry.albumTitle;
|
||||
|
||||
return (
|
||||
<div className="qrow" onDoubleClick={onPlay} title={t('queue.doubleClickPlay')}>
|
||||
<span className="grip">
|
||||
<Icon name="dots-six-vertical" />
|
||||
</span>
|
||||
<ArtTile seed={albumTitle} size={36} label={albumTitle} />
|
||||
<div className="qt">
|
||||
<div className="t">{resolved?.title ?? entry.title}</div>
|
||||
<div className="r">{resolved?.artistName ?? entry.artistName}</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="iconbtn sm"
|
||||
onClick={onRemove}
|
||||
title={t('queue.removeFromQueue')}
|
||||
>
|
||||
<Icon name="x" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user