docs(27): create phase plan for backing track sync
Phase 27: Backing Track Sync - 1 plan in 1 wave - 2 tasks (both auto) - Ready for execution Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
12ce12420f
commit
c7f6480137
|
|
@ -46,10 +46,10 @@ Plans:
|
|||
**Success Criteria** (what must be TRUE):
|
||||
1. Opening a backing track file shows the track in session screen (not just popup)
|
||||
2. No "state update on unmounted component" warnings when closing backing track quickly
|
||||
**Plans**: TBD
|
||||
**Plans**: 1 plan
|
||||
|
||||
Plans:
|
||||
- [ ] 27-01: TBD
|
||||
- [ ] 27-01-PLAN.md - Use openBackingTrack action and add async cleanup
|
||||
|
||||
### Phase 28: Metronome Responsiveness
|
||||
**Goal**: Metronome controls respond smoothly to user input
|
||||
|
|
@ -71,7 +71,7 @@ Plans:
|
|||
| Phase | Milestone | Plans Complete | Status | Completed |
|
||||
|-------|-----------|----------------|--------|-----------|
|
||||
| 26. JamTrack Polish | v1.6 | 4/4 | Complete | 2026-02-25 |
|
||||
| 27. Backing Track Sync | v1.6 | 0/TBD | Not started | - |
|
||||
| 27. Backing Track Sync | v1.6 | 0/1 | Planned | - |
|
||||
| 28. Metronome Responsiveness | v1.6 | 0/TBD | Not started | - |
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -0,0 +1,266 @@
|
|||
---
|
||||
phase: 27-backing-track-sync
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- jam-ui/src/components/client/JKSessionScreen.js
|
||||
- jam-ui/src/components/client/JKSessionBackingTrackPlayer.js
|
||||
autonomous: true
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "Opening a backing track file shows the track in session screen (not just popup)"
|
||||
- "No 'state update on unmounted component' warnings when closing backing track quickly"
|
||||
artifacts:
|
||||
- path: "jam-ui/src/components/client/JKSessionScreen.js"
|
||||
provides: "handleBackingTrackSelected using openBackingTrack action"
|
||||
contains: "openBackingTrack(result.file)"
|
||||
- path: "jam-ui/src/components/client/JKSessionBackingTrackPlayer.js"
|
||||
provides: "Duration fetch useEffect with cleanup"
|
||||
contains: "let ignore = false"
|
||||
key_links:
|
||||
- from: "JKSessionScreen.js handleBackingTrackSelected"
|
||||
to: "useMediaActions openBackingTrack"
|
||||
via: "async await call"
|
||||
pattern: "await openBackingTrack\\(result\\.file\\)"
|
||||
- from: "JKSessionBackingTrackPlayer.js useEffect"
|
||||
to: "cleanup function"
|
||||
via: "return statement"
|
||||
pattern: "return.*ignore = true"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Fix backing track sync to session screen and prevent unmounted component warnings.
|
||||
|
||||
Purpose: When users open a backing track, it should appear in the session screen (like JamTrack does), and closing the popup quickly should not produce React warnings.
|
||||
|
||||
Output: Two fixes in two files - use Redux action for track sync, add ignore flag for async cleanup.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/nuwan/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/nuwan/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/phases/27-backing-track-sync/27-RESEARCH.md
|
||||
|
||||
# Source files to modify
|
||||
@jam-ui/src/components/client/JKSessionScreen.js
|
||||
@jam-ui/src/components/client/JKSessionBackingTrackPlayer.js
|
||||
|
||||
# Reference for correct pattern (JamTrack uses similar flow)
|
||||
@jam-ui/src/hooks/useMediaActions.js
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Use openBackingTrack action in handleBackingTrackSelected</name>
|
||||
<files>jam-ui/src/components/client/JKSessionScreen.js</files>
|
||||
<action>
|
||||
In the `handleBackingTrackSelected` function (around line 1137), replace the direct `jamClient.SessionOpenBackingTrackFile()` call with the `openBackingTrack()` action from useMediaActions.
|
||||
|
||||
Current (incorrect):
|
||||
```javascript
|
||||
const handleBackingTrackSelected = async (result) => {
|
||||
try {
|
||||
await jamClient.SessionOpenBackingTrackFile(result.file, false);
|
||||
dispatch(setBackingTrackData({...}));
|
||||
dispatch(openModal('backingTrack'));
|
||||
} catch (error) {
|
||||
toast.error('Failed to open backing track');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Change to (correct):
|
||||
```javascript
|
||||
const handleBackingTrackSelected = async (result) => {
|
||||
try {
|
||||
// Use the openBackingTrack action from useMediaActions (already imported at line 153)
|
||||
// This handles: jamClient call, Redux state update, and server sync
|
||||
await openBackingTrack(result.file);
|
||||
|
||||
// Set popup data (same as before)
|
||||
dispatch(setBackingTrackData({
|
||||
backingTrack: result.file,
|
||||
session: currentSession,
|
||||
currentUser: currentUser
|
||||
}));
|
||||
dispatch(openModal('backingTrack'));
|
||||
} catch (error) {
|
||||
toast.error('Failed to open backing track');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
The `openBackingTrack` is already destructured from `useMediaActions()` at line 153. This change ensures:
|
||||
1. jamClient.SessionOpenBackingTrackFile is called (via the thunk)
|
||||
2. mediaSummary.backingTrackOpen is set to true
|
||||
3. syncTracksToServer is called to sync track to session screen
|
||||
</action>
|
||||
<verify>
|
||||
1. Grep for the change: `grep -n "openBackingTrack(result.file)" jam-ui/src/components/client/JKSessionScreen.js`
|
||||
2. Verify jamClient.SessionOpenBackingTrackFile is NOT called directly in handleBackingTrackSelected
|
||||
3. Run: `cd jam-ui && npm run build` - should complete without errors
|
||||
</verify>
|
||||
<done>
|
||||
- handleBackingTrackSelected calls `await openBackingTrack(result.file)` instead of direct jamClient call
|
||||
- Build succeeds
|
||||
- No direct jamClient.SessionOpenBackingTrackFile call in handleBackingTrackSelected
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Add ignore flag to duration fetch useEffect</name>
|
||||
<files>jam-ui/src/components/client/JKSessionBackingTrackPlayer.js</files>
|
||||
<action>
|
||||
In the duration fetch useEffect (around line 90-143), add an ignore flag to prevent state updates after unmount.
|
||||
|
||||
Current structure (no cleanup):
|
||||
```javascript
|
||||
useEffect(() => {
|
||||
const shouldInitialize = (isOpen || isPopup) && backingTrack && jamClient;
|
||||
if (shouldInitialize) {
|
||||
setIsPlaying(false);
|
||||
setCurrentTime('0:00');
|
||||
setCurrentPositionMs(0);
|
||||
|
||||
const fetchDuration = async () => {
|
||||
setIsLoadingDuration(true);
|
||||
try {
|
||||
// ... async jamClient calls ...
|
||||
setDurationMs(validDuration);
|
||||
setDuration(formatTime(validDuration));
|
||||
setIsLoadingDuration(false);
|
||||
} catch (error) {
|
||||
// ... state updates ...
|
||||
}
|
||||
};
|
||||
fetchDuration();
|
||||
}
|
||||
}, [deps]);
|
||||
```
|
||||
|
||||
Change to (with ignore flag):
|
||||
```javascript
|
||||
useEffect(() => {
|
||||
let ignore = false;
|
||||
|
||||
const shouldInitialize = (isOpen || isPopup) && backingTrack && jamClient;
|
||||
if (shouldInitialize) {
|
||||
setIsPlaying(false);
|
||||
setCurrentTime('0:00');
|
||||
setCurrentPositionMs(0);
|
||||
|
||||
const fetchDuration = async () => {
|
||||
if (ignore) return;
|
||||
setIsLoadingDuration(true);
|
||||
|
||||
try {
|
||||
if (!jamClient) {
|
||||
if (!ignore) {
|
||||
handleError('general', 'Audio engine not available', null);
|
||||
setIsLoadingDuration(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const durationInMs = await jamClient.SessionGetTracksPlayDurationMs();
|
||||
|
||||
// Check ignore AFTER async operation
|
||||
if (ignore) return;
|
||||
|
||||
const validDuration = parseInt(durationInMs, 10) || 0;
|
||||
if (validDuration === 0 || isNaN(validDuration)) {
|
||||
handleError('file', 'Failed to load track duration. The file may be invalid or unsupported.', null);
|
||||
setDuration('0:00');
|
||||
setDurationMs(0);
|
||||
setIsLoadingDuration(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setDurationMs(validDuration);
|
||||
setDuration(formatTime(validDuration));
|
||||
clearError();
|
||||
setIsLoadingDuration(false);
|
||||
} catch (error) {
|
||||
if (!ignore) {
|
||||
handleError('file', 'Failed to load track duration', error);
|
||||
setDuration('0:00');
|
||||
setDurationMs(0);
|
||||
setIsLoadingDuration(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetchDuration();
|
||||
}
|
||||
|
||||
return () => {
|
||||
ignore = true;
|
||||
};
|
||||
}, [isOpen, isPopup, backingTrack, jamClient, handleError, formatTime, clearError]);
|
||||
```
|
||||
|
||||
Key changes:
|
||||
1. Add `let ignore = false;` at start of useEffect
|
||||
2. Check `if (ignore) return;` before initial state updates in fetchDuration
|
||||
3. Check `if (ignore) return;` after the await call (the critical point)
|
||||
4. Wrap all state updates in catch block with `if (!ignore)`
|
||||
5. Add cleanup function `return () => { ignore = true; };`
|
||||
</action>
|
||||
<verify>
|
||||
1. Grep for the pattern: `grep -n "let ignore = false" jam-ui/src/components/client/JKSessionBackingTrackPlayer.js`
|
||||
2. Grep for cleanup: `grep -n "ignore = true" jam-ui/src/components/client/JKSessionBackingTrackPlayer.js`
|
||||
3. Run: `cd jam-ui && npm run build` - should complete without errors
|
||||
</verify>
|
||||
<done>
|
||||
- useEffect has `let ignore = false;` declaration
|
||||
- useEffect has cleanup function that sets `ignore = true`
|
||||
- All state updates after async operations are guarded with `if (!ignore)` or `if (ignore) return;`
|
||||
- Build succeeds
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
After both tasks complete:
|
||||
|
||||
1. **Build verification:**
|
||||
```bash
|
||||
cd jam-ui && npm run build
|
||||
```
|
||||
Should complete without errors.
|
||||
|
||||
2. **Code verification:**
|
||||
```bash
|
||||
# BT-01: Verify openBackingTrack is used
|
||||
grep -n "openBackingTrack(result.file)" jam-ui/src/components/client/JKSessionScreen.js
|
||||
|
||||
# BT-02: Verify ignore flag pattern
|
||||
grep -n "let ignore = false" jam-ui/src/components/client/JKSessionBackingTrackPlayer.js
|
||||
grep -n "ignore = true" jam-ui/src/components/client/JKSessionBackingTrackPlayer.js
|
||||
```
|
||||
|
||||
3. **No regressions:**
|
||||
- Backing track popup should still open when selecting a file
|
||||
- Existing functionality preserved
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
1. `handleBackingTrackSelected` uses `await openBackingTrack(result.file)` - enables session screen sync
|
||||
2. Duration fetch useEffect has ignore flag with cleanup - prevents unmount warnings
|
||||
3. `npm run build` succeeds with no errors
|
||||
4. No removed functionality (popup still works, duration still displays)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/27-backing-track-sync/27-01-SUMMARY.md`
|
||||
</output>
|
||||
Loading…
Reference in New Issue