fix(calls): logging out mid-call hangs up first so no ghost MatrixRTC membership is left behind (#29)
Logout stopped the client with the call still joined; the m.call.member state (expires 4 h) stayed and everyone saw the user 'in call'. The logout dialog now sends HangupCall and waits (≤4 s) until our own membership is gone from the room's RTC session before stopping the client. Verified on the local LiveKit stack: membership count 1 → 0, logout completes in ~2 s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
import React, { forwardRef, useCallback } from 'react';
|
import React, { forwardRef, useCallback } from 'react';
|
||||||
import { Dialog, Header, config, Box, Text, Button, Spinner, color } from 'folds';
|
import { Dialog, Header, config, Box, Text, Button, Spinner, color } from 'folds';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
import { AsyncStatus, useAsyncCallback } from '../hooks/useAsyncCallback';
|
import { AsyncStatus, useAsyncCallback } from '../hooks/useAsyncCallback';
|
||||||
import { logoutClient } from '../../client/initMatrix';
|
import { logoutClient } from '../../client/initMatrix';
|
||||||
|
import { callEmbedAtom } from '../state/callEmbed';
|
||||||
|
import { CallEmbed } from '../plugins/call';
|
||||||
import { useMatrixClient } from '../hooks/useMatrixClient';
|
import { useMatrixClient } from '../hooks/useMatrixClient';
|
||||||
import { useModalStyle } from '../hooks/useModalStyle';
|
import { useModalStyle } from '../hooks/useModalStyle';
|
||||||
import { useCrossSigningActive } from '../hooks/useCrossSigning';
|
import { useCrossSigningActive } from '../hooks/useCrossSigning';
|
||||||
@@ -11,6 +15,32 @@ import {
|
|||||||
VerificationStatus,
|
VerificationStatus,
|
||||||
} from '../hooks/useDeviceVerificationStatus';
|
} from '../hooks/useDeviceVerificationStatus';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask Element Call to hang up and wait (bounded) until our own MatrixRTC
|
||||||
|
* membership has actually been removed from the room, so the leave reaches
|
||||||
|
* the homeserver before the client is stopped and the token is revoked.
|
||||||
|
*/
|
||||||
|
const hangupAndWait = async (mx: MatrixClient, embed: CallEmbed): 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() + 4000;
|
||||||
|
while (stillIn() && Date.now() < deadline) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await new Promise((r) => {
|
||||||
|
setTimeout(r, 150);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
type LogoutDialogProps = {
|
type LogoutDialogProps = {
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
};
|
};
|
||||||
@@ -26,10 +56,19 @@ export const LogoutDialog = forwardRef<HTMLDivElement, LogoutDialogProps>(
|
|||||||
mx.getDeviceId() ?? undefined,
|
mx.getDeviceId() ?? undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [callEmbed, setCallEmbed] = useAtom(callEmbedAtom);
|
||||||
|
|
||||||
const [logoutState, logout] = useAsyncCallback<void, Error, []>(
|
const [logoutState, logout] = useAsyncCallback<void, Error, []>(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
|
// [Gitea #29] Logging out mid-call must hang up first, or the MatrixRTC
|
||||||
|
// membership (expires: 4 h) stays behind as a ghost participant and
|
||||||
|
// everyone else sees you "in call" until it times out.
|
||||||
|
if (callEmbed && callEmbed.joined && !callEmbed.disposed) {
|
||||||
|
await hangupAndWait(mx, callEmbed);
|
||||||
|
setCallEmbed(undefined);
|
||||||
|
}
|
||||||
await logoutClient(mx);
|
await logoutClient(mx);
|
||||||
}, [mx]),
|
}, [mx, callEmbed, setCallEmbed]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const ongoingLogout = logoutState.status === AsyncStatus.Loading;
|
const ongoingLogout = logoutState.status === AsyncStatus.Loading;
|
||||||
|
|||||||
Reference in New Issue
Block a user