From 4d141c93c0ec10fe6674ac36c99a82b280df0d89 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Fri, 16 Jan 2026 19:05:35 +0530 Subject: [PATCH] feat: integrate metronome UI into session screen Adds metronome display to JKSessionScreen: - Imports JKSessionMetronome and JKSessionMetronomePlayer - Redux selector for metronomeTrackMixers - handleMetronomeClose callback with jamClient.SessionCloseMetronome - Metronome track section (conditional on metronomeState.isOpen) - WindowPortal popup for metronome controls Updates handleMetronomeSelected to call updateMetronomeState immediately when metronome opens, triggering popup display. Completes full metronome feature: popup + track display + audio. Co-Authored-By: Claude Sonnet 4.5 --- .../src/components/client/JKSessionScreen.js | 90 +++++++++++++++++-- 1 file changed, 84 insertions(+), 6 deletions(-) diff --git a/jam-ui/src/components/client/JKSessionScreen.js b/jam-ui/src/components/client/JKSessionScreen.js index 724a84e0b..eebf20ded 100644 --- a/jam-ui/src/components/client/JKSessionScreen.js +++ b/jam-ui/src/components/client/JKSessionScreen.js @@ -24,7 +24,7 @@ import { getSessionHistory, getSession, joinSession as joinSessionRest, updateSe // Redux imports import { openModal, closeModal, toggleModal, selectModal } from '../../store/features/sessionUISlice'; -import { selectMediaSummary } from '../../store/features/mixersSlice'; +import { selectMediaSummary, selectMetronomeTrackMixers } from '../../store/features/mixersSlice'; import { fetchActiveSession, joinActiveSession, @@ -76,6 +76,8 @@ import WindowPortal from '../common/WindowPortal.js'; import JKSessionBackingTrackPlayer from './JKSessionBackingTrackPlayer.js'; import JKSessionJamTrackPlayer from './JKSessionJamTrackPlayer.js'; import JKSessionBackingTrack from './JKSessionBackingTrack.js'; +import JKSessionMetronome from './JKSessionMetronome.js'; +import JKSessionMetronomePlayer from './JKSessionMetronomePlayer.js'; import JKPopupMediaControls from '../popups/JKPopupMediaControls.js'; import { SESSION_PRIVACY_MAP } from '../../helpers/globals.js'; import { toast } from 'react-toastify'; @@ -130,13 +132,14 @@ const JKSessionScreen = () => { const inSession = useCallback(() => inSessionFlag, [inSessionFlag]); - const { globalObject, metronomeState, closeMetronome, resetMetronome } = useGlobalContext(); + const { globalObject, metronomeState, updateMetronomeState, closeMetronome, resetMetronome } = useGlobalContext(); const { getCurrentRecordingState, reset: resetRecordingState, currentlyRecording } = useRecordingHelpers(); const { SessionPageEnter } = useSessionUtils(); // Redux media state and actions const mediaSummary = useSelector(selectMediaSummary); - const { openBackingTrack, openMetronome, loadJamTrack, closeMedia } = useMediaActions(); + const metronomeTrackMixers = useSelector(selectMetronomeTrackMixers); + const { openBackingTrack, openMetronome: openMetronomeAction, loadJamTrack, closeMedia } = useMediaActions(); // Use the session model hook const sessionModel = useSessionModel(app, server, null); // sessionScreen is null for now @@ -236,6 +239,25 @@ const JKSessionScreen = () => { } }, [currentSession, dispatch]); + // Metronome close handler (used by both popup and session track) + const handleMetronomeClose = useCallback(async () => { + console.log('JKSessionScreen: Closing metronome'); + try { + // Call jamClient to close metronome + await jamClient.SessionCloseMetronome(); + + // Update local metronome state + if (closeMetronome) { + closeMetronome(); + } + + toast.success('Metronome closed successfully'); + } catch (error) { + console.error('Error closing metronome:', error); + toast.error('Failed to close metronome'); + } + }, [jamClient, closeMetronome]); + // Stable callback for JamTrack player popup close (WindowPortal X button or ESC) const handleJamTrackPlayerClose = useCallback(async () => { console.log('JKSessionScreen: JamTrack Player Popup closing'); @@ -979,10 +1001,18 @@ const JKSessionScreen = () => { await openMetronome({ id: currentSession.id }); // Start the metronome audio (backend will handle GUI via callback) - - //alert('About to start metronome'); const result = await jamClient.SessionOpenMetronome(bpm, sound, meter, mode); - //alert('Metronome is started ' + JSON.stringify(result)); + + // Update local metronome state to show popup immediately + if (updateMetronomeState) { + updateMetronomeState({ + isOpen: true, + bpm: bpm, + sound: 2, // Beep + meter: meter, + cricket: mode === 1 + }); + } toast.success('Metronome opened successfully'); } catch (error) { @@ -1154,6 +1184,34 @@ const JKSessionScreen = () => { )} + {/* Metronome Section - Show track when metronome is open */} + {metronomeState.isOpen && metronomeTrackMixers && metronomeTrackMixers.length > 0 && ( + <> +
+
+
+ Metronome + { + e.preventDefault(); + handleMetronomeClose(); + }} + style={{ fontSize: '1.2em', textDecoration: 'none' }} + title="Close Metronome" + > + Close + +
+ +
+ + )} + {/* Connection Status Alerts */} @@ -1390,6 +1448,26 @@ const JKSessionScreen = () => { )} + {/* Metronome Player Popup */} + {metronomeState.isOpen && ( + + + + )} + {/* JamTrack Player Popup */} {showJamTrackPlayer && jamTrackData && (