Add stable support for MSC4380 invite blocking (#31966)

* Add stable support for MSC4380 invite blocking

MSC4380 has completed FCP, so we can add stable support for it.

* Set account data to empty value when allowing invites

... because that's what the spec says

* Use identifiers from js-sdk
This commit is contained in:
Richard van der Hoff
2026-02-26 11:41:04 +00:00
committed by GitHub
parent 7ddd645f4d
commit 18c1b37206
4 changed files with 19 additions and 21 deletions
-1
View File
@@ -6,7 +6,6 @@ Please see LICENSE files in the repository root for full details.
*/ */
export const INVITE_RULES_ACCOUNT_DATA_TYPE = "org.matrix.msc4155.invite_permission_config"; export const INVITE_RULES_ACCOUNT_DATA_TYPE = "org.matrix.msc4155.invite_permission_config";
export const MSC4380_INVITE_RULES_ACCOUNT_DATA_TYPE = "org.matrix.msc4380.invite_permission_config";
export interface InviteConfigAccountData { export interface InviteConfigAccountData {
allowed_users?: string[]; allowed_users?: string[];
+1 -7
View File
@@ -15,11 +15,7 @@ import type { EmptyObject } from "matrix-js-sdk/src/matrix";
import type { DeviceClientInformation } from "../utils/device/types.ts"; import type { DeviceClientInformation } from "../utils/device/types.ts";
import type { UserWidget } from "../utils/WidgetUtils-types.ts"; import type { UserWidget } from "../utils/WidgetUtils-types.ts";
import { type MediaPreviewConfig } from "./media_preview.ts"; import { type MediaPreviewConfig } from "./media_preview.ts";
import { import { type INVITE_RULES_ACCOUNT_DATA_TYPE, type InviteConfigAccountData } from "./invite-rules.ts";
type INVITE_RULES_ACCOUNT_DATA_TYPE,
type InviteConfigAccountData,
type MSC4380_INVITE_RULES_ACCOUNT_DATA_TYPE,
} from "./invite-rules.ts";
// Extend Matrix JS SDK types via Typescript declaration merging to support unspecced event fields and types // Extend Matrix JS SDK types via Typescript declaration merging to support unspecced event fields and types
declare module "matrix-js-sdk/src/types" { declare module "matrix-js-sdk/src/types" {
@@ -96,8 +92,6 @@ declare module "matrix-js-sdk/src/types" {
// MSC4155: Invite filtering // MSC4155: Invite filtering
[INVITE_RULES_ACCOUNT_DATA_TYPE]: InviteConfigAccountData; [INVITE_RULES_ACCOUNT_DATA_TYPE]: InviteConfigAccountData;
[MSC4380_INVITE_RULES_ACCOUNT_DATA_TYPE]: { default_action?: "allow" | "block" };
"io.element.msc4278.media_preview_config": MediaPreviewConfig; "io.element.msc4278.media_preview_config": MediaPreviewConfig;
// Indicate whether recovery is enabled or disabled // Indicate whether recovery is enabled or disabled
@@ -5,8 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
import { EventType } from "matrix-js-sdk/src/matrix";
import { type SettingLevel } from "../SettingLevel.ts"; import { type SettingLevel } from "../SettingLevel.ts";
import { MSC4380_INVITE_RULES_ACCOUNT_DATA_TYPE } from "../../@types/invite-rules.ts";
import { _td } from "../../languageHandler.tsx"; import { _td } from "../../languageHandler.tsx";
import ServerSupportUnstableFeatureController from "./ServerSupportUnstableFeatureController.ts"; import ServerSupportUnstableFeatureController from "./ServerSupportUnstableFeatureController.ts";
import { defaultWatchManager, type SettingKey } from "../Settings.tsx"; import { defaultWatchManager, type SettingKey } from "../Settings.tsx";
@@ -17,11 +18,17 @@ import { defaultWatchManager, type SettingKey } from "../Settings.tsx";
*/ */
export default class BlockInvitesConfigController extends ServerSupportUnstableFeatureController { export default class BlockInvitesConfigController extends ServerSupportUnstableFeatureController {
public constructor(settingName: SettingKey) { public constructor(settingName: SettingKey) {
super(settingName, defaultWatchManager, [["org.matrix.msc4380"]], undefined, _td("settings|not_supported")); super(
settingName,
defaultWatchManager,
[["org.matrix.msc4380.stable"]],
"v1.18",
_td("settings|not_supported"),
);
} }
public getValueOverride(_level: SettingLevel): boolean { public getValueOverride(_level: SettingLevel): boolean {
const accountData = this.client?.getAccountData(MSC4380_INVITE_RULES_ACCOUNT_DATA_TYPE)?.getContent(); const accountData = this.client?.getAccountData(EventType.InvitePermissionConfig)?.getContent();
return accountData?.default_action == "block"; return accountData?.default_action == "block";
} }
@@ -29,8 +36,8 @@ export default class BlockInvitesConfigController extends ServerSupportUnstableF
if (!this.client) { if (!this.client) {
return false; return false;
} }
const newDefault = newValue ? "block" : "allow";
await this.client.setAccountData(MSC4380_INVITE_RULES_ACCOUNT_DATA_TYPE, { default_action: newDefault }); await this.client.setAccountData(EventType.InvitePermissionConfig, newValue ? { default_action: "block" } : {});
return true; return true;
} }
} }
@@ -34,7 +34,7 @@ describe("BlockInvitesConfigController", () => {
beforeEach(async () => { beforeEach(async () => {
cli = stubClient(); cli = stubClient();
cli.doesServerSupportUnstableFeature = jest.fn(async (feature) => { cli.doesServerSupportUnstableFeature = jest.fn(async (feature) => {
return feature == "org.matrix.msc4380"; return feature == "org.matrix.msc4380.stable";
}); });
MatrixClientBackedController.matrixClient = cli; MatrixClientBackedController.matrixClient = cli;
}); });
@@ -65,7 +65,7 @@ describe("BlockInvitesConfigController", () => {
const controller = SETTINGS.blockInvites.controller!; const controller = SETTINGS.blockInvites.controller!;
await controller.beforeChange(SettingLevel.DEVICE, null, true); await controller.beforeChange(SettingLevel.DEVICE, null, true);
expect(cli.setAccountData).toHaveBeenCalledTimes(1); expect(cli.setAccountData).toHaveBeenCalledTimes(1);
expect(cli.setAccountData).toHaveBeenCalledWith("org.matrix.msc4380.invite_permission_config", { expect(cli.setAccountData).toHaveBeenCalledWith("m.invite_permission_config", {
default_action: "block", default_action: "block",
}); });
}); });
@@ -74,9 +74,7 @@ describe("BlockInvitesConfigController", () => {
const controller = SETTINGS.blockInvites.controller!; const controller = SETTINGS.blockInvites.controller!;
await controller.beforeChange(SettingLevel.DEVICE, null, false); await controller.beforeChange(SettingLevel.DEVICE, null, false);
expect(cli.setAccountData).toHaveBeenCalledTimes(1); expect(cli.setAccountData).toHaveBeenCalledTimes(1);
expect(cli.setAccountData).toHaveBeenCalledWith("org.matrix.msc4380.invite_permission_config", { expect(cli.setAccountData).toHaveBeenCalledWith("m.invite_permission_config", {});
default_action: "allow",
});
}); });
}); });
}); });
@@ -84,13 +82,13 @@ describe("BlockInvitesConfigController", () => {
/** /**
* Add a mock implementation for {@link MatrixClient.getAccountData} which will return the given data * Add a mock implementation for {@link MatrixClient.getAccountData} which will return the given data
* in respomsnse to any request for `org.matrix.msc4380.invite_permission_config`. * in response to any request for `m.invite_permission_config`.
*/ */
function mockAccountData(cli: MatrixClient, mockAccountData: object) { function mockAccountData(cli: MatrixClient, mockAccountData: object) {
mocked(cli.getAccountData).mockImplementation((eventType) => { mocked(cli.getAccountData).mockImplementation((eventType) => {
if (eventType == "org.matrix.msc4380.invite_permission_config") { if (eventType == "m.invite_permission_config") {
return new MatrixEvent({ return new MatrixEvent({
type: "org.matrix.msc4380.invite_permission_config", type: "m.invite_permission_config",
content: mockAccountData, content: mockAccountData,
}); });
} else { } else {