From 0a9f02abcc527d64f2f7bbdd1d296008d4e94ca6 Mon Sep 17 00:00:00 2001
From: Kegan Dougal <kegan@matrix.org>
Date: Wed, 25 Jan 2017 16:51:26 +0000
Subject: [PATCH] Glue the dialog to rageshake: honour sendLogs flag.

---
 src/component-index.js                        |  60 +++++----
 .../views/dialogs/BugReportDialog.js          | 126 ++++++++++++++++++
 src/vector/rageshake.js                       |  25 ++--
 3 files changed, 171 insertions(+), 40 deletions(-)
 create mode 100644 src/components/views/dialogs/BugReportDialog.js

diff --git a/src/component-index.js b/src/component-index.js
index 3141087c..456f8176 100644
--- a/src/component-index.js
+++ b/src/component-index.js
@@ -27,60 +27,62 @@ limitations under the License.
 module.exports.components = require('matrix-react-sdk/lib/component-index').components;
 
 import structures$BottomLeftMenu from './components/structures/BottomLeftMenu';
-module.exports.components['structures.BottomLeftMenu'] = structures$BottomLeftMenu;
+structures$BottomLeftMenu && (module.exports.components['structures.BottomLeftMenu'] = structures$BottomLeftMenu);
 import structures$CompatibilityPage from './components/structures/CompatibilityPage';
-module.exports.components['structures.CompatibilityPage'] = structures$CompatibilityPage;
+structures$CompatibilityPage && (module.exports.components['structures.CompatibilityPage'] = structures$CompatibilityPage);
 import structures$LeftPanel from './components/structures/LeftPanel';
-module.exports.components['structures.LeftPanel'] = structures$LeftPanel;
+structures$LeftPanel && (module.exports.components['structures.LeftPanel'] = structures$LeftPanel);
 import structures$RightPanel from './components/structures/RightPanel';
-module.exports.components['structures.RightPanel'] = structures$RightPanel;
+structures$RightPanel && (module.exports.components['structures.RightPanel'] = structures$RightPanel);
 import structures$RoomDirectory from './components/structures/RoomDirectory';
-module.exports.components['structures.RoomDirectory'] = structures$RoomDirectory;
+structures$RoomDirectory && (module.exports.components['structures.RoomDirectory'] = structures$RoomDirectory);
 import structures$RoomSubList from './components/structures/RoomSubList';
-module.exports.components['structures.RoomSubList'] = structures$RoomSubList;
+structures$RoomSubList && (module.exports.components['structures.RoomSubList'] = structures$RoomSubList);
 import structures$SearchBox from './components/structures/SearchBox';
-module.exports.components['structures.SearchBox'] = structures$SearchBox;
+structures$SearchBox && (module.exports.components['structures.SearchBox'] = structures$SearchBox);
 import structures$ViewSource from './components/structures/ViewSource';
-module.exports.components['structures.ViewSource'] = structures$ViewSource;
+structures$ViewSource && (module.exports.components['structures.ViewSource'] = structures$ViewSource);
 import views$context_menus$MessageContextMenu from './components/views/context_menus/MessageContextMenu';
-module.exports.components['views.context_menus.MessageContextMenu'] = views$context_menus$MessageContextMenu;
+views$context_menus$MessageContextMenu && (module.exports.components['views.context_menus.MessageContextMenu'] = views$context_menus$MessageContextMenu);
 import views$context_menus$NotificationStateContextMenu from './components/views/context_menus/NotificationStateContextMenu';
-module.exports.components['views.context_menus.NotificationStateContextMenu'] = views$context_menus$NotificationStateContextMenu;
+views$context_menus$NotificationStateContextMenu && (module.exports.components['views.context_menus.NotificationStateContextMenu'] = views$context_menus$NotificationStateContextMenu);
 import views$context_menus$RoomTagContextMenu from './components/views/context_menus/RoomTagContextMenu';
-module.exports.components['views.context_menus.RoomTagContextMenu'] = views$context_menus$RoomTagContextMenu;
+views$context_menus$RoomTagContextMenu && (module.exports.components['views.context_menus.RoomTagContextMenu'] = views$context_menus$RoomTagContextMenu);
+import views$dialogs$BugReportDialog from './components/views/dialogs/BugReportDialog';
+views$dialogs$BugReportDialog && (module.exports.components['views.dialogs.BugReportDialog'] = views$dialogs$BugReportDialog);
 import views$dialogs$ChangelogDialog from './components/views/dialogs/ChangelogDialog';
-module.exports.components['views.dialogs.ChangelogDialog'] = views$dialogs$ChangelogDialog;
+views$dialogs$ChangelogDialog && (module.exports.components['views.dialogs.ChangelogDialog'] = views$dialogs$ChangelogDialog);
 import views$directory$NetworkDropdown from './components/views/directory/NetworkDropdown';
