Files
mcma-webui/src/store/slices/queue.ts
T
Senko-san 3984c7a499
Docker Build & Publish / Prune old image versions (push) Has been cancelled
Docker Build & Publish / build (push) Has been cancelled
Docker Build & Publish / push (push) Has been cancelled
feat(track): play-on-hover cover art, replacing double-click
Add a play overlay button shown on cover art hover that inserts the
track into the queue right after the current track and jumps to it,
so it takes priority over what's already queued. Replaces the
double-click-to-play interaction with a new playNow queue action.
2026-06-13 17:44:35 +03:00

110 lines
2.9 KiB
TypeScript

import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
export type QueueSource =
| 'manual'
| 'album'
| 'playlist'
| 'artist'
| 'search'
| 'radio';
export interface QueueEntry {
trackId: string;
title: string;
artistName: string;
albumTitle: string;
durationMs: number;
albumArtUrl?: string;
}
export interface QueueState {
entries: QueueEntry[];
currentIndex: number;
source: QueueSource;
sourceId: string | null;
sourceName: string | null;
}
export const queueInitialState: QueueState = {
entries: [],
currentIndex: -1,
source: 'manual',
sourceId: null,
sourceName: null,
};
export const queueSlice = createSlice({
name: 'queue',
initialState: queueInitialState,
reducers: {
setQueue(
state,
action: PayloadAction<{
entries: QueueEntry[];
startIndex?: number;
source: QueueSource;
sourceId?: string;
sourceName?: string;
}>,
) {
state.entries = action.payload.entries;
state.currentIndex = action.payload.startIndex ?? 0;
state.source = action.payload.source;
state.sourceId = action.payload.sourceId ?? null;
state.sourceName = action.payload.sourceName ?? null;
},
addToQueue(state, action: PayloadAction<QueueEntry>) {
state.entries.push(action.payload);
},
addNextInQueue(state, action: PayloadAction<QueueEntry>) {
state.entries.splice(state.currentIndex + 1, 0, action.payload);
},
playNow(state, action: PayloadAction<QueueEntry>) {
const insertAt = state.currentIndex + 1;
state.entries.splice(insertAt, 0, action.payload);
state.currentIndex = insertAt;
},
removeFromQueue(state, action: PayloadAction<number>) {
state.entries.splice(action.payload, 1);
if (action.payload < state.currentIndex) state.currentIndex--;
},
moveInQueue(state, action: PayloadAction<{ from: number; to: number }>) {
const { from, to } = action.payload;
const [entry] = state.entries.splice(from, 1);
state.entries.splice(to, 0, entry);
if (state.currentIndex === from) state.currentIndex = to;
else if (from < state.currentIndex && to >= state.currentIndex)
state.currentIndex--;
else if (from > state.currentIndex && to <= state.currentIndex)
state.currentIndex++;
},
goToIndex(state, action: PayloadAction<number>) {
state.currentIndex = action.payload;
},
nextTrack(state) {
if (state.currentIndex < state.entries.length - 1) state.currentIndex++;
},
prevTrack(state) {
if (state.currentIndex > 0) state.currentIndex--;
},
clearQueue(state) {
state.entries = [];
state.currentIndex = -1;
},
},
});
export const {
setQueue,
addToQueue,
addNextInQueue,
playNow,
removeFromQueue,
moveInQueue,
goToIndex,
nextTrack,
prevTrack,
clearQueue,
} = queueSlice.actions;
export default queueSlice.reducer;