move non-Riot-specific components over from riot-web
This commit is contained in:
parent
979452628f
commit
8ab8f76254
37 changed files with 6609 additions and 0 deletions
212
src/components/views/dialogs/BugReportDialog.js
Normal file
212
src/components/views/dialogs/BugReportDialog.js
Normal file
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
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 SdkConfig from 'matrix-react-sdk/lib/SdkConfig';
|
||||
import Modal from 'matrix-react-sdk/lib/Modal';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
|
||||
export default class BugReportDialog extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
sendLogs: true,
|
||||
busy: false,
|
||||
err: null,
|
||||
issueUrl: "",
|
||||
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._onIssueUrlChange = this._onIssueUrlChange.bind(this);
|
||||
this._onSendLogsChange = this._onSendLogsChange.bind(this);
|
||||
this._sendProgressCallback = this._sendProgressCallback.bind(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._unmounted = true;
|
||||
}
|
||||
|
||||
_onCancel(ev) {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
|
||||
_onSubmit(ev) {
|
||||
const userText =
|
||||
(this.state.text.length > 0 ? this.state.text + '\n\n': '') + 'Issue: ' +
|
||||
(this.state.issueUrl.length > 0 ? this.state.issueUrl : 'No issue link given');
|
||||
|
||||
this.setState({ busy: true, progress: null, err: null });
|
||||
this._sendProgressCallback(_t("Preparing to send logs"));
|
||||
|
||||
require(['../../../vector/submit-rageshake'], (s) => {
|
||||
s(SdkConfig.get().bug_report_endpoint_url, {
|
||||
userText,
|
||||
sendLogs: true,
|
||||
progressCallback: this._sendProgressCallback,
|
||||
}).then(() => {
|
||||
if (!this._unmounted) {
|
||||
this.props.onFinished(false);
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createTrackedDialog('Bug report sent', '', QuestionDialog, {
|
||||
title: _t('Logs sent'),
|
||||
description: _t('Thank you!'),
|
||||
hasCancelButton: false,
|
||||
});
|
||||
}
|
||||
}, (err) => {
|
||||
if (!this._unmounted) {
|
||||
this.setState({
|
||||
busy: false,
|
||||
progress: null,
|
||||
err: _t("Failed to send logs: ") + `${err.message}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_onTextChange(ev) {
|
||||
this.setState({ text: ev.target.value });
|
||||
}
|
||||
|
||||
_onIssueUrlChange(ev) {
|
||||
this.setState({ issueUrl: ev.target.value });
|
||||
}
|
||||
|
||||
_onSendLogsChange(ev) {
|
||||
this.setState({ sendLogs: ev.target.checked });
|
||||
}
|
||||
|
||||
_sendProgressCallback(progress) {
|
||||
if (this._unmounted) {
|
||||
return;
|
||||
}
|
||||
this.setState({progress: progress});
|
||||
}
|
||||
|
||||
render() {
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
|
||||
let error = null;
|
||||
if (this.state.err) {
|
||||
error = <div className="error">
|
||||
{this.state.err}
|
||||
</div>;
|
||||
}
|
||||
|
||||
let cancelButton = null;
|
||||
if (!this.state.busy) {
|
||||
cancelButton = <button onClick={this._onCancel}>
|
||||
{ _t("Cancel") }
|
||||
</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">
|
||||
{ _t("Submit debug logs") }
|
||||
</div>
|
||||
<div className="mx_Dialog_content">
|
||||
<p>
|
||||
{ _t(
|
||||
"Debug logs contain application usage data including your " +
|
||||
"username, the IDs or aliases of the rooms or groups you " +
|
||||
"have visited and the usernames of other users. They do " +
|
||||
"not contain messages.",
|
||||
) }
|
||||
</p>
|
||||
<p>
|
||||
{ _t(
|
||||
"<a>Click here</a> to create a GitHub issue.",
|
||||
{},
|
||||
{
|
||||
a: (sub) => <a
|
||||
target="_blank"
|
||||
href="https://github.com/vector-im/riot-web/issues/new"
|
||||
>
|
||||
{ sub }
|
||||
</a>,
|
||||
},
|
||||
) }
|
||||
</p>
|
||||
<div className="mx_BugReportDialog_field_container">
|
||||
<label
|
||||
htmlFor="mx_BugReportDialog_issueUrl"
|
||||
className="mx_BugReportDialog_field_label"
|
||||
>
|
||||
{ _t("GitHub issue link:") }
|
||||
</label>
|
||||
<input
|
||||
id="mx_BugReportDialog_issueUrl"
|
||||
type="text"
|
||||
className="mx_BugReportDialog_field_input"
|
||||
onChange={this._onIssueUrlChange}
|
||||
value={this.state.issueUrl}
|
||||
placeholder="https://github.com/vector-im/riot-web/issues/1337"
|
||||
/>
|
||||
</div>
|
||||
<div className="mx_BugReportDialog_field_container">
|
||||
<label
|
||||
htmlFor="mx_BugReportDialog_notes_label"
|
||||
className="mx_BugReportDialog_field_label"
|
||||
>
|
||||
{ _t("Notes:") }
|
||||
</label>
|
||||
<textarea
|
||||
className="mx_BugReportDialog_field_input"
|
||||
rows={5}
|
||||
onChange={this._onTextChange}
|
||||
value={this.state.text}
|
||||
/>
|
||||
</div>
|
||||
{progress}
|
||||
{error}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button
|
||||
className="mx_Dialog_primary danger"
|
||||
onClick={this._onSubmit}
|
||||
autoFocus={true}
|
||||
disabled={this.state.busy}
|
||||
>
|
||||
{ _t("Send logs") }
|
||||
</button>
|
||||
|
||||
{cancelButton}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
BugReportDialog.propTypes = {
|
||||
onFinished: React.PropTypes.func.isRequired,
|
||||
};
|
94
src/components/views/dialogs/ChangelogDialog.js
Normal file
94
src/components/views/dialogs/ChangelogDialog.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
Copyright 2016 Aviral Dasgupta
|
||||
|
||||
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 request from 'browser-request';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
|
||||
const REPOS = ['vector-im/riot-web', 'matrix-org/matrix-react-sdk', 'matrix-org/matrix-js-sdk'];
|
||||
|
||||
export default class ChangelogDialog extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const version = this.props.newVersion.split('-');
|
||||
const version2 = this.props.version.split('-');
|
||||
if(version == null || version2 == null) return;
|
||||
// parse versions of form: [vectorversion]-react-[react-sdk-version]-js-[js-sdk-version]
|
||||
for(let i=0; i<REPOS.length; i++) {
|
||||
const oldVersion = version2[2*i];
|
||||
const newVersion = version[2*i];
|
||||
request(`https://api.github.com/repos/${REPOS[i]}/compare/${oldVersion}...${newVersion}`, (a, b, body) => {
|
||||
if(body == null) return;
|
||||
this.setState({[REPOS[i]]: JSON.parse(body).commits});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_elementsForCommit(commit) {
|
||||
return (
|
||||
<li key={commit.sha} className="mx_ChangelogDialog_li">
|
||||
<a href={commit.html_url} target="_blank" rel="noopener">
|
||||
{commit.commit.message}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const Spinner = sdk.getComponent('views.elements.Spinner');
|
||||
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
|
||||
|
||||
const logs = REPOS.map(repo => {
|
||||
if (this.state[repo] == null) return <Spinner key={repo} />;
|
||||
return (
|
||||
<div key={repo}>
|
||||
<h2>{repo}</h2>
|
||||
<ul>
|
||||
{this.state[repo].map(this._elementsForCommit)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
|
||||
const content = (
|
||||
<div className="mx_ChangelogDialog_content">
|
||||
{this.props.version == null || this.props.newVersion == null ? <h2>{_t("Unavailable")}</h2> : logs}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<QuestionDialog
|
||||
title={_t("Changelog")}
|
||||
description={content}
|
||||
button={_t("Update")}
|
||||
onFinished={this.props.onFinished}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ChangelogDialog.propTypes = {
|
||||
version: React.PropTypes.string.isRequired,
|
||||
newVersion: React.PropTypes.string.isRequired,
|
||||
onFinished: React.PropTypes.func.isRequired,
|
||||
};
|
623
src/components/views/dialogs/DevtoolsDialog.js
Normal file
623
src/components/views/dialogs/DevtoolsDialog.js
Normal file
|
@ -0,0 +1,623 @@
|
|||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import SyntaxHighlight from '../elements/SyntaxHighlight';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||
|
||||
class DevtoolsComponent extends React.Component {
|
||||
static contextTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
class GenericEditor extends DevtoolsComponent {
|
||||
// static propTypes = {onBack: PropTypes.func.isRequired};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this._onChange = this._onChange.bind(this);
|
||||
this.onBack = this.onBack.bind(this);
|
||||
}
|
||||
|
||||
onBack() {
|
||||
if (this.state.message) {
|
||||
this.setState({ message: null });
|
||||
} else {
|
||||
this.props.onBack();
|
||||
}
|
||||
}
|
||||
|
||||
_onChange(e) {
|
||||
this.setState({[e.target.id]: e.target.type === 'checkbox' ? e.target.checked : e.target.value});
|
||||
}
|
||||
|
||||
_buttons() {
|
||||
return <div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
{ !this.state.message && <button onClick={this._send}>{ _t('Send') }</button> }
|
||||
</div>;
|
||||
}
|
||||
|
||||
textInput(id, label) {
|
||||
return <div className="mx_DevTools_inputRow">
|
||||
<div className="mx_DevTools_inputLabelCell">
|
||||
<label htmlFor={id}>{ label }</label>
|
||||
</div>
|
||||
<div className="mx_DevTools_inputCell">
|
||||
<input id={id} className="mx_TextInputDialog_input" onChange={this._onChange} value={this.state[id]} size="32" />
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class SendCustomEvent extends GenericEditor {
|
||||
static getLabel() { return _t('Send Custom Event'); }
|
||||
|
||||
static propTypes = {
|
||||
onBack: PropTypes.func.isRequired,
|
||||
forceStateEvent: PropTypes.bool,
|
||||
inputs: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this._send = this._send.bind(this);
|
||||
|
||||
const {eventType, stateKey, evContent} = Object.assign({
|
||||
eventType: '',
|
||||
stateKey: '',
|
||||
evContent: '{\n\n}',
|
||||
}, this.props.inputs);
|
||||
|
||||
this.state = {
|
||||
isStateEvent: Boolean(this.props.forceStateEvent),
|
||||
|
||||
eventType,
|
||||
stateKey,
|
||||
evContent,
|
||||
};
|
||||
}
|
||||
|
||||
send(content) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (this.state.isStateEvent) {
|
||||
return cli.sendStateEvent(this.context.roomId, this.state.eventType, content, this.state.stateKey);
|
||||
} else {
|
||||
return cli.sendEvent(this.context.roomId, this.state.eventType, content);
|
||||
}
|
||||
}
|
||||
|
||||
async _send() {
|
||||
if (this.state.eventType === '') {
|
||||
this.setState({ message: _t('You must specify an event type!') });
|
||||
return;
|
||||
}
|
||||
|
||||
let message;
|
||||
try {
|
||||
const content = JSON.parse(this.state.evContent);
|
||||
await this.send(content);
|
||||
message = _t('Event sent!');
|
||||
} catch (e) {
|
||||
message = _t('Failed to send custom event.') + ' (' + e.toString() + ')';
|
||||
}
|
||||
this.setState({ message });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.message) {
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
{ this.state.message }
|
||||
</div>
|
||||
{ this._buttons() }
|
||||
</div>;
|
||||
}
|
||||
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
{ this.textInput('eventType', _t('Event Type')) }
|
||||
{ this.state.isStateEvent && this.textInput('stateKey', _t('State Key')) }
|
||||
|
||||
<br />
|
||||
|
||||
<div className="mx_UserSettings_profileLabelCell">
|
||||
<label htmlFor="evContent"> { _t('Event Content') } </label>
|
||||
</div>
|
||||
<div>
|
||||
<textarea id="evContent" onChange={this._onChange} value={this.state.evContent} className="mx_TextInputDialog_input" cols="63" rows="5" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
{ !this.state.message && <button onClick={this._send}>{ _t('Send') }</button> }
|
||||
{ !this.state.message && !this.props.forceStateEvent && <div style={{float: "right"}}>
|
||||
<input id="isStateEvent" className="mx_DevTools_tgl mx_DevTools_tgl-flip" type="checkbox" onChange={this._onChange} checked={this.state.isStateEvent} />
|
||||
<label className="mx_DevTools_tgl-btn" data-tg-off="Event" data-tg-on="State Event" htmlFor="isStateEvent" />
|
||||
</div> }
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class SendAccountData extends GenericEditor {
|
||||
static getLabel() { return _t('Send Account Data'); }
|
||||
|
||||
static propTypes = {
|
||||
isRoomAccountData: PropTypes.bool,
|
||||
forceMode: PropTypes.bool,
|
||||
inputs: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this._send = this._send.bind(this);
|
||||
|
||||
const {eventType, evContent} = Object.assign({
|
||||
eventType: '',
|
||||
evContent: '{\n\n}',
|
||||
}, this.props.inputs);
|
||||
|
||||
this.state = {
|
||||
isRoomAccountData: Boolean(this.props.isRoomAccountData),
|
||||
|
||||
eventType,
|
||||
evContent,
|
||||
};
|
||||
}
|
||||
|
||||
send(content) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (this.state.isRoomAccountData) {
|
||||
return cli.setRoomAccountData(this.context.roomId, this.state.eventType, content);
|
||||
}
|
||||
return cli.setAccountData(this.state.eventType, content);
|
||||
}
|
||||
|
||||
async _send() {
|
||||
if (this.state.eventType === '') {
|
||||
this.setState({ message: _t('You must specify an event type!') });
|
||||
return;
|
||||
}
|
||||
|
||||
let message;
|
||||
try {
|
||||
const content = JSON.parse(this.state.evContent);
|
||||
await this.send(content);
|
||||
message = _t('Event sent!');
|
||||
} catch (e) {
|
||||
message = _t('Failed to send custom event.') + ' (' + e.toString() + ')';
|
||||
}
|
||||
this.setState({ message });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.message) {
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
{ this.state.message }
|
||||
</div>
|
||||
{ this._buttons() }
|
||||
</div>;
|
||||
}
|
||||
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
{ this.textInput('eventType', _t('Event Type')) }
|
||||
<br />
|
||||
|
||||
<div className="mx_UserSettings_profileLabelCell">
|
||||
<label htmlFor="evContent"> { _t('Event Content') } </label>
|
||||
</div>
|
||||
<div>
|
||||
<textarea id="evContent" onChange={this._onChange} value={this.state.evContent} className="mx_TextInputDialog_input" cols="63" rows="5" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
{ !this.state.message && <button onClick={this._send}>{ _t('Send') }</button> }
|
||||
{ !this.state.message && <div style={{float: "right"}}>
|
||||
<input id="isRoomAccountData" className="mx_DevTools_tgl mx_DevTools_tgl-flip" type="checkbox" onChange={this._onChange} checked={this.state.isRoomAccountData} disabled={this.props.forceMode} />
|
||||
<label className="mx_DevTools_tgl-btn" data-tg-off="Account Data" data-tg-on="Room Data" htmlFor="isRoomAccountData" />
|
||||
</div> }
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class FilteredList extends React.Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.any,
|
||||
query: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.onQuery = this.onQuery.bind(this);
|
||||
}
|
||||
|
||||
onQuery(ev) {
|
||||
if (this.props.onChange) this.props.onChange(ev.target.value);
|
||||
}
|
||||
|
||||
filterChildren() {
|
||||
if (this.props.query) {
|
||||
const lowerQuery = this.props.query.toLowerCase();
|
||||
return this.props.children.filter((child) => child.key.toLowerCase().includes(lowerQuery));
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<input size="64"
|
||||
onChange={this.onQuery}
|
||||
value={this.props.query}
|
||||
placeholder={_t('Filter results')}
|
||||
className="mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query" />
|
||||
{ this.filterChildren() }
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class RoomStateExplorer extends DevtoolsComponent {
|
||||
static getLabel() { return _t('Explore Room State'); }
|
||||
|
||||
|
||||
static propTypes = {
|
||||
onBack: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
const room = MatrixClientPeg.get().getRoom(this.context.roomId);
|
||||
this.roomStateEvents = room.currentState.events;
|
||||
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.editEv = this.editEv.bind(this);
|
||||
this.onQueryEventType = this.onQueryEventType.bind(this);
|
||||
this.onQueryStateKey = this.onQueryStateKey.bind(this);
|
||||
|
||||
this.state = {
|
||||
eventType: null,
|
||||
event: null,
|
||||
editing: false,
|
||||
|
||||
queryEventType: '',
|
||||
queryStateKey: '',
|
||||
};
|
||||
}
|
||||
|
||||
browseEventType(eventType) {
|
||||
return () => {
|
||||
this.setState({ eventType });
|
||||
};
|
||||
}
|
||||
|
||||
onViewSourceClick(event) {
|
||||
return () => {
|
||||
this.setState({ event });
|
||||
};
|
||||
}
|
||||
|
||||
onBack() {
|
||||
if (this.state.editing) {
|
||||
this.setState({ editing: false });
|
||||
} else if (this.state.event) {
|
||||
this.setState({ event: null });
|
||||
} else if (this.state.eventType) {
|
||||
this.setState({ eventType: null });
|
||||
} else {
|
||||
this.props.onBack();
|
||||
}
|
||||
}
|
||||
|
||||
editEv() {
|
||||
this.setState({ editing: true });
|
||||
}
|
||||
|
||||
onQueryEventType(filterEventType) {
|
||||
this.setState({ queryEventType: filterEventType });
|
||||
}
|
||||
|
||||
onQueryStateKey(filterStateKey) {
|
||||
this.setState({ queryStateKey: filterStateKey });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.event) {
|
||||
if (this.state.editing) {
|
||||
return <SendCustomEvent forceStateEvent={true} onBack={this.onBack} inputs={{
|
||||
eventType: this.state.event.getType(),
|
||||
evContent: JSON.stringify(this.state.event.getContent(), null, '\t'),
|
||||
stateKey: this.state.event.getStateKey(),
|
||||
}} />;
|
||||
}
|
||||
|
||||
return <div className="mx_ViewSource">
|
||||
<div className="mx_Dialog_content">
|
||||
<SyntaxHighlight className="json">
|
||||
{ JSON.stringify(this.state.event.event, null, 2) }
|
||||
</SyntaxHighlight>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
<button onClick={this.editEv}>{ _t('Edit') }</button>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
let list = null;
|
||||
|
||||
const classes = 'mx_DevTools_RoomStateExplorer_button';
|
||||
if (this.state.eventType === null) {
|
||||
list = <FilteredList query={this.state.queryEventType} onChange={this.onQueryEventType}>
|
||||
{
|
||||
Object.keys(this.roomStateEvents).map((evType) => {
|
||||
const stateGroup = this.roomStateEvents[evType];
|
||||
const stateKeys = Object.keys(stateGroup);
|
||||
|
||||
let onClickFn;
|
||||
if (stateKeys.length > 1) {
|
||||
onClickFn = this.browseEventType(evType);
|
||||
} else if (stateKeys.length === 1) {
|
||||
onClickFn = this.onViewSourceClick(stateGroup[stateKeys[0]]);
|
||||
}
|
||||
|
||||
return <button className={classes} key={evType} onClick={onClickFn}>
|
||||
{ evType }
|
||||
</button>;
|
||||
})
|
||||
}
|
||||
</FilteredList>;
|
||||
} else {
|
||||
const stateGroup = this.roomStateEvents[this.state.eventType];
|
||||
|
||||
list = <FilteredList query={this.state.queryStateKey} onChange={this.onQueryStateKey}>
|
||||
{
|
||||
Object.keys(stateGroup).map((stateKey) => {
|
||||
const ev = stateGroup[stateKey];
|
||||
return <button className={classes} key={stateKey} onClick={this.onViewSourceClick(ev)}>
|
||||
{ stateKey }
|
||||
</button>;
|
||||
})
|
||||
}
|
||||
</FilteredList>;
|
||||
}
|
||||
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
{ list }
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class AccountDataExplorer extends DevtoolsComponent {
|
||||
static getLabel() { return _t('Explore Account Data'); }
|
||||
|
||||
static propTypes = {
|
||||
onBack: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.editEv = this.editEv.bind(this);
|
||||
this._onChange = this._onChange.bind(this);
|
||||
this.onQueryEventType = this.onQueryEventType.bind(this);
|
||||
|
||||
this.state = {
|
||||
isRoomAccountData: false,
|
||||
event: null,
|
||||
editing: false,
|
||||
|
||||
queryEventType: '',
|
||||
};
|
||||
}
|
||||
|
||||
getData() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (this.state.isRoomAccountData) {
|
||||
return cli.getRoom(this.context.roomId).accountData;
|
||||
}
|
||||
return cli.store.accountData;
|
||||
}
|
||||
|
||||
onViewSourceClick(event) {
|
||||
return () => {
|
||||
this.setState({ event });
|
||||
};
|
||||
}
|
||||
|
||||
onBack() {
|
||||
if (this.state.editing) {
|
||||
this.setState({ editing: false });
|
||||
} else if (this.state.event) {
|
||||
this.setState({ event: null });
|
||||
} else {
|
||||
this.props.onBack();
|
||||
}
|
||||
}
|
||||
|
||||
_onChange(e) {
|
||||
this.setState({[e.target.id]: e.target.type === 'checkbox' ? e.target.checked : e.target.value});
|
||||
}
|
||||
|
||||
editEv() {
|
||||
this.setState({ editing: true });
|
||||
}
|
||||
|
||||
onQueryEventType(queryEventType) {
|
||||
this.setState({ queryEventType });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.event) {
|
||||
if (this.state.editing) {
|
||||
return <SendAccountData isRoomAccountData={this.state.isRoomAccountData} onBack={this.onBack} inputs={{
|
||||
eventType: this.state.event.getType(),
|
||||
evContent: JSON.stringify(this.state.event.getContent(), null, '\t'),
|
||||
}} forceMode={true} />;
|
||||
}
|
||||
|
||||
return <div className="mx_ViewSource">
|
||||
<div className="mx_Dialog_content">
|
||||
<SyntaxHighlight className="json">
|
||||
{ JSON.stringify(this.state.event.event, null, 2) }
|
||||
</SyntaxHighlight>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
<button onClick={this.editEv}>{ _t('Edit') }</button>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
|
||||
const classes = 'mx_DevTools_RoomStateExplorer_button';
|
||||
|
||||
const data = this.getData();
|
||||
Object.keys(data).forEach((evType) => {
|
||||
const ev = data[evType];
|
||||
rows.push(<button className={classes} key={evType} onClick={this.onViewSourceClick(ev)}>
|
||||
{ evType }
|
||||
</button>);
|
||||
});
|
||||
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
<FilteredList query={this.state.queryEventType} onChange={this.onQueryEventType}>
|
||||
{ rows }
|
||||
</FilteredList>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
{ !this.state.message && <div style={{float: "right"}}>
|
||||
<input id="isRoomAccountData" className="mx_DevTools_tgl mx_DevTools_tgl-flip" type="checkbox" onChange={this._onChange} checked={this.state.isRoomAccountData} />
|
||||
<label className="mx_DevTools_tgl-btn" data-tg-off="Account Data" data-tg-on="Room Data" htmlFor="isRoomAccountData" />
|
||||
</div> }
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const Entries = [
|
||||
SendCustomEvent,
|
||||
RoomStateExplorer,
|
||||
SendAccountData,
|
||||
AccountDataExplorer,
|
||||
];
|
||||
|
||||
export default class DevtoolsDialog extends React.Component {
|
||||
static childContextTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
// client: PropTypes.instanceOf(MatixClient),
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.onCancel = this.onCancel.bind(this);
|
||||
|
||||
this.state = {
|
||||
mode: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._unmounted = true;
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
return { roomId: this.props.roomId };
|
||||
}
|
||||
|
||||
_setMode(mode) {
|
||||
return () => {
|
||||
this.setState({ mode });
|
||||
};
|
||||
}
|
||||
|
||||
onBack() {
|
||||
if (this.prevMode) {
|
||||
this.setState({ mode: this.prevMode });
|
||||
this.prevMode = null;
|
||||
} else {
|
||||
this.setState({ mode: null });
|
||||
}
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
|
||||
render() {
|
||||
let body;
|
||||
|
||||
if (this.state.mode) {
|
||||
body = <div>
|
||||
<div className="mx_DevTools_label_left">{ this.state.mode.getLabel() }</div>
|
||||
<div className="mx_DevTools_label_right">Room ID: { this.props.roomId }</div>
|
||||
<div className="mx_DevTools_label_bottom" />
|
||||
<this.state.mode onBack={this.onBack} />
|
||||
</div>;
|
||||
} else {
|
||||
const classes = "mx_DevTools_RoomStateExplorer_button";
|
||||
body = <div>
|
||||
<div>
|
||||
<div className="mx_DevTools_label_left">{ _t('Toolbox') }</div>
|
||||
<div className="mx_DevTools_label_right">Room ID: { this.props.roomId }</div>
|
||||
<div className="mx_DevTools_label_bottom" />
|
||||
|
||||
<div className="mx_Dialog_content">
|
||||
{ Entries.map((Entry) => {
|
||||
const label = Entry.getLabel();
|
||||
const onClick = this._setMode(Entry);
|
||||
return <button className={classes} key={label} onClick={onClick}>{ label }</button>;
|
||||
}) }
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onCancel}>{ _t('Cancel') }</button>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
return (
|
||||
<BaseDialog className="mx_QuestionDialog" onFinished={this.props.onFinished} title={_t('Developer Tools')}>
|
||||
{ body }
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
}
|
137
src/components/views/dialogs/SetPasswordDialog.js
Normal file
137
src/components/views/dialogs/SetPasswordDialog.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations 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 { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import Modal from 'matrix-react-sdk/lib/Modal';
|
||||
|
||||
const WarmFuzzy = function(props) {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
let title = _t('You have successfully set a password!');
|
||||
if (props.didSetEmail) {
|
||||
title = _t('You have successfully set a password and an email address!');
|
||||
}
|
||||
const advice = _t('You can now return to your account after signing out, and sign in on other devices.');
|
||||
let extraAdvice = null;
|
||||
if (!props.didSetEmail) {
|
||||
extraAdvice = _t('Remember, you can always set an email address in user settings if you change your mind.');
|
||||
}
|
||||
|
||||
return <BaseDialog className="mx_SetPasswordDialog"
|
||||
onFinished={props.onFinished}
|
||||
title={ title }
|
||||
>
|
||||
<div className="mx_Dialog_content">
|
||||
<p>
|
||||
{ advice }
|
||||
</p>
|
||||
<p>
|
||||
{ extraAdvice }
|
||||
</p>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button
|
||||
className="mx_Dialog_primary"
|
||||
autoFocus={true}
|
||||
onClick={props.onFinished}>
|
||||
{ _t('Continue') }
|
||||
</button>
|
||||
</div>
|
||||
</BaseDialog>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prompt the user to set a password
|
||||
*
|
||||
* On success, `onFinished()` when finished
|
||||
*/
|
||||
export default React.createClass({
|
||||
displayName: 'SetPasswordDialog',
|
||||
propTypes: {
|
||||
onFinished: React.PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
error: null,
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
console.info('SetPasswordDialog component will mount');
|
||||
},
|
||||
|
||||
_onPasswordChanged: function(res) {
|
||||
Modal.createDialog(WarmFuzzy, {
|
||||
didSetEmail: res.didSetEmail,
|
||||
onFinished: () => {
|
||||
this._onContinueClicked();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
_onContinueClicked: function() {
|
||||
this.props.onFinished(true);
|
||||
},
|
||||
|
||||
_onPasswordChangeError: function(err) {
|
||||
let errMsg = err.error || "";
|
||||
if (err.httpStatus === 403) {
|
||||
errMsg = _t('Failed to change password. Is your password correct?');
|
||||
} else if (err.httpStatus) {
|
||||
errMsg += ' ' + _t(
|
||||
'(HTTP status %(httpStatus)s)',
|
||||
{ httpStatus: err.httpStatus },
|
||||
);
|
||||
}
|
||||
this.setState({
|
||||
error: errMsg,
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const ChangePassword = sdk.getComponent('views.settings.ChangePassword');
|
||||
|
||||
return (
|
||||
<BaseDialog className="mx_SetPasswordDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title={ _t('Please set a password!') }
|
||||
>
|
||||
<div className="mx_Dialog_content">
|
||||
<p>
|
||||
{ _t('This will allow you to return to your account after signing out, and sign in on other devices.') }
|
||||
</p>
|
||||
<ChangePassword
|
||||
className="mx_SetPasswordDialog_change_password"
|
||||
rowClassName=""
|
||||
rowLabelClassName=""
|
||||
rowInputClassName=""
|
||||
buttonClassName="mx_Dialog_primary mx_SetPasswordDialog_change_password_button"
|
||||
confirm={false}
|
||||
autoFocusNewPasswordInput={true}
|
||||
shouldAskForEmail={true}
|
||||
onError={this._onPasswordChangeError}
|
||||
onFinished={this._onPasswordChanged} />
|
||||
<div className="error">
|
||||
{ this.state.error }
|
||||
</div>
|
||||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
},
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue