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 <noreply@anthropic.com>
This commit is contained in:
Nuwan 2026-01-16 19:05:35 +05:30
parent fc3bbeeafe
commit 4d141c93c0
1 changed files with 84 additions and 6 deletions

View File

@ -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 && (
<>
<div style={{ borderLeft: '1px solid #ddd', paddingLeft: '1rem' }}></div>
<div className='metronomeTrack'>
<h5>
Metronome
<a
href="#"
className="text-muted ml-2"
onClick={(e) => {
e.preventDefault();
handleMetronomeClose();
}}
style={{ fontSize: '1.2em', textDecoration: 'none' }}
title="Close Metronome"
>
<FontAwesomeIcon icon="times" /> Close
</a>
</h5>
<JKSessionMetronome
mixers={metronomeTrackMixers[0]}
onClose={handleMetronomeClose}
/>
</div>
</>
)}
</div>
{/* Connection Status Alerts */}
@ -1390,6 +1448,26 @@ const JKSessionScreen = () => {
</WindowPortal>
)}
{/* Metronome Player Popup */}
{metronomeState.isOpen && (
<WindowPortal
title="Metronome Controls"
onClose={handleMetronomeClose}
windowFeatures="width=450,height=400,left=200,top=200,menubar=no,toolbar=no,status=no,scrollbars=yes,resizable=yes,location=no, addressbar=no"
windowId="metronome-controls"
>
<JKSessionMetronomePlayer
isOpen={metronomeState.isOpen}
onClose={handleMetronomeClose}
metronomeState={metronomeState}
jamClient={jamClient}
session={currentSession}
currentUser={currentUser}
isPopup={true}
/>
</WindowPortal>
)}
{/* JamTrack Player Popup */}
{showJamTrackPlayer && jamTrackData && (
<WindowPortal