fix: prevent opening multiple media players simultaneously
- Add media state tracking to JKSessionOpenMenu component - Disable menu items when any media (JamTrack, Backing Track, Metronome) is open - Show warning toast when user attempts to open another media type - Update backing track and metronome track styling and icons - Adjust close button styling in session screen media sections - Handle cancelled file selection dialog for backing tracks Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
fd8900c15b
commit
388dfe16f8
|
|
@ -10,6 +10,7 @@ import { UncontrolledTooltip } from 'reactstrap';
|
|||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faTimes } from '@fortawesome/free-solid-svg-icons';
|
||||
import './JKSessionMyTrack.css';
|
||||
import backingTrackIcon from "../../icons/instruments/icon_instrument_default.svg"
|
||||
|
||||
const JKSessionBackingTrack = ({
|
||||
backingTrack,
|
||||
|
|
@ -62,7 +63,21 @@ const JKSessionBackingTrack = ({
|
|||
<div className={trackClasses}>
|
||||
<div className="disabled-track-overlay" />
|
||||
<div className="session-track-contents">
|
||||
{/* Track display - header removed, now shown in parent */}
|
||||
{/* Track filename */}
|
||||
<div className="track-name" style={{
|
||||
textAlign: 'center',
|
||||
marginBottom: '4px',
|
||||
fontSize: '12px',
|
||||
fontWeight: '500',
|
||||
color: '#333',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
maxWidth: '100%',
|
||||
padding: '0 4px'
|
||||
}}>
|
||||
{getFileName(backingTrack)}
|
||||
</div>
|
||||
|
||||
{/* Instrument Icon */}
|
||||
<div
|
||||
|
|
@ -71,12 +86,12 @@ const JKSessionBackingTrack = ({
|
|||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: '8px'
|
||||
marginBottom: '65px'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
height="45"
|
||||
src={backingTrack.instrumentIcon || "/assets/content/icon_recording.png"}
|
||||
src={backingTrack.instrumentIcon || backingTrackIcon }
|
||||
width="45"
|
||||
alt="instrument"
|
||||
/>
|
||||
|
|
@ -88,8 +103,8 @@ const JKSessionBackingTrack = ({
|
|||
<SessionTrackVU
|
||||
orientation="horizontal"
|
||||
lightCount={11}
|
||||
lightWidth={17}
|
||||
lightHeight={12}
|
||||
lightWidth={25}
|
||||
lightHeight={15}
|
||||
side="best"
|
||||
mixers={mixers}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useJamClient } from '../../context/JamClientContext';
|
|||
import SessionTrackVU from './SessionTrackVU';
|
||||
import SessionTrackGain from './SessionTrackGain';
|
||||
import './JKSessionMyTrack.css';
|
||||
import computerIcon from '../../assets/img/instruments/icon_instrument_computer45_inverted.svg'
|
||||
|
||||
const JKSessionMetronome = ({
|
||||
mixers,
|
||||
|
|
@ -28,7 +29,7 @@ const JKSessionMetronome = ({
|
|||
<div className={trackClasses}>
|
||||
<div className="session-track-contents">
|
||||
{/* Metronome Title */}
|
||||
<div className="track-title" style={{ textAlign: 'center', marginBottom: '16px', fontSize: '0.9rem', fontWeight: '500', color: '#666' }}>
|
||||
<div className="track-title" style={{ textAlign: 'center', fontSize: '0.9rem', fontWeight: '500', color: '#666' }}>
|
||||
Metronome
|
||||
</div>
|
||||
|
||||
|
|
@ -39,12 +40,12 @@ const JKSessionMetronome = ({
|
|||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: '12px'
|
||||
marginBottom: '65px'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
height="55"
|
||||
src="/assets/content/icon_metronome.png"
|
||||
src={computerIcon}
|
||||
width="55"
|
||||
alt="metronome"
|
||||
style={{
|
||||
|
|
@ -55,15 +56,15 @@ const JKSessionMetronome = ({
|
|||
}}
|
||||
onError={(e) => {
|
||||
// Fallback if metronome icon doesn't exist
|
||||
e.target.src = "/assets/content/icon_recording.png";
|
||||
e.target.src = {computerIcon};
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 0db Label */}
|
||||
<div style={{ textAlign: 'center', fontSize: '0.75rem', color: '#666', marginBottom: '8px' }}>
|
||||
{/* <div style={{ textAlign: 'center', fontSize: '0.75rem', color: '#666', marginBottom: '8px' }}>
|
||||
0db
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* Track Controls */}
|
||||
<div className="track-controls">
|
||||
|
|
@ -72,8 +73,8 @@ const JKSessionMetronome = ({
|
|||
<SessionTrackVU
|
||||
orientation="horizontal"
|
||||
lightCount={11}
|
||||
lightWidth={17}
|
||||
lightHeight={12}
|
||||
lightWidth={25}
|
||||
lightHeight={15}
|
||||
side="best"
|
||||
mixers={mixers}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -5,18 +5,43 @@ import { useAuth } from '../../context/UserAuth';
|
|||
import { toast } from 'react-toastify';
|
||||
import openIcon from '../../assets/img/client/open.svg';
|
||||
|
||||
const JKSessionOpenMenu = ({ onBackingTrackSelected, onJamTrackSelected, onMetronomeSelected }) => {
|
||||
const JKSessionOpenMenu = ({
|
||||
onBackingTrackSelected,
|
||||
onJamTrackSelected,
|
||||
onMetronomeSelected,
|
||||
isBackingTrackOpen = false,
|
||||
isJamTrackOpen = false,
|
||||
isMetronomeOpen = false
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const buttonRef = useRef(null);
|
||||
const menuRef = useRef(null);
|
||||
const jamClient = useJamClient();
|
||||
|
||||
// Check if any media player is currently open
|
||||
const isAnyMediaOpen = isBackingTrackOpen || isJamTrackOpen || isMetronomeOpen;
|
||||
|
||||
// Get the name of currently open media for tooltip/message
|
||||
const getOpenMediaName = () => {
|
||||
if (isBackingTrackOpen) return 'Backing Track';
|
||||
if (isJamTrackOpen) return 'JamTrack';
|
||||
if (isMetronomeOpen) return 'Metronome';
|
||||
return null;
|
||||
};
|
||||
|
||||
const toggle = () => setIsOpen(prev => !prev);
|
||||
|
||||
const handleMenuItemClick = async (item) => {
|
||||
console.log(`Selected: ${item}`);
|
||||
setIsOpen(false);
|
||||
|
||||
// Check if any media is already open - prevent opening another
|
||||
if (isAnyMediaOpen) {
|
||||
const openMediaName = getOpenMediaName();
|
||||
toast.warning(`Please close the ${openMediaName} before opening another media.`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item === 'JamTracks') {
|
||||
if (onJamTrackSelected) {
|
||||
onJamTrackSelected();
|
||||
|
|
@ -27,7 +52,8 @@ const JKSessionOpenMenu = ({ onBackingTrackSelected, onJamTrackSelected, onMetro
|
|||
window.JK = window.JK || {};
|
||||
window.JK.HandleBackingTrackSelectedCallback = (result) => {
|
||||
console.log('Backing track selected:', result);
|
||||
if (onBackingTrackSelected) {
|
||||
// Only proceed if a file was actually selected (user didn't cancel)
|
||||
if (result && result.file && onBackingTrackSelected) {
|
||||
onBackingTrackSelected(result);
|
||||
}
|
||||
};
|
||||
|
|
@ -115,20 +141,26 @@ const JKSessionOpenMenu = ({ onBackingTrackSelected, onJamTrackSelected, onMetro
|
|||
className="dropdown-menu show"
|
||||
>
|
||||
<button
|
||||
className="dropdown-item"
|
||||
className={`dropdown-item ${isAnyMediaOpen ? 'disabled' : ''}`}
|
||||
onClick={() => handleMenuItemClick('JamTracks')}
|
||||
disabled={isAnyMediaOpen}
|
||||
style={isAnyMediaOpen ? { opacity: 0.5, cursor: 'not-allowed' } : {}}
|
||||
>
|
||||
JamTrack...
|
||||
</button>
|
||||
<button
|
||||
className="dropdown-item"
|
||||
className={`dropdown-item ${isAnyMediaOpen ? 'disabled' : ''}`}
|
||||
onClick={() => handleMenuItemClick('Backing Tracks')}
|
||||
disabled={isAnyMediaOpen}
|
||||
style={isAnyMediaOpen ? { opacity: 0.5, cursor: 'not-allowed' } : {}}
|
||||
>
|
||||
Backing Track...
|
||||
</button>
|
||||
<button
|
||||
className="dropdown-item"
|
||||
className={`dropdown-item ${isAnyMediaOpen ? 'disabled' : ''}`}
|
||||
onClick={() => handleMenuItemClick('Metronome')}
|
||||
disabled={isAnyMediaOpen}
|
||||
style={isAnyMediaOpen ? { opacity: 0.5, cursor: 'not-allowed' } : {}}
|
||||
>
|
||||
Metronome...
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1138,6 +1138,12 @@ const JKSessionScreen = () => {
|
|||
// console.log('JKSessionScreen: handleBackingTrackSelected called with:', result);
|
||||
// console.log('JKSessionScreen: Current state - showBackingTrackPopup:', showBackingTrackPopup, 'popupGuard:', popupGuard);
|
||||
|
||||
// Don't proceed if no file was selected (user cancelled the dialog)
|
||||
if (!result || !result.file) {
|
||||
console.log('JKSessionScreen: No file selected, ignoring');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Use the openBackingTrack action from useMediaActions (already destructured at line 153)
|
||||
// This handles: jamClient call, Redux state update, and server sync
|
||||
|
|
@ -1351,7 +1357,14 @@ const JKSessionScreen = () => {
|
|||
<Button className='btn-custom-outline' outline size="md" onClick={handleBroadcast}>
|
||||
<img src={broadcastIcon} alt="Broadcast" style={{ width: '21px', height: '21px', marginRight: '0.3rem' }} />
|
||||
Broadcast</Button>
|
||||
<JKSessionOpenMenu onBackingTrackSelected={handleBackingTrackSelected} onJamTrackSelected={() => dispatch(openModal('jamTrack'))} onMetronomeSelected={handleMetronomeSelected} />
|
||||
<JKSessionOpenMenu
|
||||
onBackingTrackSelected={handleBackingTrackSelected}
|
||||
onJamTrackSelected={() => dispatch(openModal('jamTrack'))}
|
||||
onMetronomeSelected={handleMetronomeSelected}
|
||||
isBackingTrackOpen={showBackingTrackPlayer}
|
||||
isJamTrackOpen={showJamTrackPlayer}
|
||||
isMetronomeOpen={metronomeState.isOpen}
|
||||
/>
|
||||
<JKSessionChatButton sessionId={sessionId} />
|
||||
<input
|
||||
type="file"
|
||||
|
|
@ -1428,12 +1441,12 @@ const JKSessionScreen = () => {
|
|||
JamTrack: {selectedJamTrack.name}
|
||||
<a
|
||||
href="#"
|
||||
className="text-muted ml-2"
|
||||
className="ml-2"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleJamTrackClose();
|
||||
}}
|
||||
style={{ fontSize: '1.2em', textDecoration: 'none' }}
|
||||
style={{ fontSize: '1em', textDecoration: 'none' }}
|
||||
title="Close JamTrack"
|
||||
>
|
||||
<FontAwesomeIcon icon="times" /> Close
|
||||
|
|
@ -1453,15 +1466,15 @@ const JKSessionScreen = () => {
|
|||
<div style={{ borderLeft: '1px solid #ddd', paddingLeft: '1rem' }}></div>
|
||||
<div className='backingTrack'>
|
||||
<h5>
|
||||
Backing Track: {mixerHelper.backingTracks[0].shortFilename || 'Audio File'}
|
||||
Backing Track
|
||||
<a
|
||||
href="#"
|
||||
className="text-muted ml-2"
|
||||
className="ml-2"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleBackingTrackMainClose();
|
||||
}}
|
||||
style={{ fontSize: '1.2em', textDecoration: 'none' }}
|
||||
style={{ fontSize: '1em', textDecoration: 'none' }}
|
||||
title="Close Backing Track"
|
||||
>
|
||||
<FontAwesomeIcon icon="times" /> Close
|
||||
|
|
@ -1485,12 +1498,12 @@ const JKSessionScreen = () => {
|
|||
Metronome
|
||||
<a
|
||||
href="#"
|
||||
className="text-muted ml-2"
|
||||
className="ml-2"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleMetronomeClose();
|
||||
}}
|
||||
style={{ fontSize: '1.2em', textDecoration: 'none' }}
|
||||
style={{ fontSize: '1em', textDecoration: 'none' }}
|
||||
title="Close Metronome"
|
||||
>
|
||||
<FontAwesomeIcon icon="times" /> Close
|
||||
|
|
|
|||
Loading…
Reference in New Issue