Files
TX/artifacts/tx-os/src/hooks/use-autoplay-hint.ts
T
riyadhafraa 28ddbe33d1 Task #441: Fix notification sound on iPad/Safari (Web Audio migration)
- 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.
2026-05-07 14:15:53 +00:00

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]);
}