From 269f9a5ccc0a60ef6289de19616d3048d3a13459 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Wed, 12 Apr 2017 11:20:42 +0100
Subject: [PATCH 1/3] Load submit-rageshake asynchronously

... because it's 250K :/
---
 .../views/dialogs/BugReportDialog.js          | 20 ++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js
index badc994b..8db796ee 100644
--- a/src/components/views/dialogs/BugReportDialog.js
+++ b/src/components/views/dialogs/BugReportDialog.js
@@ -16,7 +16,6 @@ limitations under the License.
 
 import React from 'react';
 import sdk from 'matrix-react-sdk';
-import submit_rageshake from '../../../vector/submit-rageshake';
 import SdkConfig from 'matrix-react-sdk/lib/SdkConfig';
 
 export default class BugReportDialog extends React.Component {
@@ -48,14 +47,17 @@ export default class BugReportDialog extends React.Component {
             return;
         }
         this.setState({ busy: true, err: null });
-        submit_rageshake(SdkConfig.get().bug_report_endpoint_url, {
-            userText: userText,
-            sendLogs: sendLogs,
-        }).then(() => {
-            this.setState({ busy: false });
-            this.props.onFinished(false);
-        }, (err) => {
-            this.setState({ busy: false, err: `Failed: ${err.message}` });
+
+        require(['../../../vector/submit-rageshake'], (s) => {
+            s(SdkConfig.get().bug_report_endpoint_url, {
+                userText: userText,
+                sendLogs: sendLogs,
+            }).then(() => {
+                this.setState({ busy: false });
+                this.props.onFinished(false);
+            }, (err) => {
+                this.setState({ busy: false, err: `Failed: ${err.message}` });
+            });
         });
     }
 

From d8bf57edc53f5266306dcd45491619e5bf389ef0 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Wed, 12 Apr 2017 14:53:47 +0100
Subject: [PATCH 2/3] Add progress reporting to rageshake submission

---
 .../views/dialogs/BugReportDialog.js          | 41 ++++++++++++++++---
 src/vector/submit-rageshake.js                | 19 +++++++--
 2 files changed, 51 insertions(+), 9 deletions(-)

diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js
index 8db796ee..92b3c4b1 100644
--- a/src/components/views/dialogs/BugReportDialog.js
+++ b/src/components/views/dialogs/BugReportDialog.js
@@ -26,11 +26,18 @@ export default class BugReportDialog extends React.Component {
             busy: false,
             err: null,
             text: "",
+            progress: null,
         };
+        this._unmounted = false;
         this._onSubmit = this._onSubmit.bind(this);
         this._onCancel = this._onCancel.bind(this);
         this._onTextChange = this._onTextChange.bind(this);
         this._onSendLogsChange = this._onSendLogsChange.bind(this);
+        this._sendProgressCallback = this._sendProgressCallback.bind(this);
+    }
+
+    componentWillUnmount() {
+        this._unmounted = true;
     }
 
     _onCancel(ev) {
@@ -46,17 +53,22 @@ export default class BugReportDialog extends React.Component {
             });
             return;
         }
-        this.setState({ busy: true, err: null });
+        this.setState({ busy: true, progress: null, err: null });
+        this._sendProgressCallback("Loading bug report module");
 
         require(['../../../vector/submit-rageshake'], (s) => {
             s(SdkConfig.get().bug_report_endpoint_url, {
                 userText: userText,
                 sendLogs: sendLogs,
+                progressCallback: this._sendProgressCallback,
             }).then(() => {
-                this.setState({ busy: false });
+                this.setState({ busy: false, progress: null });
                 this.props.onFinished(false);
             }, (err) => {
-                this.setState({ busy: false, err: `Failed: ${err.message}` });
+                this.setState({
+                    busy: false, progress: null,
+                    err: `Failed to send report: ${err.message}`,
+                });
             });
         });
     }
@@ -69,6 +81,13 @@ export default class BugReportDialog extends React.Component {
         this.setState({ sendLogs: ev.target.checked });
     }
 
