diff --git a/CHANGELOG.md b/CHANGELOG.md index c71874a0..adafc563 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Added +### Player Bar — album context menu on song title right-click + +**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#512](https://github.com/Psychotoxical/psysonic/pull/512)** + +* Right-clicking the **track title** in the player bar now opens the same album context menu that album cards expose — open, play next, enqueue, go to artist, favorite, rate, share, download, add to playlist. +* Mirrors the existing left-click on the title (which already navigates to the album) and is suppressed during radio playback and previews. + ### Settings — OpenDyslexic font option for dyslexic readers **By [@Psychotoxical](https://github.com/Psychotoxical), PR [#507](https://github.com/Psychotoxical/psysonic/pull/507)** diff --git a/src/components/MarqueeText.tsx b/src/components/MarqueeText.tsx index 8ea87507..d199d7d3 100644 --- a/src/components/MarqueeText.tsx +++ b/src/components/MarqueeText.tsx @@ -6,9 +6,10 @@ interface Props { className?: string; style?: React.CSSProperties; onClick?: () => void; + onContextMenu?: (e: React.MouseEvent) => void; } -export default function MarqueeText({ text, className, style, onClick }: Props) { +export default function MarqueeText({ text, className, style, onClick, onContextMenu }: Props) { const containerRef = useRef(null); const textRef = useRef(null); const [scrollAmount, setScrollAmount] = useState(0); @@ -40,6 +41,7 @@ export default function MarqueeText({ text, className, style, onClick }: Props) className={`marquee-wrap${className ? ` ${className}` : ''}`} style={style} onClick={onClick} + onContextMenu={onContextMenu} > ({ currentTrack: s.currentTrack, currentRadio: s.currentRadio, @@ -105,6 +106,7 @@ export default function PlayerBar() { setStarredOverride: s.setStarredOverride, userRatingOverrides: s.userRatingOverrides, setUserRatingOverride: s.setUserRatingOverride, + openContextMenu: s.openContextMenu, }))); const { lastfmSessionKey } = useAuthStore(); const floatingPlayerBar = useThemeStore(s => s.floatingPlayerBar); @@ -382,6 +384,21 @@ export default function PlayerBar() { 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 + ? (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} />