1// Beside play: pause, and the listener's volume (audioControls.mjs). 2import { useSyncExternalStore } from 'react'; 3import { audioControls, pause, setVolume, unpause } from './audioControls.mjs'; 4import { partyStore } from './party.mjs'; 5import { sandbox } from './sandbox.mjs'; 6 7const useAudioControls = () => useSyncExternalStore(audioControls.subscribe, audioControls.get, audioControls.get); 8 9// Shown while something plays, here or in the sandbox: pause holds it where 10// it is, and the same button goes on. 11export function PauseButton({ context, isEmbedded = false }) { 12 const { paused } = useAudioControls(); 13 const sb = useSyncExternalStore(sandbox.subscribe, sandbox.get, sandbox.get); 14 const party = useSyncExternalStore(partyStore.subscribe, partyStore.get, partyStore.get); 15 const playing = context.started || sb.status === 'playing'; 16 // a listening party runs on one clock for everyone: no pausing in it 17 const inParty = party.status !== 'off' && party.status !== 'ended'; 18 if ((!playing && !paused) || (inParty && !paused)) return null; 19 return ( 20 <button 21 onClick={() => (paused ? unpause() : pause())} 22 data-testid="jev-pause" 23 aria-pressed={paused} 24 title={paused ? 'resume: go on from where it paused' : 'pause: hold the song where it is'} 25 className="flex items-center space-x-1 px-2 hover:opacity-50" 26 > 27 <span aria-hidden>{paused ? '▶' : '⏸'}</span> 28 {!isEmbedded && <span className="hidden xl:inline">{paused ? 'resume' : 'pause'}</span>} 29 </button> 30 ); 31} 32 33export function VolumeControl() { 34 const { volume } = useAudioControls(); 35 return ( 36 <label className="flex items-center space-x-1 px-2" title={`volume: ${volume}%`}> 37 <button 38 type="button" 39 aria-label={volume === 0 ? 'unmute' : 'mute'} 40 className="hover:opacity-50" 41 onClick={() => setVolume(volume === 0 ? 100 : 0)} 42 > 43 <span aria-hidden>{volume === 0 ? '🔇' : volume < 50 ? '🔉' : '🔊'}</span> 44 </button> 45 <input 46 type="range" 47 min="0" 48 max="100" 49 step="1" 50 value={volume} 51 onChange={(e) => setVolume(e.target.value)} 52 data-testid="jev-volume" 53 aria-label="volume" 54 className="hidden sm:block w-16 lg:w-20 accent-current" 55 /> 56 </label> 57 ); 58}