diff --git a/src/SdkConfig.js b/src/SdkConfig.js index 1452aaa64b..8d8e93a889 100644 --- a/src/SdkConfig.js +++ b/src/SdkConfig.js @@ -19,6 +19,8 @@ var DEFAULTS = { integrations_ui_url: "https://scalar.vector.im/", // Base URL to the REST interface of the integrations server integrations_rest_url: "https://scalar.vector.im/api", + // Where to send bug reports. If not specified, bugs cannot be sent. + bug_report_endpoint_url: null, }; class SdkConfig { diff --git a/src/component-index.js b/src/component-index.js index c50ee0dfc8..d3a61516d3 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -71,6 +71,8 @@ import views$create_room$Presets from './components/views/create_room/Presets'; views$create_room$Presets && (module.exports.components['views.create_room.Presets'] = views$create_room$Presets); import views$create_room$RoomAlias from './components/views/create_room/RoomAlias'; views$create_room$RoomAlias && (module.exports.components['views.create_room.RoomAlias'] = views$create_room$RoomAlias); +import views$dialogs$BugReportDialog from './components/views/dialogs/BugReportDialog'; +views$dialogs$BugReportDialog && (module.exports.components['views.dialogs.BugReportDialog'] = views$dialogs$BugReportDialog); import views$dialogs$BaseDialog from './components/views/dialogs/BaseDialog'; views$dialogs$BaseDialog && (module.exports.components['views.dialogs.BaseDialog'] = views$dialogs$BaseDialog); import views$dialogs$ChatInviteDialog from './components/views/dialogs/ChatInviteDialog'; diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 1e060ae7ff..99f3d9cd3c 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -26,6 +26,7 @@ var UserSettingsStore = require('../../UserSettingsStore'); var GeminiScrollbar = require('react-gemini-scrollbar'); var Email = require('../../email'); var AddThreepid = require('../../AddThreepid'); +var SdkConfig = require('../../SdkConfig'); import AccessibleButton from '../views/elements/AccessibleButton'; // if this looks like a release, use the 'version' from package.json; else use @@ -388,6 +389,11 @@ module.exports = React.createClass({ Modal.createDialog(DeactivateAccountDialog, {}); }, + _onBugReportClicked: function() { + const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog"); + Modal.createDialog(BugReportDialog, {}); + }, + _onInviteStateChange: function(event, member, oldMembership) { if (member.userId === this._me && oldMembership === "invite") { this.forceUpdate(); @@ -547,6 +553,23 @@ module.exports = React.createClass({ ); }, + _renderBugReport: function() { + if (!SdkConfig.get().bug_report_endpoint_url) { + return
+ } + return ( +
+

Bug Report

+
+

Found a bug?

+ +
+
+ ); + }, + _renderLabs: function() { // default to enabled if undefined if (this.props.enableLabs === false) return null; @@ -800,6 +823,7 @@ module.exports = React.createClass({ {this._renderDevicesPanel()} {this._renderCryptoInfo()} {this._renderBulkOptions()} + {this._renderBugReport()}

Advanced

diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js new file mode 100644 index 0000000000..e8a8b2b869 --- /dev/null +++ b/src/components/views/dialogs/BugReportDialog.js @@ -0,0 +1,124 @@ +/* +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 '../../../index'; + +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; + } + // TODO: Make the HTTP hit + this.setState({ busy: true, err: null }); + setTimeout(() => { + this.setState({ busy: false, err: "No bug report endpoint." }); + }, 1000); + } + + _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 =
+ {this.state.err} +
; + } + + const okLabel = this.state.busy ? : 'Send'; + + let cancelButton = null; + if (!this.state.busy) { + cancelButton = ; + } + + return ( +
+
+ Report a bug +
+
+

Please describe the bug. What did you do? + What did you expect to happen? + What actually happened?

+