28ddbe33d1
- Rewrote artifacts/tx-os/src/lib/notification-sounds.ts to use the
Web Audio API (lazy AudioContext + decoded AudioBuffer cache) instead
of HTMLAudioElement. unlock() resumes the ctx for any non-running
state (handles iOS `interrupted`), plays a 1-sample silent buffer to
satisfy iOS Safari's gesture requirement, and pre-warms decodes.
decodeAudioData wrapped to support both Promise and callback forms
for older WebKit.
- play() dispatches an AUTOPLAY_BLOCKED_EVENT with `{ isIos }` detail
when ctx isn't running. Throttle is 3 s; new bypassThrottle option
(used by testPlay) skips the gate AND avoids advancing lastPlayAt
so settings previews can't suppress real socket chimes.
- use-audio-unlock.ts keeps gesture listeners attached and re-calls
unlock() on every gesture (handles iPad ctx auto-suspend on
background / AirPlay).
- use-autoplay-hint.ts reads detail.isIos and switches between
notifSettings.autoplayHint and notifSettings.autoplayHintIos
(added to en.json + ar.json with Control Center / silent-switch
guidance).
- notification-settings.tsx per-sound preview button now calls
notificationPlayer.testPlay(id).
- Test globals __txosNotifPlayCount/__txosNotifLastSound preserved;
added __txosNotifCtxState() accessor (dev/test-only, gated by
import.meta.env.MODE).
- Added tests/notification-sound-webaudio.spec.mjs covering the
preview button gesture path: counter increments, throttle bypass
works, singleton AudioContext reaches `running` state.
Notes:
- Pre-existing api-server tsc errors in executive-meetings.ts and the
failing `test` workflow are unrelated to this task and out of scope.
36 lines
1.3 KiB
TypeScript
36 lines
1.3 KiB
TypeScript
import { useEffect } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useToast } from "@/hooks/use-toast";
|
|
import {
|
|
AUTOPLAY_BLOCKED_EVENT,
|
|
type AutoplayBlockedDetail,
|
|
} from "@/lib/notification-sounds";
|
|
|
|
/**
|
|
* When a notification sound is attempted before the browser has
|
|
* accepted a user gesture — or, on iPad, when the AudioContext refuses
|
|
* to resume because the device is in silent mode — the player
|
|
* dispatches a custom event. We surface a toast with the appropriate
|
|
* guidance. On iOS the toast also tells the user to check Control
|
|
* Center / the silent switch since "click anywhere" alone won't help
|
|
* when the hardware mute is on.
|
|
*/
|
|
export function useAutoplayHint(): void {
|
|
const { t } = useTranslation();
|
|
const { toast } = useToast();
|
|
useEffect(() => {
|
|
const handler = (e: Event) => {
|
|
const detail = (e as CustomEvent<AutoplayBlockedDetail>).detail;
|
|
const isIos = !!detail?.isIos;
|
|
toast({
|
|
title: t("notifSettings.autoplayHintTitle"),
|
|
description: isIos
|
|
? t("notifSettings.autoplayHintIos")
|
|
: t("notifSettings.autoplayHint"),
|
|
});
|
|
};
|
|
window.addEventListener(AUTOPLAY_BLOCKED_EVENT, handler);
|
|
return () => window.removeEventListener(AUTOPLAY_BLOCKED_EVENT, handler);
|
|
}, [t, toast]);
|
|
}
|