Merge remote-tracking branch 'origin/develop' into dbkr/attended_transfer
This commit is contained in:
commit
299467c515
116 changed files with 6088 additions and 1613 deletions
|
@ -22,7 +22,6 @@ import {MatrixClient} from "matrix-js-sdk/src/client";
|
|||
import {_t} from '../../../languageHandler';
|
||||
import {IDialogProps} from "./IDialogProps";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import FormButton from "../elements/FormButton";
|
||||
import Dropdown from "../elements/Dropdown";
|
||||
import SearchBox from "../../structures/SearchBox";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
|
@ -110,7 +109,7 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
|
|||
const title = <React.Fragment>
|
||||
<RoomAvatar room={selectedSpace} height={40} width={40} />
|
||||
<div>
|
||||
<h1>{ _t("Add existing spaces/rooms") }</h1>
|
||||
<h1>{ _t("Add existing rooms") }</h1>
|
||||
{ spaceOptionSection }
|
||||
</div>
|
||||
</React.Fragment>;
|
||||
|
@ -128,29 +127,9 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
|
|||
className="mx_textinput_icon mx_textinput_search"
|
||||
placeholder={ _t("Filter your rooms and spaces") }
|
||||
onSearch={setQuery}
|
||||
autoComplete={true}
|
||||
/>
|
||||
<AutoHideScrollbar className="mx_AddExistingToSpaceDialog_content" id="mx_AddExistingToSpaceDialog">
|
||||
{ spaces.length > 0 ? (
|
||||
<div className="mx_AddExistingToSpaceDialog_section mx_AddExistingToSpaceDialog_section_spaces">
|
||||
<h3>{ _t("Spaces") }</h3>
|
||||
{ spaces.map(space => {
|
||||
return <Entry
|
||||
key={space.roomId}
|
||||
room={space}
|
||||
checked={selectedToAdd.has(space)}
|
||||
onChange={(checked) => {
|
||||
if (checked) {
|
||||
selectedToAdd.add(space);
|
||||
} else {
|
||||
selectedToAdd.delete(space);
|
||||
}
|
||||
setSelectedToAdd(new Set(selectedToAdd));
|
||||
}}
|
||||
/>;
|
||||
}) }
|
||||
</div>
|
||||
) : null }
|
||||
|
||||
{ rooms.length > 0 ? (
|
||||
<div className="mx_AddExistingToSpaceDialog_section">
|
||||
<h3>{ _t("Rooms") }</h3>
|
||||
|
@ -172,6 +151,27 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
|
|||
</div>
|
||||
) : undefined }
|
||||
|
||||
{ spaces.length > 0 ? (
|
||||
<div className="mx_AddExistingToSpaceDialog_section mx_AddExistingToSpaceDialog_section_spaces">
|
||||
<h3>{ _t("Spaces") }</h3>
|
||||
{ spaces.map(space => {
|
||||
return <Entry
|
||||
key={space.roomId}
|
||||
room={space}
|
||||
checked={selectedToAdd.has(space)}
|
||||
onChange={(checked) => {
|
||||
if (checked) {
|
||||
selectedToAdd.add(space);
|
||||
} else {
|
||||
selectedToAdd.delete(space);
|
||||
}
|
||||
setSelectedToAdd(new Set(selectedToAdd));
|
||||
}}
|
||||
/>;
|
||||
}) }
|
||||
</div>
|
||||
) : null }
|
||||
|
||||
{ spaces.length + rooms.length < 1 ? <span className="mx_AddExistingToSpaceDialog_noResults">
|
||||
{ _t("No results") }
|
||||
</span> : undefined }
|
||||
|
@ -185,8 +185,8 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
|
|||
</AccessibleButton>
|
||||
</span>
|
||||
|
||||
<FormButton
|
||||
label={busy ? _t("Applying...") : _t("Apply")}
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
disabled={busy || selectedToAdd.size < 1}
|
||||
onClick={async () => {
|
||||
setBusy(true);
|
||||
|
@ -200,7 +200,9 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
|
|||
}
|
||||
setBusy(false);
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{ busy ? _t("Adding...") : _t("Add") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</BaseDialog>;
|
||||
};
|
||||
|
|
|
@ -45,6 +45,7 @@ import {Room} from "matrix-js-sdk/src/models/room";
|
|||
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||
import {mediaFromMxc} from "../../../customisations/Media";
|
||||
import {getAddressType} from "../../../UserAddress";
|
||||
|
||||
// we have a number of types defined from the Matrix spec which can't reasonably be altered here.
|
||||
/* eslint-disable camelcase */
|
||||
|
@ -681,19 +682,20 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
console.error(err);
|
||||
this.setState({
|
||||
busy: false,
|
||||
errorText: _t("We couldn't create your DM. Please check the users you want to invite and try again."),
|
||||
errorText: _t("We couldn't create your DM."),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_inviteUsers = () => {
|
||||
_inviteUsers = async () => {
|
||||
const startTime = CountlyAnalytics.getTimestamp();
|
||||
this.setState({busy: true});
|
||||
this._convertFilter();
|
||||
const targets = this._convertFilter();
|
||||
const targetIds = targets.map(t => t.userId);
|
||||
|
||||
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
|
||||
const cli = MatrixClientPeg.get();
|
||||
const room = cli.getRoom(this.props.roomId);
|
||||
if (!room) {
|
||||
console.error("Failed to find the room to invite users to");
|
||||
this.setState({
|
||||
|
@ -703,12 +705,34 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
return;
|
||||
}
|
||||
|
||||
inviteMultipleToRoom(this.props.roomId, targetIds).then(result => {
|
||||
try {
|
||||
const result = await inviteMultipleToRoom(this.props.roomId, targetIds)
|
||||
CountlyAnalytics.instance.trackSendInvite(startTime, this.props.roomId, targetIds.length);
|
||||
if (!this._shouldAbortAfterInviteError(result)) { // handles setting error message too
|
||||
this.props.onFinished();
|
||||
}
|
||||
}).catch(err => {
|
||||
|
||||
if (cli.isRoomEncrypted(this.props.roomId) &&
|
||||
SettingsStore.getValue("feature_room_history_key_sharing")) {
|
||||
const visibilityEvent = room.currentState.getStateEvents(
|
||||
"m.room.history_visibility", "",
|
||||
);
|
||||
const visibility = visibilityEvent && visibilityEvent.getContent() &&
|
||||
visibilityEvent.getContent().history_visibility;
|
||||
if (visibility == "world_readable" || visibility == "shared") {
|
||||
const invitedUsers = [];
|
||||
for (const [addr, state] of Object.entries(result.states)) {
|
||||
if (state === "invited" && getAddressType(addr) === "mx-user-id") {
|
||||
invitedUsers.push(addr);
|
||||
}
|
||||
}
|
||||
console.log("Sharing history with", invitedUsers);
|
||||
cli.sendSharedHistoryKeys(
|
||||
this.props.roomId, invitedUsers,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.setState({
|
||||
busy: false,
|
||||
|
@ -716,7 +740,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
"We couldn't invite those users. Please check the users you want to invite and try again.",
|
||||
),
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_transferCall = async () => {
|
||||
|
@ -912,19 +936,21 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
};
|
||||
|
||||
_toggleMember = (member: Member) => {
|
||||
let filterText = this.state.filterText;
|
||||
const targets = this.state.targets.map(t => t); // cheap clone for mutation
|
||||
const idx = targets.indexOf(member);
|
||||
if (idx >= 0) {
|
||||
targets.splice(idx, 1);
|
||||
} else {
|
||||
targets.push(member);
|
||||
filterText = ""; // clear the filter when the user accepts a suggestion
|
||||
}
|
||||
this.setState({targets, filterText});
|
||||
if (!this.state.busy) {
|
||||
let filterText = this.state.filterText;
|
||||
const targets = this.state.targets.map(t => t); // cheap clone for mutation
|
||||
const idx = targets.indexOf(member);
|
||||
if (idx >= 0) {
|
||||
targets.splice(idx, 1);
|
||||
} else {
|
||||
targets.push(member);
|
||||
filterText = ""; // clear the filter when the user accepts a suggestion
|
||||
}
|
||||
this.setState({targets, filterText});
|
||||
|
||||
if (this._editorRef && this._editorRef.current) {
|
||||
this._editorRef.current.focus();
|
||||
if (this._editorRef && this._editorRef.current) {
|
||||
this._editorRef.current.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1216,10 +1242,12 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
let buttonText;
|
||||
let goButtonFn;
|
||||
let consultSection;
|
||||
let keySharingWarning = <span />;
|
||||
|
||||
const identityServersEnabled = SettingsStore.getValue(UIFeature.IdentityServer);
|
||||
|
||||
const userId = MatrixClientPeg.get().getUserId();
|
||||
const cli = MatrixClientPeg.get();
|
||||
const userId = cli.getUserId();
|
||||
if (this.props.kind === KIND_DM) {
|
||||
title = _t("Direct Messages");
|
||||
|
||||
|
@ -1315,6 +1343,25 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
|
||||
buttonText = _t("Invite");
|
||||
goButtonFn = this._inviteUsers;
|
||||
|
||||
if (SettingsStore.getValue("feature_room_history_key_sharing") &&
|
||||
cli.isRoomEncrypted(this.props.roomId)) {
|
||||
const room = cli.getRoom(this.props.roomId);
|
||||
const visibilityEvent = room.currentState.getStateEvents(
|
||||
"m.room.history_visibility", "",
|
||||
);
|
||||
const visibility = visibilityEvent && visibilityEvent.getContent() &&
|
||||
visibilityEvent.getContent().history_visibility;
|
||||
if (visibility === "world_readable" || visibility === "shared") {
|
||||
keySharingWarning =
|
||||
<p className='mx_InviteDialog_helpText'>
|
||||
<img
|
||||
src={require("../../../../res/img/element-icons/info.svg")}
|
||||
width={14} height={14} />
|
||||
{" " + _t("Invited people will be able to read old messages.")}
|
||||
</p>;
|
||||
}
|
||||
}
|
||||
} else if (this.props.kind === KIND_CALL_TRANSFER) {
|
||||
title = _t("Transfer");
|
||||
buttonText = _t("Transfer");
|
||||
|
@ -1354,6 +1401,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
|||
{spinner}
|
||||
</div>
|
||||
</div>
|
||||
{keySharingWarning}
|
||||
{this._renderIdentityServerWarning()}
|
||||
<div className='error'>{this.state.errorText}</div>
|
||||
<div className='mx_InviteDialog_userSections'>
|
||||
|
|
|
@ -28,7 +28,6 @@ import {getTopic} from "../elements/RoomTopic";
|
|||
import {avatarUrlForRoom} from "../../../Avatar";
|
||||
import ToggleSwitch from "../elements/ToggleSwitch";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import FormButton from "../elements/FormButton";
|
||||
import Modal from "../../../Modal";
|
||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||
import {allSettled} from "../../../utils/promise";
|
||||
|
@ -127,23 +126,24 @@ const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFin
|
|||
<div>
|
||||
{ _t("Make this space private") }
|
||||
<ToggleSwitch
|
||||
checked={joinRule === "private"}
|
||||
onChange={checked => setJoinRule(checked ? "private" : "invite")}
|
||||
checked={joinRule !== "public"}
|
||||
onChange={checked => setJoinRule(checked ? "invite" : "public")}
|
||||
disabled={!canSetJoinRule}
|
||||
aria-label={_t("Make this space private")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormButton
|
||||
<AccessibleButton
|
||||
kind="danger"
|
||||
label={_t("Leave Space")}
|
||||
onClick={() => {
|
||||
defaultDispatcher.dispatch({
|
||||
action: "leave_room",
|
||||
room_id: space.roomId,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{ _t("Leave Space") }
|
||||
</AccessibleButton>
|
||||
|
||||
<div className="mx_SpaceSettingsDialog_buttons">
|
||||
<AccessibleButton onClick={() => Modal.createDialog(DevtoolsDialog, {roomId: space.roomId})}>
|
||||
|
@ -152,7 +152,9 @@ const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFin
|
|||
<AccessibleButton onClick={onFinished} disabled={busy} kind="link">
|
||||
{ _t("Cancel") }
|
||||
</AccessibleButton>
|
||||
<FormButton onClick={onSave} disabled={busy} label={busy ? _t("Saving...") : _t("Save Changes")} />
|
||||
<AccessibleButton onClick={onSave} disabled={busy} kind="primary">
|
||||
{ busy ? _t("Saving...") : _t("Save Changes") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>
|
||||
</BaseDialog>;
|
||||
|
|
|
@ -50,7 +50,7 @@ export default class VerificationRequestDialog extends React.Component {
|
|||
const member = this.props.member ||
|
||||
otherUserId && MatrixClientPeg.get().getUser(otherUserId);
|
||||
const title = request && request.isSelfVerification ?
|
||||
_t("Verify other session") : _t("Verification Request");
|
||||
_t("Verify other login") : _t("Verification Request");
|
||||
|
||||
return <BaseDialog className="mx_InfoDialog" onFinished={this.props.onFinished}
|
||||
contentId="mx_Dialog_content"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Copyright 2018, 2019 New Vector Ltd
|
||||
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2018-2021 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,14 +16,15 @@ limitations under the License.
|
|||
|
||||
import {debounce} from "lodash";
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import React, {ChangeEvent, FormEvent} from 'react';
|
||||
import {ISecretStorageKeyInfo} from "matrix-js-sdk/src";
|
||||
|
||||
import * as sdk from '../../../../index';
|
||||
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
|
||||
import Field from '../../elements/Field';
|
||||
import AccessibleButton from '../../elements/AccessibleButton';
|
||||
|
||||
import { _t } from '../../../../languageHandler';
|
||||
import {_t} from '../../../../languageHandler';
|
||||
import {IDialogProps} from "../IDialogProps";
|
||||
|
||||
// Maximum acceptable size of a key file. It's 59 characters including the spaces we encode,
|
||||
// so this should be plenty and allow for people putting extra whitespace in the file because
|
||||
|
@ -34,22 +34,30 @@ const KEY_FILE_MAX_SIZE = 128;
|
|||
// Don't shout at the user that their key is invalid every time they type a key: wait a short time
|
||||
const VALIDATION_THROTTLE_MS = 200;
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
keyInfo: ISecretStorageKeyInfo;
|
||||
checkPrivateKey: (k: {passphrase?: string, recoveryKey?: string}) => boolean;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
recoveryKey: string;
|
||||
recoveryKeyValid: boolean | null;
|
||||
recoveryKeyCorrect: boolean | null;
|
||||
recoveryKeyFileError: boolean | null;
|
||||
forceRecoveryKey: boolean;
|
||||
passPhrase: string;
|
||||
keyMatches: boolean | null;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,
|
||||
}
|
||||
export default class AccessSecretStorageDialog extends React.PureComponent<IProps, IState> {
|
||||
private fileUpload = React.createRef<HTMLInputElement>();
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._fileUpload = React.createRef();
|
||||
|
||||
this.state = {
|
||||
recoveryKey: "",
|
||||
recoveryKeyValid: null,
|
||||
|
@ -61,21 +69,21 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
};
|
||||
}
|
||||
|
||||
_onCancel = () => {
|
||||
private onCancel = () => {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
};
|
||||
|
||||
_onUseRecoveryKeyClick = () => {
|
||||
private onUseRecoveryKeyClick = () => {
|
||||
this.setState({
|
||||
forceRecoveryKey: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_validateRecoveryKeyOnChange = debounce(() => {
|
||||
this._validateRecoveryKey();
|
||||
private validateRecoveryKeyOnChange = debounce(async () => {
|
||||
await this.validateRecoveryKey();
|
||||
}, VALIDATION_THROTTLE_MS);
|
||||
|
||||
async _validateRecoveryKey() {
|
||||
private async validateRecoveryKey() {
|
||||
if (this.state.recoveryKey === '') {
|
||||
this.setState({
|
||||
recoveryKeyValid: null,
|
||||
|
@ -102,27 +110,27 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
_onRecoveryKeyChange = (e) => {
|
||||
private onRecoveryKeyChange = (ev: ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({
|
||||
recoveryKey: e.target.value,
|
||||
recoveryKey: ev.target.value,
|
||||
recoveryKeyFileError: null,
|
||||
});
|
||||
|
||||
// also clear the file upload control so that the user can upload the same file
|
||||
// the did before (otherwise the onchange wouldn't fire)
|
||||
if (this._fileUpload.current) this._fileUpload.current.value = null;
|
||||
if (this.fileUpload.current) this.fileUpload.current.value = null;
|
||||
|
||||
// We don't use Field's validation here because a) we want it in a separate place rather
|
||||
// than in a tooltip and b) we want it to display feedback based on the uploaded file
|
||||
// as well as the text box. Ideally we would refactor Field's validation logic so we could
|
||||
// re-use some of it.
|
||||
this._validateRecoveryKeyOnChange();
|
||||
}
|
||||
this.validateRecoveryKeyOnChange();
|
||||
};
|
||||
|
||||
_onRecoveryKeyFileChange = async e => {
|
||||
if (e.target.files.length === 0) return;
|
||||
private onRecoveryKeyFileChange = async (ev: ChangeEvent<HTMLInputElement>) => {
|
||||
if (ev.target.files.length === 0) return;
|
||||
|
||||
const f = e.target.files[0];
|
||||
const f = ev.target.files[0];
|
||||
|
||||
if (f.size > KEY_FILE_MAX_SIZE) {
|
||||
this.setState({
|
||||
|
@ -140,7 +148,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
recoveryKeyFileError: null,
|
||||
recoveryKey: contents.trim(),
|
||||
});
|
||||
this._validateRecoveryKey();
|
||||
await this.validateRecoveryKey();
|
||||
} else {
|
||||
this.setState({
|
||||
recoveryKeyFileError: true,
|
||||
|
@ -150,14 +158,14 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private onRecoveryKeyFileUploadClick = () => {
|
||||
this.fileUpload.current.click();
|
||||
}
|
||||
|
||||
_onRecoveryKeyFileUploadClick = () => {
|
||||
this._fileUpload.current.click();
|
||||
}
|
||||
|
||||
_onPassPhraseNext = async (e) => {
|
||||
e.preventDefault();
|
||||
private onPassPhraseNext = async (ev: FormEvent<HTMLFormElement>) => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (this.state.passPhrase.length <= 0) return;
|
||||
|
||||
|
@ -169,10 +177,10 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
} else {
|
||||
this.setState({ keyMatches });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_onRecoveryKeyNext = async (e) => {
|
||||
e.preventDefault();
|
||||
private onRecoveryKeyNext = async (ev: FormEvent<HTMLFormElement>) => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!this.state.recoveryKeyValid) return;
|
||||
|
||||
|
@ -184,16 +192,16 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
} else {
|
||||
this.setState({ keyMatches });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_onPassPhraseChange = (e) => {
|
||||
private onPassPhraseChange = (ev: ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({
|
||||
passPhrase: e.target.value,
|
||||
passPhrase: ev.target.value,
|
||||
keyMatches: null,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getKeyValidationText() {
|
||||
private getKeyValidationText(): string {
|
||||
if (this.state.recoveryKeyFileError) {
|
||||
return _t("Wrong file type");
|
||||
} else if (this.state.recoveryKeyCorrect) {
|
||||
|
@ -208,7 +216,8 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
// Caution: Making this an import will break tests.
|
||||
const BaseDialog = sdk.getComponent("views.dialogs.BaseDialog");
|
||||
|
||||
const hasPassphrase = (
|
||||
this.props.keyInfo &&
|
||||
|
@ -244,18 +253,18 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
{
|
||||
button: s => <AccessibleButton className="mx_linkButton"
|
||||
element="span"
|
||||
onClick={this._onUseRecoveryKeyClick}
|
||||
onClick={this.onUseRecoveryKeyClick}
|
||||
>
|
||||
{s}
|
||||
</AccessibleButton>,
|
||||
},
|
||||
)}</p>
|
||||
|
||||
<form className="mx_AccessSecretStorageDialog_primaryContainer" onSubmit={this._onPassPhraseNext}>
|
||||
<form className="mx_AccessSecretStorageDialog_primaryContainer" onSubmit={this.onPassPhraseNext}>
|
||||
<input
|
||||
type="password"
|
||||
className="mx_AccessSecretStorageDialog_passPhraseInput"
|
||||
onChange={this._onPassPhraseChange}
|
||||
onChange={this.onPassPhraseChange}
|
||||
value={this.state.passPhrase}
|
||||
autoFocus={true}
|
||||
autoComplete="new-password"
|
||||
|
@ -264,9 +273,9 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
{keyStatus}
|
||||
<DialogButtons
|
||||
primaryButton={_t('Continue')}
|
||||
onPrimaryButtonClick={this._onPassPhraseNext}
|
||||
onPrimaryButtonClick={this.onPassPhraseNext}
|
||||
hasCancel={true}
|
||||
onCancel={this._onCancel}
|
||||
onCancel={this.onCancel}
|
||||
focus={false}
|
||||
primaryDisabled={this.state.passPhrase.length === 0}
|
||||
/>
|
||||
|
@ -291,7 +300,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
|
||||
<form
|
||||
className="mx_AccessSecretStorageDialog_primaryContainer"
|
||||
onSubmit={this._onRecoveryKeyNext}
|
||||
onSubmit={this.onRecoveryKeyNext}
|
||||
spellCheck={false}
|
||||
autoComplete="off"
|
||||
>
|
||||
|
@ -301,7 +310,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
type="password"
|
||||
label={_t('Security Key')}
|
||||
value={this.state.recoveryKey}
|
||||
onChange={this._onRecoveryKeyChange}
|
||||
onChange={this.onRecoveryKeyChange}
|
||||
forceValidity={this.state.recoveryKeyCorrect}
|
||||
autoComplete="off"
|
||||
/>
|
||||
|
@ -312,10 +321,10 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
<div>
|
||||
<input type="file"
|
||||
className="mx_AccessSecretStorageDialog_recoveryKeyEntry_fileInput"
|
||||
ref={this._fileUpload}
|
||||
onChange={this._onRecoveryKeyFileChange}
|
||||
ref={this.fileUpload}
|
||||
onChange={this.onRecoveryKeyFileChange}
|
||||
/>
|
||||
<AccessibleButton kind="primary" onClick={this._onRecoveryKeyFileUploadClick}>
|
||||
<AccessibleButton kind="primary" onClick={this.onRecoveryKeyFileUploadClick}>
|
||||
{_t("Upload")}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
|
@ -323,11 +332,11 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
{recoveryKeyFeedback}
|
||||
<DialogButtons
|
||||
primaryButton={_t('Continue')}
|
||||
onPrimaryButtonClick={this._onRecoveryKeyNext}
|
||||
onPrimaryButtonClick={this.onRecoveryKeyNext}
|
||||
hasCancel={true}
|
||||
cancelButton={_t("Go Back")}
|
||||
cancelButtonClass='danger'
|
||||
onCancel={this._onCancel}
|
||||
onCancel={this.onCancel}
|
||||
focus={false}
|
||||
primaryDisabled={!this.state.recoveryKeyValid}
|
||||
/>
|
||||
|
@ -341,9 +350,9 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
title={title}
|
||||
titleClass={titleClass}
|
||||
>
|
||||
<div>
|
||||
{content}
|
||||
</div>
|
||||
<div>
|
||||
{content}
|
||||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue