Catch SDK sticky-events error instead of pre-flight check

This commit is contained in:
fkwp
2026-06-09 17:43:59 +02:00
parent 3711748aa8
commit 49168d01fa
2 changed files with 38 additions and 51 deletions

View File

@@ -38,7 +38,6 @@ import { useAudioContext } from "../useAudioContext";
import { ActiveCall } from "./InCallView"; import { ActiveCall } from "./InCallView";
import { import {
flushPromises, flushPromises,
mockConfig,
mockEmitter, mockEmitter,
mockMatrixRoom, mockMatrixRoom,
mockMatrixRoomMember, mockMatrixRoomMember,
@@ -51,7 +50,6 @@ import { GroupCallErrorBoundary } from "./GroupCallErrorBoundary";
import { ElementWidgetActions, type WidgetHelpers } from "../widget"; import { ElementWidgetActions, type WidgetHelpers } from "../widget";
import { LazyEventEmitter } from "../LazyEventEmitter"; import { LazyEventEmitter } from "../LazyEventEmitter";
import { MatrixRTCTransportMissingError } from "../utils/errors"; import { MatrixRTCTransportMissingError } from "../utils/errors";
import { MatrixRTCMode } from "../config/ConfigOptions";
import { ProcessorProvider } from "../livekit/TrackProcessorContext"; import { ProcessorProvider } from "../livekit/TrackProcessorContext";
import { MediaDevicesContext } from "../MediaDevicesContext"; import { MediaDevicesContext } from "../MediaDevicesContext";
import { constant } from "../state/Behavior"; import { constant } from "../state/Behavior";
@@ -134,7 +132,6 @@ function createGroupCallView(
widget: WidgetHelpers | null, widget: WidgetHelpers | null,
joined = true, joined = true,
options: { options: {
doesServerSupportUnstableFeature?: (feature: string) => Promise<boolean>;
withErrorBoundary?: boolean; withErrorBoundary?: boolean;
} = {}, } = {},
): { ): {
@@ -146,9 +143,6 @@ function createGroupCallView(
getUserId: () => localRtcMember.userId, getUserId: () => localRtcMember.userId,
getDeviceId: () => localRtcMember.deviceId, getDeviceId: () => localRtcMember.deviceId,
getRoom: (rId) => (rId === roomId ? room : null), getRoom: (rId) => (rId === roomId ? room : null),
doesServerSupportUnstableFeature:
options.doesServerSupportUnstableFeature ??
vi.fn().mockResolvedValue(true),
} as Partial<MatrixClient> as MatrixClient; } as Partial<MatrixClient> as MatrixClient;
const room = mockMatrixRoom({ const room = mockMatrixRoom({
relations: { relations: {
@@ -416,28 +410,37 @@ test.skip("GroupCallView shows errors that occur during joining", async () => {
screen.getByText("Call is not supported"); screen.getByText("Call is not supported");
}); });
test("shows StickyEventsRequiredError when matrix_2_0 is forced but homeserver lacks MSC4354", async () => { test("translates UnsupportedStickyEventsEndpointError to the StickyEventsRequiredError screen", async () => {
mockConfig({ matrix_rtc_mode: MatrixRTCMode.Matrix_2_0 }); // Match the shape the SDK emits on
createGroupCallView(null, true, { // MatrixRTCSessionEvent.MembershipManagerError when matrix_2_0 mode is
doesServerSupportUnstableFeature: vi.fn().mockResolvedValue(false), // configured but the homeserver does not advertise MSC4354.
const stickyError = new Error("Server does not support the sticky events");
stickyError.name = "UnsupportedStickyEventsEndpointError";
const { rtcSession } = createGroupCallView(null, true, {
withErrorBoundary: true, withErrorBoundary: true,
}); });
await act(() =>
rtcSession.emit(MatrixRTCSessionEvent.MembershipManagerError, stickyError),
);
await screen.findByText("Homeserver does not support Matrix 2.0 calls"); await screen.findByText("Homeserver does not support Matrix 2.0 calls");
}); });
test("does not show StickyEventsRequiredError when homeserver supports MSC4354", async () => { test("falls back to ConnectionLostError for unrecognised membership manager errors", async () => {
mockConfig({ matrix_rtc_mode: MatrixRTCMode.Matrix_2_0 }); const { rtcSession } = createGroupCallView(null, true, {
const { getByText } = createGroupCallView(null, true, {
doesServerSupportUnstableFeature: vi.fn().mockResolvedValue(true),
withErrorBoundary: true, withErrorBoundary: true,
}); });
// Give the async support check a chance to resolve.
await flushPromises(); await act(() =>
expect( rtcSession.emit(
screen.queryByText("Homeserver does not support Matrix 2.0 calls"), MatrixRTCSessionEvent.MembershipManagerError,
).toBeNull(); new Error("something else broke"),
// The normal call UI (mocked ActiveCall) renders instead. ),
expect(getByText("Leave")).toBeInTheDocument(); );
await screen.findByText("Connection lost");
}); });
test("user can reconnect after a membership manager error", async () => { test("user can reconnect after a membership manager error", async () => {

View File

@@ -13,12 +13,7 @@ import {
useMemo, useMemo,
useState, useState,
} from "react"; } from "react";
import { import { type MatrixClient, JoinRule, type Room } from "matrix-js-sdk";
type MatrixClient,
JoinRule,
type Room,
UNSTABLE_MSC4354_STICKY_EVENTS,
} from "matrix-js-sdk";
import { import {
Room as LivekitRoom, Room as LivekitRoom,
isE2EESupported as isE2EESupportedBrowser, isE2EESupported as isE2EESupportedBrowser,
@@ -75,8 +70,6 @@ import {
StickyEventsRequiredError, StickyEventsRequiredError,
UnknownCallError, UnknownCallError,
} from "../utils/errors.ts"; } from "../utils/errors.ts";
import { Config } from "../config/Config.ts";
import { MatrixRTCMode } from "../config/ConfigOptions.ts";
import { GroupCallErrorBoundary } from "./GroupCallErrorBoundary.tsx"; import { GroupCallErrorBoundary } from "./GroupCallErrorBoundary.tsx";
import { useTypedEventEmitter } from "../useEvents"; import { useTypedEventEmitter } from "../useEvents";
import { muteAllAudio$ } from "../state/MuteAllAudioModel.ts"; import { muteAllAudio$ } from "../state/MuteAllAudioModel.ts";
@@ -170,30 +163,21 @@ export const GroupCallView: FC<Props> = ({
useTypedEventEmitter( useTypedEventEmitter(
rtcSession, rtcSession,
MatrixRTCSessionEvent.MembershipManagerError, MatrixRTCSessionEvent.MembershipManagerError,
(error) => setExternalError(new ConnectionLostError()), (error) => {
// The SDK throws this typed error when matrix_rtc_mode=matrix_2_0 is in
// effect but the homeserver does not advertise MSC4354 (sticky events).
// Surface the actual cause instead of a generic connection-lost screen.
if (
error instanceof Error &&
error.name === "UnsupportedStickyEventsEndpointError"
) {
setExternalError(new StickyEventsRequiredError());
} else {
setExternalError(new ConnectionLostError());
}
},
); );
// If the deployment pins matrix_rtc_mode=matrix_2_0 but this homeserver
// doesn't advertise MSC4354 (sticky events), the call will fail to connect
// with a generic "Connection lost" message. Surface the real cause up front.
useEffect(() => {
if (Config.get().matrix_rtc_mode !== MatrixRTCMode.Matrix_2_0) return;
let cancelled = false;
client
.doesServerSupportUnstableFeature(UNSTABLE_MSC4354_STICKY_EVENTS)
.then((supported) => {
if (!cancelled && !supported) {
setExternalError(new StickyEventsRequiredError());
}
})
.catch((e) => {
logger.warn("Failed to check sticky-events homeserver support", e);
});
return (): void => {
cancelled = true;
};
}, [client]);
useEffect(() => { useEffect(() => {
// Sanity check the room object // Sanity check the room object
if (client.getRoom(rtcSession.room.roomId) !== rtcSession.room) if (client.getRoom(rtcSession.room.roomId) !== rtcSession.room)