fix(05-03): prevent tracks from disappearing when metronome clicked
Fixed critical bug where all tracks (Audio Inputs and Session Mix) disappeared when user clicked "Open" → "Metronome". Root cause: useEffect in JKSessionScreen.js depended on entire sessionHelper object, which got new reference whenever currentSession updated (including when metronome_open flag changed). This triggered unnecessary onSessionChange() calls that refetched all mixers, causing race condition where myTracks became empty. Changes: 1. **JKSessionScreen.js (line 540)**: Changed useEffect dependency from `sessionHelper` to `sessionHelper.id()` (primitive value that only changes when session ID changes, not on metadata updates) 2. **useMixerHelper.js (lines 63, 388-396, 453-454)**: Added previousMyTracksRef to store last known good myTracks. Returns previous value instead of empty array when allMixers is temporarily empty during state transitions. 3. **useMixerStore.js (lines 198-201, 213-220, 231)**: Added verification logging to confirm onSessionChange is not called unnecessarily. Can be removed after testing confirms fix works. Expected behavior after fix: - ✅ Tracks DO NOT disappear when metronome clicked - ✅ onSessionChange only called on initial session join, not on metadata updates - ✅ Audio Inputs and Session Mix remain visible throughout metronome operations - ✅ Backing tracks and jam tracks still work correctly (regression test) NOTE: This fix does NOT implement metronome UI display. Metronome audio will play but no mixer controls will appear in session screen. That can be added later as separate task. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d264241c78
commit
4fa9ed17fd
|
|
@ -537,7 +537,7 @@ const JKSessionScreen = () => {
|
|||
useEffect(() => {
|
||||
if (!isConnected || !hasJoined) return;
|
||||
onSessionChange(sessionHelper);
|
||||
}, [isConnected, hasJoined, sessionHelper]);
|
||||
}, [isConnected, hasJoined, sessionHelper.id()]);
|
||||
|
||||
const ensureAppropriateProfile = async (musicianAccess) => {
|
||||
return new Promise(async function (resolve, reject) {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ import { getAvatarUrl, getInstrumentIcon45, getInstrumentIcon24 } from '../helpe
|
|||
const useMixerHelper = () => {
|
||||
const dispatch = useDispatch();
|
||||
const allMixersRef = useRef({});
|
||||
const previousMyTracksRef = useRef([]);
|
||||
|
||||
// Redux selectors - replace all useState calls
|
||||
const chatMixer = useSelector(selectChatMixer);
|
||||
|
|
@ -376,14 +377,30 @@ const useMixerHelper = () => {
|
|||
|
||||
// Compute myTracks - memoized to prevent infinite re-renders
|
||||
const myTracks = useMemo(() => {
|
||||
//console.debug("useMixerHelper: computing myTracks", { isConnected, currentSession, jamClient, allMixers });
|
||||
console.debug("useMixerHelper: computing myTracks", {
|
||||
isConnected,
|
||||
currentSession,
|
||||
jamClient,
|
||||
allMixers,
|
||||
allMixersCount: allMixers ? Object.keys(allMixers).length : 0
|
||||
});
|
||||
|
||||
if (!isConnected || !inSession || !jamClient || !allMixers) return [];
|
||||
if (!isConnected || !inSession || !jamClient || !allMixers) {
|
||||
return previousMyTracksRef.current; // Return previous value, not []
|
||||
}
|
||||
|
||||
// Safety check: if allMixers is empty during state transition, return previous value
|
||||
if (typeof allMixers === 'object' && Object.keys(allMixers).length === 0) {
|
||||
console.warn("useMixerHelper: allMixers is empty, returning previous myTracks");
|
||||
return previousMyTracksRef.current; // Return previous value, not []
|
||||
}
|
||||
|
||||
//const participant = currentSession.participants?.[jamClient.clientId];
|
||||
const participant = getParticipant(server.clientId);
|
||||
|
||||
if (!participant) return [];
|
||||
if (!participant) {
|
||||
return previousMyTracksRef.current; // Return previous value, not []
|
||||
}
|
||||
|
||||
const tracks = [];
|
||||
const connStatsClientId = participant.client_role === 'child' && participant.parent_client_id
|
||||
|
|
@ -433,6 +450,8 @@ const useMixerHelper = () => {
|
|||
});
|
||||
}
|
||||
|
||||
// Store for next time
|
||||
previousMyTracksRef.current = tracks;
|
||||
return tracks;
|
||||
}, [currentSession, isConnected, jamClient, allMixers, mixMode, findMixerForTrack, getParticipant, server.clientId]);
|
||||
|
||||
|
|
|
|||
|
|
@ -195,7 +195,12 @@ export default function useMixerStore() {
|
|||
}, []);
|
||||
|
||||
const onSessionChange = useCallback(async (newSession) => {
|
||||
console.log("[MixerStore] onSessionChange START:", {
|
||||
sessionId: newSession.id(),
|
||||
timestamp: Date.now()
|
||||
});
|
||||
console.debug("MixerStore: onSessionChange", newSession);
|
||||
|
||||
if (!newSession) return;
|
||||
|
||||
if (!inSession) {
|
||||
|
|
@ -205,9 +210,15 @@ export default function useMixerStore() {
|
|||
setSession(newSession);
|
||||
|
||||
if (jamClient) {
|
||||
console.log("[MixerStore] Fetching mixer control state...");
|
||||
const newMasterMixers = await jamClient.SessionGetAllControlState(true);
|
||||
const newPersonalMixers = await jamClient.SessionGetAllControlState(false);
|
||||
|
||||
console.log("[MixerStore] Fetched mixers:", {
|
||||
masterCount: newMasterMixers.length,
|
||||
personalCount: newPersonalMixers.length
|
||||
});
|
||||
|
||||
// Initialize the mixerHelper with the constructor parameters
|
||||
console.debug("MixerStore: onSessionChange: initializing mixerHelper", { newSession, newMasterMixers, newPersonalMixers, metro, noAudioUsers, clientsWithAudioOverride });
|
||||
|
||||
|
|
@ -217,6 +228,7 @@ export default function useMixerStore() {
|
|||
// console.debug("MixerStore: onSessionChange: ", m);
|
||||
// setMixers(m);
|
||||
}
|
||||
console.log("[MixerStore] onSessionChange END");
|
||||
}, [jamClient, mixerHelper, metro, noAudioUsers, clientsWithAudioOverride, inSession, sessionEnded]);
|
||||
|
||||
// // Mixer actions
|
||||
|
|
|
|||
Loading…
Reference in New Issue