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

View File

@@ -13,12 +13,7 @@ import {
useMemo,
useState,
} from "react";
import {
type MatrixClient,
JoinRule,
type Room,
UNSTABLE_MSC4354_STICKY_EVENTS,
} from "matrix-js-sdk";
import { type MatrixClient, JoinRule, type Room } from "matrix-js-sdk";
import {
Room as LivekitRoom,
isE2EESupported as isE2EESupportedBrowser,
@@ -75,8 +70,6 @@ import {
StickyEventsRequiredError,
UnknownCallError,
} from "../utils/errors.ts";
import { Config } from "../config/Config.ts";
import { MatrixRTCMode } from "../config/ConfigOptions.ts";
import { GroupCallErrorBoundary } from "./GroupCallErrorBoundary.tsx";
import { useTypedEventEmitter } from "../useEvents";
import { muteAllAudio$ } from "../state/MuteAllAudioModel.ts";
@@ -170,30 +163,21 @@ export const GroupCallView: FC<Props> = ({
useTypedEventEmitter(
rtcSession,
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(() => {
// Sanity check the room object
if (client.getRoom(rtcSession.room.roomId) !== rtcSession.room)