Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Backport Disable some slash commands in LocalRoom #9193

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions src/SlashCommands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
import VoipUserMapper from './VoipUserMapper';
import { htmlSerializeFromMdIfNeeded } from './editor/serialize';
import { leaveRoomBehaviour } from "./utils/leave-behaviour";
import { isLocalRoom } from './utils/localRoom/isLocalRoom';

// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
interface HTMLInputEvent extends Event {
Expand Down Expand Up @@ -206,6 +207,12 @@ function successSync(value: any) {
return success(Promise.resolve(value));
}

const isCurrentLocalRoom = (): boolean => {
const cli = MatrixClientPeg.get();
const room = cli.getRoom(RoomViewStore.instance.getRoomId());
return isLocalRoom(room);
};

/* Disable the "unexpected this" error for these commands - all of the run
* functions are called with `this` bound to the Command instance.
*/
Expand Down Expand Up @@ -297,6 +304,7 @@ export const Commands = [
command: 'upgraderoom',
args: '<new_version>',
description: _td('Upgrades a room to a new version'),
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
if (args) {
const cli = MatrixClientPeg.get();
Expand Down Expand Up @@ -380,6 +388,7 @@ export const Commands = [
aliases: ['roomnick'],
args: '<display_name>',
description: _td('Changes your display nickname in the current room only'),
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
if (args) {
const cli = MatrixClientPeg.get();
Expand All @@ -399,6 +408,7 @@ export const Commands = [
command: 'roomavatar',
args: '[<mxc_url>]',
description: _td('Changes the avatar of the current room'),
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
let promise = Promise.resolve(args);
if (!args) {
Expand All @@ -417,6 +427,7 @@ export const Commands = [
command: 'myroomavatar',
args: '[<mxc_url>]',
description: _td('Changes your avatar in this current room only'),
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
const cli = MatrixClientPeg.get();
const room = cli.getRoom(roomId);
Expand Down Expand Up @@ -462,6 +473,7 @@ export const Commands = [
command: 'topic',
args: '[<topic>]',
description: _td('Gets or sets the room topic'),
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
const cli = MatrixClientPeg.get();
if (args) {
Expand Down Expand Up @@ -498,6 +510,7 @@ export const Commands = [
command: 'roomname',
args: '<name>',
description: _td('Sets the room name'),
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
if (args) {
return success(MatrixClientPeg.get().setRoomName(roomId, args));
Expand All @@ -512,7 +525,7 @@ export const Commands = [
args: '<user-id> [<reason>]',
description: _td('Invites user with given id to current room'),
analyticsName: "Invite",
isEnabled: () => shouldShowComponent(UIComponent.InviteUsers),
isEnabled: () => !isCurrentLocalRoom() && shouldShowComponent(UIComponent.InviteUsers),
runFn: function(roomId, args) {
if (args) {
const [address, reason] = args.split(/\s+(.+)/);
Expand Down Expand Up @@ -694,6 +707,7 @@ export const Commands = [
args: '[<room-address>]',
description: _td('Leave room'),
analyticsName: "Part",
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
const cli = MatrixClientPeg.get();

Expand Down Expand Up @@ -746,6 +760,7 @@ export const Commands = [
aliases: ["kick"],
args: '<user-id> [reason]',
description: _td('Removes user with given id from this room'),
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
if (args) {
const matches = args.match(/^(\S+?)( +(.*))?$/);
Expand All @@ -762,6 +777,7 @@ export const Commands = [
command: 'ban',
args: '<user-id> [reason]',
description: _td('Bans user with given id'),
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
if (args) {
const matches = args.match(/^(\S+?)( +(.*))?$/);
Expand All @@ -778,6 +794,7 @@ export const Commands = [
command: 'unban',
args: '<user-id>',
description: _td('Unbans user with given ID'),
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
if (args) {
const matches = args.match(/^(\S+)$/);
Expand Down Expand Up @@ -857,7 +874,8 @@ export const Commands = [
isEnabled(): boolean {
const cli = MatrixClientPeg.get();
const room = cli.getRoom(RoomViewStore.instance.getRoomId());
return room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId());
return room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId())
&& !isLocalRoom(room);
},
runFn: function(roomId, args) {
if (args) {
Expand Down Expand Up @@ -897,7 +915,8 @@ export const Commands = [
isEnabled(): boolean {
const cli = MatrixClientPeg.get();
const room = cli.getRoom(RoomViewStore.instance.getRoomId());
return room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId());
return room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId())
&& !isLocalRoom(room);
},
runFn: function(roomId, args) {
if (args) {
Expand Down Expand Up @@ -936,7 +955,9 @@ export const Commands = [
command: 'addwidget',
args: '<url | embed code | Jitsi url>',
description: _td('Adds a custom widget by URL to the room'),
isEnabled: () => SettingsStore.getValue(UIFeature.Widgets) && shouldShowComponent(UIComponent.AddIntegrations),
isEnabled: () => SettingsStore.getValue(UIFeature.Widgets)
&& shouldShowComponent(UIComponent.AddIntegrations)
&& !isCurrentLocalRoom(),
runFn: function(roomId, widgetUrl) {
if (!widgetUrl) {
return reject(newTranslatableError("Please supply a widget URL or embed code"));
Expand Down Expand Up @@ -1059,6 +1080,7 @@ export const Commands = [
new Command({
command: 'discardsession',
description: _td('Forces the current outbound group session in an encrypted room to be discarded'),
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId) {
try {
MatrixClientPeg.get().forceDiscardSession(roomId);
Expand All @@ -1074,7 +1096,7 @@ export const Commands = [
command: 'remakeolm',
description: _td('Developer command: Discards the current outbound group session and sets up new Olm sessions'),
isEnabled: () => {
return SettingsStore.getValue("developerMode");
return SettingsStore.getValue("developerMode") && !isCurrentLocalRoom();
},
runFn: (roomId) => {
try {
Expand Down Expand Up @@ -1125,6 +1147,7 @@ export const Commands = [
command: "whois",
description: _td("Displays information about a user"),
args: "<user-id>",
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, userId) {
if (!userId || !userId.startsWith("@") || !userId.includes(":")) {
return reject(this.getUsage());
Expand Down Expand Up @@ -1160,7 +1183,7 @@ export const Commands = [
description: _td("Switches to this room's virtual room, if it has one"),
category: CommandCategories.advanced,
isEnabled(): boolean {
return CallHandler.instance.getSupportsVirtualRooms();
return CallHandler.instance.getSupportsVirtualRooms() && !isCurrentLocalRoom();
},
runFn: (roomId) => {
return success((async () => {
Expand Down Expand Up @@ -1244,6 +1267,7 @@ export const Commands = [
command: "holdcall",
description: _td("Places the call in the current room on hold"),
category: CommandCategories.other,
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
const call = CallHandler.instance.getCallForRoom(roomId);
if (!call) {
Expand All @@ -1258,6 +1282,7 @@ export const Commands = [
command: "unholdcall",
description: _td("Takes the call in the current room off hold"),
category: CommandCategories.other,
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
const call = CallHandler.instance.getCallForRoom(roomId);
if (!call) {
Expand All @@ -1272,6 +1297,7 @@ export const Commands = [
command: "converttodm",
description: _td("Converts the room to a DM"),
category: CommandCategories.other,
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
const room = MatrixClientPeg.get().getRoom(roomId);
return success(guessAndSetDMRoom(room, true));
Expand All @@ -1282,6 +1308,7 @@ export const Commands = [
command: "converttoroom",
description: _td("Converts the DM to a room"),
category: CommandCategories.other,
isEnabled: () => !isCurrentLocalRoom(),
runFn: function(roomId, args) {
const room = MatrixClientPeg.get().getRoom(roomId);
return success(guessAndSetDMRoom(room, false));
Expand Down
162 changes: 160 additions & 2 deletions test/SlashCommands-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,53 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { MatrixClient } from 'matrix-js-sdk/src/matrix';
import { MatrixClient, Room } from 'matrix-js-sdk/src/matrix';
import { mocked } from 'jest-mock';

import { getCommand } from '../src/SlashCommands';
import { Command, Commands, getCommand } from '../src/SlashCommands';
import { createTestClient } from './test-utils';
import { MatrixClientPeg } from '../src/MatrixClientPeg';
import { LocalRoom, LOCAL_ROOM_ID_PREFIX } from '../src/models/LocalRoom';
import { RoomViewStore } from '../src/stores/RoomViewStore';
import SettingsStore from '../src/settings/SettingsStore';
import CallHandler from '../src/CallHandler';

describe('SlashCommands', () => {
let client: MatrixClient;
const roomId = "!room:example.com";
let room: Room;
const localRoomId = LOCAL_ROOM_ID_PREFIX + "test";
let localRoom: LocalRoom;
let command: Command;

const findCommand = (cmd: string): Command => {
return Commands.find((command: Command) => command.command === cmd);
};

const setCurrentRoom = (): void => {
mocked(RoomViewStore.instance.getRoomId).mockReturnValue(roomId);
mocked(client.getRoom).mockImplementation((rId: string): Room => {
if (rId === roomId) return room;
});
};

const setCurrentLocalRoon = (): void => {
mocked(RoomViewStore.instance.getRoomId).mockReturnValue(localRoomId);
mocked(client.getRoom).mockImplementation((rId: string): Room => {
if (rId === localRoomId) return localRoom;
});
};

beforeEach(() => {
jest.clearAllMocks();

client = createTestClient();
jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(client);

room = new Room(roomId, client, client.getUserId());
localRoom = new LocalRoom(localRoomId, client, client.getUserId());

jest.spyOn(RoomViewStore.instance, "getRoomId");
});

describe('/topic', () => {
Expand All @@ -37,4 +72,127 @@ describe('SlashCommands', () => {
expect(client.setRoomTopic).toHaveBeenCalledWith("room-id", "pizza", undefined);
});
});

describe.each([
["upgraderoom"],
["myroomnick"],
["roomavatar"],
["myroomavatar"],
["topic"],
["roomname"],
["invite"],
["part"],
["remove"],
["ban"],
["unban"],
["op"],
["deop"],
["addwidget"],
["discardsession"],
["whois"],
["holdcall"],
["unholdcall"],
["converttodm"],
["converttoroom"],
])("/%s", (commandName: string) => {
beforeEach(() => {
command = findCommand(commandName);
});

describe("isEnabled", () => {
it("should return true for Room", () => {
setCurrentRoom();
expect(command.isEnabled()).toBe(true);
});

it("should return false for LocalRoom", () => {
setCurrentLocalRoon();
expect(command.isEnabled()).toBe(false);
});
});
});

describe("/tovirtual", () => {
beforeEach(() => {
command = findCommand("tovirtual");
});

describe("isEnabled", () => {
describe("when virtual rooms are supported", () => {
beforeEach(() => {
jest.spyOn(CallHandler.instance, "getSupportsVirtualRooms").mockReturnValue(true);
});

it("should return true for Room", () => {
setCurrentRoom();
expect(command.isEnabled()).toBe(true);
});

it("should return false for LocalRoom", () => {
setCurrentLocalRoon();
expect(command.isEnabled()).toBe(false);
});
});

describe("when virtual rooms are not supported", () => {
beforeEach(() => {
jest.spyOn(CallHandler.instance, "getSupportsVirtualRooms").mockReturnValue(false);
});

it("should return false for Room", () => {
setCurrentRoom();
expect(command.isEnabled()).toBe(false);
});

it("should return false for LocalRoom", () => {
setCurrentLocalRoon();
expect(command.isEnabled()).toBe(false);
});
});
});
});

describe("/remakeolm", () => {
beforeEach(() => {
command = findCommand("remakeolm");
});

describe("isEnabled", () => {
describe("when developer mode is enabled", () => {
beforeEach(() => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => {
if (settingName === "developerMode") return true;
});
});

it("should return true for Room", () => {
setCurrentRoom();
expect(command.isEnabled()).toBe(true);
});

it("should return false for LocalRoom", () => {
setCurrentLocalRoon();
expect(command.isEnabled()).toBe(false);
});
});

describe("when developer mode is not enabled", () => {
beforeEach(() => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => {
if (settingName === "developerMode") return false;
});
});

it("should return false for Room", () => {
setCurrentRoom();
expect(command.isEnabled()).toBe(false);
});

it("should return false for LocalRoom", () => {
setCurrentLocalRoon();
expect(command.isEnabled()).toBe(false);
});
});
});
});
});