feat: add all to queue — show "Add All to Queue" when a song is playing

Replace "Play All" with "Add All to Queue" button on offline page when
a song is already playing, so it doesn't interrupt playback. Adds
ADD_ALL_TO_QUEUE batch action to music provider.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-31 21:37:30 -07:00
parent 02278f4cf8
commit c2af4327aa
2 changed files with 24 additions and 5 deletions

View File

@ -11,6 +11,7 @@ import {
Download,
HardDrive,
Loader2,
ListPlus,
Play,
RefreshCw,
Trash2,
@ -27,7 +28,7 @@ function formatBytes(bytes: number) {
}
export default function OfflinePage() {
const { state, playTrack } = useMusicPlayer()
const { state, playTrack, addAllToQueue } = useMusicPlayer()
const {
offlineTracks,
queue,
@ -107,10 +108,17 @@ export default function OfflinePage() {
</Button>
{offlineTracks.length > 0 && (
<>
<Button size="sm" onClick={playAllOffline}>
<Play className="h-4 w-4 mr-1.5" />
Play All
</Button>
{state.currentTrack ? (
<Button size="sm" variant="outline" onClick={() => addAllToQueue(offlineTracks)}>
<ListPlus className="h-4 w-4 mr-1.5" />
Add All to Queue
</Button>
) : (
<Button size="sm" onClick={playAllOffline}>
<Play className="h-4 w-4 mr-1.5" />
Play All
</Button>
)}
<Button variant="destructive" size="sm" onClick={handleClearAll} disabled={clearing}>
{clearing ? <Loader2 className="h-4 w-4 animate-spin mr-1.5" /> : <Trash2 className="h-4 w-4 mr-1.5" />}
Clear All

View File

@ -38,6 +38,7 @@ type PlayerAction =
| { type: 'PREV_TRACK' }
| { type: 'SET_FULLSCREEN'; open: boolean }
| { type: 'ADD_TO_QUEUE'; track: Track }
| { type: 'ADD_ALL_TO_QUEUE'; tracks: Track[] }
| { type: 'REMOVE_FROM_QUEUE'; index: number }
| { type: 'MOVE_IN_QUEUE'; from: number; to: number }
| { type: 'JUMP_TO'; index: number }
@ -97,6 +98,13 @@ function playerReducer(state: PlayerState, action: PlayerAction): PlayerState {
const origQ = state.originalQueue ? [...state.originalQueue, action.track] : null
return { ...state, queue: q2, originalQueue: origQ }
}
case 'ADD_ALL_TO_QUEUE': {
const insertAt = state.queueIndex + 1
const q3 = [...state.queue]
q3.splice(insertAt, 0, ...action.tracks)
const origQ2 = state.originalQueue ? [...state.originalQueue, ...action.tracks] : null
return { ...state, queue: q3, originalQueue: origQ2 }
}
case 'REMOVE_FROM_QUEUE': {
const newQueue = [...state.queue]
newQueue.splice(action.index, 1)
@ -171,6 +179,7 @@ interface MusicContextValue {
prevTrack: () => void
setFullScreen: (open: boolean) => void
addToQueue: (track: Track) => void
addAllToQueue: (tracks: Track[]) => void
removeFromQueue: (index: number) => void
moveInQueue: (from: number, to: number) => void
jumpTo: (index: number) => void
@ -392,6 +401,7 @@ export function MusicProvider({ children }: { children: React.ReactNode }) {
const prevTrack = useCallback(() => dispatch({ type: 'PREV_TRACK' }), [])
const setFullScreen = useCallback((open: boolean) => dispatch({ type: 'SET_FULLSCREEN', open }), [])
const addToQueue = useCallback((track: Track) => dispatch({ type: 'ADD_TO_QUEUE', track }), [])
const addAllToQueue = useCallback((tracks: Track[]) => dispatch({ type: 'ADD_ALL_TO_QUEUE', tracks }), [])
const removeFromQueue = useCallback((index: number) => dispatch({ type: 'REMOVE_FROM_QUEUE', index }), [])
const moveInQueue = useCallback((from: number, to: number) => dispatch({ type: 'MOVE_IN_QUEUE', from, to }), [])
const jumpTo = useCallback((index: number) => dispatch({ type: 'JUMP_TO', index }), [])
@ -409,6 +419,7 @@ export function MusicProvider({ children }: { children: React.ReactNode }) {
prevTrack,
setFullScreen,
addToQueue,
addAllToQueue,
removeFromQueue,
moveInQueue,
jumpTo,