Files
cinny/src/app/utils/pttHoldWatchdog.ts
T
jaredandClaude Opus 5 f23b215efa fix(calls): release a push-to-talk hold after 5 minutes (#136)
Elbow-on-the-keyboard guard: a PTT hold longer than 5 min (fixed, not a
setting) is released exactly like a keyup — pttActive off, mic muted — with a
toast 'Push to talk released after 5 minutes — press the key again to keep
talking.' A fresh press re-engages normally. One watchdog shared by the DOM
path and the desktop-global hotkey path; keydown auto-repeat no longer
restarts anything; non-PTT mode, deafen and the mic button are untouched.
Helper unit-tested with fake timers; verified headless with Playwright's clock:
held → still open at +4 min → released + toast at +5 min → re-press works.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-19 17:13:14 -04:00

41 lines
1.0 KiB
TypeScript

/**
* [Gitea #136] Elbow-on-the-keyboard guard for push-to-talk: a hold longer
* than `maxMs` is released as if the key came up. `arm()` on press, `disarm()`
* on release (a genuine release + re-press restarts the clock). Timers are
* injectable for tests.
*/
export const PTT_MAX_HOLD_MS = 5 * 60 * 1000;
type Timers = {
setTimeout: (fn: () => void, ms: number) => unknown;
clearTimeout: (handle: unknown) => void;
};
const realTimers: Timers = {
setTimeout: (fn, ms) => setTimeout(fn, ms),
clearTimeout: (h) => clearTimeout(h as ReturnType<typeof setTimeout>),
};
export const createPttHoldWatchdog = (
onExpire: () => void,
maxMs = PTT_MAX_HOLD_MS,
timers: Timers = realTimers,
) => {
let handle: unknown;
const disarm = () => {
if (handle !== undefined) timers.clearTimeout(handle);
handle = undefined;
};
return {
arm: () => {
disarm();
handle = timers.setTimeout(() => {
handle = undefined;
onExpire();
}, maxMs);
},
disarm,
armed: () => handle !== undefined,
};
};