import toast from 'react-hot-toast'; // Haptic + audio confirmation. At a loud, dark door the sound and the buzz are // what staff actually register — the green flash is confirmation for the person // standing in front of them. export function playSuccessSound() { try { const ctx = new AudioContext(); const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.connect(gain); gain.connect(ctx.destination); osc.frequency.value = 880; osc.type = 'sine'; gain.gain.value = 0.3; osc.start(); gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.15); osc.stop(ctx.currentTime + 0.15); } catch {} } export function playErrorSound() { try { const ctx = new AudioContext(); const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.connect(gain); gain.connect(ctx.destination); osc.frequency.value = 300; osc.type = 'square'; gain.gain.value = 0.2; osc.start(); gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.3); osc.stop(ctx.currentTime + 0.3); } catch {} } export function vibrate(pattern: number | number[]) { try { if (navigator.vibrate) navigator.vibrate(pattern); } catch {} } /** * Dismiss a toast after `ms` regardless of react-hot-toast's hover/touch pause. * * The pauses every countdown while the pointer is "inside" its * container, and schedules no dismissal at all for toasts created while paused. * On a touchscreen, tapping a toast button (the Undo toast) synthesizes a * mouseenter with no mouseleave until the next tap somewhere else — so the * "Undone" toast that follows would sit there indefinitely. Callers create the * toast with `duration: Infinity` and let this timer close it. */ export function dismissAfter(id: string, ms: number): string { window.setTimeout(() => toast.dismiss(id), ms); return id; }