Files
cinny/src/app/plugins/call/hangup.ts
T
jaredandClaude Opus 5 070a1ea012 fix(calls): hang up explicitly when answering another call while in one (#195)
Answering from the in-call banner is the only call-to-call switch path; it
started the new embed and let the atom dispose the old one, which just removed
the iframe — our m.call.member in the old room lingered ~17 s (until the
delayed leave expired), so everyone there still saw us in the call.

hangupAndWait moves out of LogoutDialog into plugins/call/hangup.ts and
handleAnswer now hangs up, waits for our membership to clear (bounded 4 s),
disposes the old embed itself (its HangupCall echo would otherwise land after
startCall and clear the NEW embed from the atom — seen in testing), then joins
the new call. Measured headless: old membership gone in 1 s, new call live
with both participants.

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

36 lines
1.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { MatrixClient } from 'matrix-js-sdk';
import { CallEmbed } from './CallEmbed';
/**
* Ask EC to hang up and wait (bounded) until our own RTC membership has left
* the room's session. Used wherever the embed is about to be torn down for a
* reason other than the user pressing End — logout (#29) and answering another
* call while in one (#195) — because disposing the iframe alone leaves a ghost
* `m.call.member` for everyone else until the delayed leave event expires
* (~17–30 s measured).
*/
export const hangupCallAndWait = async (
mx: MatrixClient,
embed: CallEmbed,
timeoutMs = 4000,
): Promise<void> => {
const myUserId = mx.getUserId();
const myDeviceId = mx.getDeviceId();
const stillIn = (): boolean =>
mx.matrixRTC
.getRoomSession(embed.room)
.memberships.some((m) => m.sender === myUserId && m.deviceId === myDeviceId);
try {
await embed.hangup();
} catch {
// widget already gone — fall through to the wait/timeout
}
const deadline = Date.now() + timeoutMs;
while (stillIn() && Date.now() < deadline) {
// eslint-disable-next-line no-await-in-loop
await new Promise((r) => {
setTimeout(r, 150);
});
}
};