fix(calls): incoming ring stops on every device once answered or declined elsewhere, or when the caller hangs up (#161)
CI / Build & Quality Checks (push) Successful in 1m30s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 6s
CI / Trigger Desktop Build (push) Successful in 6s
CI / Playwright smoke (e2e) (push) Successful in 1m39s
CI / Build & Quality Checks (push) Successful in 1m30s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 6s
CI / Trigger Desktop Build (push) Successful in 6s
CI / Playwright smoke (e2e) (push) Successful in 1m39s
The incoming-call dialog only went away on Ignore/Answer/Reject on THAT device or when the notification lifetime expired, so a DM call answered on the desktop kept the phone ringing for up to two minutes, and a caller who gave up left everyone ringing. While a ring is showing we now watch the room's MatrixRTC session and timeline: our own membership from any device (answered elsewhere), our own RTCDecline for this ring (declined elsewhere), or an empty session after it has settled (caller hung up) all dismiss it. Verified with two alice devices + bob on the local LiveKit stack: answer elsewhere → dismissed; decline elsewhere → dismissed; caller End → both dialogs gone in 0.5 s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
||||
RoomEvent,
|
||||
} from 'matrix-js-sdk';
|
||||
import { IRTCNotificationContent, RTCNotificationType } from 'matrix-js-sdk/lib/matrixrtc/types';
|
||||
import { MatrixRTCSessionEvent } from 'matrix-js-sdk/lib/matrixrtc/MatrixRTCSession';
|
||||
import { CryptoBackend } from 'matrix-js-sdk/lib/common-crypto/CryptoBackend';
|
||||
import {
|
||||
CallEmbedContextProvider,
|
||||
@@ -559,6 +560,50 @@ function IncomingCallListener({ callEmbed, joined }: IncomingCallListenerProps)
|
||||
setCallInfo(undefined);
|
||||
}, []);
|
||||
|
||||
// [Gitea #161] Stop ringing here when the call was handled ELSEWHERE:
|
||||
// - answered on another of our devices (our own m.call.member appears),
|
||||
// - declined on another of our devices (our own RTCDecline for this ring),
|
||||
// - the caller hung up before we picked up (nobody left in the session).
|
||||
// Without this every other device kept ringing for the full lifetime.
|
||||
useEffect(() => {
|
||||
if (!callInfo) return undefined;
|
||||
const { room, refEventId } = callInfo;
|
||||
const myUserId = mx.getSafeUserId();
|
||||
const dismiss = () =>
|
||||
setCallInfo((current) => (current?.refEventId === refEventId ? undefined : current));
|
||||
|
||||
const session = mx.matrixRTC.getRoomSession(room);
|
||||
const checkMemberships = () => {
|
||||
const { memberships } = session;
|
||||
if (memberships.some((m) => m.sender === myUserId))
|
||||
dismiss(); // answered elsewhere
|
||||
else if (memberships.length === 0) dismiss(); // caller gone
|
||||
};
|
||||
const onTimeline: EventTimelineSetHandlerMap[RoomEvent.Timeline] = (
|
||||
event,
|
||||
eventRoom,
|
||||
_s,
|
||||
_r,
|
||||
data,
|
||||
) => {
|
||||
if (eventRoom?.roomId !== room.roomId || !data.liveEvent) return;
|
||||
if (event.getType() === EventType.RTCDecline && event.getSender() === myUserId) {
|
||||
const related = event.getRelation()?.event_id;
|
||||
if (!related || related === refEventId) dismiss(); // declined elsewhere
|
||||
}
|
||||
};
|
||||
session.on(MatrixRTCSessionEvent.MembershipsChanged, checkMemberships);
|
||||
mx.on(RoomEvent.Timeline, onTimeline);
|
||||
// The caller's membership may not have arrived yet when the ring starts —
|
||||
// only treat "empty" as hung-up after it has had a moment to sync.
|
||||
const settle = setTimeout(checkMemberships, 5000);
|
||||
return () => {
|
||||
session.off(MatrixRTCSessionEvent.MembershipsChanged, checkMemberships);
|
||||
mx.removeListener(RoomEvent.Timeline, onTimeline);
|
||||
clearTimeout(settle);
|
||||
};
|
||||
}, [mx, callInfo]);
|
||||
|
||||
const handleReject = useCallback(
|
||||
(room: Room, eventId: string) => {
|
||||
// Best-effort: the local UI dismisses regardless (below), but a failed
|
||||
|
||||
Reference in New Issue
Block a user