-module.exports.components['views.directory.NetworkDropdown'] = views$directory$NetworkDropdown;
+views$directory$NetworkDropdown && (module.exports.components['views.directory.NetworkDropdown'] = views$directory$NetworkDropdown);
 import views$elements$ImageView from './components/views/elements/ImageView';
-module.exports.components['views.elements.ImageView'] = views$elements$ImageView;
+views$elements$ImageView && (module.exports.components['views.elements.ImageView'] = views$elements$ImageView);
 import views$elements$Spinner from './components/views/elements/Spinner';
-module.exports.components['views.elements.Spinner'] = views$elements$Spinner;
+views$elements$Spinner && (module.exports.components['views.elements.Spinner'] = views$elements$Spinner);
 import views$globals$GuestWarningBar from './components/views/globals/GuestWarningBar';
-module.exports.components['views.globals.GuestWarningBar'] = views$globals$GuestWarningBar;
+views$globals$GuestWarningBar && (module.exports.components['views.globals.GuestWarningBar'] = views$globals$GuestWarningBar);
 import views$globals$MatrixToolbar from './components/views/globals/MatrixToolbar';
-module.exports.components['views.globals.MatrixToolbar'] = views$globals$MatrixToolbar;
+views$globals$MatrixToolbar && (module.exports.components['views.globals.MatrixToolbar'] = views$globals$MatrixToolbar);
 import views$globals$NewVersionBar from './components/views/globals/NewVersionBar';
-module.exports.components['views.globals.NewVersionBar'] = views$globals$NewVersionBar;
+views$globals$NewVersionBar && (module.exports.components['views.globals.NewVersionBar'] = views$globals$NewVersionBar);
 import views$login$VectorCustomServerDialog from './components/views/login/VectorCustomServerDialog';
-module.exports.components['views.login.VectorCustomServerDialog'] = views$login$VectorCustomServerDialog;
+views$login$VectorCustomServerDialog && (module.exports.components['views.login.VectorCustomServerDialog'] = views$login$VectorCustomServerDialog);
 import views$login$VectorLoginFooter from './components/views/login/VectorLoginFooter';
-module.exports.components['views.login.VectorLoginFooter'] = views$login$VectorLoginFooter;
+views$login$VectorLoginFooter && (module.exports.components['views.login.VectorLoginFooter'] = views$login$VectorLoginFooter);
 import views$login$VectorLoginHeader from './components/views/login/VectorLoginHeader';
-module.exports.components['views.login.VectorLoginHeader'] = views$login$VectorLoginHeader;
+views$login$VectorLoginHeader && (module.exports.components['views.login.VectorLoginHeader'] = views$login$VectorLoginHeader);
 import views$messages$DateSeparator from './components/views/messages/DateSeparator';
-module.exports.components['views.messages.DateSeparator'] = views$messages$DateSeparator;
+views$messages$DateSeparator && (module.exports.components['views.messages.DateSeparator'] = views$messages$DateSeparator);
 import views$messages$MessageTimestamp from './components/views/messages/MessageTimestamp';
-module.exports.components['views.messages.MessageTimestamp'] = views$messages$MessageTimestamp;
+views$messages$MessageTimestamp && (module.exports.components['views.messages.MessageTimestamp'] = views$messages$MessageTimestamp);
 import views$rooms$DNDRoomTile from './components/views/rooms/DNDRoomTile';
-module.exports.components['views.rooms.DNDRoomTile'] = views$rooms$DNDRoomTile;
+views$rooms$DNDRoomTile && (module.exports.components['views.rooms.DNDRoomTile'] = views$rooms$DNDRoomTile);
 import views$rooms$RoomDropTarget from './components/views/rooms/RoomDropTarget';
-module.exports.components['views.rooms.RoomDropTarget'] = views$rooms$RoomDropTarget;
+views$rooms$RoomDropTarget && (module.exports.components['views.rooms.RoomDropTarget'] = views$rooms$RoomDropTarget);
 import views$rooms$RoomTooltip from './components/views/rooms/RoomTooltip';
-module.exports.components['views.rooms.RoomTooltip'] = views$rooms$RoomTooltip;
+views$rooms$RoomTooltip && (module.exports.components['views.rooms.RoomTooltip'] = views$rooms$RoomTooltip);
 import views$rooms$SearchBar from './components/views/rooms/SearchBar';
-module.exports.components['views.rooms.SearchBar'] = views$rooms$SearchBar;
+views$rooms$SearchBar && (module.exports.components['views.rooms.SearchBar'] = views$rooms$SearchBar);
 import views$settings$IntegrationsManager from './components/views/settings/IntegrationsManager';
-module.exports.components['views.settings.IntegrationsManager'] = views$settings$IntegrationsManager;
+views$settings$IntegrationsManager && (module.exports.components['views.settings.IntegrationsManager'] = views$settings$IntegrationsManager);
 import views$settings$Notifications from './components/views/settings/Notifications';
