From 9a2a9343d2709882142df62c51470d77defd16da Mon Sep 17 00:00:00 2001 From: JSC Date: Sat, 4 Oct 2025 19:16:25 +0200 Subject: [PATCH] feat: add context menu to Playlist for adding tracks to play next queue --- src/components/player/Playlist.tsx | 137 +++++++++++++++++------------ src/lib/api/services/player.ts | 8 ++ 2 files changed, 88 insertions(+), 57 deletions(-) diff --git a/src/components/player/Playlist.tsx b/src/components/player/Playlist.tsx index 9144253..5b59275 100644 --- a/src/components/player/Playlist.tsx +++ b/src/components/player/Playlist.tsx @@ -3,11 +3,17 @@ import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { ScrollArea } from '@/components/ui/scroll-area' +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuTrigger, +} from '@/components/ui/context-menu' import { filesService } from '@/lib/api/services/files' -import { type PlayerPlaylist } from '@/lib/api/services/player' +import { type PlayerPlaylist, playerService } from '@/lib/api/services/player' import { cn } from '@/lib/utils' import { formatDuration } from '@/utils/format-duration' -import { Music, Play, Search, X } from 'lucide-react' +import { Music, Play, Search, X, ListPlus } from 'lucide-react' interface PlaylistProps { playlist: PlayerPlaylist @@ -28,6 +34,14 @@ export function Playlist({ sound.name.toLowerCase().includes(searchQuery.toLowerCase()) ) + const handleAddToPlayNext = async (soundId: number) => { + try { + await playerService.addToPlayNext(soundId) + } catch (error) { + console.error('Failed to add track to play next:', error) + } + } + return (
{/* Header */} @@ -68,64 +82,73 @@ export function Playlist({ {filteredSounds.map((sound) => { const originalIndex = playlist.sounds.findIndex((s) => s.id === sound.id) return ( -
onTrackSelect(originalIndex)} - > - {/* Track number/play icon - 1 column */} -
- {currentIndex === originalIndex ? ( - - ) : ( - {originalIndex + 1} - )} -
+ + +
onTrackSelect(originalIndex)} + > + {/* Track number/play icon - 1 column */} +
+ {currentIndex === originalIndex ? ( + + ) : ( + {originalIndex + 1} + )} +
- {/* Thumbnail - 1 column */} -
-
- {sound.thumbnail ? ( - - ) : ( - - )} -
-
+ {/* Thumbnail - 1 column */} +
+
+ {sound.thumbnail ? ( + + ) : ( + + )} +
+
- {/* Track name - 6 columns (takes most space) */} -
- - {sound.name} - -
+ {/* Track name - 6 columns (takes most space) */} +
+ + {sound.name} + +
- {/* Duration - 2 columns */} -
- - {formatDuration(sound.duration)} - -
-
- )})} + {/* Duration - 2 columns */} +
+ + {formatDuration(sound.duration)} + +
+
+ + + handleAddToPlayNext(sound.id)}> + + Add to play next + + + + )})}
diff --git a/src/lib/api/services/player.ts b/src/lib/api/services/player.ts index e8c90e6..2461332 100644 --- a/src/lib/api/services/player.ts +++ b/src/lib/api/services/player.ts @@ -38,6 +38,7 @@ export interface PlayerState { index?: number current_sound?: PlayerSound playlist?: PlayerPlaylist + play_next_queue: PlayerSound[] } export interface PlayerSeekRequest { @@ -147,6 +148,13 @@ export class PlayerService { async getState(): Promise { return apiClient.get('/api/v1/player/state') } + + /** + * Add a sound to the play next queue + */ + async addToPlayNext(soundId: number): Promise { + return apiClient.post(`/api/v1/player/play-next/${soundId}`) + } } export const playerService = new PlayerService()