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
36 lines
1.2 KiB
TypeScript
36 lines
1.2 KiB
TypeScript
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);
|
||
});
|
||
}
|
||
};
|