feat(mobile): haptic tick on PTT press/release and on reactions (#125)

utils/haptics.ts: tick('ptt-on' | 'ptt-off' | 'reaction') → 10/10/8 ms
navigator.vibrate, a no-op without the API (iOS), when the system prefers
reduced motion, or when the new Settings → Calls "Haptic Feedback" switch
(default on, only rendered where the API exists) is off. PTT is observed
once through pttActiveAtom so the keyboard, global-hotkey and on-screen
paths all tick; reactions tick where the reaction event is sent in the
room and thread timelines (quick bar, hover bar, sheet and emoji board
all funnel there).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
2026-09-20 15:15:04 -04:00
co-authored by Claude Opus 5
parent 0c45bde832
commit 492b57c60d
8 changed files with 102 additions and 2 deletions
@@ -122,6 +122,7 @@ import { DenoiseTester } from './DenoiseTester';
import { SettingsSelect } from '../../../components/settings-select/SettingsSelect';
import { isBindableCallKey } from '../../../utils/callKeybind';
import { StorageUsage } from './StorageUsage';
import { hapticsSupported } from '../../../utils/haptics';
/**
* P5-47 — opt-in TDS window chrome toggle (desktop only). Renders nothing in the
@@ -1653,6 +1654,7 @@ function Calls() {
const [pttMode, setPttMode] = useSetting(settingsAtom, 'pttMode');
const [callRejoin, setCallRejoin] = useSetting(settingsAtom, 'callRejoinAfterRestart');
const [haptics, setHaptics] = useSetting(settingsAtom, 'hapticFeedback');
const [pttKey, setPttKey] = useSetting(settingsAtom, 'pttKey');
const [deafenKey, setDeafenKey] = useSetting(settingsAtom, 'deafenKey');
const [deafenHotkey, setDeafenHotkey] = useSetting(settingsAtom, 'deafenHotkey');
@@ -1963,6 +1965,13 @@ function Calls() {
/>
}
/>
{hapticsSupported() && (
<SettingTile
title="Haptic Feedback"
description="A short vibration when push-to-talk engages or releases and when you send a reaction. Off automatically when your system prefers reduced motion."
after={<Switch variant="Primary" value={haptics} onChange={setHaptics} />}
/>
)}
<SettingTile
title="Push to Talk"
description="Mute your microphone by default. Hold the PTT key to speak."