mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-05 20:26:19 +02:00
Add support for experimental MSC4335 M_USER_EXCEEDED_LIMIT error code
This commit is contained in:
parent
93a8b67ed0
commit
aa1099fd36
@ -18,6 +18,7 @@ import {
|
||||
type UploadOpts,
|
||||
type UploadProgress,
|
||||
THREAD_RELATION_TYPE,
|
||||
MatrixError,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import {
|
||||
type ImageInfo,
|
||||
@ -31,6 +32,7 @@ import encrypt from "matrix-encrypt-attachment";
|
||||
import extractPngChunks from "png-chunks-extract";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { removeElement } from "matrix-js-sdk/src/utils";
|
||||
import { type ReactNode } from "react";
|
||||
|
||||
import dis from "./dispatcher/dispatcher";
|
||||
import { _t } from "./languageHandler";
|
||||
@ -56,6 +58,7 @@ import { createThumbnail } from "./utils/image-media";
|
||||
import { attachMentions, attachRelation } from "./utils/messages.ts";
|
||||
import { doMaybeLocalRoomAction } from "./utils/local-room";
|
||||
import { blobIsAnimated } from "./utils/Image.ts";
|
||||
import MSC4335UserLimitExceededDialog from "./components/views/dialogs/MSC4335UserLimitExceededDialog.tsx";
|
||||
|
||||
// scraped out of a macOS hidpi (5660ppm) screenshot png
|
||||
// 5669 px (x-axis) , 5669 px (y-axis) , per metre
|
||||
@ -665,16 +668,36 @@ export default class ContentMessages {
|
||||
}
|
||||
|
||||
if (!upload.cancelled) {
|
||||
let desc = _t("upload_failed_generic", { fileName: upload.fileName });
|
||||
if (unwrappedError instanceof HTTPError && unwrappedError.httpStatus === 413) {
|
||||
desc = _t("upload_failed_size", {
|
||||
fileName: upload.fileName,
|
||||
if (
|
||||
unwrappedError instanceof MatrixError &&
|
||||
unwrappedError.errcode === "ORG.MATRIX.MSC4335_USER_LIMIT_EXCEEDED" &&
|
||||
typeof unwrappedError.data["org.matrix.msc4335.info_uri"] === "string"
|
||||
) {
|
||||
// Support for experimental MSC4335 M_USER_LIMIT_EXCEEDED error
|
||||
const canUpgrade =
|
||||
typeof unwrappedError.data["org.matrix.msc4335.can_upgrade"] === "boolean"
|
||||
? unwrappedError.data["org.matrix.msc4335.can_upgrade"]
|
||||
: false;
|
||||
Modal.createDialog(MSC4335UserLimitExceededDialog, {
|
||||
title: _t("upload_failed_title"),
|
||||
error: {
|
||||
infoUri: unwrappedError.data["org.matrix.msc4335.info_uri"],
|
||||
canUpgrade,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
let desc: ReactNode = _t("upload_failed_generic", { fileName: upload.fileName });
|
||||
if (unwrappedError instanceof HTTPError && unwrappedError.httpStatus === 413) {
|
||||
desc = _t("upload_failed_size", {
|
||||
fileName: upload.fileName,
|
||||
});
|
||||
}
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: _t("upload_failed_title"),
|
||||
description: desc,
|
||||
});
|
||||
}
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: _t("upload_failed_title"),
|
||||
description: desc,
|
||||
});
|
||||
|
||||
dis.dispatch<UploadErrorPayload>({ action: Action.UploadFailed, upload, error });
|
||||
}
|
||||
} finally {
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX, useMemo } from "react";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
|
||||
|
||||
interface MSC4335Data {
|
||||
infoUri: string;
|
||||
canUpgrade: boolean;
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
onFinished?: (success?: boolean) => void;
|
||||
title?: string;
|
||||
error: MSC4335Data;
|
||||
}
|
||||
|
||||
export default function MSC4335UserLimitExceededDialog({ onFinished: _onFinished, title, error }: IProps): JSX.Element {
|
||||
function onFinished(success?: boolean): void {
|
||||
_onFinished?.(success);
|
||||
};
|
||||
|
||||
function onClick(): void {
|
||||
// noop as using href
|
||||
};
|
||||
|
||||
const matrixClient = useMatrixClientContext();
|
||||
const isMatrixDotOrg = useMemo(() => matrixClient?.getDomain() === "matrix.org", [matrixClient]);
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
className="mx_ErrorDialog"
|
||||
title={title || _t("msc4335_user_limit_exceeded|title")}
|
||||
contentId="mx_Dialog_content"
|
||||
onFinished={onFinished}
|
||||
>
|
||||
<div className="mx_Dialog_content" id="mx_Dialog_content">
|
||||
{error.canUpgrade
|
||||
? (isMatrixDotOrg ? _t(
|
||||
"msc4335_matrix_org_user_limit_exceeded|soft_limit",
|
||||
) : _t("msc4335_user_limit_exceeded|soft_limit"))
|
||||
: (isMatrixDotOrg ? _t(
|
||||
"msc4335_matrix_org_user_limit_exceeded|hard_limit",
|
||||
) : _t("msc4335_user_limit_exceeded|hard_limit"))
|
||||
}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<div className="mx_Dialog_buttons_row">
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
element="a"
|
||||
href={error.infoUri}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
data-testid="learn-more"
|
||||
onClick={onClick}
|
||||
>
|
||||
{error.canUpgrade
|
||||
? (isMatrixDotOrg ? _t(
|
||||
"msc4335_matrix_org_user_limit_exceeded|soft_limit_button",
|
||||
) : _t("msc4335_user_limit_exceeded|soft_limit_button"))
|
||||
: (isMatrixDotOrg ? _t(
|
||||
"msc4335_matrix_org_user_limit_exceeded|hard_limit_button",
|
||||
) : _t("msc4335_user_limit_exceeded|hard_limit_button"))
|
||||
}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
@ -1708,6 +1708,20 @@
|
||||
"toast_description": "%(brand)s is experimental on a mobile web browser. For a better experience and the latest features, use our free native app.",
|
||||
"toast_title": "Use app for a better experience"
|
||||
},
|
||||
"msc4335_matrix_org_user_limit_exceeded": {
|
||||
"hard_limit": "You have reached the usage limits of your plan on the matrix.org homeserver.",
|
||||
"hard_limit_button": "Learn more",
|
||||
"soft_limit": "You have reached the usage limits of your Free plan on the matrix.org homeserver. You can upgrade to Premium to increase the limits and to support the work of the Matrix.org Foundation.",
|
||||
"soft_limit_button": "Get Premium",
|
||||
"title": "Usage limit exceeded"
|
||||
},
|
||||
"msc4335_user_limit_exceeded": {
|
||||
"hard_limit": "You have exceeded a usage limit imposed by your homeserver.",
|
||||
"hard_limit_button": "Learn more",
|
||||
"soft_limit": "You have exceeded a usage limit imposed by your homeserver. You may be able to increase your limit.",
|
||||
"soft_limit_button": "Learn more",
|
||||
"title": "Account limit exceeded"
|
||||
},
|
||||
"name_and_id": "%(name)s (%(userId)s)",
|
||||
"no_more_results": "No more results",
|
||||
"notif_panel": {
|
||||
|
||||
@ -24,6 +24,7 @@ import { BlurhashEncoder } from "../../src/BlurhashEncoder";
|
||||
import Modal from "../../src/Modal";
|
||||
import ErrorDialog from "../../src/components/views/dialogs/ErrorDialog";
|
||||
import { _t } from "../../src/languageHandler";
|
||||
import MSC4335UserLimitExceededDialog from "../../src/components/views/dialogs/MSC4335UserLimitExceededDialog";
|
||||
|
||||
jest.mock("matrix-encrypt-attachment", () => ({ encryptAttachment: jest.fn().mockResolvedValue({}) }));
|
||||
|
||||
@ -317,6 +318,55 @@ describe("ContentMessages", () => {
|
||||
);
|
||||
dialogSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("handles MSC4335 M_USER_LIMIT_EXCEEDED error with hard limit", async () => {
|
||||
mocked(client.uploadContent).mockRejectedValue(
|
||||
new MatrixError({
|
||||
"errcode": "ORG.MATRIX.MSC4335_USER_LIMIT_EXCEEDED",
|
||||
"error": "User limit exceeded",
|
||||
"org.matrix.msc4335.info_uri": "https://example.com/info",
|
||||
}),
|
||||
);
|
||||
const file = new File([], "fileName", { type: "image/jpeg" });
|
||||
const dialogSpy = jest.spyOn(Modal, "createDialog");
|
||||
await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined);
|
||||
expect(dialogSpy).toHaveBeenCalledWith(
|
||||
MSC4335UserLimitExceededDialog,
|
||||
expect.objectContaining({
|
||||
title: _t("upload_failed_title"),
|
||||
error: {
|
||||
infoUri: "https://example.com/info",
|
||||
canUpgrade: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
dialogSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("handles MSC4335 M_USER_LIMIT_EXCEEDED error with soft limit", async () => {
|
||||
mocked(client.uploadContent).mockRejectedValue(
|
||||
new MatrixError({
|
||||
"errcode": "ORG.MATRIX.MSC4335_USER_LIMIT_EXCEEDED",
|
||||
"error": "User limit exceeded",
|
||||
"org.matrix.msc4335.info_uri": "https://example.com/info",
|
||||
"org.matrix.msc4335.can_upgrade": true,
|
||||
}),
|
||||
);
|
||||
const file = new File([], "fileName", { type: "image/jpeg" });
|
||||
const dialogSpy = jest.spyOn(Modal, "createDialog");
|
||||
await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined);
|
||||
expect(dialogSpy).toHaveBeenCalledWith(
|
||||
MSC4335UserLimitExceededDialog,
|
||||
expect.objectContaining({
|
||||
title: _t("upload_failed_title"),
|
||||
error: {
|
||||
infoUri: "https://example.com/info",
|
||||
canUpgrade: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
dialogSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getCurrentUploads", () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user