review: Improve error structure + better RTCFocus error message
This commit is contained in:
@@ -74,6 +74,7 @@
|
|||||||
},
|
},
|
||||||
"disconnected_banner": "Connectivity to the server has been lost.",
|
"disconnected_banner": "Connectivity to the server has been lost.",
|
||||||
"error": {
|
"error": {
|
||||||
|
"call_is_not_supported": "Call is not supported",
|
||||||
"call_not_found": "Call not found",
|
"call_not_found": "Call not found",
|
||||||
"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>",
|
"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>",
|
||||||
"connection_lost": "Connection lost",
|
"connection_lost": "Connection lost",
|
||||||
@@ -82,6 +83,7 @@
|
|||||||
"e2ee_unsupported_description": "Your web browser does not support encrypted calls. Supported browsers include Chrome, Safari, and Firefox 117+.",
|
"e2ee_unsupported_description": "Your web browser does not support encrypted calls. Supported browsers include Chrome, Safari, and Firefox 117+.",
|
||||||
"generic": "Something went wrong",
|
"generic": "Something went wrong",
|
||||||
"generic_description": "Submitting debug logs will help us track down the problem.",
|
"generic_description": "Submitting debug logs will help us track down the problem.",
|
||||||
|
"matrix_rtc_focus_missing": "The server is not configured to work with \"{{brand}}\". Please contact your server admin (Error Code: {{ errorCode }}).",
|
||||||
"insufficient_capacity": "Insufficient capacity",
|
"insufficient_capacity": "Insufficient capacity",
|
||||||
"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.",
|
"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.",
|
||||||
"open_elsewhere": "Opened in another tab",
|
"open_elsewhere": "Opened in another tab",
|
||||||
|
|||||||
@@ -5,15 +5,17 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|||||||
Please see LICENSE in the repository root for full details.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type FC, type ReactNode } from "react";
|
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
|
ErrorIcon,
|
||||||
HostIcon,
|
HostIcon,
|
||||||
|
OfflineIcon,
|
||||||
PopOutIcon,
|
PopOutIcon,
|
||||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
|
|
||||||
|
import type { ComponentType, FC, ReactNode, SVGAttributes } from "react";
|
||||||
import { ErrorView } from "./ErrorView";
|
import { ErrorView } from "./ErrorView";
|
||||||
import { type ElementCallError, type ErrorCode } from "./utils/ec-errors.ts";
|
import { type ElementCallError, ErrorCategory } from "./utils/ec-errors.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An error consisting of a terse message to be logged to the console and a
|
* An error consisting of a terse message to be logged to the console and a
|
||||||
@@ -68,22 +70,41 @@ export class InsufficientCapacityError extends RichError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ECErrorProps = {
|
type ECErrorProps = {
|
||||||
errorCode: ErrorCode;
|
error: ElementCallError;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GenericECError: FC<{ errorCode: ErrorCode }> = ({
|
const GenericECError: FC<{ error: ElementCallError }> = ({
|
||||||
errorCode,
|
error,
|
||||||
}: ECErrorProps) => {
|
}: ECErrorProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
let title: string;
|
||||||
|
let icon: ComponentType<SVGAttributes<SVGElement>>;
|
||||||
|
switch (error.category) {
|
||||||
|
case ErrorCategory.CONFIGURATION_ISSUE:
|
||||||
|
title = t("error.call_is_not_supported");
|
||||||
|
icon = HostIcon;
|
||||||
|
break;
|
||||||
|
case ErrorCategory.NETWORK_CONNECTIVITY:
|
||||||
|
title = t("error.connection_lost");
|
||||||
|
icon = OfflineIcon;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
title = t("error.generic");
|
||||||
|
icon = ErrorIcon;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<ErrorView Icon={HostIcon} title={t("error.generic")}>
|
<ErrorView Icon={icon} title={title}>
|
||||||
<p>
|
<p>
|
||||||
<Trans
|
{error.localisedMessage ? (
|
||||||
i18nKey="error.unexpected_ec_error"
|
error.localisedMessage
|
||||||
components={[<b />, <code />]}
|
) : (
|
||||||
values={{ errorCode }}
|
<Trans
|
||||||
/>
|
i18nKey="error.unexpected_ec_error"
|
||||||
|
components={[<b />, <code />]}
|
||||||
|
values={{ errorCode: error.code }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</ErrorView>
|
</ErrorView>
|
||||||
);
|
);
|
||||||
@@ -92,7 +113,7 @@ const GenericECError: FC<{ errorCode: ErrorCode }> = ({
|
|||||||
export class ElementCallRichError extends RichError {
|
export class ElementCallRichError extends RichError {
|
||||||
public ecError: ElementCallError;
|
public ecError: ElementCallError;
|
||||||
public constructor(ecError: ElementCallError) {
|
public constructor(ecError: ElementCallError) {
|
||||||
super(ecError.message, <GenericECError errorCode={ecError.code} />);
|
super(ecError.message, <GenericECError error={ecError} />);
|
||||||
this.ecError = ecError;
|
this.ecError = ecError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ test("It fails with configuration error if no live kit url config is set in fall
|
|||||||
}) as unknown as MatrixRTCSession;
|
}) as unknown as MatrixRTCSession;
|
||||||
|
|
||||||
await expect(enterRTCSession(mockedSession, false)).rejects.toThrowError(
|
await expect(enterRTCSession(mockedSession, false)).rejects.toThrowError(
|
||||||
expect.objectContaining({ code: ErrorCode.MISSING_LIVE_KIT_SERVICE_URL }),
|
expect.objectContaining({ code: ErrorCode.MISSING_MATRIX_RTC_FOCUS }),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { AutoDiscovery } from "matrix-js-sdk/src/autodiscovery";
|
|||||||
import { PosthogAnalytics } from "./analytics/PosthogAnalytics";
|
import { PosthogAnalytics } from "./analytics/PosthogAnalytics";
|
||||||
import { Config } from "./config/Config";
|
import { Config } from "./config/Config";
|
||||||
import { ElementWidgetActions, widget, type WidgetHelpers } from "./widget";
|
import { ElementWidgetActions, widget, type WidgetHelpers } from "./widget";
|
||||||
import { ElementCallError, ErrorCode } from "./utils/ec-errors.ts";
|
import { MatrixRTCFocusMissingError } from "./utils/ec-errors.ts";
|
||||||
|
|
||||||
const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
|
const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
|
||||||
|
|
||||||
@@ -81,11 +81,7 @@ async function makePreferredLivekitFoci(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (preferredFoci.length === 0)
|
if (preferredFoci.length === 0)
|
||||||
throw new ElementCallError(
|
throw new MatrixRTCFocusMissingError(domain ?? "");
|
||||||
`No livekit_service_url is configured so we could not create a focus.
|
|
||||||
Currently we skip computing a focus based on other users in the room.`,
|
|
||||||
ErrorCode.MISSING_LIVE_KIT_SERVICE_URL,
|
|
||||||
);
|
|
||||||
return Promise.resolve(preferredFoci);
|
return Promise.resolve(preferredFoci);
|
||||||
|
|
||||||
// TODO: we want to do something like this:
|
// TODO: we want to do something like this:
|
||||||
|
|||||||
@@ -5,29 +5,68 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|||||||
Please see LICENSE in the repository root for full details.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { t } from "i18next";
|
||||||
|
|
||||||
export enum ErrorCode {
|
export enum ErrorCode {
|
||||||
/**
|
/**
|
||||||
* Configuration problem due to no MatrixRTC backend/SFU is exposed via .well-known and no fallback configured.
|
* Configuration problem due to no MatrixRTC backend/SFU is exposed via .well-known and no fallback configured.
|
||||||
*/
|
*/
|
||||||
MISSING_LIVE_KIT_SERVICE_URL = "MISSING_LIVE_KIT_SERVICE_URL",
|
MISSING_MATRIX_RTC_FOCUS = "MISSING_MATRIX_RTC_FOCUS",
|
||||||
CONNECTION_LOST_ERROR = "CONNECTION_LOST_ERROR",
|
CONNECTION_LOST_ERROR = "CONNECTION_LOST_ERROR",
|
||||||
// UNKNOWN_ERROR = "UNKNOWN_ERROR",
|
// UNKNOWN_ERROR = "UNKNOWN_ERROR",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ErrorCategory {
|
||||||
|
/** Calling is not supported, server miss-configured (JWT service missing, no MSC support ...)*/
|
||||||
|
CONFIGURATION_ISSUE = "CONFIGURATION_ISSUE",
|
||||||
|
NETWORK_CONNECTIVITY = "NETWORK_CONNECTIVITY",
|
||||||
|
// SYSTEM_FAILURE / FEDERATION_FAILURE ..
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structure for errors that occur when using ElementCall.
|
* Structure for errors that occur when using ElementCall.
|
||||||
*/
|
*/
|
||||||
export class ElementCallError extends Error {
|
export class ElementCallError extends Error {
|
||||||
public code: ErrorCode;
|
public code: ErrorCode;
|
||||||
|
public category: ErrorCategory;
|
||||||
|
public localisedMessage?: string;
|
||||||
|
|
||||||
public constructor(message: string, code: ErrorCode) {
|
public constructor(
|
||||||
super(message);
|
name: string,
|
||||||
|
code: ErrorCode,
|
||||||
|
category: ErrorCategory,
|
||||||
|
localisedMessage?: string,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this.localisedMessage = localisedMessage;
|
||||||
|
this.category = category;
|
||||||
this.code = code;
|
this.code = code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MatrixRTCFocusMissingError extends ElementCallError {
|
||||||
|
public domain: string;
|
||||||
|
|
||||||
|
public constructor(domain: string) {
|
||||||
|
super(
|
||||||
|
"MatrixRTCFocusMissingError",
|
||||||
|
ErrorCode.MISSING_MATRIX_RTC_FOCUS,
|
||||||
|
ErrorCategory.CONFIGURATION_ISSUE,
|
||||||
|
t("error.matrix_rtc_focus_missing", {
|
||||||
|
brand: domain,
|
||||||
|
errorCode: ErrorCode.MISSING_MATRIX_RTC_FOCUS,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
this.domain = domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class ConnectionLostError extends ElementCallError {
|
export class ConnectionLostError extends ElementCallError {
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super("Connection lost", ErrorCode.CONNECTION_LOST_ERROR);
|
super(
|
||||||
|
"Connection lost",
|
||||||
|
ErrorCode.CONNECTION_LOST_ERROR,
|
||||||
|
ErrorCategory.NETWORK_CONNECTIVITY,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user