/* Copyright 2024 New Vector Ltd. Copyright 2018-2021 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ import React, { type ReactNode } from "react"; import { MatrixError, ConnectionError } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; import { _t, _td, lookupString, type Tags, type TranslatedString, type TranslationKey } from "../languageHandler"; import SdkConfig from "../SdkConfig"; import { type ValidatedServerConfig } from "./ValidatedServerConfig"; import ExternalLink from "../components/views/elements/ExternalLink"; import Modal from "../Modal.tsx"; import ErrorDialog from "../components/views/dialogs/ErrorDialog.tsx"; export const resourceLimitStrings = { "monthly_active_user": _td("error|mau"), "hs_blocked": _td("error|hs_blocked"), "": _td("error|resource_limits"), }; export const adminContactStrings = { "": _td("error|admin_contact"), }; /** * Produce a translated error message for a * M_RESOURCE_LIMIT_EXCEEDED error * * @param {string} limitType The limit_type from the error * @param {string} adminContact The admin_contact from the error * @param {Object} strings Translatable string for different * limit_type. Must include at least the empty string key * which is the default. Strings may include an 'a' tag * for the admin contact link. * @param {Object} extraTranslations Extra translation substitution functions * for any tags in the strings apart from 'a' * @returns {*} Translated string or react component */ export function messageForResourceLimitError( limitType: string | undefined, adminContact: string | undefined, strings: Record, extraTranslations?: Tags, ): TranslatedString { let errString = limitType ? strings[limitType] : undefined; if (errString === undefined) errString = strings[""]; const linkSub = (sub: string): ReactNode => { if (adminContact) { return ( {sub} ); } else { return sub; } }; if (lookupString(errString).includes("")) { return _t(errString, {}, Object.assign({ a: linkSub }, extraTranslations)); } else { return _t(errString, {}, extraTranslations!); } } export function messageForSyncError(err: Error): ReactNode { if (err instanceof MatrixError && err.errcode === "M_RESOURCE_LIMIT_EXCEEDED") { const limitError = messageForResourceLimitError( err.data.limit_type, err.data.admin_contact, resourceLimitStrings, ); const adminContact = messageForResourceLimitError( err.data.limit_type, err.data.admin_contact, adminContactStrings, ); return (
{limitError}
{adminContact}
); } else { return
{_t("error|sync")}
; } } export function messageForLoginError( err: MatrixError, serverConfig: Pick, ): ReactNode { if (err.errcode === "M_RESOURCE_LIMIT_EXCEEDED") { const errorTop = messageForResourceLimitError( err.data.limit_type, err.data.admin_contact, resourceLimitStrings, ); const errorDetail = messageForResourceLimitError( err.data.limit_type, err.data.admin_contact, adminContactStrings, ); return (
{errorTop}
{errorDetail}
); } else if (err.httpStatus === 401 || err.httpStatus === 403) { if (err.errcode === "M_USER_DEACTIVATED") { return _t("auth|account_deactivated"); } else if (SdkConfig.get("disable_custom_urls")) { return (
{_t("auth|incorrect_credentials")}
{_t("auth|incorrect_credentials_detail", { hs: serverConfig.hsName, })}
); } else { return _t("auth|incorrect_credentials"); } } else { return messageForConnectionError(err, serverConfig); } } export function messageForConnectionError( err: Error, serverConfig: Pick, ): ReactNode { let errorText = _t("error|connection"); if (err instanceof ConnectionError) { if ( window.location.protocol === "https:" && (serverConfig.hsUrl.startsWith("http:") || !serverConfig.hsUrl.startsWith("http")) ) { return ( {_t( "error|mixed_content", {}, { a: (sub) => { return (
{sub} ); }, }, )} ); } return ( {_t( "error|tls", {}, { a: (sub) => ( {sub} ), }, )} ); } else if (err instanceof MatrixError) { if (err.errcode) { errorText += `(${err.errcode})`; } else if (err.httpStatus) { errorText += ` (HTTP ${err.httpStatus})`; } } return errorText; } /** * Utility for handling unexpected errors: pops up the error dialog. * * Example usage: * ``` * try { * /// complicated operation * } catch (e) { * logErrorAndShowErrorDialog("Failed complicated operation", e); * } * ``` * * This isn't particularly intended to be pretty; rather it lets the user know that *something* has gone wrong so that * they can report a bug. The general idea is that it's better to let the user know of a failure, even if they * can't do anything about it, than it is to fail silently with the appearance of success. * * @param title - Title for the error dialog. * @param error - The thrown error. Becomes the content of the error dialog. */ export function logErrorAndShowErrorDialog(title: string, error: any): void { logger.error(`${title}:`, error); Modal.createDialog(ErrorDialog, { title, description: `${error}` }); }