fix(player): player bar context menu acts on the current song, not its album (#1117)

* fix(player): player bar context menu acts on the current song, not its album

Right-clicking the current track in the player bar built an album object
from the playing track and opened the album context menu, so "Add to
playlist" added the whole album instead of the song. It now opens a
song-scoped menu for the current track. Left-click on the title still
navigates to the album.

* docs(changelog): note player bar add-to-playlist fix (#1117)
This commit is contained in:
Psychotoxical
2026-06-17 17:07:47 +02:00
committed by GitHub
parent 116196f0d4
commit 44d373d7bb
3 changed files with 32 additions and 12 deletions
+19
View File
@@ -144,6 +144,25 @@ describe('PlayerBar — control wiring', () => {
});
});
describe('PlayerBar — current track context menu', () => {
it('right-clicking the track name opens a song-scoped menu for the current track (#1116)', () => {
const track = makeTrack({ id: 'cur', albumId: 'alb-1' });
usePlayerStore.setState({ currentTrack: track, isPlaying: true });
const spy = vi.spyOn(usePlayerStore.getState(), 'openContextMenu');
const { container } = renderWithProviders(<PlayerBar />);
const trackName = container.querySelector('.player-track-name');
expect(trackName).not.toBeNull();
fireEvent.contextMenu(trackName!);
expect(spy).toHaveBeenCalledTimes(1);
const [, , item, type, , , , , pin] = spy.mock.calls[0];
expect(item).toBe(track);
expect(type).toBe('song');
expect(pin).toBe(true);
});
});
describe('PlayerBar — empty state (no current track)', () => {
it('still renders the region landmark when no track is loaded', () => {
usePlayerStore.setState({ currentTrack: null, isPlaying: false });
+7 -12
View File
@@ -1,7 +1,7 @@
import { Cast, Heart, Maximize2, Music } from 'lucide-react';
import type { TFunction } from 'i18next';
import { queueSongRating } from '../../store/pendingStarSync';
import type { InternetRadioStation, SubsonicAlbum, SubsonicOpenArtistRef } from '../../api/subsonicTypes';
import type { InternetRadioStation, SubsonicOpenArtistRef } from '../../api/subsonicTypes';
import type { PlayerState, Track } from '../../store/playerStoreTypes';
import type { RadioMetadata } from '../../hooks/useRadioMetadata';
import type { PreviewingTrack } from '../../store/previewStore';
@@ -137,19 +137,14 @@ export function PlayerTrackInfo({
className="player-track-name"
style={{ cursor: !isRadio && !showPreviewMeta && currentTrack?.albumId ? 'pointer' : 'default' }}
onClick={() => !isRadio && !showPreviewMeta && currentTrack?.albumId && navigate(`/album/${currentTrack.albumId}`)}
onContextMenu={!isRadio && !showPreviewMeta && currentTrack?.albumId
onContextMenu={!isRadio && !showPreviewMeta && currentTrack
? (e) => {
e.preventDefault();
const album: SubsonicAlbum = {
id: currentTrack.albumId!,
name: currentTrack.album,
artist: currentTrack.artist,
artistId: currentTrack.artistId ?? '',
coverArt: currentTrack.coverArt,
songCount: 0,
duration: 0,
};
openContextMenu(e.clientX, e.clientY, album, 'album', undefined, undefined, undefined, undefined, true);
// The player bar represents the current song, so its menu is
// song-scoped (e.g. "Add to playlist" adds this track, not the
// whole album). pinToPlaybackServer: the track plays from the
// playback server, which may differ from the active one.
openContextMenu(e.clientX, e.clientY, currentTrack, 'song', undefined, undefined, undefined, undefined, true);
}
: undefined}
/>