From 16af15c8587931f44cd37a41f65f30271ee49475 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Thu, 15 Jan 2026 00:49:29 +0530 Subject: [PATCH] feat(05-03): implement seek slider with UAT-003 fix - Add handleSeek with useCallback wrapper - Implement UAT-003 fix pattern (pending seek while paused, immediate while playing) - Store pending seek in pendingSeekRef when paused, update UI immediately - Apply pending seek on resume in handlePlay (already implemented) - Add seek slider with correct min/max/value bindings - Disable slider during operations and before duration loaded - Parse slider value to int before passing to handler - Error handling for seek failures Co-Authored-By: Claude Sonnet 4.5 --- .../client/JKSessionJamTrackPlayer.js | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/jam-ui/src/components/client/JKSessionJamTrackPlayer.js b/jam-ui/src/components/client/JKSessionJamTrackPlayer.js index d48eb71e4..eae14dcc2 100644 --- a/jam-ui/src/components/client/JKSessionJamTrackPlayer.js +++ b/jam-ui/src/components/client/JKSessionJamTrackPlayer.js @@ -183,6 +183,29 @@ const JKSessionJamTrackPlayer = ({ } }, [isOperating, jamClient, dispatch]); + // Seek handler + const handleSeek = useCallback(async (newPositionMs) => { + if (isOperating || !jamClient || !fqIdRef.current) return; + + try { + setError(null); + + // UAT-003 fix: if paused, store pending seek + if (jamTrackState.isPaused) { + pendingSeekRef.current = newPositionMs; + dispatch(setJamTrackState({ currentPositionMs: newPositionMs })); + return; + } + + // If playing, seek immediately + await jamClient.JamTrackSeekMs(fqIdRef.current, newPositionMs); + dispatch(setJamTrackState({ currentPositionMs: newPositionMs })); + } catch (err) { + console.error('[JamTrack] Seek error:', err); + setError({ type: 'playback', message: 'Failed to seek' }); + } + }, [isOperating, jamClient, jamTrackState.isPaused, dispatch]); + // Helper: Format milliseconds to MM:SS const formatTime = (ms) => { if (!ms || isNaN(ms)) return '00:00'; @@ -279,6 +302,18 @@ const JKSessionJamTrackPlayer = ({ +
+ handleSeek(parseInt(e.target.value, 10))} + disabled={isOperating || !jamTrackState.durationMs} + style={{ width: '300px' }} + /> +
+

{formatTime(jamTrackState.currentPositionMs)} / {formatTime(jamTrackState.durationMs)}