Merge branch 'develop' into hs/bridge-info
This commit is contained in:
commit
0a8cc416bf
203 changed files with 5331 additions and 2496 deletions
|
@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, {createRef} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
|
||||
|
@ -32,6 +32,7 @@ import IdentityAuthClient from '../../../IdentityAuthClient';
|
|||
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from '../../../utils/IdentityServerUtils';
|
||||
import { abbreviateUrl } from '../../../utils/UrlUtils';
|
||||
import {sleep} from "../../../utils/promise";
|
||||
import {Key} from "../../../Keyboard";
|
||||
|
||||
const TRUNCATE_QUERY_LIST = 40;
|
||||
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
|
||||
|
@ -106,10 +107,14 @@ module.exports = createReactClass({
|
|||
};
|
||||
},
|
||||
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._textinput = createRef();
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
if (this.props.focus) {
|
||||
// Set the cursor at the end of the text input
|
||||
this.refs.textinput.value = this.props.value;
|
||||
this._textinput.current.value = this.props.value;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -126,8 +131,8 @@ module.exports = createReactClass({
|
|||
let selectedList = this.state.selectedList.slice();
|
||||
// Check the text input field to see if user has an unconverted address
|
||||
// If there is and it's valid add it to the local selectedList
|
||||
if (this.refs.textinput.value !== '') {
|
||||
selectedList = this._addAddressesToList([this.refs.textinput.value]);
|
||||
if (this._textinput.current.value !== '') {
|
||||
selectedList = this._addAddressesToList([this._textinput.current.value]);
|
||||
if (selectedList === null) return;
|
||||
}
|
||||
this.props.onFinished(true, selectedList);
|
||||
|
@ -138,39 +143,41 @@ module.exports = createReactClass({
|
|||
},
|
||||
|
||||
onKeyDown: function(e) {
|
||||
if (e.keyCode === 27) { // escape
|
||||
const textInput = this._textinput.current ? this._textinput.current.value : undefined;
|
||||
|
||||
if (e.key === Key.ESCAPE) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.props.onFinished(false);
|
||||
} else if (e.keyCode === 38) { // up arrow
|
||||
} else if (e.key === Key.ARROW_UP) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (this.addressSelector) this.addressSelector.moveSelectionUp();
|
||||
} else if (e.keyCode === 40) { // down arrow
|
||||
} else if (e.key === Key.ARROW_DOWN) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (this.addressSelector) this.addressSelector.moveSelectionDown();
|
||||
} else if (this.state.suggestedList.length > 0 && (e.keyCode === 188 || e.keyCode === 13 || e.keyCode === 9)) { // comma or enter or tab
|
||||
} else if (this.state.suggestedList.length > 0 && [Key.COMMA, Key.ENTER, Key.TAB].includes(e.key)) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (this.addressSelector) this.addressSelector.chooseSelection();
|
||||
} else if (this.refs.textinput.value.length === 0 && this.state.selectedList.length && e.keyCode === 8) { // backspace
|
||||
} else if (textInput.length === 0 && this.state.selectedList.length && e.key === Key.BACKSPACE) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.onDismissed(this.state.selectedList.length - 1)();
|
||||
} else if (e.keyCode === 13) { // enter
|
||||
} else if (e.key === Key.ENTER) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (this.refs.textinput.value === '') {
|
||||
if (textInput === '') {
|
||||
// if there's nothing in the input box, submit the form
|
||||
this.onButtonClick();
|
||||
} else {
|
||||
this._addAddressesToList([this.refs.textinput.value]);
|
||||
this._addAddressesToList([textInput]);
|
||||
}
|
||||
} else if (e.keyCode === 188 || e.keyCode === 9) { // comma or tab
|
||||
} else if (textInput && (e.key === Key.COMMA || e.key === Key.TAB)) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this._addAddressesToList([this.refs.textinput.value]);
|
||||
this._addAddressesToList([textInput]);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -647,7 +654,7 @@ module.exports = createReactClass({
|
|||
onPaste={this._onPaste}
|
||||
rows="1"
|
||||
id="textinput"
|
||||
ref="textinput"
|
||||
ref={this._textinput}
|
||||
className="mx_AddressPickerDialog_input"
|
||||
onChange={this.onQueryChanged}
|
||||
placeholder={this.getPlaceholder()}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018, 2019 New Vector Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -17,16 +18,15 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import FocusLock from 'react-focus-lock';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
|
||||
import { KeyCode } from '../../../Keyboard';
|
||||
import { Key } from '../../../Keyboard';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import { _t } from "../../../languageHandler";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
|
||||
/**
|
||||
* Basic container for modal dialogs.
|
||||
|
@ -83,16 +83,6 @@ export default createReactClass({
|
|||
};
|
||||
},
|
||||
|
||||
childContextTypes: {
|
||||
matrixClient: PropTypes.instanceOf(MatrixClient),
|
||||
},
|
||||
|
||||
getChildContext: function() {
|
||||
return {
|
||||
matrixClient: this._matrixClient,
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount() {
|
||||
this._matrixClient = MatrixClientPeg.get();
|
||||
},
|
||||
|
@ -101,7 +91,7 @@ export default createReactClass({
|
|||
if (this.props.onKeyDown) {
|
||||
this.props.onKeyDown(e);
|
||||
}
|
||||
if (this.props.hasCancel && e.keyCode === KeyCode.ESCAPE) {
|
||||
if (this.props.hasCancel && e.key === Key.ESCAPE) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.props.onFinished(false);
|
||||
|
@ -121,32 +111,38 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
return (
|
||||
<FocusTrap onKeyDown={this._onKeyDown}
|
||||
className={classNames({
|
||||
[this.props.className]: true,
|
||||
'mx_Dialog_fixedWidth': this.props.fixedWidth,
|
||||
})}
|
||||
role="dialog"
|
||||
aria-labelledby='mx_BaseDialog_title'
|
||||
// This should point to a node describing the dialog.
|
||||
// If we were about to completely follow this recommendation we'd need to
|
||||
// make all the components relying on BaseDialog to be aware of it.
|
||||
// So instead we will use the whole content as the description.
|
||||
// Description comes first and if the content contains more text,
|
||||
// AT users can skip its presentation.
|
||||
aria-describedby={this.props.contentId}
|
||||
>
|
||||
<div className={classNames('mx_Dialog_header', {
|
||||
'mx_Dialog_headerWithButton': !!this.props.headerButton,
|
||||
})}>
|
||||
<div className={classNames('mx_Dialog_title', this.props.titleClass)} id='mx_BaseDialog_title'>
|
||||
{ this.props.title }
|
||||
<MatrixClientContext.Provider value={this._matrixClient}>
|
||||
<FocusLock
|
||||
returnFocus={true}
|
||||
lockProps={{
|
||||
onKeyDown: this._onKeyDown,
|
||||
role: "dialog",
|
||||
["aria-labelledby"]: "mx_BaseDialog_title",
|
||||
// This should point to a node describing the dialog.
|
||||
// If we were about to completely follow this recommendation we'd need to
|
||||
// make all the components relying on BaseDialog to be aware of it.
|
||||
// So instead we will use the whole content as the description.
|
||||
// Description comes first and if the content contains more text,
|
||||
// AT users can skip its presentation.
|
||||
["aria-describedby"]: this.props.contentId,
|
||||
}}
|
||||
className={classNames({
|
||||
[this.props.className]: true,
|
||||
'mx_Dialog_fixedWidth': this.props.fixedWidth,
|
||||
})}
|
||||
>
|
||||
<div className={classNames('mx_Dialog_header', {
|
||||
'mx_Dialog_headerWithButton': !!this.props.headerButton,
|
||||
})}>
|
||||
<div className={classNames('mx_Dialog_title', this.props.titleClass)} id='mx_BaseDialog_title'>
|
||||
{ this.props.title }
|
||||
</div>
|
||||
{ this.props.headerButton }
|
||||
{ cancelButton }
|
||||
</div>
|
||||
{ this.props.headerButton }
|
||||
{ cancelButton }
|
||||
</div>
|
||||
{ this.props.children }
|
||||
</FocusTrap>
|
||||
{ this.props.children }
|
||||
</FocusLock>
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -25,8 +25,8 @@ import Modal from '../../../Modal';
|
|||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default class BugReportDialog extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
sendLogs: true,
|
||||
busy: false,
|
||||
|
|
|
@ -173,7 +173,7 @@ export default createReactClass({
|
|||
const domain = MatrixClientPeg.get().getDomain();
|
||||
aliasField = (
|
||||
<div className="mx_CreateRoomDialog_aliasContainer">
|
||||
<RoomAliasField id="alias" ref={ref => this._aliasFieldRef = ref} onChange={this.onAliasChange} domain={domain} />
|
||||
<RoomAliasField id="alias" ref={ref => this._aliasFieldRef = ref} onChange={this.onAliasChange} domain={domain} value={this.state.alias} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -25,8 +25,8 @@ import * as Lifecycle from '../../../Lifecycle';
|
|||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default class DeactivateAccountDialog extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._onOk = this._onOk.bind(this);
|
||||
this._onCancel = this._onCancel.bind(this);
|
||||
|
|
|
@ -97,7 +97,7 @@ export default class DeviceVerifyDialog extends React.Component {
|
|||
const client = MatrixClientPeg.get();
|
||||
const verifyingOwnDevice = this.props.userId === client.getUserId();
|
||||
try {
|
||||
if (!verifyingOwnDevice && SettingsStore.getValue("feature_dm_verification")) {
|
||||
if (!verifyingOwnDevice && SettingsStore.getValue("feature_cross_signing")) {
|
||||
const roomId = await ensureDMExistsAndOpen(this.props.userId);
|
||||
// throws upon cancellation before having started
|
||||
this._verifier = await client.requestVerificationDM(
|
||||
|
|
|
@ -16,23 +16,19 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Room } from "matrix-js-sdk";
|
||||
|
||||
import sdk from '../../../index';
|
||||
import SyntaxHighlight from '../elements/SyntaxHighlight';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import Field from "../elements/Field";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
|
||||
class DevtoolsComponent extends React.Component {
|
||||
static contextTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
class GenericEditor extends DevtoolsComponent {
|
||||
class GenericEditor extends React.PureComponent {
|
||||
// static propTypes = {onBack: PropTypes.func.isRequired};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._onChange = this._onChange.bind(this);
|
||||
this.onBack = this.onBack.bind(this);
|
||||
}
|
||||
|
@ -67,12 +63,15 @@ class SendCustomEvent extends GenericEditor {
|
|||
|
||||
static propTypes = {
|
||||
onBack: PropTypes.func.isRequired,
|
||||
room: PropTypes.instanceOf(Room).isRequired,
|
||||
forceStateEvent: PropTypes.bool,
|
||||
inputs: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
static contextType = MatrixClientContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._send = this._send.bind(this);
|
||||
|
||||
const {eventType, stateKey, evContent} = Object.assign({
|
||||
|
@ -91,11 +90,11 @@ class SendCustomEvent extends GenericEditor {
|
|||
}
|
||||
|
||||
send(content) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const cli = this.context;
|
||||
if (this.state.isStateEvent) {
|
||||
return cli.sendStateEvent(this.context.roomId, this.state.eventType, content, this.state.stateKey);
|
||||
return cli.sendStateEvent(this.props.room.roomId, this.state.eventType, content, this.state.stateKey);
|
||||
} else {
|
||||
return cli.sendEvent(this.context.roomId, this.state.eventType, content);
|
||||
return cli.sendEvent(this.props.room.roomId, this.state.eventType, content);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,13 +153,16 @@ class SendAccountData extends GenericEditor {
|
|||
static getLabel() { return _t('Send Account Data'); }
|
||||
|
||||
static propTypes = {
|
||||
room: PropTypes.instanceOf(Room).isRequired,
|
||||
isRoomAccountData: PropTypes.bool,
|
||||
forceMode: PropTypes.bool,
|
||||
inputs: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
static contextType = MatrixClientContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._send = this._send.bind(this);
|
||||
|
||||
const {eventType, evContent} = Object.assign({
|
||||
|
@ -177,9 +179,9 @@ class SendAccountData extends GenericEditor {
|
|||
}
|
||||
|
||||
send(content) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const cli = this.context;
|
||||
if (this.state.isRoomAccountData) {
|
||||
return cli.setRoomAccountData(this.context.roomId, this.state.eventType, content);
|
||||
return cli.setRoomAccountData(this.props.room.roomId, this.state.eventType, content);
|
||||
}
|
||||
return cli.setAccountData(this.state.eventType, content);
|
||||
}
|
||||
|
@ -234,7 +236,7 @@ class SendAccountData extends GenericEditor {
|
|||
const INITIAL_LOAD_TILES = 20;
|
||||
const LOAD_TILES_STEP_SIZE = 50;
|
||||
|
||||
class FilteredList extends React.Component {
|
||||
class FilteredList extends React.PureComponent {
|
||||
static propTypes = {
|
||||
children: PropTypes.any,
|
||||
query: PropTypes.string,
|
||||
|
@ -247,8 +249,8 @@ class FilteredList extends React.Component {
|
|||
return children.filter((child) => child.key.toLowerCase().includes(lcQuery));
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
filteredChildren: FilteredList.filterChildren(this.props.children, this.props.query),
|
||||
|
@ -305,19 +307,20 @@ class FilteredList extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
class RoomStateExplorer extends DevtoolsComponent {
|
||||
class RoomStateExplorer extends React.PureComponent {
|
||||
static getLabel() { return _t('Explore Room State'); }
|
||||
|
||||
|
||||
static propTypes = {
|
||||
onBack: PropTypes.func.isRequired,
|
||||
room: PropTypes.instanceOf(Room).isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
static contextType = MatrixClientContext;
|
||||
|
||||
const room = MatrixClientPeg.get().getRoom(this.context.roomId);
|
||||
this.roomStateEvents = room.currentState.events;
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.roomStateEvents = this.props.room.currentState.events;
|
||||
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.editEv = this.editEv.bind(this);
|
||||
|
@ -373,7 +376,7 @@ class RoomStateExplorer extends DevtoolsComponent {
|
|||
render() {
|
||||
if (this.state.event) {
|
||||
if (this.state.editing) {
|
||||
return <SendCustomEvent forceStateEvent={true} onBack={this.onBack} inputs={{
|
||||
return <SendCustomEvent room={this.props.room} 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(),
|
||||
|
@ -442,15 +445,18 @@ class RoomStateExplorer extends DevtoolsComponent {
|
|||
}
|
||||
}
|
||||
|
||||
class AccountDataExplorer extends DevtoolsComponent {
|
||||
class AccountDataExplorer extends React.PureComponent {
|
||||
static getLabel() { return _t('Explore Account Data'); }
|
||||
|
||||
static propTypes = {
|
||||
onBack: PropTypes.func.isRequired,
|
||||
room: PropTypes.instanceOf(Room).isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
static contextType = MatrixClientContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.editEv = this.editEv.bind(this);
|
||||
|
@ -467,11 +473,10 @@ class AccountDataExplorer extends DevtoolsComponent {
|
|||
}
|
||||
|
||||
getData() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (this.state.isRoomAccountData) {
|
||||
return cli.getRoom(this.context.roomId).accountData;
|
||||
return this.props.room.accountData;
|
||||
}
|
||||
return cli.store.accountData;
|
||||
return this.context.store.accountData;
|
||||
}
|
||||
|
||||
onViewSourceClick(event) {
|
||||
|
@ -505,10 +510,14 @@ class AccountDataExplorer extends DevtoolsComponent {
|
|||
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 <SendAccountData
|
||||
room={this.props.room}
|
||||
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">
|
||||
|
@ -553,17 +562,20 @@ class AccountDataExplorer extends DevtoolsComponent {
|
|||
}
|
||||
}
|
||||
|
||||
class ServersInRoomList extends DevtoolsComponent {
|
||||
class ServersInRoomList extends React.PureComponent {
|
||||
static getLabel() { return _t('View Servers in Room'); }
|
||||
|
||||
static propTypes = {
|
||||
onBack: PropTypes.func.isRequired,
|
||||
room: PropTypes.instanceOf(Room).isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
static contextType = MatrixClientContext;
|
||||
|
||||
const room = MatrixClientPeg.get().getRoom(this.context.roomId);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const room = this.props.room;
|
||||
const servers = new Set();
|
||||
room.currentState.getStateEvents("m.room.member").forEach(ev => servers.add(ev.getSender().split(":")[1]));
|
||||
this.servers = Array.from(servers).map(s =>
|
||||
|
@ -602,19 +614,14 @@ const Entries = [
|
|||
ServersInRoomList,
|
||||
];
|
||||
|
||||
export default class DevtoolsDialog extends React.Component {
|
||||
static childContextTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
// client: PropTypes.instanceOf(MatixClient),
|
||||
};
|
||||
|
||||
export default class DevtoolsDialog extends React.PureComponent {
|
||||
static propTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.onCancel = this.onCancel.bind(this);
|
||||
|
||||
|
@ -627,10 +634,6 @@ export default class DevtoolsDialog extends React.Component {
|
|||
this._unmounted = true;
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
return { roomId: this.props.roomId };
|
||||
}
|
||||
|
||||
_setMode(mode) {
|
||||
return () => {
|
||||
this.setState({ mode });
|
||||
|
@ -654,15 +657,17 @@ export default class DevtoolsDialog extends React.Component {
|
|||
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>;
|
||||
body = <MatrixClientContext.Consumer>
|
||||
{(cli) => <React.Fragment>
|
||||
<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} room={cli.getRoom(this.props.roomId)} />
|
||||
</React.Fragment>}
|
||||
</MatrixClientContext.Consumer>;
|
||||
} else {
|
||||
const classes = "mx_DevTools_RoomStateExplorer_button";
|
||||
body = <div>
|
||||
body = <React.Fragment>
|
||||
<div>
|
||||
<div className="mx_DevTools_label_left">{ _t('Toolbox') }</div>
|
||||
<div className="mx_DevTools_label_right">Room ID: { this.props.roomId }</div>
|
||||
|
@ -679,7 +684,7 @@ export default class DevtoolsDialog extends React.Component {
|
|||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onCancel}>{ _t('Cancel') }</button>
|
||||
</div>
|
||||
</div>;
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
|
|
|
@ -99,7 +99,7 @@ export default createReactClass({
|
|||
this.props.onFinished(true);
|
||||
}
|
||||
},
|
||||
});
|
||||
}, null, /* priority = */ false, /* static = */ true);
|
||||
},
|
||||
|
||||
_onShareClicked: function() {
|
||||
|
|
|
@ -30,8 +30,8 @@ export default class ReportEventDialog extends PureComponent {
|
|||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
reason: "",
|
||||
|
|
|
@ -15,13 +15,13 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, {createRef} from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import classnames from 'classnames';
|
||||
import { KeyCode } from '../../../Keyboard';
|
||||
import { Key } from '../../../Keyboard';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { SAFE_LOCALPART_REGEX } from '../../../Registration';
|
||||
|
||||
|
@ -62,8 +62,13 @@ export default createReactClass({
|
|||
};
|
||||
},
|
||||
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._input_value = createRef();
|
||||
this._uiAuth = createRef();
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.refs.input_value.select();
|
||||
this._input_value.current.select();
|
||||
|
||||
this._matrixClient = MatrixClientPeg.get();
|
||||
},
|
||||
|
@ -96,14 +101,14 @@ export default createReactClass({
|
|||
},
|
||||
|
||||
onKeyUp: function(ev) {
|
||||
if (ev.keyCode === KeyCode.ENTER) {
|
||||
if (ev.key === Key.ENTER) {
|
||||
this.onSubmit();
|
||||
}
|
||||
},
|
||||
|
||||
onSubmit: function(ev) {
|
||||
if (this.refs.uiAuth) {
|
||||
this.refs.uiAuth.tryContinue();
|
||||
if (this._uiAuth.current) {
|
||||
this._uiAuth.current.tryContinue();
|
||||
}
|
||||
this.setState({
|
||||
doingUIAuth: true,
|
||||
|
@ -215,7 +220,7 @@ export default createReactClass({
|
|||
onAuthFinished={this._onUIAuthFinished}
|
||||
inputs={{}}
|
||||
poll={true}
|
||||
ref="uiAuth"
|
||||
ref={this._uiAuth}
|
||||
continueIsManaged={true}
|
||||
/>;
|
||||
}
|
||||
|
@ -257,7 +262,7 @@ export default createReactClass({
|
|||
>
|
||||
<div className="mx_Dialog_content" id='mx_Dialog_content'>
|
||||
<div className="mx_SetMxIdDialog_input_group">
|
||||
<input type="text" ref="input_value" value={this.state.username}
|
||||
<input type="text" ref={this._input_value} value={this.state.username}
|
||||
autoFocus={true}
|
||||
onChange={this.onValueChange}
|
||||
onKeyUp={this.onKeyUp}
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, {createRef} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Room, User, Group, RoomMember, MatrixEvent} from 'matrix-js-sdk';
|
||||
import sdk from '../../../index';
|
||||
|
@ -74,6 +74,8 @@ export default class ShareDialog extends React.Component {
|
|||
// MatrixEvent defaults to share linkSpecificEvent
|
||||
linkSpecificEvent: this.props.target instanceof MatrixEvent,
|
||||
};
|
||||
|
||||
this._link = createRef();
|
||||
}
|
||||
|
||||
static _selectText(target) {
|
||||
|
@ -94,7 +96,7 @@ export default class ShareDialog extends React.Component {
|
|||
onCopyClick(e) {
|
||||
e.preventDefault();
|
||||
|
||||
ShareDialog._selectText(this.refs.link);
|
||||
ShareDialog._selectText(this._link.current);
|
||||
|
||||
let successful;
|
||||
try {
|
||||
|
@ -106,7 +108,7 @@ export default class ShareDialog extends React.Component {
|
|||
const buttonRect = e.target.getBoundingClientRect();
|
||||
const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
|
||||
const {close} = ContextMenu.createMenu(GenericTextContextMenu, {
|
||||
...toRightOf(buttonRect, 11),
|
||||
...toRightOf(buttonRect, 2),
|
||||
message: successful ? _t('Copied!') : _t('Failed to copy'),
|
||||
});
|
||||
// Drop a reference to this close handler for componentWillUnmount
|
||||
|
@ -195,7 +197,7 @@ export default class ShareDialog extends React.Component {
|
|||
>
|
||||
<div className="mx_ShareDialog_content">
|
||||
<div className="mx_ShareDialog_matrixto">
|
||||
<a ref="link"
|
||||
<a ref={this._link}
|
||||
href={matrixToUrl}
|
||||
onClick={ShareDialog.onLinkClick}
|
||||
className="mx_ShareDialog_matrixto_link"
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, {createRef} from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
|
@ -42,15 +42,19 @@ export default createReactClass({
|
|||
};
|
||||
},
|
||||
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._textinput = createRef();
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
if (this.props.focus) {
|
||||
// Set the cursor at the end of the text input
|
||||
this.refs.textinput.value = this.props.value;
|
||||
this._textinput.current.value = this.props.value;
|
||||
}
|
||||
},
|
||||
|
||||
onOk: function() {
|
||||
this.props.onFinished(true, this.refs.textinput.value);
|
||||
this.props.onFinished(true, this._textinput.current.value);
|
||||
},
|
||||
|
||||
onCancel: function() {
|
||||
|
@ -70,7 +74,13 @@ export default createReactClass({
|
|||
<label htmlFor="textinput"> { this.props.description } </label>
|
||||
</div>
|
||||
<div>
|
||||
<input id="textinput" ref="textinput" className="mx_TextInputDialog_input" defaultValue={this.props.value} autoFocus={this.props.focus} size="64" />
|
||||
<input
|
||||
id="textinput"
|
||||
ref={this._textinput}
|
||||
className="mx_TextInputDialog_input"
|
||||
defaultValue={this.props.value}
|
||||
autoFocus={this.props.focus}
|
||||
size="64" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../../index';
|
||||
import MatrixClientPeg from '../../../../MatrixClientPeg';
|
||||
import Modal from '../../../../Modal';
|
||||
|
@ -28,12 +27,13 @@ import {Key} from "../../../../Keyboard";
|
|||
const RESTORE_TYPE_PASSPHRASE = 0;
|
||||
const RESTORE_TYPE_RECOVERYKEY = 1;
|
||||
|
||||
/**
|
||||
/*
|
||||
* Dialog for restoring e2e keys from a backup and the user's recovery key
|
||||
*/
|
||||
export default createReactClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
export default class RestoreKeyBackupDialog extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
backupInfo: null,
|
||||
loading: false,
|
||||
loadError: null,
|
||||
|
@ -45,27 +45,27 @@ export default createReactClass({
|
|||
passPhrase: '',
|
||||
restoreType: null,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
componentWillMount: function() {
|
||||
componentDidMount() {
|
||||
this._loadBackupStatus();
|
||||
},
|
||||
}
|
||||
|
||||
_onCancel: function() {
|
||||
_onCancel = () => {
|
||||
this.props.onFinished(false);
|
||||
},
|
||||
}
|
||||
|
||||
_onDone: function() {
|
||||
_onDone = () => {
|
||||
this.props.onFinished(true);
|
||||
},
|
||||
}
|
||||
|
||||
_onUseRecoveryKeyClick: function() {
|
||||
_onUseRecoveryKeyClick = () => {
|
||||
this.setState({
|
||||
forceRecoveryKey: true,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_onResetRecoveryClick: function() {
|
||||
_onResetRecoveryClick = () => {
|
||||
this.props.onFinished(false);
|
||||
Modal.createTrackedDialogAsync('Key Backup', 'Key Backup',
|
||||
import('../../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'),
|
||||
|
@ -75,16 +75,16 @@ export default createReactClass({
|
|||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
_onRecoveryKeyChange: function(e) {
|
||||
_onRecoveryKeyChange = (e) => {
|
||||
this.setState({
|
||||
recoveryKey: e.target.value,
|
||||
recoveryKeyValid: MatrixClientPeg.get().isValidRecoveryKey(e.target.value),
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_onPassPhraseNext: async function() {
|
||||
_onPassPhraseNext = async () => {
|
||||
this.setState({
|
||||
loading: true,
|
||||
restoreError: null,
|
||||
|
@ -105,9 +105,9 @@ export default createReactClass({
|
|||
restoreError: e,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_onRecoveryKeyNext: async function() {
|
||||
_onRecoveryKeyNext = async () => {
|
||||
this.setState({
|
||||
loading: true,
|
||||
restoreError: null,
|
||||
|
@ -128,27 +128,27 @@ export default createReactClass({
|
|||
restoreError: e,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_onPassPhraseChange: function(e) {
|
||||
_onPassPhraseChange = (e) => {
|
||||
this.setState({
|
||||
passPhrase: e.target.value,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_onPassPhraseKeyPress: function(e) {
|
||||
_onPassPhraseKeyPress = (e) => {
|
||||
if (e.key === Key.ENTER) {
|
||||
this._onPassPhraseNext();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_onRecoveryKeyKeyPress: function(e) {
|
||||
_onRecoveryKeyKeyPress = (e) => {
|
||||
if (e.key === Key.ENTER && this.state.recoveryKeyValid) {
|
||||
this._onRecoveryKeyNext();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_loadBackupStatus: async function() {
|
||||
async _loadBackupStatus() {
|
||||
this.setState({
|
||||
loading: true,
|
||||
loadError: null,
|
||||
|
@ -167,9 +167,9 @@ export default createReactClass({
|
|||
loading: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
|
||||
|
@ -296,7 +296,7 @@ export default createReactClass({
|
|||
|
||||
content = <div>
|
||||
<p>{_t(
|
||||
"<b>Warning</b>: you should only set up key backup " +
|
||||
"<b>Warning</b>: You should only set up key backup " +
|
||||
"from a trusted computer.", {},
|
||||
{ b: sub => <b>{sub}</b> },
|
||||
)}</p>
|
||||
|
@ -322,7 +322,7 @@ export default createReactClass({
|
|||
/>
|
||||
</div>
|
||||
{_t(
|
||||
"If you've forgotten your recovery passphrase you can "+
|
||||
"If you've forgotten your recovery key you can "+
|
||||
"<button>set up new recovery options</button>"
|
||||
, {}, {
|
||||
button: s => <AccessibleButton className="mx_linkButton"
|
||||
|
@ -345,5 +345,5 @@ export default createReactClass({
|
|||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
Copyright 2018, 2019 New Vector Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 '../../../../index';
|
||||
import MatrixClientPeg from '../../../../MatrixClientPeg';
|
||||
|
||||
import { _t } from '../../../../languageHandler';
|
||||
import { Key } from "../../../../Keyboard";
|
||||
|
||||
/*
|
||||
* Access Secure Secret Storage by requesting the user's passphrase.
|
||||
*/
|
||||
export default class AccessSecretStorageDialog extends React.PureComponent {
|
||||
static propTypes = {
|
||||
// { passphrase, pubkey }
|
||||
keyInfo: PropTypes.object.isRequired,
|
||||
// Function from one of { passphrase, recoveryKey } -> boolean
|
||||
checkPrivateKey: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
recoveryKey: "",
|
||||
recoveryKeyValid: false,
|
||||
forceRecoveryKey: false,
|
||||
passPhrase: '',
|
||||
keyMatches: null,
|
||||
};
|
||||
}
|
||||
|
||||
_onCancel = () => {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
|
||||
_onUseRecoveryKeyClick = () => {
|
||||
this.setState({
|
||||
forceRecoveryKey: true,
|
||||
});
|
||||
}
|
||||
|
||||
_onResetRecoveryClick = () => {
|
||||
this.props.onFinished(false);
|
||||
throw new Error("Resetting secret storage unimplemented");
|
||||
}
|
||||
|
||||
_onRecoveryKeyChange = (e) => {
|
||||
this.setState({
|
||||
recoveryKey: e.target.value,
|
||||
recoveryKeyValid: MatrixClientPeg.get().isValidRecoveryKey(e.target.value),
|
||||
keyMatches: null,
|
||||
});
|
||||
}
|
||||
|
||||
_onPassPhraseNext = async () => {
|
||||
this.setState({ keyMatches: null });
|
||||
const input = { passphrase: this.state.passPhrase };
|
||||
const keyMatches = await this.props.checkPrivateKey(input);
|
||||
if (keyMatches) {
|
||||
this.props.onFinished(input);
|
||||
} else {
|
||||
this.setState({ keyMatches });
|
||||
}
|
||||
}
|
||||
|
||||
_onRecoveryKeyNext = async () => {
|
||||
this.setState({ keyMatches: null });
|
||||
const input = { recoveryKey: this.state.recoveryKey };
|
||||
const keyMatches = await this.props.checkPrivateKey(input);
|
||||
if (keyMatches) {
|
||||
this.props.onFinished(input);
|
||||
} else {
|
||||
this.setState({ keyMatches });
|
||||
}
|
||||
}
|
||||
|
||||
_onPassPhraseChange = (e) => {
|
||||
this.setState({
|
||||
passPhrase: e.target.value,
|
||||
keyMatches: null,
|
||||
});
|
||||
}
|
||||
|
||||
_onPassPhraseKeyPress = (e) => {
|
||||
if (e.key === Key.ENTER && this.state.passPhrase.length > 0) {
|
||||
this._onPassPhraseNext();
|
||||
}
|
||||
}
|
||||
|
||||
_onRecoveryKeyKeyPress = (e) => {
|
||||
if (e.key === Key.ENTER && this.state.recoveryKeyValid) {
|
||||
this._onRecoveryKeyNext();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
|
||||
const hasPassphrase = (
|
||||
this.props.keyInfo &&
|
||||
this.props.keyInfo.passphrase &&
|
||||
this.props.keyInfo.passphrase.salt &&
|
||||
this.props.keyInfo.passphrase.iterations
|
||||
);
|
||||
|
||||
let content;
|
||||
let title;
|
||||
if (hasPassphrase && !this.state.forceRecoveryKey) {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
title = _t("Enter secret storage passphrase");
|
||||
|
||||
let keyStatus;
|
||||
if (this.state.keyMatches === false) {
|
||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
|
||||
{"\uD83D\uDC4E "}{_t(
|
||||
"Unable to access secret storage. Please verify that you " +
|
||||
"entered the correct passphrase.",
|
||||
)}
|
||||
</div>;
|
||||
} else {
|
||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus"></div>;
|
||||
}
|
||||
|
||||
content = <div>
|
||||
<p>{_t(
|
||||
"<b>Warning</b>: You should only access secret storage " +
|
||||
"from a trusted computer.", {},
|
||||
{ b: sub => <b>{sub}</b> },
|
||||
)}</p>
|
||||
<p>{_t(
|
||||
"Access your secure message history and your cross-signing " +
|
||||
"identity for verifying other devices by entering your passphrase.",
|
||||
)}</p>
|
||||
|
||||
<div className="mx_AccessSecretStorageDialog_primaryContainer">
|
||||
<input type="password"
|
||||
className="mx_AccessSecretStorageDialog_passPhraseInput"
|
||||
onChange={this._onPassPhraseChange}
|
||||
onKeyPress={this._onPassPhraseKeyPress}
|
||||
value={this.state.passPhrase}
|
||||
autoFocus={true}
|
||||
/>
|
||||
{keyStatus}
|
||||
<DialogButtons primaryButton={_t('Next')}
|
||||
onPrimaryButtonClick={this._onPassPhraseNext}
|
||||
hasCancel={true}
|
||||
onCancel={this._onCancel}
|
||||
focus={false}
|
||||
primaryDisabled={this.state.passPhrase.length === 0}
|
||||
/>
|
||||
</div>
|
||||
{_t(
|
||||
"If you've forgotten your passphrase you can "+
|
||||
"<button1>use your recovery key</button1> or " +
|
||||
"<button2>set up new recovery options</button2>."
|
||||
, {}, {
|
||||
button1: s => <AccessibleButton className="mx_linkButton"
|
||||
element="span"
|
||||
onClick={this._onUseRecoveryKeyClick}
|
||||
>
|
||||
{s}
|
||||
</AccessibleButton>,
|
||||
button2: s => <AccessibleButton className="mx_linkButton"
|
||||
element="span"
|
||||
onClick={this._onResetRecoveryClick}
|
||||
>
|
||||
{s}
|
||||
</AccessibleButton>,
|
||||
})}
|
||||
</div>;
|
||||
} else {
|
||||
title = _t("Enter secret storage recovery key");
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
||||
let keyStatus;
|
||||
if (this.state.recoveryKey.length === 0) {
|
||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus"></div>;
|
||||
} else if (this.state.recoveryKeyValid) {
|
||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
|
||||
{"\uD83D\uDC4D "}{_t("This looks like a valid recovery key!")}
|
||||
</div>;
|
||||
} else if (this.state.keyMatches === false) {
|
||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
|
||||
{"\uD83D\uDC4E "}{_t(
|
||||
"Unable to access secret storage. Please verify that you " +
|
||||
"entered the correct recovery key.",
|
||||
)}
|
||||
</div>;
|
||||
} else {
|
||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
|
||||
{"\uD83D\uDC4E "}{_t("Not a valid recovery key")}
|
||||
</div>;
|
||||
}
|
||||
|
||||
content = <div>
|
||||
<p>{_t(
|
||||
"<b>Warning</b>: You should only access secret storage " +
|
||||
"from a trusted computer.", {},
|
||||
{ b: sub => <b>{sub}</b> },
|
||||
)}</p>
|
||||
<p>{_t(
|
||||
"Access your secure message history and your cross-signing " +
|
||||
"identity for verifying other devices by entering your recovery key.",
|
||||
)}</p>
|
||||
|
||||
<div className="mx_AccessSecretStorageDialog_primaryContainer">
|
||||
<input className="mx_AccessSecretStorageDialog_recoveryKeyInput"
|
||||
onChange={this._onRecoveryKeyChange}
|
||||
onKeyPress={this._onRecoveryKeyKeyPress}
|
||||
value={this.state.recoveryKey}
|
||||
autoFocus={true}
|
||||
/>
|
||||
{keyStatus}
|
||||
<DialogButtons primaryButton={_t('Next')}
|
||||
onPrimaryButtonClick={this._onRecoveryKeyNext}
|
||||
hasCancel={true}
|
||||
onCancel={this._onCancel}
|
||||
focus={false}
|
||||
primaryDisabled={!this.state.recoveryKeyValid}
|
||||
/>
|
||||
</div>
|
||||
{_t(
|
||||
"If you've forgotten your recovery key you can "+
|
||||
"<button>set up new recovery options</button>."
|
||||
, {}, {
|
||||
button: s => <AccessibleButton className="mx_linkButton"
|
||||
element="span"
|
||||
onClick={this._onResetRecoveryClick}
|
||||
>
|
||||
{s}
|
||||
</AccessibleButton>,
|
||||
})}
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseDialog className='mx_AccessSecretStorageDialog'
|
||||
onFinished={this.props.onFinished}
|
||||
title={title}
|
||||
>
|
||||
<div>
|
||||
{content}
|
||||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue