Tasks completed: 2/2
- Use openBackingTrack action in handleBackingTrackSelected
- Add ignore flag to duration fetch useEffect
SUMMARY: .planning/phases/27-backing-track-sync/27-01-SUMMARY.md
- Add let ignore = false at start of duration fetch useEffect
- Check ignore flag after async jamClient call
- Add cleanup function that sets ignore = true
- Prevents 'state update on unmounted component' React warnings
when closing backing track popup quickly
- Add jamServer to useMediaActions by creating object from subscribe/unsubscribe
- Pass jamServer to loadJamTrackThunk so it can subscribe to packaging progress
- Improve error handling to extract message from Response objects (not just Error)
- Fixes "Failed to start packaging: undefined" error
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Added loadJamTrack call in handleJamTrackSelect else branch
- When syncResult.isSynchronized is false, triggers download flow
- This transitions state through checking → packaging → downloading → keying → synchronized
- Fixes empty JamTrack player for non-synchronized tracks
- Change condition from 'idle' OR 'synchronized' to only 'synchronized'
- Controls only appear after JamTrack is synchronized with backend
- Download progress UI handles loading states appropriately
- Change condition from 'synchronized' OR 'idle' to only 'synchronized'
- Stems only appear after JamTrack is synchronized with backend
- Already-synchronized tracks show stems immediately (checkJamTrackSync returns synchronized)
- Import checkJamTrackSync from mediaSlice
- Call checkJamTrackSync BEFORE setSelectedJamTrack/setJamTrackStems
- Show different toast messages based on sync state
- This ensures downloadState transitions from 'idle' appropriately
Phase 26: JamTrack Polish
- Plan 26-03 removes 'idle' from valid render states
- Closes UAT gap: stems/controls appearing before sync
- 1 plan in wave 3, depends on 26-02
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Import and call cleanupJamTrackCallbacks in cleanup useEffect
- Wrap controls section in condition: only render when downloadState
is 'idle' or 'synchronized' and not in initial loading
- Fixes memory leak from orphaned window.jamTrackDownload* globals
- Fixes UX issue where controls appeared before track ready
- Replace TODO console.log with actual navigation
- Open JamTrack editor at /jamtracks/{id} in new tab
- Use noopener,noreferrer for security best practices
- Change window dimensions from 600x500 to 460x350
- Disable scrollbars since content now fits properly
- Player content is 420px wide + 20px padding on each side
- 3 phases: JamTrack Polish, Backing Track Sync, Metronome Responsiveness
- 8 requirements mapped to phases
- Research complete for all features
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 5 comprehensive test sections covering all memory leak scenarios
- Timer cleanup verification test
- Callback accumulation verification test
- Async operation cleanup verification test
- 15-minute modal idle stability test
- 15-minute active recording stability test
- Troubleshooting guide for each failure type
- Add ignore flag pattern to audio preference useEffect
- Add ignore flag pattern to video sources useEffect
- Guard state updates with !ignore check
- Prevents "state update on unmounted component" warnings
- Use selectIsRecording from Redux instead of local hook state
- Dispatch setRecordingStopped() when stopping recording
- Add recording name validation before starting
- Replace alert() with toast notifications
- Wait for native client callback before updating Redux state
- Add client_id to useJamServer payload
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Pass jamClient to useRecordingHelpers in JKSessionScreen
- Add thisClientStartedRecording to Redux for cross-instance state sharing
- Capture shouldCallRestStop before async operations to prevent race conditions
- Add comprehensive logging for debugging recording start/stop flow
- Fix error handling to properly parse 422 response details
- Update tests with new Redux state and API mock responses
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The original research was incorrect - jam-ui uses StartMediaRecording
(enum 428) not StartRecording (enum 377). These are different methods
in the C++ client.
Reverted to original method signatures:
- StartMediaRecording(id, groupedTracks, recordSettings)
- FrontStopRecording(id, groupedTracks)
The JSON parsing fix in rest.js (previous commit) is the actual fix
that resolves recording.id being undefined.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The startRecording, stopRecording, and getRecordingPromise functions
were returning the raw Response object instead of the parsed JSON data.
This caused recording.id to be undefined, leading to the C++ client
crash with "Assertion failed: (!clientId.isEmpty())".
- Add .json() parsing before resolving the Promise
- Fixes crash when clicking Start Recording
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change StartMediaRecording to StartRecording with unpacked params
- Unpack videoType, recordChat, recordFramerate from params object
- Match legacy recordingModel.js signature for C++ client