Skip to content

Commit 2ba803f

Browse files
committed
review: Improve error structure + better RTCFocus error message
1 parent 1098091 commit 2ba803f

File tree

5 files changed

+81
-23
lines changed

5 files changed

+81
-23
lines changed

locales/en/app.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
},
7575
"disconnected_banner": "Connectivity to the server has been lost.",
7676
"error": {
77+
"call_is_not_supported": "Call is not supported",
7778
"call_not_found": "Call not found",
7879
"call_not_found_description": "<0>That link doesn't appear to belong to any existing call. Check that you have the right link, or <1>create a new one</1>.</0>",
7980
"connection_lost": "Connection lost",
@@ -82,6 +83,7 @@
8283
"e2ee_unsupported_description": "Your web browser does not support encrypted calls. Supported browsers include Chrome, Safari, and Firefox 117+.",
8384
"generic": "Something went wrong",
8485
"generic_description": "Submitting debug logs will help us track down the problem.",
86+
"matrix_rtc_focus_missing": "The server is not configured to work with \"{{brand}}\". Please contact your server admin (Error Code: {{ errorCode }}).",
8587
"insufficient_capacity": "Insufficient capacity",
8688
"insufficient_capacity_description": "The server has reached its maximum capacity and you cannot join the call at this time. Try again later, or contact your server admin if the problem persists.",
8789
"open_elsewhere": "Opened in another tab",

src/RichError.tsx

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
55
Please see LICENSE in the repository root for full details.
66
*/
77

8-
import { type FC, type ReactNode } from "react";
98
import { Trans, useTranslation } from "react-i18next";
109
import {
10+
ErrorIcon,
1111
HostIcon,
12+
OfflineIcon,
1213
PopOutIcon,
1314
} from "@vector-im/compound-design-tokens/assets/web/icons";
1415

16+
import type { ComponentType, FC, ReactNode, SVGAttributes } from "react";
1517
import { ErrorView } from "./ErrorView";
16-
import { type ElementCallError, type ErrorCode } from "./utils/ec-errors.ts";
18+
import { type ElementCallError, ErrorCategory } from "./utils/ec-errors.ts";
1719

1820
/**
1921
* An error consisting of a terse message to be logged to the console and a
@@ -68,22 +70,41 @@ export class InsufficientCapacityError extends RichError {
6870
}
6971

7072
type ECErrorProps = {
71-
errorCode: ErrorCode;
73+
error: ElementCallError;
7274
};
7375

74-
const GenericECError: FC<{ errorCode: ErrorCode }> = ({
75-
errorCode,
76+
const GenericECError: FC<{ error: ElementCallError }> = ({
77+
error,
7678
}: ECErrorProps) => {
7779
const { t } = useTranslation();
7880

81+
let title: string;
82+
let icon: ComponentType<SVGAttributes<SVGElement>>;
83+
switch (error.category) {
84+
case ErrorCategory.CONFIGURATION_ISSUE:
85+
title = t("error.call_is_not_supported");
86+
icon = HostIcon;
87+
break;
88+
case ErrorCategory.NETWORK_CONNECTIVITY:
89+
title = t("error.connection_lost");
90+
icon = OfflineIcon;
91+
break;
92+
default:
93+
title = t("error.generic");
94+
icon = ErrorIcon;
95+
}
7996
return (
80-
<ErrorView Icon={HostIcon} title={t("error.generic")}>
97+
<ErrorView Icon={icon} title={title}>
8198
<p>
82-
<Trans
83-
i18nKey="error.unexpected_ec_error"
84-
components={[<b />, <code />]}
85-
values={{ errorCode }}
86-
/>
99+
{error.localisedMessage ? (
100+
error.localisedMessage
101+
) : (
102+
<Trans
103+
i18nKey="error.unexpected_ec_error"
104+
components={[<b />, <code />]}
105+
values={{ errorCode: error.code }}
106+
/>
107+
)}
87108
</p>
88109
</ErrorView>
89110
);
@@ -92,7 +113,7 @@ const GenericECError: FC<{ errorCode: ErrorCode }> = ({
92113
export class ElementCallRichError extends RichError {
93114
public ecError: ElementCallError;
94115
public constructor(ecError: ElementCallError) {
95-
super(ecError.message, <GenericECError errorCode={ecError.code} />);
116+
super(ecError.message, <GenericECError error={ecError} />);
96117
this.ecError = ecError;
97118
}
98119
}

src/rtcSessionHelpers.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ test("It fails with configuration error if no live kit url config is set in fall
156156
}) as unknown as MatrixRTCSession;
157157

158158
await expect(enterRTCSession(mockedSession, false)).rejects.toThrowError(
159-
expect.objectContaining({ code: ErrorCode.MISSING_LIVE_KIT_SERVICE_URL }),
159+
expect.objectContaining({ code: ErrorCode.MISSING_MATRIX_RTC_FOCUS }),
160160
);
161161
});
162162

src/rtcSessionHelpers.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { AutoDiscovery } from "matrix-js-sdk/src/autodiscovery";
1818
import { PosthogAnalytics } from "./analytics/PosthogAnalytics";
1919
import { Config } from "./config/Config";
2020
import { ElementWidgetActions, widget, type WidgetHelpers } from "./widget";
21-
import { ElementCallError, ErrorCode } from "./utils/ec-errors.ts";
21+
import { MatrixRTCFocusMissingError } from "./utils/ec-errors.ts";
2222

2323
const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
2424

@@ -81,11 +81,7 @@ async function makePreferredLivekitFoci(
8181
}
8282

8383
if (preferredFoci.length === 0)
84-
throw new ElementCallError(
85-
`No livekit_service_url is configured so we could not create a focus.
86-
Currently we skip computing a focus based on other users in the room.`,
87-
ErrorCode.MISSING_LIVE_KIT_SERVICE_URL,
88-
);
84+
throw new MatrixRTCFocusMissingError(domain ?? "");
8985
return Promise.resolve(preferredFoci);
9086

9187
// TODO: we want to do something like this:

src/utils/ec-errors.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,68 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
55
Please see LICENSE in the repository root for full details.
66
*/
77

8+
import { t } from "i18next";
9+
810
export enum ErrorCode {
911
/**
1012
* Configuration problem due to no MatrixRTC backend/SFU is exposed via .well-known and no fallback configured.
1113
*/
12-
MISSING_LIVE_KIT_SERVICE_URL = "MISSING_LIVE_KIT_SERVICE_URL",
14+
MISSING_MATRIX_RTC_FOCUS = "MISSING_MATRIX_RTC_FOCUS",
1315
CONNECTION_LOST_ERROR = "CONNECTION_LOST_ERROR",
1416
// UNKNOWN_ERROR = "UNKNOWN_ERROR",
1517
}
1618

19+
export enum ErrorCategory {
20+
/** Calling is not supported, server miss-configured (JWT service missing, no MSC support ...)*/
21+
CONFIGURATION_ISSUE = "CONFIGURATION_ISSUE",
22+
NETWORK_CONNECTIVITY = "NETWORK_CONNECTIVITY",
23+
// SYSTEM_FAILURE / FEDERATION_FAILURE ..
24+
}
25+
1726
/**
1827
* Structure for errors that occur when using ElementCall.
1928
*/
2029
export class ElementCallError extends Error {
2130
public code: ErrorCode;
31+
public category: ErrorCategory;
32+
public localisedMessage?: string;
2233

23-
public constructor(message: string, code: ErrorCode) {
24-
super(message);
34+
public constructor(
35+
name: string,
36+
code: ErrorCode,
37+
category: ErrorCategory,
38+
localisedMessage?: string,
39+
) {
40+
super();
41+
this.localisedMessage = localisedMessage;
42+
this.category = category;
2543
this.code = code;
2644
}
2745
}
2846

47+
export class MatrixRTCFocusMissingError extends ElementCallError {
48+
public domain: string;
49+
50+
public constructor(domain: string) {
51+
super(
52+
"MatrixRTCFocusMissingError",
53+
ErrorCode.MISSING_MATRIX_RTC_FOCUS,
54+
ErrorCategory.CONFIGURATION_ISSUE,
55+
t("error.matrix_rtc_focus_missing", {
56+
brand: domain,
57+
errorCode: ErrorCode.MISSING_MATRIX_RTC_FOCUS,
58+
}),
59+
);
60+
this.domain = domain;
61+
}
62+
}
63+
2964
export class ConnectionLostError extends ElementCallError {
3065
public constructor() {
31-
super("Connection lost", ErrorCode.CONNECTION_LOST_ERROR);
66+
super(
67+
"Connection lost",
68+
ErrorCode.CONNECTION_LOST_ERROR,
69+
ErrorCategory.NETWORK_CONNECTIVITY,
70+
);
3271
}
3372
}

0 commit comments

Comments
 (0)