import { useEffect, useRef, useState } from 'react'; import { useSocket } from '../context/SocketContext.jsx'; import { useSettingsNamespace } from '../settings/index.js'; import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js'; const SHOW_MS = 3200; function normalizePayload(payload = {}) { const rewardName = typeof payload.rewardName === 'string' && payload.rewardName.trim() ? payload.rewardName.trim() : 'Unknown Event'; const goal = Number.isFinite(Number(payload.goal)) ? Math.max(1, Math.floor(Number(payload.goal))) : 0; const count = Number.isFinite(Number(payload.count)) ? Math.max(0, Math.floor(Number(payload.count))) : goal; return { rewardName, goal, count, ts: Date.now(), }; } export default function RewardRunOverlay() { const socket = useSocket(); const { value: audioSettings } = useSettingsNamespace('audio', AUDIO_SETTINGS_DEFAULTS); const masterVolume = Number.isFinite(audioSettings?.masterVolume) ? audioSettings.masterVolume : AUDIO_SETTINGS_DEFAULTS.masterVolume; const alertVolume = Number.isFinite(audioSettings?.alertVolume) ? audioSettings.alertVolume : AUDIO_SETTINGS_DEFAULTS.alertVolume; const effectiveAlertVolume = Math.max(0, Math.min(1, masterVolume * alertVolume)); const [overlay, setOverlay] = useState(null); const audioRef = useRef(null); useEffect(() => { audioRef.current = new Audio('/tada.mp3'); audioRef.current.volume = effectiveAlertVolume; audioRef.current.load(); }, [effectiveAlertVolume]); useEffect(() => { let timer = null; function playSound() { const audio = audioRef.current; if (!audio) return; audio.volume = effectiveAlertVolume; audio.currentTime = 0; audio.play().catch(() => {}); } function onRewardRun(payload = {}) { const next = normalizePayload(payload); setOverlay(next); playSound(); if (timer) clearTimeout(timer); timer = setTimeout(() => { setOverlay(null); }, SHOW_MS); } function onLocalRewardRun(event) { onRewardRun(event?.detail || {}); } socket.on('buttonBox:rewardRun', onRewardRun); window.addEventListener('buttonBox:rewardRunLocalTest', onLocalRewardRun); return () => { if (timer) clearTimeout(timer); socket.off('buttonBox:rewardRun', onRewardRun); window.removeEventListener('buttonBox:rewardRunLocalTest', onLocalRewardRun); }; }, [effectiveAlertVolume, socket]); if (!overlay) return null; return (
Running event: {overlay.rewardName}!
{overlay.count}/{overlay.goal}