mirror of
https://github.com/element-hq/element-web
synced 2024-11-27 11:47:23 +03:00
Merge pull request #5471 from vector-im/t3chguy/devtools-1
T3chguy/devtools 1
This commit is contained in:
commit
c0b343c89f
3 changed files with 509 additions and 116 deletions
|
@ -15,35 +15,24 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||
|
||||
class SendCustomEvent extends React.Component {
|
||||
static propTypes = {
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onBack: React.PropTypes.func.isRequired,
|
||||
|
||||
eventType: React.PropTypes.string.isRequired,
|
||||
evContent: React.PropTypes.string.isRequired,
|
||||
class DevtoolsComponent extends React.Component {
|
||||
static contextTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
eventType: '',
|
||||
evContent: '{\n\n}',
|
||||
};
|
||||
class GenericEditor extends DevtoolsComponent {
|
||||
// static propTypes = {onBack: PropTypes.func.isRequired};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this._send = this._send.bind(this);
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this._onChange = this._onChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
message: null,
|
||||
input_eventType: this.props.eventType,
|
||||
input_evContent: this.props.evContent,
|
||||
};
|
||||
this.onBack = this.onBack.bind(this);
|
||||
}
|
||||
|
||||
onBack() {
|
||||
|
@ -54,6 +43,10 @@ class SendCustomEvent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
_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>
|
||||
|
@ -61,19 +54,64 @@ class SendCustomEvent extends React.Component {
|
|||
</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} 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) {
|
||||
return MatrixClientPeg.get().sendEvent(this.props.roomId, this.state.input_eventType, 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.input_eventType === '') {
|
||||
if (this.state.eventType === '') {
|
||||
this.setState({ message: _t('You must specify an event type!') });
|
||||
return;
|
||||
}
|
||||
|
||||
let message;
|
||||
try {
|
||||
const content = JSON.parse(this.state.input_evContent);
|
||||
const content = JSON.parse(this.state.evContent);
|
||||
await this.send(content);
|
||||
message = _t('Event sent!');
|
||||
} catch (e) {
|
||||
|
@ -82,14 +120,6 @@ class SendCustomEvent extends React.Component {
|
|||
this.setState({ message });
|
||||
}
|
||||
|
||||
_additionalFields() {
|
||||
return <div />;
|
||||
}
|
||||
|
||||
_onChange(e) {
|
||||
this.setState({[`input_${e.target.id}`]: e.target.value});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.message) {
|
||||
return <div>
|
||||
|
@ -102,87 +132,176 @@ class SendCustomEvent extends React.Component {
|
|||
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
{ this._additionalFields() }
|
||||
<div className="mx_TextInputDialog_label">
|
||||
<label htmlFor="eventType"> { _t('Event Type') } </label>
|
||||
</div>
|
||||
<div>
|
||||
<input id="eventType" onChange={this._onChange} value={this.state.input_eventType} className="mx_TextInputDialog_input" size="64" />
|
||||
</div>
|
||||
{ this.textInput('eventType', _t('Event Type')) }
|
||||
{ this.state.isStateEvent && this.textInput('stateKey', _t('State Key')) }
|
||||
|
||||
<div className="mx_TextInputDialog_label">
|
||||
<br />
|
||||
|
||||
<div className="mx_UserSettings_profileLabelCell">
|
||||
<label htmlFor="evContent"> { _t('Event Content') } </label>
|
||||
</div>
|
||||
<div>
|
||||
<textarea id="evContent" onChange={this._onChange} value={this.state.input_evContent} className="mx_TextInputDialog_input" cols="63" rows="5" />
|
||||
<textarea id="evContent" onChange={this._onChange} value={this.state.evContent} className="mx_TextInputDialog_input" cols="63" rows="5" />
|
||||
</div>
|
||||
</div>
|
||||
{ this._buttons() }
|
||||
<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 SendCustomStateEvent extends SendCustomEvent {
|
||||
class SendAccountData extends GenericEditor {
|
||||
static getLabel() { return _t('Send Account Data'); }
|
||||
|
||||
static propTypes = {
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onBack: React.PropTypes.func.isRequired,
|
||||
|
||||
eventType: React.PropTypes.string.isRequired,
|
||||
evContent: React.PropTypes.string.isRequired,
|
||||
stateKey: React.PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
eventType: '',
|
||||
evContent: '{\n\n}',
|
||||
stateKey: '',
|
||||
isRoomAccountData: PropTypes.bool,
|
||||
forceMode: PropTypes.bool,
|
||||
inputs: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state['input_stateKey'] = this.props.stateKey;
|
||||
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();
|
||||
return cli.sendStateEvent(this.props.roomId, this.state.input_eventType, content, this.state.input_stateKey);
|
||||
if (this.state.isRoomAccountData) {
|
||||
return cli.setRoomAccountData(this.context.roomId, this.state.eventType, content);
|
||||
}
|
||||
return cli.setAccountData(this.state.eventType, content);
|
||||
}
|
||||
|
||||
_additionalFields() {
|
||||
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_TextInputDialog_label">
|
||||
<label htmlFor="stateKey"> { _t('State Key') } </label>
|
||||
<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>
|
||||
<input id="stateKey" onChange={this._onChange} value={this.state.input_stateKey} className="mx_TextInputDialog_input" size="64" />
|
||||
<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 RoomStateExplorer extends React.Component {
|
||||
class FilteredList extends React.Component {
|
||||
static propTypes = {
|
||||
setMode: React.PropTypes.func.isRequired,
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onBack: React.PropTypes.func.isRequired,
|
||||
children: PropTypes.any,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.onQuery = this.onQuery.bind(this);
|
||||
|
||||
this.state = {
|
||||
query: '',
|
||||
};
|
||||
}
|
||||
|
||||
onQuery(ev) {
|
||||
this.setState({ query: ev.target.value });
|
||||
}
|
||||
|
||||
filterChildren() {
|
||||
if (this.state.query) {
|
||||
const lowerQuery = this.state.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.state.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.props.roomId);
|
||||
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.onQuery = this.onQuery.bind(this);
|
||||
}
|
||||
|
||||
state = {
|
||||
query: '',
|
||||
eventType: null,
|
||||
event: null,
|
||||
};
|
||||
this.state = {
|
||||
eventType: null,
|
||||
event: null,
|
||||
editing: false,
|
||||
};
|
||||
}
|
||||
|
||||
browseEventType(eventType) {
|
||||
return () => {
|
||||
|
@ -197,7 +316,9 @@ class RoomStateExplorer extends React.Component {
|
|||
}
|
||||
|
||||
onBack() {
|
||||
if (this.state.event) {
|
||||
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 });
|
||||
|
@ -207,20 +328,19 @@ class RoomStateExplorer extends React.Component {
|
|||
}
|
||||
|
||||
editEv() {
|
||||
const ev = this.state.event;
|
||||
this.props.setMode(SendCustomStateEvent, {
|
||||
eventType: ev.getType(),
|
||||
evContent: JSON.stringify(ev.getContent(), null, '\t'),
|
||||
stateKey: ev.getStateKey(),
|
||||
});
|
||||
}
|
||||
|
||||
onQuery(ev) {
|
||||
this.setState({ query: ev.target.value });
|
||||
this.setState({ editing: true });
|
||||
}
|
||||
|
||||
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">
|
||||
<pre>{ JSON.stringify(this.state.event.event, null, 2) }</pre>
|
||||
|
@ -234,11 +354,9 @@ class RoomStateExplorer extends React.Component {
|
|||
|
||||
const rows = [];
|
||||
|
||||
const classes = 'mx_DevTools_RoomStateExplorer_button';
|
||||
if (this.state.eventType === null) {
|
||||
Object.keys(this.roomStateEvents).forEach((evType) => {
|
||||
// Skip this entry if does not contain search query
|
||||
if (this.state.query && !evType.toLowerCase().includes(this.state.query.toLowerCase())) return;
|
||||
|
||||
const stateGroup = this.roomStateEvents[evType];
|
||||
const stateKeys = Object.keys(stateGroup);
|
||||
|
||||
|
@ -249,7 +367,7 @@ class RoomStateExplorer extends React.Component {
|
|||
onClickFn = this.onViewSourceClick(stateGroup[stateKeys[0]]);
|
||||
}
|
||||
|
||||
rows.push(<button className="mx_DevTools_RoomStateExplorer_button" key={evType} onClick={onClickFn}>
|
||||
rows.push(<button className={classes} key={evType} onClick={onClickFn}>
|
||||
{ evType }
|
||||
</button>);
|
||||
});
|
||||
|
@ -257,12 +375,8 @@ class RoomStateExplorer extends React.Component {
|
|||
const evType = this.state.eventType;
|
||||
const stateGroup = this.roomStateEvents[evType];
|
||||
Object.keys(stateGroup).forEach((stateKey) => {
|
||||
// Skip this entry if does not contain search query
|
||||
if (this.state.query && !stateKey.toLowerCase().includes(this.state.query.toLowerCase())) return;
|
||||
|
||||
const ev = stateGroup[stateKey];
|
||||
rows.push(<button className="mx_DevTools_RoomStateExplorer_button" key={stateKey}
|
||||
onClick={this.onViewSourceClick(ev)}>
|
||||
rows.push(<button className={classes} key={stateKey} onClick={this.onViewSourceClick(ev)}>
|
||||
{ stateKey }
|
||||
</button>);
|
||||
});
|
||||
|
@ -270,8 +384,9 @@ class RoomStateExplorer extends React.Component {
|
|||
|
||||
return <div>
|
||||
<div className="mx_Dialog_content">
|
||||
<input onChange={this.onQuery} placeholder={_t('Filter results')} size="64" className="mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query" value={this.state.query} />
|
||||
{ rows }
|
||||
<FilteredList>
|
||||
{ rows }
|
||||
</FilteredList>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onBack}>{ _t('Back') }</button>
|
||||
|
@ -280,40 +395,157 @@ class RoomStateExplorer extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default class DevtoolsDialog extends React.Component {
|
||||
class AccountDataExplorer extends DevtoolsComponent {
|
||||
static getLabel() { return _t('Explore Account Data'); }
|
||||
|
||||
static propTypes = {
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
onFinished: React.PropTypes.func.isRequired,
|
||||
onBack: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
mode: null,
|
||||
modeArgs: {},
|
||||
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.state = {
|
||||
isRoomAccountData: false,
|
||||
event: null,
|
||||
editing: false,
|
||||
};
|
||||
}
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
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">
|
||||
<pre>{ JSON.stringify(this.state.event.event, null, 2) }</pre>
|
||||
</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>
|
||||
{ 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.setMode = this.setMode.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.setMode(mode);
|
||||
this.setState({ mode });
|
||||
};
|
||||
}
|
||||
|
||||
setMode(mode, modeArgs={}) {
|
||||
this.setState({ mode, modeArgs });
|
||||
}
|
||||
|
||||
onBack() {
|
||||
this.setState({ mode: null });
|
||||
if (this.prevMode) {
|
||||
this.setState({ mode: this.prevMode });
|
||||
this.prevMode = null;
|
||||
} else {
|
||||
this.setState({ mode: null });
|
||||
}
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
|
@ -324,14 +556,27 @@ export default class DevtoolsDialog extends React.Component {
|
|||
let body;
|
||||
|
||||
if (this.state.mode) {
|
||||
body =
|
||||
<this.state.mode {...this.props} {...this.state.modeArgs} onBack={this.onBack} setMode={this.setMode} />;
|
||||
} else {
|
||||
body = <div>
|
||||
<div className="mx_Dialog_content">
|
||||
<button onClick={this._setMode(SendCustomEvent)}>{ _t('Send Custom Event') }</button>
|
||||
<button onClick={this._setMode(SendCustomStateEvent)}>{ _t('Send Custom State Event') }</button>
|
||||
<button onClick={this._setMode(RoomStateExplorer)}>{ _t('Explore Room State') }</button>
|
||||
<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>
|
||||
|
@ -342,7 +587,6 @@ export default class DevtoolsDialog extends React.Component {
|
|||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
return (
|
||||
<BaseDialog className="mx_QuestionDialog" onFinished={this.props.onFinished} title={_t('Developer Tools')}>
|
||||
<div>Room ID: { this.props.roomId }</div>
|
||||
{ body }
|
||||
</BaseDialog>
|
||||
);
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
"What's new?": "What's new?",
|
||||
"A new version of Riot is available.": "A new version of Riot is available.",
|
||||
"To return to your account in future you need to <u>set a password</u>": "To return to your account in future you need to <u>set a password</u>",
|
||||
"Toolbox": "Toolbox",
|
||||
"Set Password": "Set Password",
|
||||
"Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).",
|
||||
"Checking for an update...": "Checking for an update...",
|
||||
|
@ -106,7 +107,8 @@
|
|||
"Edit": "Edit",
|
||||
"Filter results": "Filter results",
|
||||
"Send Custom Event": "Send Custom Event",
|
||||
"Send Custom State Event": "Send Custom State Event",
|
||||
"Send Account Data": "Send Account Data",
|
||||
"Explore Account Data": "Explore Account Data",
|
||||
"Explore Room State": "Explore Room State",
|
||||
"Developer Tools": "Developer Tools",
|
||||
"You have successfully set a password!": "You have successfully set a password!",
|
||||
|
|
|
@ -17,3 +17,150 @@ limitations under the License.
|
|||
.mx_DevTools_RoomStateExplorer_button, .mx_DevTools_RoomStateExplorer_query {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.mx_DevTools_label_left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.mx_DevTools_label_right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mx_DevTools_label_bottom {
|
||||
clear: both;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.mx_DevTools_inputRow
|
||||
{
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.mx_DevTools_inputLabelCell
|
||||
{
|
||||
padding-bottom: 21px;
|
||||
display: table-cell;
|
||||
font-weight: bold;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.mx_DevTools_inputCell {
|
||||
display: table-cell;
|
||||
padding-bottom: 21px;
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.mx_DevTools_inputCell input
|
||||
{
|
||||
display: inline-block;
|
||||
border: 0;
|
||||
border-bottom: 1px solid $input-underline-color;
|
||||
padding: 0;
|
||||
width: 240px;
|
||||
color: $input-fg-color;
|
||||
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.mx_DevTools_tgl {
|
||||
display: none;
|
||||
|
||||
// add default box-sizing for this scope
|
||||
&,
|
||||
&:after,
|
||||
&:before,
|
||||
& *,
|
||||
& *:after,
|
||||
& *:before,
|
||||
& + .mx_DevTools_tgl-btn {
|
||||
box-sizing: border-box;
|
||||
&::selection {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
+ .mx_DevTools_tgl-btn {
|
||||
outline: 0;
|
||||
display: block;
|
||||
width: 7em;
|
||||
height: 2em;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
&:after,
|
||||
&:before {
|
||||
position: relative;
|
||||
display: block;
|
||||
content: "";
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&:after {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:checked + .mx_DevTools_tgl-btn:after {
|
||||
left: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_DevTools_tgl-flip {
|
||||
+ .mx_DevTools_tgl-btn {
|
||||
padding: 2px;
|
||||
transition: all .2s ease;
|
||||
font-family: sans-serif;
|
||||
perspective: 100px;
|
||||
&:after,
|
||||
&:before {
|
||||
display: inline-block;
|
||||
transition: all .4s ease;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
line-height: 2em;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
top: 0;
|
||||
left: 0;
|
||||
backface-visibility: hidden;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: attr(data-tg-on);
|
||||
background: #02C66F;
|
||||
transform: rotateY(-180deg);
|
||||
}
|
||||
|
||||
&:before {
|
||||
background: #FF3A19;
|
||||
content: attr(data-tg-off);
|
||||
}
|
||||
|
||||
&:active:before {
|
||||
transform: rotateY(-20deg);
|
||||
}
|
||||
}
|
||||
|
||||
&:checked + .mx_DevTools_tgl-btn {
|
||||
&:before {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
|
||||
&:after {
|
||||
transform: rotateY(0);
|
||||
left: 0;
|
||||
background: #7FC6A6;
|
||||
}
|
||||
|
||||
&:active:after {
|
||||
transform: rotateY(20deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue