From 6c166f05601fc519fb9a5efc67d65d234ff12ce6 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Tue, 15 Sep 2020 15:49:25 +0100
Subject: [PATCH] If no bug_report_endpoint_url, hide rageshaking from the App

---
 src/SlashCommands.tsx                         | 15 ++++++++---
 src/autocomplete/CommandProvider.tsx          |  4 +--
 .../views/dialogs/SlashCommandHelpDialog.js   |  1 +
 .../views/rooms/BasicMessageComposer.tsx      |  3 ++-
 .../settings/tabs/user/HelpUserSettingsTab.js | 25 ++++++++++++-------
 5 files changed, 33 insertions(+), 15 deletions(-)

diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx
index 6456368211..7ba2022c6d 100644
--- a/src/SlashCommands.tsx
+++ b/src/SlashCommands.tsx
@@ -43,6 +43,7 @@ import { ensureDMExists } from "./createRoom";
 import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload";
 import { Action } from "./dispatcher/actions";
 import { EffectiveMembership, getEffectiveMembership, leaveRoomBehaviour } from "./utils/membership";
+import SdkConfig from "./SdkConfig";
 
 // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
 interface HTMLInputEvent extends Event {
@@ -87,6 +88,7 @@ interface ICommandOpts {
     runFn?: RunFn;
     category: string;
     hideCompletionAfterSpace?: boolean;
+    isEnabled?(): boolean;
 }
 
 export class Command {
@@ -97,6 +99,7 @@ export class Command {
     runFn: undefined | RunFn;
     category: string;
     hideCompletionAfterSpace: boolean;
+    _isEnabled?: () => boolean;
 
     constructor(opts: ICommandOpts) {
         this.command = opts.command;
@@ -106,6 +109,7 @@ export class Command {
         this.runFn = opts.runFn;
         this.category = opts.category || CommandCategories.other;
         this.hideCompletionAfterSpace = opts.hideCompletionAfterSpace || false;
+        this._isEnabled = opts.isEnabled;
     }
 
     getCommand() {
@@ -125,6 +129,10 @@ export class Command {
     getUsage() {
         return _t('Usage') + ': ' + this.getCommandWithArgs();
     }
+
+    isEnabled() {
+        return this._isEnabled ? this._isEnabled() : true;
+    }
 }
 
 function reject(error) {
@@ -971,6 +979,7 @@ export const Commands = [
         command: "rageshake",
         aliases: ["bugreport"],
         description: _td("Send a bug report with logs"),
+        isEnabled: () => !!SdkConfig.get().bug_report_endpoint_url,
         args: "<description>",
         runFn: function(roomId, args) {
             return success(
@@ -1048,7 +1057,7 @@ Commands.forEach(cmd => {
     });
 });
 
-export function parseCommandString(input) {
+export function parseCommandString(input: string) {
     // trim any trailing whitespace, as it can confuse the parser for
     // IRC-style commands
     input = input.replace(/\s+$/, '');
@@ -1075,10 +1084,10 @@ export function parseCommandString(input) {
  * processing the command, or 'promise' if a request was sent out.
  * Returns null if the input didn't match a command.
  */
-export function getCommand(roomId, input) {
+export function getCommand(roomId: string, input: string) {
     const {cmd, args} = parseCommandString(input);
 
-    if (CommandMap.has(cmd)) {
+    if (CommandMap.has(cmd) && CommandMap.get(cmd).isEnabled()) {
         return () => CommandMap.get(cmd).run(roomId, args, cmd);
     }
 }
diff --git a/src/autocomplete/CommandProvider.tsx b/src/autocomplete/CommandProvider.tsx
index 3ff8ff0469..c2d1290e08 100644
--- a/src/autocomplete/CommandProvider.tsx
+++ b/src/autocomplete/CommandProvider.tsx
@@ -47,7 +47,7 @@ export default class CommandProvider extends AutocompleteProvider {
         if (command[0] !== command[1]) {
             // The input looks like a command with arguments, perform exact match
             const name = command[1].substr(1); // strip leading `/`
-            if (CommandMap.has(name)) {
+            if (CommandMap.has(name) && CommandMap.get(name).isEnabled()) {
                 // some commands, namely `me` and `ddg` don't suit having the usage shown whilst typing their arguments
                 if (CommandMap.get(name).hideCompletionAfterSpace) return [];
                 matches = [CommandMap.get(name)];
@@ -63,7 +63,7 @@ export default class CommandProvider extends AutocompleteProvider {
         }
 
 
-        return matches.map((result) => {
+        return matches.filter(cmd => cmd.isEnabled()).map((result) => {
             let completion = result.getCommand() + ' ';
             const usedAlias = result.aliases.find(alias => `/${alias}` === command[1]);
             // If the command (or an alias) is the same as the one they entered, we don't want to discard their arguments
diff --git a/src/components/views/dialogs/SlashCommandHelpDialog.js b/src/components/views/dialogs/SlashCommandHelpDialog.js
index bae5b37993..5b4148e939 100644
--- a/src/components/views/dialogs/SlashCommandHelpDialog.js
+++ b/src/components/views/dialogs/SlashCommandHelpDialog.js
@@ -24,6 +24,7 @@ export default ({onFinished}) => {
 
     const categories = {};
     Commands.forEach(cmd => {
+        if (!cmd.isEnabled()) return;
         if (!categories[cmd.category]) {
             categories[cmd.category] = [];
         }
diff --git a/src/components/views/rooms/BasicMessageComposer.tsx b/src/components/views/rooms/BasicMessageComposer.tsx
index 6024f272ec..7c2eb83a94 100644
--- a/src/components/views/rooms/BasicMessageComposer.tsx
+++ b/src/components/views/rooms/BasicMessageComposer.tsx
@@ -207,7 +207,8 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
         // If the user is entering a command, only consider them typing if it is one which sends a message into the room
         if (isTyping && this.props.model.parts[0].type === "command") {
             const {cmd} = parseCommandString(this.props.model.parts[0].text);
-            if (!CommandMap.has(cmd) || CommandMap.get(cmd).category !== CommandCategories.messages) {
+            const command = CommandMap.get(cmd);
+            if (!command || !command.isEnabled() || command.category !== CommandCategories.messages) {
                 isTyping = false;
             }
         }
diff --git a/src/components/views/settings/tabs/user/HelpUserSettingsTab.js b/src/components/views/settings/tabs/user/HelpUserSettingsTab.js
index 64807ddb21..85ba22a353 100644
--- a/src/components/views/settings/tabs/user/HelpUserSettingsTab.js
+++ b/src/components/views/settings/tabs/user/HelpUserSettingsTab.js
@@ -204,9 +204,9 @@ export default class HelpUserSettingsTab extends React.Component {
             updateButton = <UpdateCheckButton />;
         }
 
-        return (
-            <div className="mx_SettingsTab mx_HelpUserSettingsTab">
-                <div className="mx_SettingsTab_heading">{_t("Help & About")}</div>
+        let bugReportingSection;
+        if (SdkConfig.get().bug_report_endpoint_url) {
+            bugReportingSection = (
                 <div className="mx_SettingsTab_section">
                     <span className='mx_SettingsTab_subheading'>{_t('Bug reporting')}</span>
                     <div className='mx_SettingsTab_subsectionText'>
@@ -223,22 +223,24 @@ export default class HelpUserSettingsTab extends React.Component {
                                 {_t("Submit debug logs")}
                             </AccessibleButton>
                         </div>
-                        <div className='mx_HelpUserSettingsTab_debugButton'>
-                            <AccessibleButton onClick={this._onClearCacheAndReload} kind='danger'>
-                                {_t("Clear cache and reload")}
-                            </AccessibleButton>
-                        </div>
                         {
                             _t( "To report a Matrix-related security issue, please read the Matrix.org " +
                                 "<a>Security Disclosure Policy</a>.", {},
                                 {
                                     'a': (sub) =>
                                         <a href="https://matrix.org/security-disclosure-policy/"
-                                        rel="noreferrer noopener" target="_blank">{sub}</a>,
+                                           rel="noreferrer noopener" target="_blank">{sub}</a>,
                                 })
                         }
                     </div>
                 </div>
+            );
+        }
+
+        return (
+            <div className="mx_SettingsTab mx_HelpUserSettingsTab">
+                <div className="mx_SettingsTab_heading">{_t("Help & About")}</div>
+                { bugReportingSection }
                 <div className='mx_SettingsTab_section'>
                     <span className='mx_SettingsTab_subheading'>{_t("FAQ")}</span>
                     <div className='mx_SettingsTab_subsectionText'>
@@ -268,6 +270,11 @@ export default class HelpUserSettingsTab extends React.Component {
                                           data-spoiler={MatrixClientPeg.get().getAccessToken()}>
                             &lt;{ _t("click to reveal") }&gt;
                         </AccessibleButton>
+                        <div className='mx_HelpUserSettingsTab_debugButton'>
+                            <AccessibleButton onClick={this._onClearCacheAndReload} kind='danger'>
+                                {_t("Clear cache and reload")}
+                            </AccessibleButton>
+                        </div>
                     </div>
                 </div>
             </div>