+    _sendProgressCallback(progress) {
+        if (this._unmounted) {
+            return;
+        }
+        this.setState({progress: progress});
+    }
+
     render() {
         const Loader = sdk.getComponent("elements.Spinner");
 
@@ -79,8 +98,6 @@ export default class BugReportDialog extends React.Component {
             </div>;
         }
 
-        const okLabel = this.state.busy ? <Loader /> : 'Send';
-
         let cancelButton = null;
         if (!this.state.busy) {
             cancelButton = <button onClick={this._onCancel}>
@@ -88,6 +105,16 @@ export default class BugReportDialog extends React.Component {
             </button>;
         }
 
+        let progress = null;
+        if (this.state.busy) {
+            progress = (
+                <div className="progress">
+                    <Loader />
+                    {this.state.progress} ...
+                </div>
+            );
+        }
+
         return (
             <div className="mx_BugReportDialog">
                 <div className="mx_Dialog_title">
@@ -110,6 +137,7 @@ export default class BugReportDialog extends React.Component {
                     <input type="checkbox" checked={this.state.sendLogs}
                         onChange={this._onSendLogsChange} id="mx_BugReportDialog_logs"/>
                     <label htmlFor="mx_BugReportDialog_logs">Send logs</label>
+                    {progress}
                     {error}
                 </div>
                 <div className="mx_Dialog_buttons">
@@ -117,8 +145,9 @@ export default class BugReportDialog extends React.Component {
                         className="mx_Dialog_primary danger"
                         onClick={this._onSubmit}
                         autoFocus={true}
+                        disabled={this.state.busy}
                     >
-                        {okLabel}
+                        Send
                     </button>
 
                     {cancelButton}
diff --git a/src/vector/submit-rageshake.js b/src/vector/submit-rageshake.js
index 7430e3be..6ed49a1f 100644
--- a/src/vector/submit-rageshake.js
+++ b/src/vector/submit-rageshake.js
@@ -30,10 +30,17 @@ if (!TextEncoder) {
 
 /**
  * Send a bug report.
+ *
  * @param {string} bugReportEndpoint HTTP url to send the report to
+ *
  * @param {object} opts optional dictionary of options
+ *
  * @param {string} opts.userText Any additional user input.
+ *
  * @param {boolean} opts.sendLogs True to send logs
+ *
+ * @param {function(string)} opts.progressCallback Callback to call with progress updates
+ *
  * @return {Promise} Resolved when the bug report is sent.
  */
 export default async function sendBugReport(bugReportEndpoint, opts) {
@@ -42,7 +49,9 @@ export default async function sendBugReport(bugReportEndpoint, opts) {
     }
 
     opts = opts || {};
+    const progressCallback = opts.progressCallback || (() => {});
 
+    progressCallback("Collecting app version information");
     let version = "UNKNOWN";
     try {
         version = await PlatformPeg.get().getAppVersion();
@@ -63,6 +72,7 @@ export default async function sendBugReport(bugReportEndpoint, opts) {
     body.append('user_agent', userAgent);
 
     if (opts.sendLogs) {
+        progressCallback("Collecting logs");
         const logs = await rageshake.getLogsForReport();
         for (let entry of logs) {
             // encode as UTF-8
@@ -72,17 +82,20 @@ export default async function sendBugReport(bugReportEndpoint, opts) {
         }
     }
 
-    await _submitReport(bugReportEndpoint, body);
+    progressCallback("Uploading report");
+    await _submitReport(bugReportEndpoint, body, progressCallback);
 }
 
-function _submitReport(endpoint, body) {
+function _submitReport(endpoint, body, progressCallback) {
     const deferred = q.defer();
 
     const req = new XMLHttpRequest();
     req.open("POST", endpoint);
     req.timeout = 5 * 60 * 1000;
     req.onreadystatechange = function() {
-        if (req.readyState === XMLHttpRequest.DONE) {
+        if (req.readyState === XMLHttpRequest.LOADING) {
+            progressCallback("Waiting for response from server");
+        } else if (req.readyState === XMLHttpRequest.DONE) {
             on_done();
         }
     };

From 25907301a3e4b13db87bc7421a88fd2933de7590 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Tue, 18 Apr 2017 12:53:15 +0100
Subject: [PATCH 3/3] More unmounted guards in BugReportDialog

---
 src/components/views/dialogs/BugReportDialog.js | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js
index 92b3c4b1..7a65ac58 100644
--- a/src/components/views/dialogs/BugReportDialog.js
+++ b/src/components/views/dialogs/BugReportDialog.js
@@ -62,13 +62,17 @@ export default class BugReportDialog extends React.Component {
                 sendLogs: sendLogs,
                 progressCallback: this._sendProgressCallback,
             }).then(() => {
-                this.setState({ busy: false, progress: null });
-                this.props.onFinished(false);
+                if (!this._unmounted) {
+                    this.setState({ busy: false, progress: null });
+                    this.props.onFinished(false);
+                }
             }, (err) => {
-                this.setState({
-                    busy: false, progress: null,
-                    err: `Failed to send report: ${err.message}`,
-                });
+                if (!this._unmounted) {
+                    this.setState({
+                        busy: false, progress: null,
+                        err: `Failed to send report: ${err.message}`,
+                    });
+                }
             });
         });
     }