diff --git a/channel.ts b/channel.ts index 78d191a..5de40a9 100644 --- a/channel.ts +++ b/channel.ts @@ -305,6 +305,44 @@ export class Channel { this.broadcast(); } + moveTracks(indices: number[], targetIndex: number) { + if (indices.length === 0) return; + + // Get the tracks being moved + const sorted = [...indices].sort((a, b) => a - b); + const tracksToMove = sorted.map(i => this.queue[i]).filter(Boolean); + if (tracksToMove.length === 0) return; + + const currentTrackId = this.currentTrack?.id; + + // Remove tracks from their current positions (from end to preserve indices) + for (let i = sorted.length - 1; i >= 0; i--) { + this.queue.splice(sorted[i], 1); + } + + // Adjust target index for removed items that were before it + let adjustedTarget = targetIndex; + for (const idx of sorted) { + if (idx < targetIndex) adjustedTarget--; + } + + // Insert at new position + this.queue.splice(adjustedTarget, 0, ...tracksToMove); + + // Update currentIndex to follow the currently playing track + if (currentTrackId) { + const newIndex = this.queue.findIndex(t => t.id === currentTrackId); + if (newIndex !== -1) { + this.currentIndex = newIndex; + } + } + + this.queueDirty = true; + this.persistQueue(); + this.persistState(); + this.broadcast(); + } + broadcast() { const now = Date.now(); const includeQueue = this.queueDirty || (now - this.lastQueueBroadcast >= 60000); diff --git a/public/trackContainer.js b/public/trackContainer.js index 30ad0b0..05ecc2b 100644 --- a/public/trackContainer.js +++ b/public/trackContainer.js @@ -276,6 +276,7 @@ // Drag start/end handlers - library always, queue/playlist with permissions const canDrag = type === 'library' || (type === 'queue' && canEditQueue) || (type === 'playlist' && isPlaylistOwner); + console.log(`[Drag] wireTrackEvents: type=${type} canDrag=${canDrag} canEditQueue=${canEditQueue}`); if (canDrag) { div.ondragstart = (e) => handleDragStart(e, track, originalIndex, div); div.ondragend = (e) => handleDragEnd(e, div); @@ -283,6 +284,7 @@ // Drop handlers - queue and playlist accept drops if (canEditQueue && type === 'queue') { + console.log(`[Drag] Wiring drop handlers for queue track ${originalIndex}`); div.ondragover = (e) => handleDragOver(e, div, originalIndex); div.ondragleave = (e) => handleDragLeave(e, div); div.ondrop = (e) => handleDrop(e, div, originalIndex); @@ -339,6 +341,7 @@ } function handleDragStart(e, track, index, div) { + console.log(`[Drag] handleDragStart: type=${type} index=${index} track=${track.title || track.filename}`); isDragging = true; const trackId = track.id || track.filename; dragSource = type; @@ -415,20 +418,28 @@ } function handleDrop(e, div, index) { + console.log(`[Drag] handleDrop: type=${type} index=${index} dropTargetIndex=${dropTargetIndex} dragSource=${dragSource} draggedIndices=${draggedIndices}`); e.preventDefault(); e.stopPropagation(); div.classList.remove("drop-above", "drop-below"); element.classList.remove("drop-target"); - if (dropTargetIndex === null) return; + if (dropTargetIndex === null) { + console.log(`[Drag] handleDrop: dropTargetIndex is null, aborting`); + return; + } if (type === 'queue') { if (dragSource === 'queue' && draggedIndices.length > 0) { // Reorder within queue const minDragged = Math.min(...draggedIndices); const maxDragged = Math.max(...draggedIndices); + console.log(`[Drag] Reorder check: dropTargetIndex=${dropTargetIndex} minDragged=${minDragged} maxDragged=${maxDragged}`); if (dropTargetIndex < minDragged || dropTargetIndex > maxDragged + 1) { + console.log(`[Drag] Calling reorderQueue(${draggedIndices}, ${dropTargetIndex})`); reorderQueue(draggedIndices, dropTargetIndex); + } else { + console.log(`[Drag] Skipping reorder - dropping on self`); } } else if ((dragSource === 'library' || dragSource === 'playlist') && draggedTrackIds.length > 0) { // Insert tracks from library or playlist diff --git a/routes/channels.ts b/routes/channels.ts index 71ec552..acfaf08 100644 --- a/routes/channels.ts +++ b/routes/channels.ts @@ -221,7 +221,7 @@ export async function handleModifyQueue(req: Request, server: any, channelId: st try { const body = await req.json(); - const { add, remove, set, insertAt } = body; + const { add, remove, set, insertAt, move, to } = body; if (Array.isArray(set)) { const tracks = buildTracksFromIds(set, state.library); @@ -229,6 +229,12 @@ export async function handleModifyQueue(req: Request, server: any, channelId: st return Response.json({ success: true, queueLength: channel.queue.length }); } + // Move/reorder tracks within queue + if (Array.isArray(move) && typeof to === "number") { + channel.moveTracks(move, to); + return Response.json({ success: true, queueLength: channel.queue.length }); + } + if (Array.isArray(remove) && remove.length > 0) { const indices = remove.filter((i: unknown) => typeof i === "number"); channel.removeTracksByIndex(indices);