mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
feat(queue): preserve Play Next order toggle (#464)
* feat(queue): add preservePlayNextOrder setting + playNext store action - New Track.playNextAdded flag (analogous to autoAdded / radioAdded). Stale flags behind queueIndex are harmless — only forward streak scan. - New playerStore action playNext(tracks): tags incoming tracks and delegates to enqueueAt for unified undo + server sync. - New authStore boolean preservePlayNextOrder (default false). When on, playNext appends behind the existing Play-Next streak (Spotify-style) instead of inserting directly after the current track. * refactor(context-menu): centralise Play Next; add Settings toggle + i18n - Replace 3 inline splice/enqueueAt call sites in ContextMenu with the new playNext action. Side-benefit: the single-song path now goes through enqueueAt and gets undo + queue sync (previously missing). - Settings → Audio → Playback: new toggle below Gapless. - 8 locales: preservePlayNextOrder + preservePlayNextOrderDesc. * docs(contributors): credit + changelog entry for #464
This commit is contained in:
committed by
GitHub
parent
e1f2cb4c37
commit
0fab2849e5
@@ -130,6 +130,7 @@ interface AuthState {
|
||||
preloadMode: 'off' | 'balanced' | 'early' | 'custom';
|
||||
preloadCustomSeconds: number;
|
||||
infiniteQueueEnabled: boolean;
|
||||
preservePlayNextOrder: boolean;
|
||||
showArtistImages: boolean;
|
||||
showTrayIcon: boolean;
|
||||
minimizeToTray: boolean;
|
||||
@@ -307,6 +308,7 @@ interface AuthState {
|
||||
setPreloadMode: (v: 'off' | 'balanced' | 'early' | 'custom') => void;
|
||||
setPreloadCustomSeconds: (v: number) => void;
|
||||
setInfiniteQueueEnabled: (v: boolean) => void;
|
||||
setPreservePlayNextOrder: (v: boolean) => void;
|
||||
setShowArtistImages: (v: boolean) => void;
|
||||
setShowTrayIcon: (v: boolean) => void;
|
||||
setMinimizeToTray: (v: boolean) => void;
|
||||
@@ -444,6 +446,7 @@ export const useAuthStore = create<AuthState>()(
|
||||
preloadMode: 'balanced',
|
||||
preloadCustomSeconds: 30,
|
||||
infiniteQueueEnabled: false,
|
||||
preservePlayNextOrder: false,
|
||||
showArtistImages: false,
|
||||
showTrayIcon: true,
|
||||
minimizeToTray: false,
|
||||
@@ -607,6 +610,7 @@ export const useAuthStore = create<AuthState>()(
|
||||
setPreloadMode: (v: 'off' | 'balanced' | 'early' | 'custom') => set({ preloadMode: v }),
|
||||
setPreloadCustomSeconds: (v: number) => set({ preloadCustomSeconds: v }),
|
||||
setInfiniteQueueEnabled: (v) => set({ infiniteQueueEnabled: v }),
|
||||
setPreservePlayNextOrder: (v) => set({ preservePlayNextOrder: v }),
|
||||
setShowArtistImages: (v) => set({ showArtistImages: v }),
|
||||
setShowTrayIcon: (v) => set({ showTrayIcon: v }),
|
||||
setMinimizeToTray: (v) => set({ minimizeToTray: v }),
|
||||
|
||||
@@ -73,6 +73,10 @@ export interface Track {
|
||||
size?: number;
|
||||
autoAdded?: boolean;
|
||||
radioAdded?: boolean;
|
||||
/** Inserted via "Play Next". Used by the preserve-order toggle to find the
|
||||
* end of the current Play-Next streak. Stale flags behind queueIndex are
|
||||
* harmless — the streak scan only looks forward from queueIndex+1. */
|
||||
playNextAdded?: boolean;
|
||||
}
|
||||
|
||||
export function songToTrack(song: SubsonicSong): Track {
|
||||
@@ -260,6 +264,12 @@ interface PlayerState {
|
||||
setProgress: (t: number, duration: number) => void;
|
||||
enqueue: (tracks: Track[], _orbitConfirmed?: boolean) => void;
|
||||
enqueueAt: (tracks: Track[], insertIndex: number, _orbitConfirmed?: boolean) => void;
|
||||
/** "Play Next" — inserts after the current track. When
|
||||
* `preservePlayNextOrder` is on, appends to the existing Play-Next streak
|
||||
* (Spotify-style); otherwise inserts directly after the current track and
|
||||
* pushes any earlier Play-Next items down (default). Falls back to
|
||||
* `playTrack` when nothing is currently playing. */
|
||||
playNext: (tracks: Track[]) => void;
|
||||
enqueueRadio: (tracks: Track[], artistId?: string) => void;
|
||||
setRadioArtistId: (artistId: string) => void;
|
||||
/** For Lucky Mix: drop upcoming tail; keep the currently playing item only. */
|
||||
@@ -3247,6 +3257,23 @@ export const usePlayerStore = create<PlayerState>()(
|
||||
});
|
||||
},
|
||||
|
||||
playNext: (tracks) => {
|
||||
if (tracks.length === 0) return;
|
||||
const state = get();
|
||||
const tagged = tracks.map(t => ({ ...t, playNextAdded: true as const }));
|
||||
if (!state.currentTrack) {
|
||||
state.playTrack(tagged[0], tagged);
|
||||
return;
|
||||
}
|
||||
const baseIdx = state.queueIndex + 1;
|
||||
let insertIdx = baseIdx;
|
||||
if (useAuthStore.getState().preservePlayNextOrder) {
|
||||
const q = state.queue;
|
||||
while (insertIdx < q.length && q[insertIdx].playNextAdded) insertIdx++;
|
||||
}
|
||||
get().enqueueAt(tagged, insertIdx);
|
||||
},
|
||||
|
||||
clearQueue: () => {
|
||||
invoke('audio_stop').catch(console.error);
|
||||
isAudioPaused = false;
|
||||
|
||||
Reference in New Issue
Block a user