react-hot-toast pauses every countdown while the pointer is inside the Toaster and schedules no dismissal for toasts created while paused. On a touchscreen, tapping the Undo button synthesizes a mouseenter with no mouseleave, so the "Undone" toast never went away. Every toast on the door screen now uses duration: Infinity and is closed by dismissAfter. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
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 <Toaster> 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;
|
|
}
|