Merge pull request #4302 from matrix-org/t3chguy/cmds

rework SlashCommands to better expose aliases
This commit is contained in:
Michael Telatynski 2020-03-31 17:04:31 +01:00 committed by GitHub
commit 56dda3895c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 161 additions and 162 deletions

View file

@ -2,6 +2,7 @@
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd Copyright 2018 New Vector Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,7 +18,8 @@ limitations under the License.
*/ */
import React from 'react'; import * as React from 'react';
import {MatrixClientPeg} from './MatrixClientPeg'; import {MatrixClientPeg} from './MatrixClientPeg';
import dis from './dispatcher'; import dis from './dispatcher';
import * as sdk from './index'; import * as sdk from './index';
@ -34,11 +36,16 @@ import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/I
import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks"; import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks";
import {inviteUsersToRoom} from "./RoomInvite"; import {inviteUsersToRoom} from "./RoomInvite";
const singleMxcUpload = async () => { // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
interface HTMLInputEvent extends Event {
target: HTMLInputElement & EventTarget;
}
const singleMxcUpload = async (): Promise<any> => {
return new Promise((resolve) => { return new Promise((resolve) => {
const fileSelector = document.createElement('input'); const fileSelector = document.createElement('input');
fileSelector.setAttribute('type', 'file'); fileSelector.setAttribute('type', 'file');
fileSelector.onchange = (ev) => { fileSelector.onchange = (ev: HTMLInputEvent) => {
const file = ev.target.files[0]; const file = ev.target.files[0];
const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog"); const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog");
@ -62,28 +69,49 @@ export const CommandCategories = {
"other": _td("Other"), "other": _td("Other"),
}; };
type RunFn = ((roomId: string, args: string, cmd: string) => {error: any} | {promise: Promise<any>});
interface ICommandOpts {
command: string;
aliases?: string[];
args?: string;
description: string;
runFn?: RunFn;
category: string;
hideCompletionAfterSpace?: boolean;
}
class Command { class Command {
constructor({name, args='', description, runFn, category=CommandCategories.other, hideCompletionAfterSpace=false}) { command: string;
this.command = '/' + name; aliases: string[];
this.args = args; args: undefined | string;
this.description = description; description: string;
this.runFn = runFn; runFn: undefined | RunFn;
this.category = category; category: string;
this.hideCompletionAfterSpace = hideCompletionAfterSpace; hideCompletionAfterSpace: boolean;
constructor(opts: ICommandOpts) {
this.command = opts.command;
this.aliases = opts.aliases || [];
this.args = opts.args || "";
this.description = opts.description;
this.runFn = opts.runFn;
this.category = opts.category || CommandCategories.other;
this.hideCompletionAfterSpace = opts.hideCompletionAfterSpace || false;
} }
getCommand() { getCommand() {
return this.command; return `/${this.command}`;
} }
getCommandWithArgs() { getCommandWithArgs() {
return this.getCommand() + " " + this.args; return this.getCommand() + " " + this.args;
} }
run(roomId, args) { run(roomId: string, args: string, cmd: string) {
// if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me` // if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me`
if (!this.runFn) return; if (!this.runFn) return;
return this.runFn.bind(this)(roomId, args); return this.runFn.bind(this)(roomId, args, cmd);
} }
getUsage() { getUsage() {
@ -95,7 +123,7 @@ function reject(error) {
return {error}; return {error};
} }
function success(promise) { function success(promise?: Promise<any>) {
return {promise}; return {promise};
} }
@ -103,11 +131,9 @@ function success(promise) {
* functions are called with `this` bound to the Command instance. * functions are called with `this` bound to the Command instance.
*/ */
/* eslint-disable babel/no-invalid-this */ export const Commands = [
new Command({
export const CommandMap = { command: 'shrug',
shrug: new Command({
name: 'shrug',
args: '<message>', args: '<message>',
description: _td('Prepends ¯\\_(ツ)_/¯ to a plain-text message'), description: _td('Prepends ¯\\_(ツ)_/¯ to a plain-text message'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -119,8 +145,8 @@ export const CommandMap = {
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
plain: new Command({ new Command({
name: 'plain', command: 'plain',
args: '<message>', args: '<message>',
description: _td('Sends a message as plain text, without interpreting it as markdown'), description: _td('Sends a message as plain text, without interpreting it as markdown'),
runFn: function(roomId, messages) { runFn: function(roomId, messages) {
@ -128,8 +154,8 @@ export const CommandMap = {
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
html: new Command({ new Command({
name: 'html', command: 'html',
args: '<message>', args: '<message>',
description: _td('Sends a message as html, without interpreting it as markdown'), description: _td('Sends a message as html, without interpreting it as markdown'),
runFn: function(roomId, messages) { runFn: function(roomId, messages) {
@ -137,11 +163,11 @@ export const CommandMap = {
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
ddg: new Command({ new Command({
name: 'ddg', command: 'ddg',
args: '<query>', args: '<query>',
description: _td('Searches DuckDuckGo for results'), description: _td('Searches DuckDuckGo for results'),
runFn: function(roomId, args) { runFn: function() {
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
// TODO Don't explain this away, actually show a search UI here. // TODO Don't explain this away, actually show a search UI here.
Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, { Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, {
@ -153,9 +179,8 @@ export const CommandMap = {
category: CommandCategories.actions, category: CommandCategories.actions,
hideCompletionAfterSpace: true, hideCompletionAfterSpace: true,
}), }),
new Command({
upgraderoom: new Command({ command: 'upgraderoom',
name: 'upgraderoom',
args: '<new_version>', args: '<new_version>',
description: _td('Upgrades a room to a new version'), description: _td('Upgrades a room to a new version'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -224,9 +249,8 @@ export const CommandMap = {
}, },
category: CommandCategories.admin, category: CommandCategories.admin,
}), }),
new Command({
nick: new Command({ command: 'nick',
name: 'nick',
args: '<display_name>', args: '<display_name>',
description: _td('Changes your display nickname'), description: _td('Changes your display nickname'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -237,9 +261,9 @@ export const CommandMap = {
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
}), }),
new Command({
myroomnick: new Command({ command: 'myroomnick',
name: 'myroomnick', aliases: ['roomnick'],
args: '<display_name>', args: '<display_name>',
description: _td('Changes your display nickname in the current room only'), description: _td('Changes your display nickname in the current room only'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -256,9 +280,8 @@ export const CommandMap = {
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
}), }),
new Command({
roomavatar: new Command({ command: 'roomavatar',
name: 'roomavatar',
args: '[<mxc_url>]', args: '[<mxc_url>]',
description: _td('Changes the avatar of the current room'), description: _td('Changes the avatar of the current room'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -274,9 +297,8 @@ export const CommandMap = {
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
}), }),
new Command({
myroomavatar: new Command({ command: 'myroomavatar',
name: 'myroomavatar',
args: '[<mxc_url>]', args: '[<mxc_url>]',
description: _td('Changes your avatar in this current room only'), description: _td('Changes your avatar in this current room only'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -301,9 +323,8 @@ export const CommandMap = {
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
}), }),
new Command({
myavatar: new Command({ command: 'myavatar',
name: 'myavatar',
args: '[<mxc_url>]', args: '[<mxc_url>]',
description: _td('Changes your avatar in all rooms'), description: _td('Changes your avatar in all rooms'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -319,9 +340,8 @@ export const CommandMap = {
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
}), }),
new Command({
topic: new Command({ command: 'topic',
name: 'topic',
args: '[<topic>]', args: '[<topic>]',
description: _td('Gets or sets the room topic'), description: _td('Gets or sets the room topic'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -345,9 +365,8 @@ export const CommandMap = {
}, },
category: CommandCategories.admin, category: CommandCategories.admin,
}), }),
new Command({
roomname: new Command({ command: 'roomname',
name: 'roomname',
args: '<name>', args: '<name>',
description: _td('Sets the room name'), description: _td('Sets the room name'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -358,9 +377,8 @@ export const CommandMap = {
}, },
category: CommandCategories.admin, category: CommandCategories.admin,
}), }),
new Command({
invite: new Command({ command: 'invite',
name: 'invite',
args: '<user-id>', args: '<user-id>',
description: _td('Invites user with given id to current room'), description: _td('Invites user with given id to current room'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -399,7 +417,7 @@ export const CommandMap = {
} }
} }
const inviter = new MultiInviter(roomId); const inviter = new MultiInviter(roomId);
return success(finished.then(([useDefault] = []) => { return success(finished.then(([useDefault]: any) => {
if (useDefault) { if (useDefault) {
useDefaultIdentityServer(); useDefaultIdentityServer();
} else if (useDefault === false) { } else if (useDefault === false) {
@ -417,12 +435,12 @@ export const CommandMap = {
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
}), }),
new Command({
join: new Command({ command: 'join',
name: 'join', aliases: ['j', 'goto'],
args: '<room-alias>', args: '<room-alias>',
description: _td('Joins room with given alias'), description: _td('Joins room with given alias'),
runFn: function(roomId, args) { runFn: function(_, args) {
if (args) { if (args) {
// Note: we support 2 versions of this command. The first is // Note: we support 2 versions of this command. The first is
// the public-facing one for most users and the other is a // the public-facing one for most users and the other is a
@ -530,9 +548,8 @@ export const CommandMap = {
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
}), }),
new Command({
part: new Command({ command: 'part',
name: 'part',
args: '[<room-alias>]', args: '[<room-alias>]',
description: _td('Leave room'), description: _td('Leave room'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -578,9 +595,8 @@ export const CommandMap = {
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
}), }),
new Command({
kick: new Command({ command: 'kick',
name: 'kick',
args: '<user-id> [reason]', args: '<user-id> [reason]',
description: _td('Kicks user with given id'), description: _td('Kicks user with given id'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -594,10 +610,8 @@ export const CommandMap = {
}, },
category: CommandCategories.admin, category: CommandCategories.admin,
}), }),
new Command({
// Ban a user from the room with an optional reason command: 'ban',
ban: new Command({
name: 'ban',
args: '<user-id> [reason]', args: '<user-id> [reason]',
description: _td('Bans user with given id'), description: _td('Bans user with given id'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -611,10 +625,8 @@ export const CommandMap = {
}, },
category: CommandCategories.admin, category: CommandCategories.admin,
}), }),
new Command({
// Unban a user from ythe room command: 'unban',
unban: new Command({
name: 'unban',
args: '<user-id>', args: '<user-id>',
description: _td('Unbans user with given ID'), description: _td('Unbans user with given ID'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -629,9 +641,8 @@ export const CommandMap = {
}, },
category: CommandCategories.admin, category: CommandCategories.admin,
}), }),
new Command({
ignore: new Command({ command: 'ignore',
name: 'ignore',
args: '<user-id>', args: '<user-id>',
description: _td('Ignores a user, hiding their messages from you'), description: _td('Ignores a user, hiding their messages from you'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -660,9 +671,8 @@ export const CommandMap = {
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
}), }),
new Command({
unignore: new Command({ command: 'unignore',
name: 'unignore',
args: '<user-id>', args: '<user-id>',
description: _td('Stops ignoring a user, showing their messages going forward'), description: _td('Stops ignoring a user, showing their messages going forward'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -692,10 +702,8 @@ export const CommandMap = {
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
}), }),
new Command({
// Define the power level of a user command: 'op',
op: new Command({
name: 'op',
args: '<user-id> [<power-level>]', args: '<user-id> [<power-level>]',
description: _td('Define the power level of a user'), description: _td('Define the power level of a user'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -705,7 +713,7 @@ export const CommandMap = {
if (matches) { if (matches) {
const userId = matches[1]; const userId = matches[1];
if (matches.length === 4 && undefined !== matches[3]) { if (matches.length === 4 && undefined !== matches[3]) {
powerLevel = parseInt(matches[3]); powerLevel = parseInt(matches[3], 10);
} }
if (!isNaN(powerLevel)) { if (!isNaN(powerLevel)) {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
@ -721,10 +729,8 @@ export const CommandMap = {
}, },
category: CommandCategories.admin, category: CommandCategories.admin,
}), }),
new Command({
// Reset the power level of a user command: 'deop',
deop: new Command({
name: 'deop',
args: '<user-id>', args: '<user-id>',
description: _td('Deops user with given id'), description: _td('Deops user with given id'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -743,9 +749,8 @@ export const CommandMap = {
}, },
category: CommandCategories.admin, category: CommandCategories.admin,
}), }),
new Command({
devtools: new Command({ command: 'devtools',
name: 'devtools',
description: _td('Opens the Developer Tools dialog'), description: _td('Opens the Developer Tools dialog'),
runFn: function(roomId) { runFn: function(roomId) {
const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog'); const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog');
@ -754,9 +759,8 @@ export const CommandMap = {
}, },
category: CommandCategories.advanced, category: CommandCategories.advanced,
}), }),
new Command({
addwidget: new Command({ command: 'addwidget',
name: 'addwidget',
args: '<url>', args: '<url>',
description: _td('Adds a custom widget by URL to the room'), description: _td('Adds a custom widget by URL to the room'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -775,10 +779,8 @@ export const CommandMap = {
}, },
category: CommandCategories.admin, category: CommandCategories.admin,
}), }),
new Command({
// Verify a user, device, and pubkey tuple command: 'verify',
verify: new Command({
name: 'verify',
args: '<user-id> <device-id> <device-signing-key>', args: '<user-id> <device-id> <device-signing-key>',
description: _td('Verifies a user, session, and pubkey tuple'), description: _td('Verifies a user, session, and pubkey tuple'),
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -843,20 +845,8 @@ export const CommandMap = {
}, },
category: CommandCategories.advanced, category: CommandCategories.advanced,
}), }),
new Command({
// Command definitions for autocompletion ONLY: command: 'discardsession',
// /me is special because its not handled by SlashCommands.js and is instead done inside the Composer classes
me: new Command({
name: 'me',
args: '<message>',
description: _td('Displays action'),
category: CommandCategories.messages,
hideCompletionAfterSpace: true,
}),
discardsession: new Command({
name: 'discardsession',
description: _td('Forces the current outbound group session in an encrypted room to be discarded'), description: _td('Forces the current outbound group session in an encrypted room to be discarded'),
runFn: function(roomId) { runFn: function(roomId) {
try { try {
@ -868,9 +858,8 @@ export const CommandMap = {
}, },
category: CommandCategories.advanced, category: CommandCategories.advanced,
}), }),
new Command({
rainbow: new Command({ command: "rainbow",
name: "rainbow",
description: _td("Sends the given message coloured as a rainbow"), description: _td("Sends the given message coloured as a rainbow"),
args: '<message>', args: '<message>',
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -879,9 +868,8 @@ export const CommandMap = {
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
new Command({
rainbowme: new Command({ command: "rainbowme",
name: "rainbowme",
description: _td("Sends the given emote coloured as a rainbow"), description: _td("Sends the given emote coloured as a rainbow"),
args: '<message>', args: '<message>',
runFn: function(roomId, args) { runFn: function(roomId, args) {
@ -890,9 +878,8 @@ export const CommandMap = {
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
new Command({
help: new Command({ command: "help",
name: "help",
description: _td("Displays list of commands with usages and descriptions"), description: _td("Displays list of commands with usages and descriptions"),
runFn: function() { runFn: function() {
const SlashCommandHelpDialog = sdk.getComponent('dialogs.SlashCommandHelpDialog'); const SlashCommandHelpDialog = sdk.getComponent('dialogs.SlashCommandHelpDialog');
@ -902,18 +889,16 @@ export const CommandMap = {
}, },
category: CommandCategories.advanced, category: CommandCategories.advanced,
}), }),
new Command({
whois: new Command({ command: "whois",
name: "whois",
description: _td("Displays information about a user"), description: _td("Displays information about a user"),
args: '<user-id>', args: "<user-id>",
runFn: function(roomId, userId) { runFn: function(roomId, userId) {
if (!userId || !userId.startsWith("@") || !userId.includes(":")) { if (!userId || !userId.startsWith("@") || !userId.includes(":")) {
return reject(this.getUsage()); return reject(this.getUsage());
} }
const member = MatrixClientPeg.get().getRoom(roomId).getMember(userId); const member = MatrixClientPeg.get().getRoom(roomId).getMember(userId);
dis.dispatch({ dis.dispatch({
action: 'view_user', action: 'view_user',
member: member || {userId}, member: member || {userId},
@ -922,17 +907,26 @@ export const CommandMap = {
}, },
category: CommandCategories.advanced, category: CommandCategories.advanced,
}), }),
};
/* eslint-enable babel/no-invalid-this */
// Command definitions for autocompletion ONLY:
// /me is special because its not handled by SlashCommands.js and is instead done inside the Composer classes
new Command({
command: 'me',
args: '<message>',
description: _td('Displays action'),
category: CommandCategories.messages,
hideCompletionAfterSpace: true,
}),
];
// helpful aliases // build a map from names and aliases to the Command objects.
const aliases = { export const CommandMap = new Map();
j: "join", Commands.forEach(cmd => {
newballsplease: "discardsession", CommandMap.set(cmd.command, cmd);
goto: "join", // because it handles event permalinks magically cmd.aliases.forEach(alias => {
roomnick: "myroomnick", CommandMap.set(alias, cmd);
}; });
});
/** /**
@ -959,10 +953,7 @@ export function getCommand(roomId, input) {
cmd = input; cmd = input;
} }
if (aliases[cmd]) { if (CommandMap.has(cmd)) {
cmd = aliases[cmd]; return () => CommandMap.get(cmd).run(roomId, args, cmd);
}
if (CommandMap[cmd]) {
return () => CommandMap[cmd].run(roomId, args);
} }
} }

View file

@ -23,17 +23,16 @@ import AutocompleteProvider from './AutocompleteProvider';
import QueryMatcher from './QueryMatcher'; import QueryMatcher from './QueryMatcher';
import {TextualCompletion} from './Components'; import {TextualCompletion} from './Components';
import type {Completion, SelectionRange} from "./Autocompleter"; import type {Completion, SelectionRange} from "./Autocompleter";
import {CommandMap} from '../SlashCommands'; import {Commands, CommandMap} from '../SlashCommands';
const COMMANDS = Object.values(CommandMap);
const COMMAND_RE = /(^\/\w*)(?: .*)?/g; const COMMAND_RE = /(^\/\w*)(?: .*)?/g;
export default class CommandProvider extends AutocompleteProvider { export default class CommandProvider extends AutocompleteProvider {
constructor() { constructor() {
super(COMMAND_RE); super(COMMAND_RE);
this.matcher = new QueryMatcher(COMMANDS, { this.matcher = new QueryMatcher(Commands, {
keys: ['command', 'args', 'description'], keys: ['command', 'args', 'description'],
funcs: [({aliases}) => aliases.join(" ")], // aliases
}); });
} }
@ -46,31 +45,40 @@ export default class CommandProvider extends AutocompleteProvider {
if (command[0] !== command[1]) { if (command[0] !== command[1]) {
// The input looks like a command with arguments, perform exact match // The input looks like a command with arguments, perform exact match
const name = command[1].substr(1); // strip leading `/` const name = command[1].substr(1); // strip leading `/`
if (CommandMap[name]) { if (CommandMap.has(name)) {
// some commands, namely `me` and `ddg` don't suit having the usage shown whilst typing their arguments // some commands, namely `me` and `ddg` don't suit having the usage shown whilst typing their arguments
if (CommandMap[name].hideCompletionAfterSpace) return []; if (CommandMap.get(name).hideCompletionAfterSpace) return [];
matches = [CommandMap[name]]; matches = [CommandMap.get(name)];
} }
} else { } else {
if (query === '/') { if (query === '/') {
// If they have just entered `/` show everything // If they have just entered `/` show everything
matches = COMMANDS; matches = Commands;
} else { } else {
// otherwise fuzzy match against all of the fields // otherwise fuzzy match against all of the fields
matches = this.matcher.match(command[1]); matches = this.matcher.match(command[1]);
} }
} }
return matches.map((result) => ({
// If the command is the same as the one they entered, we don't want to discard their arguments return matches.map((result) => {
completion: result.command === command[1] ? command[0] : (result.command + ' '), let completion = result.getCommand() + ' ';
type: "command", const usedAlias = result.aliases.find(alias => `/${alias}` === command[1]);
component: <TextualCompletion // If the command (or an alias) is the same as the one they entered, we don't want to discard their arguments
title={result.command} if (usedAlias || result.getCommand() === command[1]) {
subtitle={result.args} completion = command[0];
description={_t(result.description)} />, }
range,
})); return {
completion,
type: "command",
component: <TextualCompletion
title={`/${usedAlias || result.command}`}
subtitle={result.args}
description={_t(result.description)} />,
range,
};
});
} }
getName() { getName() {

View file

@ -16,14 +16,14 @@ limitations under the License.
import React from 'react'; import React from 'react';
import {_t} from "../../../languageHandler"; import {_t} from "../../../languageHandler";
import {CommandCategories, CommandMap} from "../../../SlashCommands"; import {CommandCategories, Commands} from "../../../SlashCommands";
import * as sdk from "../../../index"; import * as sdk from "../../../index";
export default ({onFinished}) => { export default ({onFinished}) => {
const InfoDialog = sdk.getComponent('dialogs.InfoDialog'); const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
const categories = {}; const categories = {};
Object.values(CommandMap).forEach(cmd => { Commands.forEach(cmd => {
if (!categories[cmd.category]) { if (!categories[cmd.category]) {
categories[cmd.category] = []; categories[cmd.category] = [];
} }
@ -41,7 +41,7 @@ export default ({onFinished}) => {
categories[category].forEach(cmd => { categories[category].forEach(cmd => {
rows.push(<tr key={cmd.command}> rows.push(<tr key={cmd.command}>
<td><strong>{cmd.command}</strong></td> <td><strong>{cmd.getCommand()}</strong></td>
<td>{cmd.args}</td> <td>{cmd.args}</td>
<td>{cmd.description}</td> <td>{cmd.description}</td>
</tr>); </tr>);

View file

@ -198,12 +198,12 @@
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!", "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!",
"Verified key": "Verified key", "Verified key": "Verified key",
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.", "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.",
"Displays action": "Displays action",
"Forces the current outbound group session in an encrypted room to be discarded": "Forces the current outbound group session in an encrypted room to be discarded", "Forces the current outbound group session in an encrypted room to be discarded": "Forces the current outbound group session in an encrypted room to be discarded",
"Sends the given message coloured as a rainbow": "Sends the given message coloured as a rainbow", "Sends the given message coloured as a rainbow": "Sends the given message coloured as a rainbow",
"Sends the given emote coloured as a rainbow": "Sends the given emote coloured as a rainbow", "Sends the given emote coloured as a rainbow": "Sends the given emote coloured as a rainbow",
"Displays list of commands with usages and descriptions": "Displays list of commands with usages and descriptions", "Displays list of commands with usages and descriptions": "Displays list of commands with usages and descriptions",
"Displays information about a user": "Displays information about a user", "Displays information about a user": "Displays information about a user",
"Displays action": "Displays action",
"Reason": "Reason", "Reason": "Reason",
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.",
"%(targetName)s accepted an invitation.": "%(targetName)s accepted an invitation.", "%(targetName)s accepted an invitation.": "%(targetName)s accepted an invitation.",