-module.exports.components['views.settings.Notifications'] = views$settings$Notifications;
+views$settings$Notifications && (module.exports.components['views.settings.Notifications'] = views$settings$Notifications);
diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js
new file mode 100644
index 00000000..e3c8945a
--- /dev/null
+++ b/src/components/views/dialogs/BugReportDialog.js
@@ -0,0 +1,126 @@
+/*
+Copyright 2017 OpenMarket Ltd
+
+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 sdk from 'matrix-react-sdk';
+import rageshake from '../../../vector/rageshake';
+
+export default class BugReportDialog extends React.Component {
+    constructor(props, context) {
+        super(props, context);
+        this.state = {
+            sendLogs: true,
+            busy: false,
+            err: null,
+            text: "",
+        };
+        this._onSubmit = this._onSubmit.bind(this);
+        this._onCancel = this._onCancel.bind(this);
+        this._onTextChange = this._onTextChange.bind(this);
+        this._onSendLogsChange = this._onSendLogsChange.bind(this);
+    }
+
+    _onCancel(ev) {
+        this.props.onFinished(false);
+    }
+
+    _onSubmit(ev) {
+        const sendLogs = this.state.sendLogs;
+        const userText = this.state.text;
+        if (!sendLogs && userText.trim().length === 0) {
+            this.setState({
+                err: "Please describe the bug and/or send logs.",
+            });
+            return;
+        }
+        this.setState({ busy: true, err: null });
+        rageshake.sendBugReport(userText, sendLogs).then(() => {
+            this.setState({ busy: false });
+        }, (err) => {
+            this.setState({ busy: false, err: `Failed: ${err.message}` });
+        });
+    }
+
+    _onTextChange(ev) {
+        this.setState({ text: ev.target.value });
+    }
+
+    _onSendLogsChange(ev) {
+        this.setState({ sendLogs: ev.target.checked });
+    }
+
+    render() {
+        const Loader = sdk.getComponent("elements.Spinner");
+
+        let error = null;
+        if (this.state.err) {
+            error = <div className="error">
+                {this.state.err}
+            </div>;
+        }
+
+        const okLabel = this.state.busy ? <Loader /> : 'Send';
+
+        let cancelButton = null;
+        if (!this.state.busy) {
+            cancelButton = <button onClick={this._onCancel}>
+                Cancel
+            </button>;
+        }
+
+        return (
+            <div className="mx_BugReportDialog">
+                <div className="mx_Dialog_title">
+                    Report a bug
+                </div>
+                <div className="mx_Dialog_content">
+                    <p>Please describe the bug. What did you do?
+                    What did you expect to happen?
+                    What actually happened?</p>
+                    <textarea
+                        className="mx_BugReportDialog_input"
+                        rows={5}
+                        onChange={this._onTextChange}
+                        value={this.state.text}
+                        placeholder="Describe your problem here."
+                    />
+                    <p>In order to diagnose problems, logs from this client will be sent with
+                    this bug report.
+                    If you would prefer to only send the text above, please untick:</p>
+                    <input type="checkbox" checked={this.state.sendLogs}
+                        onChange={this._onSendLogsChange} id="mx_BugReportDialog_logs"/>
+                    <label htmlFor="mx_BugReportDialog_logs">Send logs</label>
+                    {error}
+                </div>
+                <div className="mx_Dialog_buttons">
+                    <button
+                        className="mx_Dialog_primary danger"
+                        onClick={this._onSubmit}
+                        autoFocus={true}
+                    >
+                        {okLabel}
+                    </button>
+
+                    {cancelButton}
+                </div>
+            </div>
+        );
+    }
+}
+
+BugReportDialog.propTypes = {
+    onFinished: React.PropTypes.func.isRequired,
+};
diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js
index 086ab155..61e29a31 100644
--- a/src/vector/rageshake.js
+++ b/src/vector/rageshake.js
@@ -430,9 +430,10 @@ module.exports = {
     /**
      * Send a bug report.
      * @param {string} userText Any additional user input.
+     * @param {boolean} sendLogs True to send logs
      * @return {Promise} Resolved when the bug report is sent.
      */
-    sendBugReport: async function(userText) {
+    sendBugReport: async function(userText, sendLogs) {
         if (!logger) {
             throw new Error(
                 "No console logger, did you forget to call init()?"
@@ -457,16 +458,18 @@ module.exports = {
         // sending to work going off the in-memory console logs.
         console.log("Sending bug report.");
         let logs = [];
-        if (store) {
-            // flush most recent logs
-            await store.flush();
-            logs = await store.consume();
-        }
-        else {
-            logs.push({
-                lines: logger.flush(true),
-                id: "-",
-            });
+        if (sendLogs) {
+            if (store) {
+                // flush most recent logs
+                await store.flush();
+                logs = await store.consume();
+            }
+            else {
+                logs.push({
+                    lines: logger.flush(true),
+                    id: "-",
+                });
+            }
         }
 
         await q.Promise((resolve, reject) => {