diff --git a/res/css/_components.scss b/res/css/_components.scss index 4c2829b68c..dff174e943 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -70,6 +70,7 @@ @import "./views/dialogs/_SetPasswordDialog.scss"; @import "./views/dialogs/_SettingsDialog.scss"; @import "./views/dialogs/_ShareDialog.scss"; +@import "./views/dialogs/_SlashCommandHelpDialog.scss"; @import "./views/dialogs/_TermsDialog.scss"; @import "./views/dialogs/_UnknownDeviceDialog.scss"; @import "./views/dialogs/_UploadConfirmDialog.scss"; diff --git a/res/css/views/dialogs/_SlashCommandHelpDialog.scss b/res/css/views/dialogs/_SlashCommandHelpDialog.scss new file mode 100644 index 0000000000..786a28deef --- /dev/null +++ b/res/css/views/dialogs/_SlashCommandHelpDialog.scss @@ -0,0 +1,24 @@ +/* +Copyright Michael Telatynski <7t3chguy@gmail.com> + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_SlashCommandHelpDialog .mx_SlashCommandHelpDialog_headerRow h2 { + margin-bottom: 2px; +} + +.mx_SlashCommandHelpDialog .mx_Dialog_content { + margin-top: 12px; + margin-bottom: 34px; +} diff --git a/src/SlashCommands.js b/src/SlashCommands.js index da3c0f91d8..72ace22cb6 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -20,11 +20,9 @@ limitations under the License. import React from 'react'; import MatrixClientPeg from './MatrixClientPeg'; import dis from './dispatcher'; -import Tinter from './Tinter'; import sdk from './index'; import {_t, _td} from './languageHandler'; import Modal from './Modal'; -import SettingsStore, {SettingLevel} from './settings/SettingsStore'; import {MATRIXTO_URL_PATTERN} from "./linkify-matrix"; import * as querystring from "querystring"; import MultiInviter from './utils/MultiInviter'; @@ -54,12 +52,21 @@ const singleMxcUpload = async () => { }); }; +export const CommandCategories = { + "messages": _td("Messages"), + "actions": _td("Actions"), + "admin": _td("Admin"), + "advanced": _td("Advanced"), + "other": _td("Other"), +}; + class Command { - constructor({name, args='', description, runFn, hideCompletionAfterSpace=false}) { + constructor({name, args='', description, runFn, category=CommandCategories.other, hideCompletionAfterSpace=false}) { this.command = '/' + name; this.args = args; this.description = description; this.runFn = runFn; + this.category = category; this.hideCompletionAfterSpace = hideCompletionAfterSpace; } @@ -106,6 +113,7 @@ export const CommandMap = { } return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); }, + category: CommandCategories.messages, }), ddg: new Command({ @@ -121,6 +129,7 @@ export const CommandMap = { }); return success(); }, + category: CommandCategories.actions, hideCompletionAfterSpace: true, }), @@ -199,6 +208,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.admin, }), nick: new Command({ @@ -211,6 +221,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.actions, }), myroomnick: new Command({ @@ -229,6 +240,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.actions, }), myroomavatar: new Command({ @@ -255,6 +267,7 @@ export const CommandMap = { return cli.sendStateEvent(roomId, 'm.room.member', content, userId); })); }, + category: CommandCategories.actions, }), myavatar: new Command({ @@ -272,31 +285,7 @@ export const CommandMap = { return MatrixClientPeg.get().setAvatarUrl(url); })); }, - }), - - tint: new Command({ - name: 'tint', - args: ' []', - description: _td('Changes colour scheme of current room'), - runFn: function(roomId, args) { - if (args) { - const matches = args.match(/^(#([\da-fA-F]{3}|[\da-fA-F]{6}))( +(#([\da-fA-F]{3}|[\da-fA-F]{6})))?$/); - if (matches) { - Tinter.tint(matches[1], matches[4]); - const colorScheme = {}; - colorScheme.primary_color = matches[1]; - if (matches[4]) { - colorScheme.secondary_color = matches[4]; - } else { - colorScheme.secondary_color = colorScheme.primary_color; - } - return success( - SettingsStore.setValue('roomColor', roomId, SettingLevel.ROOM_ACCOUNT, colorScheme), - ); - } - } - return reject(this.getUsage()); - }, + category: CommandCategories.actions, }), topic: new Command({ @@ -322,6 +311,7 @@ export const CommandMap = { }); return success(); }, + category: CommandCategories.admin, }), roomname: new Command({ @@ -334,6 +324,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.admin, }), invite: new Command({ @@ -357,6 +348,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.actions, }), join: new Command({ @@ -461,6 +453,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.actions, }), part: new Command({ @@ -508,6 +501,7 @@ export const CommandMap = { }), ); }, + category: CommandCategories.actions, }), kick: new Command({ @@ -523,6 +517,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.admin, }), // Ban a user from the room with an optional reason @@ -539,6 +534,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.admin, }), // Unban a user from ythe room @@ -556,6 +552,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.admin, }), ignore: new Command({ @@ -586,6 +583,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.actions, }), unignore: new Command({ @@ -617,6 +615,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.actions, }), // Define the power level of a user @@ -645,6 +644,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.admin, }), // Reset the power level of a user @@ -666,6 +666,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.admin, }), devtools: new Command({ @@ -676,6 +677,7 @@ export const CommandMap = { Modal.createDialog(DevtoolsDialog, {roomId}); return success(); }, + category: CommandCategories.advanced, }), addwidget: new Command({ @@ -696,6 +698,7 @@ export const CommandMap = { return reject(_t("You cannot modify widgets in this room.")); } }, + category: CommandCategories.admin, }), // Verify a user, device, and pubkey tuple @@ -765,6 +768,7 @@ export const CommandMap = { } return reject(this.getUsage()); }, + category: CommandCategories.advanced, }), // Command definitions for autocompletion ONLY: @@ -774,6 +778,7 @@ export const CommandMap = { name: 'me', args: '', description: _td('Displays action'), + category: CommandCategories.messages, hideCompletionAfterSpace: true, }), @@ -788,6 +793,7 @@ export const CommandMap = { } return success(); }, + category: CommandCategories.advanced, }), rainbow: new Command({ @@ -798,6 +804,7 @@ export const CommandMap = { if (!args) return reject(this.getUserId()); return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, textToHtmlRainbow(args))); }, + category: CommandCategories.messages, }), rainbowme: new Command({ @@ -808,6 +815,19 @@ export const CommandMap = { if (!args) return reject(this.getUserId()); return success(MatrixClientPeg.get().sendHtmlEmote(roomId, args, textToHtmlRainbow(args))); }, + category: CommandCategories.messages, + }), + + help: new Command({ + name: "help", + description: _td("Displays list of commands with usages and descriptions"), + runFn: function() { + const SlashCommandHelpDialog = sdk.getComponent('dialogs.SlashCommandHelpDialog'); + + Modal.createTrackedDialog('Slash Commands', 'Help', SlashCommandHelpDialog); + return success(); + }, + category: CommandCategories.advanced, }), }; /* eslint-enable babel/no-invalid-this */ diff --git a/src/components/views/dialogs/InfoDialog.js b/src/components/views/dialogs/InfoDialog.js index 1a59aaf97c..d01b737309 100644 --- a/src/components/views/dialogs/InfoDialog.js +++ b/src/components/views/dialogs/InfoDialog.js @@ -20,20 +20,24 @@ import React from 'react'; import PropTypes from 'prop-types'; import sdk from '../../../index'; import { _t } from '../../../languageHandler'; +import classNames from "classnames"; export default React.createClass({ displayName: 'InfoDialog', propTypes: { + className: PropTypes.string, title: PropTypes.string, description: PropTypes.node, button: PropTypes.string, onFinished: PropTypes.func, + hasCloseButton: PropTypes.bool, }, getDefaultProps: function() { return { title: '', description: '', + hasCloseButton: false, }; }, @@ -48,9 +52,9 @@ export default React.createClass({ -
+
{ this.props.description }
+ +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import {_t} from "../../../languageHandler"; +import {CommandCategories, CommandMap} from "../../../SlashCommands"; +import sdk from "../../../index"; + +export default ({onFinished}) => { + const InfoDialog = sdk.getComponent('dialogs.InfoDialog'); + + const categories = {}; + Object.values(CommandMap).forEach(cmd => { + if (!categories[cmd.category]) { + categories[cmd.category] = []; + } + categories[cmd.category].push(cmd); + }); + + const body = Object.values(CommandCategories).filter(c => categories[c]).map((category) => { + const rows = [ + + +

{_t(category)}

+ + , + ]; + + categories[category].forEach(cmd => { + rows.push( + {cmd.command} + {cmd.args} + {cmd.description} + ); + }); + + return rows; + }); + + return + + {body} + + } + hasCloseButton={true} + onFinished={onFinished} />; +}; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 15aa1ff313..78b6c1616a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -131,6 +131,10 @@ "Missing room_id in request": "Missing room_id in request", "Room %(roomId)s not visible": "Room %(roomId)s not visible", "Missing user_id in request": "Missing user_id in request", + "Messages": "Messages", + "Actions": "Actions", + "Advanced": "Advanced", + "Other": "Other", "Usage": "Usage", "Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prepends ¯\\_(ツ)_/¯ to a plain-text message", "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", @@ -148,7 +152,6 @@ "Changes your display nickname in the current room only": "Changes your display nickname in the current room only", "Changes your avatar in this current room only": "Changes your avatar in this current room only", "Changes your avatar in all rooms": "Changes your avatar in all rooms", - "Changes colour scheme of current room": "Changes colour scheme of current room", "Gets or sets the room topic": "Gets or sets the room topic", "This room has no topic.": "This room has no topic.", "Sets the room name": "Sets the room name", @@ -182,6 +185,7 @@ "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 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", "Unrecognised command:": "Unrecognised command:", "Reason": "Reason", "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", @@ -509,7 +513,6 @@ "Backup has an invalid signature from unverified device ": "Backup has an invalid signature from unverified device ", "Backup is not signed by any of your devices": "Backup is not signed by any of your devices", "This backup is trusted because it has been restored on this device": "This backup is trusted because it has been restored on this device", - "Advanced": "Advanced", "Backup version: ": "Backup version: ", "Algorithm: ": "Algorithm: ", "Your keys are not being backed up from this device.": "Your keys are not being backed up from this device.", @@ -1262,6 +1265,7 @@ "Share Room Message": "Share Room Message", "Link to selected message": "Link to selected message", "COPY": "COPY", + "Command Help": "Command Help", "To help us prevent this in future, please send us logs.": "To help us prevent this in future, please send us logs.", "Missing session data": "Missing session data", "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.", @@ -1418,7 +1422,6 @@ "Join millions for free on the largest public server": "Join millions for free on the largest public server", "Premium": "Premium", "Premium hosting for organisations Learn more": "Premium hosting for organisations Learn more", - "Other": "Other", "Find other public servers or use a custom server": "Find other public servers or use a custom server", "Sorry, your browser is not able to run Riot.": "Sorry, your browser is not able to run Riot.", "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.",