From 56d025f15061e093f40c9a7ede1afc3542d62429 Mon Sep 17 00:00:00 2001 From: kveld9 Date: Wed, 15 Apr 2026 12:36:32 -0300 Subject: [PATCH] added context menu to search panel songs --- src/components/ContextMenu.tsx | 16 ++++++++++++++-- src/pages/AdvancedSearch.tsx | 28 ++++++++++++++++++++++++---- src/pages/PlaylistDetail.tsx | 20 +++++++++++++------- src/pages/SearchResults.tsx | 28 ++++++++++++++++++++++++---- 4 files changed, 75 insertions(+), 17 deletions(-) diff --git a/src/components/ContextMenu.tsx b/src/components/ContextMenu.tsx index 9791fd89..97054899 100644 --- a/src/components/ContextMenu.tsx +++ b/src/components/ContextMenu.tsx @@ -91,16 +91,28 @@ export function AddToPlaylistSubmenu({ songIds, onDone, dropDown, triggerId }: { const newIds = songIds.filter((id) => !existingIds.has(id)); if (newIds.length > 0) { await updatePlaylist(pl.id, [...songs.map((s) => s.id), ...newIds]); + showToast(t('playlists.addSuccess', { count: newIds.length, playlist: pl.name })); + } else { + showToast(t('playlists.addAllSkipped', { count: songIds.length, playlist: pl.name }), 3000, 'info'); } touchPlaylist(pl.id); - } catch {} + } catch { + showToast(t('playlists.addError'), 3000, 'error'); + } setAdding(null); onDone(); }; const handleCreate = async () => { const name = newName.trim() || t('playlists.unnamed'); - await createPlaylist(name, songIds); + try { + const pl = await createPlaylist(name, songIds); + if (pl?.id) { + showToast(t('playlists.createAndAddSuccess', { count: songIds.length, playlist: pl.name || name })); + } + } catch { + showToast(t('playlists.createError'), 3000, 'error'); + } onDone(); }; diff --git a/src/pages/AdvancedSearch.tsx b/src/pages/AdvancedSearch.tsx index dcbb82c9..d0189bfd 100644 --- a/src/pages/AdvancedSearch.tsx +++ b/src/pages/AdvancedSearch.tsx @@ -12,6 +12,7 @@ import ArtistRow from '../components/ArtistRow'; import CustomSelect from '../components/CustomSelect'; import { useDragDrop } from '../contexts/DragDropContext'; import { useAuthStore } from '../store/authStore'; +import { useShallow } from 'zustand/react/shallow'; type ResultType = 'all' | 'artists' | 'albums' | 'songs'; @@ -35,8 +36,23 @@ export default function AdvancedSearch() { const qFromUrl = params.get('q') ?? ''; const navigate = useNavigate(); const psyDrag = useDragDrop(); - const playTrack = usePlayerStore(s => s.playTrack); - const openContextMenu = usePlayerStore(s => s.openContextMenu); + const total = results + ? results.artists.length + results.albums.length + results.songs.length + : 0; + + const { playTrack, openContextMenu } = usePlayerStore( + useShallow(s => ({ + playTrack: s.playTrack, + openContextMenu: s.openContextMenu, + })) + ); + + const [contextMenuSongId, setContextMenuSongId] = useState(null); + const contextMenuOpen = usePlayerStore(s => s.contextMenu.isOpen); + + useEffect(() => { + if (!contextMenuOpen) setContextMenuSongId(null); + }, [contextMenuOpen]); const [query, setQuery] = useState(params.get('q') ?? ''); const [genre, setGenre] = useState(''); @@ -288,11 +304,15 @@ export default function AdvancedSearch() { return (
playTrack(track, results.songs.map(songToTrack))} role="row" - onContextMenu={e => { e.preventDefault(); openContextMenu(e.clientX, e.clientY, track, 'song'); }} + onContextMenu={e => { + e.preventDefault(); + setContextMenuSongId(song.id); + openContextMenu(e.clientX, e.clientY, track, 'song'); + }} onMouseDown={e => { if (e.button !== 0) return; e.preventDefault(); diff --git a/src/pages/PlaylistDetail.tsx b/src/pages/PlaylistDetail.tsx index 90017df8..62e9a40a 100644 --- a/src/pages/PlaylistDetail.tsx +++ b/src/pages/PlaylistDetail.tsx @@ -366,6 +366,7 @@ export default function PlaylistDetail() { savePlaylist(next); setSuggestions(prev => prev.filter(s => s.id !== song.id)); setSearchResults(prev => prev.filter(s => s.id !== song.id)); + showToast(t('playlists.addSuccess', { count: 1, playlist: playlist?.name })); }; // ── Rating / Star ───────────────────────────────────────────── @@ -759,13 +760,18 @@ export default function PlaylistDetail() { )} {searchResults.map(song => { const isSelected = selectedSearchIds.has(song.id); - return ( -
addSong(song)} - > + return ( +
addSong(song)} + onContextMenu={e => { + e.preventDefault(); + setContextMenuSongId(song.id); + openContextMenu(e.clientX, e.clientY, songToTrack(song), 'album-song'); + }} + > (null); const [loading, setLoading] = useState(false); - const playTrack = usePlayerStore(s => s.playTrack); - const currentTrack = usePlayerStore(s => s.currentTrack); - const psyDrag = useDragDrop(); const musicLibraryFilterVersion = useAuthStore(s => s.musicLibraryFilterVersion); const showBitrate = useThemeStore(s => s.showBitrate); + const psyDrag = useDragDrop(); + + const { playTrack, enqueue, openContextMenu, currentTrack } = usePlayerStore( + useShallow(s => ({ + playTrack: s.playTrack, + enqueue: s.enqueue, + openContextMenu: s.openContextMenu, + currentTrack: s.currentTrack, + })) + ); + + const [contextMenuSongId, setContextMenuSongId] = useState(null); + const contextMenuOpen = usePlayerStore(s => s.contextMenu.isOpen); + + useEffect(() => { + if (!contextMenuOpen) setContextMenuSongId(null); + }, [contextMenuOpen]); useEffect(() => { if (!query.trim()) { setResults(null); return; } @@ -84,9 +99,14 @@ export default function SearchResults() { {results.songs.map(song => (
playSong(song, results.songs)} + onContextMenu={e => { + e.preventDefault(); + setContextMenuSongId(song.id); + openContextMenu(e.clientX, e.clientY, songToTrack(song), 'album-song'); + }} role="row" onMouseDown={e => { if (e.button !== 0) return;