Support additional_creators in /upgraderoom (MSC4289) (#31934)
Signed-off-by: Andy Balaam <andy.balaam@matrix.org>
This commit is contained in:
+19
-4
@@ -61,6 +61,7 @@ import { CommandCategories } from "./slash-commands/interface";
|
|||||||
import { Command } from "./slash-commands/command";
|
import { Command } from "./slash-commands/command";
|
||||||
import { goto, join } from "./slash-commands/join";
|
import { goto, join } from "./slash-commands/join";
|
||||||
import { manuallyVerifyDevice } from "./components/views/dialogs/ManualDeviceKeyVerificationDialog";
|
import { manuallyVerifyDevice } from "./components/views/dialogs/ManualDeviceKeyVerificationDialog";
|
||||||
|
import { parseUpgradeRoomArgs } from "./slash-commands/upgraderoom/parseUpgradeRoomArgs";
|
||||||
|
|
||||||
export { CommandCategories, Command };
|
export { CommandCategories, Command };
|
||||||
|
|
||||||
@@ -146,11 +147,15 @@ export const Commands = [
|
|||||||
}),
|
}),
|
||||||
new Command({
|
new Command({
|
||||||
command: "upgraderoom",
|
command: "upgraderoom",
|
||||||
args: "<new_version>",
|
args: "<new_version> [<additional-creator-user-id> ...]",
|
||||||
description: _td("slash_command|upgraderoom"),
|
description: _td("slash_command|upgraderoom"),
|
||||||
isEnabled: (cli) => !isCurrentLocalRoom(cli),
|
isEnabled: (cli) => !isCurrentLocalRoom(cli),
|
||||||
runFn: function (cli, roomId, threadId, args) {
|
runFn: function (cli, roomId, threadId, args) {
|
||||||
if (args) {
|
if (!args) {
|
||||||
|
return reject(this.getUsage());
|
||||||
|
}
|
||||||
|
const parsedArgs = parseUpgradeRoomArgs(args);
|
||||||
|
if (parsedArgs) {
|
||||||
const room = cli.getRoom(roomId);
|
const room = cli.getRoom(roomId);
|
||||||
if (!room?.currentState.mayClientSendStateEvent("m.room.tombstone", cli)) {
|
if (!room?.currentState.mayClientSendStateEvent("m.room.tombstone", cli)) {
|
||||||
return reject(new UserFriendlyError("slash_command|upgraderoom_permission_error"));
|
return reject(new UserFriendlyError("slash_command|upgraderoom_permission_error"));
|
||||||
@@ -158,7 +163,7 @@ export const Commands = [
|
|||||||
|
|
||||||
const { finished } = Modal.createDialog(
|
const { finished } = Modal.createDialog(
|
||||||
RoomUpgradeWarningDialog,
|
RoomUpgradeWarningDialog,
|
||||||
{ roomId: roomId, targetVersion: args },
|
{ roomId: roomId, targetVersion: parsedArgs.targetVersion },
|
||||||
/*className=*/ undefined,
|
/*className=*/ undefined,
|
||||||
/*isPriority=*/ false,
|
/*isPriority=*/ false,
|
||||||
/*isStatic=*/ true,
|
/*isStatic=*/ true,
|
||||||
@@ -167,7 +172,17 @@ export const Commands = [
|
|||||||
return success(
|
return success(
|
||||||
finished.then(async ([resp]): Promise<void> => {
|
finished.then(async ([resp]): Promise<void> => {
|
||||||
if (!resp?.continue) return;
|
if (!resp?.continue) return;
|
||||||
await upgradeRoom(room, args, resp.invite);
|
await upgradeRoom(
|
||||||
|
room,
|
||||||
|
parsedArgs.targetVersion,
|
||||||
|
resp.invite,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
parsedArgs.additionalCreators,
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type UpgradeRoomParsedArgs = {
|
||||||
|
targetVersion: string;
|
||||||
|
additionalCreators?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the supplied arguments for a room upgrade, or return null if the
|
||||||
|
* arguments are not valid. The arguments must be a room version followed by
|
||||||
|
* zero or more valid user IDs.
|
||||||
|
*/
|
||||||
|
export function parseUpgradeRoomArgs(args: string): UpgradeRoomParsedArgs | null {
|
||||||
|
const parts = args.split(/\s+/);
|
||||||
|
if (parts.length === 0 || parts[0] === "") {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
const targetVersion = parts[0];
|
||||||
|
let additionalCreators: string[] | undefined;
|
||||||
|
for (let i = 1; i < parts.length; ++i) {
|
||||||
|
if (additionalCreators === undefined) {
|
||||||
|
additionalCreators = [];
|
||||||
|
}
|
||||||
|
additionalCreators.push(parts[i]);
|
||||||
|
}
|
||||||
|
return { targetVersion, additionalCreators };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,6 +53,7 @@ export async function upgradeRoom(
|
|||||||
awaitRoom = false,
|
awaitRoom = false,
|
||||||
progressCallback?: (progress: RoomUpgradeProgress) => void,
|
progressCallback?: (progress: RoomUpgradeProgress) => void,
|
||||||
inhibitInviteProgressDialog = false,
|
inhibitInviteProgressDialog = false,
|
||||||
|
additionalCreators?: string[],
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const cli = room.client;
|
const cli = room.client;
|
||||||
let spinnerModal: IHandle<any> | undefined;
|
let spinnerModal: IHandle<any> | undefined;
|
||||||
@@ -91,7 +92,7 @@ export async function upgradeRoom(
|
|||||||
|
|
||||||
let newRoomId: string;
|
let newRoomId: string;
|
||||||
try {
|
try {
|
||||||
({ replacement_room: newRoomId } = await cli.upgradeRoom(room.roomId, targetVersion));
|
({ replacement_room: newRoomId } = await cli.upgradeRoom(room.roomId, targetVersion, additionalCreators));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!handleError) throw e;
|
if (!handleError) throw e;
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ import { warnSelfDemote } from "../../src/components/views/right_panel/UserInfo"
|
|||||||
import dispatcher from "../../src/dispatcher/dispatcher";
|
import dispatcher from "../../src/dispatcher/dispatcher";
|
||||||
import QuestionDialog from "../../src/components/views/dialogs/QuestionDialog";
|
import QuestionDialog from "../../src/components/views/dialogs/QuestionDialog";
|
||||||
import ErrorDialog from "../../src/components/views/dialogs/ErrorDialog";
|
import ErrorDialog from "../../src/components/views/dialogs/ErrorDialog";
|
||||||
|
import RoomUpgradeWarningDialog, {
|
||||||
|
type IFinishedOpts,
|
||||||
|
} from "../../src/components/views/dialogs/RoomUpgradeWarningDialog";
|
||||||
|
import { parseUpgradeRoomArgs } from "../../src/slash-commands/upgraderoom/parseUpgradeRoomArgs";
|
||||||
|
|
||||||
jest.mock("../../src/components/views/right_panel/UserInfo");
|
jest.mock("../../src/components/views/right_panel/UserInfo");
|
||||||
|
|
||||||
@@ -128,6 +132,56 @@ describe("SlashCommands", () => {
|
|||||||
it("should be enabled by default", () => {
|
it("should be enabled by default", () => {
|
||||||
expect(command.isEnabled(client, roomId)).toBe(true);
|
expect(command.isEnabled(client, roomId)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should return usage if given no args", () => {
|
||||||
|
expect(command.run(client, roomId, null, undefined).error).toBe(command.getUsage());
|
||||||
|
expect(command.run(client, roomId, null, "").error).toBe(command.getUsage());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should accept arguments of a room version with no additional creators", () => {
|
||||||
|
expect(parseUpgradeRoomArgs("12")).toEqual({ targetVersion: "12" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should accept arguments of a room version and additional creators", () => {
|
||||||
|
expect(parseUpgradeRoomArgs("13 @u:s.co")).toEqual({
|
||||||
|
targetVersion: "13",
|
||||||
|
additionalCreators: ["@u:s.co"],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(parseUpgradeRoomArgs("14 @u:s.co @v:s.co @w:z.uk")).toEqual({
|
||||||
|
targetVersion: "14",
|
||||||
|
additionalCreators: ["@u:s.co", "@v:s.co", "@w:z.uk"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should upgrade the room when given valid arguments", async () => {
|
||||||
|
// Given we mock out creating dialogs and upgrading rooms
|
||||||
|
const createDialog = jest.spyOn(Modal, "createDialog");
|
||||||
|
const upgradeRoom = jest.fn().mockResolvedValue({ replacement_room: "!newroom" });
|
||||||
|
const resp: IFinishedOpts = { continue: true, invite: false };
|
||||||
|
createDialog.mockReturnValue({
|
||||||
|
finished: Promise.resolve([resp]),
|
||||||
|
close: jest.fn(),
|
||||||
|
});
|
||||||
|
client.upgradeRoom = upgradeRoom;
|
||||||
|
|
||||||
|
// When we run a room upgrade
|
||||||
|
const result = command.run(client, roomId, null, "12 @foo:bar.com @baz:qux.uk");
|
||||||
|
expect(result.promise).toBeDefined();
|
||||||
|
await result.promise;
|
||||||
|
|
||||||
|
// Then we warned the user
|
||||||
|
expect(createDialog).toHaveBeenCalledWith(
|
||||||
|
RoomUpgradeWarningDialog,
|
||||||
|
{ roomId: "!room:example.com", targetVersion: "12" },
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// And when they said yes, we called into upgradeRoom
|
||||||
|
expect(upgradeRoom).toHaveBeenCalledWith("!room:example.com", "12", ["@foo:bar.com", "@baz:qux.uk"]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("/op", () => {
|
describe("/op", () => {
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ describe("<JoinRuleSettings />", () => {
|
|||||||
|
|
||||||
fireEvent.click(within(dialog).getByText("Upgrade"));
|
fireEvent.click(within(dialog).getByText("Upgrade"));
|
||||||
|
|
||||||
expect(client.upgradeRoom).toHaveBeenCalledWith(roomId, preferredRoomVersion);
|
expect(client.upgradeRoom).toHaveBeenCalledWith(roomId, preferredRoomVersion, undefined);
|
||||||
|
|
||||||
expect(within(dialog).getByText("Upgrading room")).toBeInTheDocument();
|
expect(within(dialog).getByText("Upgrading room")).toBeInTheDocument();
|
||||||
|
|
||||||
@@ -245,7 +245,7 @@ describe("<JoinRuleSettings />", () => {
|
|||||||
|
|
||||||
fireEvent.click(within(dialog).getByText("Upgrade"));
|
fireEvent.click(within(dialog).getByText("Upgrade"));
|
||||||
|
|
||||||
expect(client.upgradeRoom).toHaveBeenCalledWith(roomId, preferredRoomVersion);
|
expect(client.upgradeRoom).toHaveBeenCalledWith(roomId, preferredRoomVersion, undefined);
|
||||||
|
|
||||||
expect(within(dialog).getByText("Upgrading room")).toBeInTheDocument();
|
expect(within(dialog).getByText("Upgrading room")).toBeInTheDocument();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user