fix: gapless player wiped the queue on natural track end (stale closure)
The engine is created once (mount effect), so its onIndex/onEndOfQueue callbacks
captured the first-render syncIndex/setPlaying. useGlobal's setData reads the
`data` from the render that created the setter when given a functional updater —
that's the INITIAL empty state. So when a track ended and the engine called
syncIndex(i) → setState(s => ({...s, index:i})), `s` was {queue:[], index:0}:
the queue got wiped, `current` went undefined, playback stopped and the dock
vanished. Manual track selection was unaffected because playQueue passes a plain
object (no stale `data` read) — which is why it seemed to work.
Route the two engine-invoked setters through refs kept current each render, so a
natural advance mirrors into the live state instead of the stale initial one.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,13 @@ export const MusicPlayerHost = () => {
|
||||
const isRestoringRef = useRef(false);
|
||||
const seekToRef = useRef<number | null>(null);
|
||||
const engineIndexRef = useRef(0);
|
||||
// The engine is created once, so its callbacks would capture first-render closures. useMusicPlayer's
|
||||
// functional setters (syncIndex/setPlaying) read the state captured at THAT render (the initial EMPTY
|
||||
// queue) — calling them from a stale closure wipes the queue. Route them through refs kept current.
|
||||
const syncIndexRef = useRef(syncIndex);
|
||||
syncIndexRef.current = syncIndex;
|
||||
const setPlayingRef = useRef(setPlaying);
|
||||
setPlayingRef.current = setPlaying;
|
||||
|
||||
const withToken = (u: string) => (token ? `${u}${u.includes('?') ? '&' : '?'}token=${encodeURIComponent(token)}` : u);
|
||||
const streamUrl = (t: PlayerTrack) =>
|
||||
@@ -92,9 +99,9 @@ export const MusicPlayerHost = () => {
|
||||
},
|
||||
onIndex: (i) => {
|
||||
engineIndexRef.current = i; // engine advanced on its own → mirror to UI without restarting
|
||||
syncIndex(i);
|
||||
syncIndexRef.current(i);
|
||||
},
|
||||
onEndOfQueue: () => setPlaying(false),
|
||||
onEndOfQueue: () => setPlayingRef.current(false),
|
||||
onLoadingChange: setLoading,
|
||||
});
|
||||
engineRef.current = engine;
|
||||
|
||||
Reference in New Issue
Block a user