mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 14:55:43 +00:00
fix: context menu Play Now playback and resize behaviour (#1174)
* fix(playlist): play the playlist from the context menu Play Now action The Playlists-page context menu 'Play Now' only navigated to the playlist detail page instead of starting playback. It now loads the playlist's songs and plays them via the shared playPlaylistAll action (same path as the detail page 'Play All' button), with a guard against load failures. * fix(ui): close the context menu on window resize The context menu is absolutely positioned at fixed coordinates, so resizing the window left it stranded and drifting off-screen. Whether a resize closed it was inconsistent across setups (it stayed open on some Windows and Linux environments). Always close it on resize so the behaviour is the same everywhere. * docs(changelog): context menu Play Now and resize fixes (#1174) * docs(changelog): credit the reporter for the context menu fixes
This commit is contained in:
@@ -127,6 +127,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
|
### Context menu "Play Now" and resize behaviour
|
||||||
|
|
||||||
|
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1174](https://github.com/Psychotoxical/psysonic/pull/1174)**, reported by [@peri4ko](https://github.com/peri4ko)
|
||||||
|
|
||||||
|
* On the Playlists page, right-clicking a playlist and choosing "Play Now" only opened the playlist instead of playing it. It now starts playback.
|
||||||
|
* Resizing the window while a context menu was open could leave the menu stranded and drifting off-screen. The context menu now closes when the window is resized.
|
||||||
|
|
||||||
### Artist header showing the plain image instead of the external background
|
### Artist header showing the plain image instead of the external background
|
||||||
|
|
||||||
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1172](https://github.com/Psychotoxical/psysonic/pull/1172)**
|
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1172](https://github.com/Psychotoxical/psysonic/pull/1172)**
|
||||||
|
|||||||
@@ -132,6 +132,18 @@ export default function ContextMenu() {
|
|||||||
}
|
}
|
||||||
}, [contextMenu.isOpen, contextMenu.x, contextMenu.y]);
|
}, [contextMenu.isOpen, contextMenu.x, contextMenu.y]);
|
||||||
|
|
||||||
|
// Close on any window resize. The menu is absolutely positioned at fixed
|
||||||
|
// coordinates, so a resize would otherwise leave it stranded and drifting
|
||||||
|
// off-screen. Whether a resize closed the menu was inconsistent across
|
||||||
|
// setups (it stayed open on some Windows and Linux environments); always
|
||||||
|
// closing it here makes the behaviour the same everywhere.
|
||||||
|
useEffect(() => {
|
||||||
|
if (!contextMenu.isOpen) return;
|
||||||
|
const onResize = () => closeContextMenu();
|
||||||
|
window.addEventListener('resize', onResize);
|
||||||
|
return () => window.removeEventListener('resize', onResize);
|
||||||
|
}, [contextMenu.isOpen, closeContextMenu]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (contextMenu.isOpen) {
|
if (contextMenu.isOpen) {
|
||||||
previousFocusRef.current = document.activeElement as HTMLElement | null;
|
previousFocusRef.current = document.activeElement as HTMLElement | null;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Play, ChevronRight, FolderTree, ListMusic, Trash2 } from 'lucide-react';
|
import { Play, ChevronRight, FolderTree, ListMusic, Trash2 } from 'lucide-react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import type { SubsonicPlaylist } from '../../api/subsonicTypes';
|
import type { SubsonicPlaylist } from '../../api/subsonicTypes';
|
||||||
import { usePlaylistStore } from '../../store/playlistStore';
|
import { usePlaylistStore } from '../../store/playlistStore';
|
||||||
import { MultiPlaylistToPlaylistSubmenu, SinglePlaylistToPlaylistSubmenu } from './PlaylistToPlaylistSubmenus';
|
import { MultiPlaylistToPlaylistSubmenu, SinglePlaylistToPlaylistSubmenu } from './PlaylistToPlaylistSubmenus';
|
||||||
@@ -16,7 +15,6 @@ export default function PlaylistContextItems(props: ContextMenuItemsProps) {
|
|||||||
offlinePolicy,
|
offlinePolicy,
|
||||||
} = props;
|
} = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -24,7 +22,14 @@ export default function PlaylistContextItems(props: ContextMenuItemsProps) {
|
|||||||
const playlist = item as SubsonicPlaylist;
|
const playlist = item as SubsonicPlaylist;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="context-menu-item" onClick={() => handleAction(() => navigate(`/playlists/${playlist.id}`))}>
|
<div className="context-menu-item" onClick={() => handleAction(async () => {
|
||||||
|
const { playPlaylistById } = await import('../../utils/playlist/playPlaylistById');
|
||||||
|
try {
|
||||||
|
await playPlaylistById(playlist.id);
|
||||||
|
} catch {
|
||||||
|
// Network/load failure — leave playback untouched rather than crash.
|
||||||
|
}
|
||||||
|
})}>
|
||||||
<Play size={14} /> {t('contextMenu.playNow')}
|
<Play size={14} /> {t('contextMenu.playNow')}
|
||||||
</div>
|
</div>
|
||||||
<div className="context-menu-divider" />
|
<div className="context-menu-divider" />
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { getPlaylist } from '../../api/subsonicPlaylists';
|
||||||
|
import { songToTrack } from '../playback/songToTrack';
|
||||||
|
import { usePlayerStore } from '../../store/playerStore';
|
||||||
|
import { usePlaylistStore } from '../../store/playlistStore';
|
||||||
|
import { playPlaylistAll } from './playlistBulkPlayActions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a playlist's songs and start playback immediately ("Play Now").
|
||||||
|
*
|
||||||
|
* Used where only the playlist metadata is on hand — the playlist context menu
|
||||||
|
* on the Playlists overview — so the tracks have to be fetched first. Once
|
||||||
|
* loaded it defers to {@link playPlaylistAll}, the same action the playlist
|
||||||
|
* detail "Play All" button uses, so playback behaviour stays in one place.
|
||||||
|
*/
|
||||||
|
export async function playPlaylistById(id: string): Promise<void> {
|
||||||
|
const { songs } = await getPlaylist(id);
|
||||||
|
const tracks = songs.map(songToTrack);
|
||||||
|
const { playTrack, enqueue } = usePlayerStore.getState();
|
||||||
|
const { touchPlaylist } = usePlaylistStore.getState();
|
||||||
|
playPlaylistAll({ songsLength: tracks.length, id, tracks, touchPlaylist, playTrack, enqueue });
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user