Apply prettier formatting
This commit is contained in:
parent
1cac306093
commit
526645c791
1576 changed files with 65385 additions and 62478 deletions
|
@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
|
||||
import Spinner from "../../../../components/views/elements/Spinner";
|
||||
import DialogButtons from "../../../../components/views/elements/DialogButtons";
|
||||
import dis from "../../../../dispatcher/dispatcher";
|
||||
import { _t } from '../../../../languageHandler';
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import SettingsStore from "../../../../settings/SettingsStore";
|
||||
import EventIndexPeg from "../../../../indexing/EventIndexPeg";
|
||||
import { Action } from "../../../../dispatcher/actions";
|
||||
|
@ -50,7 +50,7 @@ export default class DisableEventIndexDialog extends React.Component<IProps, ISt
|
|||
disabling: true,
|
||||
});
|
||||
|
||||
await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, false);
|
||||
await SettingsStore.setValue("enableEventIndexing", null, SettingLevel.DEVICE, false);
|
||||
await EventIndexPeg.deleteEventIndex();
|
||||
this.props.onFinished(true);
|
||||
dis.fire(Action.ViewUserSettings);
|
||||
|
@ -59,10 +59,10 @@ export default class DisableEventIndexDialog extends React.Component<IProps, ISt
|
|||
public render(): React.ReactNode {
|
||||
return (
|
||||
<BaseDialog onFinished={this.props.onFinished} title={_t("Are you sure?")}>
|
||||
{ _t("If disabled, messages from encrypted rooms won't appear in search results.") }
|
||||
{ this.state.disabling ? <Spinner /> : <div /> }
|
||||
{_t("If disabled, messages from encrypted rooms won't appear in search results.")}
|
||||
{this.state.disabling ? <Spinner /> : <div />}
|
||||
<DialogButtons
|
||||
primaryButton={_t('Disable')}
|
||||
primaryButton={_t("Disable")}
|
||||
onPrimaryButtonClick={this.onDisable}
|
||||
primaryButtonClass="danger"
|
||||
cancelButtonClass="warning"
|
||||
|
|
|
@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
import { _t } from '../../../../languageHandler';
|
||||
import SdkConfig from '../../../../SdkConfig';
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import SdkConfig from "../../../../SdkConfig";
|
||||
import SettingsStore from "../../../../settings/SettingsStore";
|
||||
import Modal from '../../../../Modal';
|
||||
import Modal from "../../../../Modal";
|
||||
import { formatBytes, formatCountLong } from "../../../../utils/FormattingUtils";
|
||||
import EventIndexPeg from "../../../../indexing/EventIndexPeg";
|
||||
import { SettingLevel } from "../../../../settings/SettingLevel";
|
||||
import Field from '../../../../components/views/elements/Field';
|
||||
import Field from "../../../../components/views/elements/Field";
|
||||
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
|
||||
import DialogButtons from "../../../../components/views/elements/DialogButtons";
|
||||
import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps";
|
||||
|
@ -52,8 +52,7 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
|
|||
crawlingRoomsCount: 0,
|
||||
roomCount: 0,
|
||||
currentRoom: null,
|
||||
crawlerSleepTime:
|
||||
SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'),
|
||||
crawlerSleepTime: SettingsStore.getValueAt(SettingLevel.DEVICE, "crawlerSleepTime"),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -149,43 +148,48 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
|
|||
if (this.state.currentRoom === null) {
|
||||
crawlerState = _t("Not currently indexing messages for any room.");
|
||||
} else {
|
||||
crawlerState = (
|
||||
_t("Currently indexing: %(currentRoom)s", { currentRoom: this.state.currentRoom })
|
||||
);
|
||||
crawlerState = _t("Currently indexing: %(currentRoom)s", { currentRoom: this.state.currentRoom });
|
||||
}
|
||||
|
||||
const doneRooms = Math.max(0, (this.state.roomCount - this.state.crawlingRoomsCount));
|
||||
const doneRooms = Math.max(0, this.state.roomCount - this.state.crawlingRoomsCount);
|
||||
|
||||
const eventIndexingSettings = (
|
||||
<div>
|
||||
{ _t(
|
||||
{_t(
|
||||
"%(brand)s is securely caching encrypted messages locally for them " +
|
||||
"to appear in search results:",
|
||||
"to appear in search results:",
|
||||
{ brand },
|
||||
) }
|
||||
<div className='mx_SettingsTab_subsectionText'>
|
||||
{ crawlerState }<br />
|
||||
{ _t("Space used:") } { formatBytes(this.state.eventIndexSize, 0) }<br />
|
||||
{ _t("Indexed messages:") } { formatCountLong(this.state.eventCount) }<br />
|
||||
{ _t("Indexed rooms:") } { _t("%(doneRooms)s out of %(totalRooms)s", {
|
||||
)}
|
||||
<div className="mx_SettingsTab_subsectionText">
|
||||
{crawlerState}
|
||||
<br />
|
||||
{_t("Space used:")} {formatBytes(this.state.eventIndexSize, 0)}
|
||||
<br />
|
||||
{_t("Indexed messages:")} {formatCountLong(this.state.eventCount)}
|
||||
<br />
|
||||
{_t("Indexed rooms:")}{" "}
|
||||
{_t("%(doneRooms)s out of %(totalRooms)s", {
|
||||
doneRooms: formatCountLong(doneRooms),
|
||||
totalRooms: formatCountLong(this.state.roomCount),
|
||||
}) } <br />
|
||||
})}{" "}
|
||||
<br />
|
||||
<Field
|
||||
label={_t('Message downloading sleep time(ms)')}
|
||||
type='number'
|
||||
label={_t("Message downloading sleep time(ms)")}
|
||||
type="number"
|
||||
value={this.state.crawlerSleepTime.toString()}
|
||||
onChange={this.onCrawlerSleepTimeChange} />
|
||||
onChange={this.onCrawlerSleepTimeChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseDialog className='mx_ManageEventIndexDialog'
|
||||
<BaseDialog
|
||||
className="mx_ManageEventIndexDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title={_t("Message search")}
|
||||
>
|
||||
{ eventIndexingSettings }
|
||||
{eventIndexingSettings}
|
||||
<DialogButtons
|
||||
primaryButton={_t("Done")}
|
||||
onPrimaryButtonClick={this.props.onFinished}
|
||||
|
|
|
@ -15,14 +15,14 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createRef } from 'react';
|
||||
import FileSaver from 'file-saver';
|
||||
import React, { createRef } from "react";
|
||||
import FileSaver from "file-saver";
|
||||
import { IPreparedKeyBackupVersion } from "matrix-js-sdk/src/crypto/backup";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { MatrixClientPeg } from '../../../../MatrixClientPeg';
|
||||
import { _t, _td } from '../../../../languageHandler';
|
||||
import { accessSecretStorage } from '../../../../SecurityManager';
|
||||
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
|
||||
import { _t, _td } from "../../../../languageHandler";
|
||||
import { accessSecretStorage } from "../../../../SecurityManager";
|
||||
import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
|
||||
import { copyNode } from "../../../../utils/strings";
|
||||
import PassphraseField from "../../../../components/views/auth/PassphraseField";
|
||||
|
@ -73,9 +73,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
|
|||
this.state = {
|
||||
secureSecretStorage: null,
|
||||
phase: Phase.Passphrase,
|
||||
passPhrase: '',
|
||||
passPhrase: "",
|
||||
passPhraseValid: false,
|
||||
passPhraseConfirm: '',
|
||||
passPhraseConfirm: "",
|
||||
copied: false,
|
||||
downloaded: false,
|
||||
};
|
||||
|
@ -106,9 +106,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
|
|||
|
||||
private onDownloadClick = (): void => {
|
||||
const blob = new Blob([this.keyBackupInfo.recovery_key], {
|
||||
type: 'text/plain;charset=us-ascii',
|
||||
type: "text/plain;charset=us-ascii",
|
||||
});
|
||||
FileSaver.saveAs(blob, 'security-key.txt');
|
||||
FileSaver.saveAs(blob, "security-key.txt");
|
||||
|
||||
this.setState({
|
||||
downloaded: true,
|
||||
|
@ -126,16 +126,13 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
|
|||
try {
|
||||
if (secureSecretStorage) {
|
||||
await accessSecretStorage(async () => {
|
||||
info = await MatrixClientPeg.get().prepareKeyBackupVersion(
|
||||
null /* random key */,
|
||||
{ secureSecretStorage: true },
|
||||
);
|
||||
info = await MatrixClientPeg.get().prepareKeyBackupVersion(null /* random key */, {
|
||||
secureSecretStorage: true,
|
||||
});
|
||||
info = await MatrixClientPeg.get().createKeyBackupVersion(info);
|
||||
});
|
||||
} else {
|
||||
info = await MatrixClientPeg.get().createKeyBackupVersion(
|
||||
this.keyBackupInfo,
|
||||
);
|
||||
info = await MatrixClientPeg.get().createKeyBackupVersion(this.keyBackupInfo);
|
||||
}
|
||||
await MatrixClientPeg.get().scheduleAllGroupSessionsForBackup();
|
||||
this.setState({
|
||||
|
@ -206,9 +203,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
|
|||
|
||||
private onSetAgainClick = (): void => {
|
||||
this.setState({
|
||||
passPhrase: '',
|
||||
passPhrase: "",
|
||||
passPhraseValid: false,
|
||||
passPhraseConfirm: '',
|
||||
passPhraseConfirm: "",
|
||||
phase: Phase.Passphrase,
|
||||
});
|
||||
};
|
||||
|
@ -238,49 +235,56 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
|
|||
};
|
||||
|
||||
private renderPhasePassPhrase(): JSX.Element {
|
||||
return <form onSubmit={this.onPassPhraseNextClick}>
|
||||
<p>{ _t(
|
||||
"<b>Warning</b>: You should only set up key backup from a trusted computer.", {},
|
||||
{ b: sub => <b>{ sub }</b> },
|
||||
) }</p>
|
||||
<p>{ _t(
|
||||
"We'll store an encrypted copy of your keys on our server. " +
|
||||
"Secure your backup with a Security Phrase.",
|
||||
) }</p>
|
||||
<p>{ _t("For maximum security, this should be different from your account password.") }</p>
|
||||
return (
|
||||
<form onSubmit={this.onPassPhraseNextClick}>
|
||||
<p>
|
||||
{_t(
|
||||
"<b>Warning</b>: You should only set up key backup from a trusted computer.",
|
||||
{},
|
||||
{ b: (sub) => <b>{sub}</b> },
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{_t(
|
||||
"We'll store an encrypted copy of your keys on our server. " +
|
||||
"Secure your backup with a Security Phrase.",
|
||||
)}
|
||||
</p>
|
||||
<p>{_t("For maximum security, this should be different from your account password.")}</p>
|
||||
|
||||
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
|
||||
<PassphraseField
|
||||
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
||||
onChange={this.onPassPhraseChange}
|
||||
minScore={PASSWORD_MIN_SCORE}
|
||||
value={this.state.passPhrase}
|
||||
onValidate={this.onPassPhraseValidate}
|
||||
fieldRef={this.passphraseField}
|
||||
autoFocus={true}
|
||||
label={_td("Enter a Security Phrase")}
|
||||
labelEnterPassword={_td("Enter a Security Phrase")}
|
||||
labelStrongPassword={_td("Great! This Security Phrase looks strong enough.")}
|
||||
labelAllowedButUnsafe={_td("Great! This Security Phrase looks strong enough.")}
|
||||
/>
|
||||
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
|
||||
<PassphraseField
|
||||
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
||||
onChange={this.onPassPhraseChange}
|
||||
minScore={PASSWORD_MIN_SCORE}
|
||||
value={this.state.passPhrase}
|
||||
onValidate={this.onPassPhraseValidate}
|
||||
fieldRef={this.passphraseField}
|
||||
autoFocus={true}
|
||||
label={_td("Enter a Security Phrase")}
|
||||
labelEnterPassword={_td("Enter a Security Phrase")}
|
||||
labelStrongPassword={_td("Great! This Security Phrase looks strong enough.")}
|
||||
labelAllowedButUnsafe={_td("Great! This Security Phrase looks strong enough.")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogButtons
|
||||
primaryButton={_t('Next')}
|
||||
onPrimaryButtonClick={this.onPassPhraseNextClick}
|
||||
hasCancel={false}
|
||||
disabled={!this.state.passPhraseValid}
|
||||
/>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Next")}
|
||||
onPrimaryButtonClick={this.onPassPhraseNextClick}
|
||||
hasCancel={false}
|
||||
disabled={!this.state.passPhraseValid}
|
||||
/>
|
||||
|
||||
<details>
|
||||
<summary>{ _t("Advanced") }</summary>
|
||||
<AccessibleButton kind='primary' onClick={this.onSkipPassPhraseClick}>
|
||||
{ _t("Set up with a Security Key") }
|
||||
</AccessibleButton>
|
||||
</details>
|
||||
</form>;
|
||||
<details>
|
||||
<summary>{_t("Advanced")}</summary>
|
||||
<AccessibleButton kind="primary" onClick={this.onSkipPassPhraseClick}>
|
||||
{_t("Set up with a Security Key")}
|
||||
</AccessibleButton>
|
||||
</details>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhasePassPhraseConfirm(): JSX.Element {
|
||||
|
@ -303,68 +307,71 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
|
|||
|
||||
let passPhraseMatch = null;
|
||||
if (matchText) {
|
||||
passPhraseMatch = <div className="mx_CreateKeyBackupDialog_passPhraseMatch">
|
||||
<div>{ matchText }</div>
|
||||
<AccessibleButton kind="link" onClick={this.onSetAgainClick}>
|
||||
{ changeText }
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
}
|
||||
return <form onSubmit={this.onPassPhraseConfirmNextClick}>
|
||||
<p>{ _t(
|
||||
"Enter your Security Phrase a second time to confirm it.",
|
||||
) }</p>
|
||||
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
|
||||
<div>
|
||||
<input type="password"
|
||||
onChange={this.onPassPhraseConfirmChange}
|
||||
value={this.state.passPhraseConfirm}
|
||||
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
||||
placeholder={_t("Repeat your Security Phrase...")}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
{ passPhraseMatch }
|
||||
passPhraseMatch = (
|
||||
<div className="mx_CreateKeyBackupDialog_passPhraseMatch">
|
||||
<div>{matchText}</div>
|
||||
<AccessibleButton kind="link" onClick={this.onSetAgainClick}>
|
||||
{changeText}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>
|
||||
<DialogButtons
|
||||
primaryButton={_t('Next')}
|
||||
onPrimaryButtonClick={this.onPassPhraseConfirmNextClick}
|
||||
hasCancel={false}
|
||||
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
|
||||
/>
|
||||
</form>;
|
||||
);
|
||||
}
|
||||
return (
|
||||
<form onSubmit={this.onPassPhraseConfirmNextClick}>
|
||||
<p>{_t("Enter your Security Phrase a second time to confirm it.")}</p>
|
||||
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
|
||||
<div>
|
||||
<input
|
||||
type="password"
|
||||
onChange={this.onPassPhraseConfirmChange}
|
||||
value={this.state.passPhraseConfirm}
|
||||
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
||||
placeholder={_t("Repeat your Security Phrase...")}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
{passPhraseMatch}
|
||||
</div>
|
||||
</div>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Next")}
|
||||
onPrimaryButtonClick={this.onPassPhraseConfirmNextClick}
|
||||
hasCancel={false}
|
||||
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhaseShowKey(): JSX.Element {
|
||||
return <div>
|
||||
<p>{ _t(
|
||||
"Your Security Key is a safety net - you can use it to restore " +
|
||||
"access to your encrypted messages if you forget your Security Phrase.",
|
||||
) }</p>
|
||||
<p>{ _t(
|
||||
"Keep a copy of it somewhere secure, like a password manager or even a safe.",
|
||||
) }</p>
|
||||
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||
<div className="mx_CreateKeyBackupDialog_recoveryKeyHeader">
|
||||
{ _t("Your Security Key") }
|
||||
</div>
|
||||
<div className="mx_CreateKeyBackupDialog_recoveryKeyContainer">
|
||||
<div className="mx_CreateKeyBackupDialog_recoveryKey">
|
||||
<code ref={this.recoveryKeyNode}>{ this.keyBackupInfo.recovery_key }</code>
|
||||
</div>
|
||||
<div className="mx_CreateKeyBackupDialog_recoveryKeyButtons">
|
||||
<button className="mx_Dialog_primary" onClick={this.onCopyClick}>
|
||||
{ _t("Copy") }
|
||||
</button>
|
||||
<button className="mx_Dialog_primary" onClick={this.onDownloadClick}>
|
||||
{ _t("Download") }
|
||||
</button>
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{_t(
|
||||
"Your Security Key is a safety net - you can use it to restore " +
|
||||
"access to your encrypted messages if you forget your Security Phrase.",
|
||||
)}
|
||||
</p>
|
||||
<p>{_t("Keep a copy of it somewhere secure, like a password manager or even a safe.")}</p>
|
||||
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||
<div className="mx_CreateKeyBackupDialog_recoveryKeyHeader">{_t("Your Security Key")}</div>
|
||||
<div className="mx_CreateKeyBackupDialog_recoveryKeyContainer">
|
||||
<div className="mx_CreateKeyBackupDialog_recoveryKey">
|
||||
<code ref={this.recoveryKeyNode}>{this.keyBackupInfo.recovery_key}</code>
|
||||
</div>
|
||||
<div className="mx_CreateKeyBackupDialog_recoveryKeyButtons">
|
||||
<button className="mx_Dialog_primary" onClick={this.onCopyClick}>
|
||||
{_t("Copy")}
|
||||
</button>
|
||||
<button className="mx_Dialog_primary" onClick={this.onDownloadClick}>
|
||||
{_t("Download")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhaseKeepItSafe(): JSX.Element {
|
||||
|
@ -372,77 +379,81 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
|
|||
if (this.state.copied) {
|
||||
introText = _t(
|
||||
"Your Security Key has been <b>copied to your clipboard</b>, paste it to:",
|
||||
{}, { b: s => <b>{ s }</b> },
|
||||
{},
|
||||
{ b: (s) => <b>{s}</b> },
|
||||
);
|
||||
} else if (this.state.downloaded) {
|
||||
introText = _t(
|
||||
"Your Security Key is in your <b>Downloads</b> folder.",
|
||||
{}, { b: s => <b>{ s }</b> },
|
||||
);
|
||||
introText = _t("Your Security Key is in your <b>Downloads</b> folder.", {}, { b: (s) => <b>{s}</b> });
|
||||
}
|
||||
return <div>
|
||||
{ introText }
|
||||
<ul>
|
||||
<li>{ _t("<b>Print it</b> and store it somewhere safe", {}, { b: s => <b>{ s }</b> }) }</li>
|
||||
<li>{ _t("<b>Save it</b> on a USB key or backup drive", {}, { b: s => <b>{ s }</b> }) }</li>
|
||||
<li>{ _t("<b>Copy it</b> to your personal cloud storage", {}, { b: s => <b>{ s }</b> }) }</li>
|
||||
</ul>
|
||||
<DialogButtons primaryButton={_t("Continue")}
|
||||
onPrimaryButtonClick={this.createBackup}
|
||||
hasCancel={false}>
|
||||
<button onClick={this.onKeepItSafeBackClick}>{ _t("Back") }</button>
|
||||
</DialogButtons>
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
{introText}
|
||||
<ul>
|
||||
<li>{_t("<b>Print it</b> and store it somewhere safe", {}, { b: (s) => <b>{s}</b> })}</li>
|
||||
<li>{_t("<b>Save it</b> on a USB key or backup drive", {}, { b: (s) => <b>{s}</b> })}</li>
|
||||
<li>{_t("<b>Copy it</b> to your personal cloud storage", {}, { b: (s) => <b>{s}</b> })}</li>
|
||||
</ul>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Continue")}
|
||||
onPrimaryButtonClick={this.createBackup}
|
||||
hasCancel={false}
|
||||
>
|
||||
<button onClick={this.onKeepItSafeBackClick}>{_t("Back")}</button>
|
||||
</DialogButtons>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderBusyPhase(): JSX.Element {
|
||||
return <div>
|
||||
<Spinner />
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhaseDone(): JSX.Element {
|
||||
return <div>
|
||||
<p>{ _t(
|
||||
"Your keys are being backed up (the first backup could take a few minutes).",
|
||||
) }</p>
|
||||
<DialogButtons primaryButton={_t('OK')}
|
||||
onPrimaryButtonClick={this.onDone}
|
||||
hasCancel={false}
|
||||
/>
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
<p>{_t("Your keys are being backed up (the first backup could take a few minutes).")}</p>
|
||||
<DialogButtons primaryButton={_t("OK")} onPrimaryButtonClick={this.onDone} hasCancel={false} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhaseOptOutConfirm(): JSX.Element {
|
||||
return <div>
|
||||
{ _t(
|
||||
"Without setting up Secure Message Recovery, you won't be able to restore your " +
|
||||
"encrypted message history if you log out or use another session.",
|
||||
) }
|
||||
<DialogButtons primaryButton={_t('Set up Secure Message Recovery')}
|
||||
onPrimaryButtonClick={this.onSetUpClick}
|
||||
hasCancel={false}
|
||||
>
|
||||
<button onClick={this.onCancel}>I understand, continue without</button>
|
||||
</DialogButtons>
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
{_t(
|
||||
"Without setting up Secure Message Recovery, you won't be able to restore your " +
|
||||
"encrypted message history if you log out or use another session.",
|
||||
)}
|
||||
<DialogButtons
|
||||
primaryButton={_t("Set up Secure Message Recovery")}
|
||||
onPrimaryButtonClick={this.onSetUpClick}
|
||||
hasCancel={false}
|
||||
>
|
||||
<button onClick={this.onCancel}>I understand, continue without</button>
|
||||
</DialogButtons>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private titleForPhase(phase: Phase): string {
|
||||
switch (phase) {
|
||||
case Phase.Passphrase:
|
||||
return _t('Secure your backup with a Security Phrase');
|
||||
return _t("Secure your backup with a Security Phrase");
|
||||
case Phase.PassphraseConfirm:
|
||||
return _t('Confirm your Security Phrase');
|
||||
return _t("Confirm your Security Phrase");
|
||||
case Phase.OptOutConfirm:
|
||||
return _t('Warning!');
|
||||
return _t("Warning!");
|
||||
case Phase.ShowKey:
|
||||
case Phase.KeepItSafe:
|
||||
return _t('Make a copy of your Security Key');
|
||||
return _t("Make a copy of your Security Key");
|
||||
case Phase.BackingUp:
|
||||
return _t('Starting backup...');
|
||||
return _t("Starting backup...");
|
||||
case Phase.Done:
|
||||
return _t('Success!');
|
||||
return _t("Success!");
|
||||
default:
|
||||
return _t("Create key backup");
|
||||
}
|
||||
|
@ -451,15 +462,17 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
|
|||
public render(): JSX.Element {
|
||||
let content;
|
||||
if (this.state.error) {
|
||||
content = <div>
|
||||
<p>{ _t("Unable to create key backup") }</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t('Retry')}
|
||||
onPrimaryButtonClick={this.createBackup}
|
||||
hasCancel={true}
|
||||
onCancel={this.onCancel}
|
||||
/>
|
||||
</div>;
|
||||
content = (
|
||||
<div>
|
||||
<p>{_t("Unable to create key backup")}</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Retry")}
|
||||
onPrimaryButtonClick={this.createBackup}
|
||||
hasCancel={true}
|
||||
onCancel={this.onCancel}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
switch (this.state.phase) {
|
||||
case Phase.Passphrase:
|
||||
|
@ -487,14 +500,13 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
|
|||
}
|
||||
|
||||
return (
|
||||
<BaseDialog className='mx_CreateKeyBackupDialog'
|
||||
<BaseDialog
|
||||
className="mx_CreateKeyBackupDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title={this.titleForPhase(this.state.phase)}
|
||||
hasCancel={[Phase.Passphrase, Phase.Done].includes(this.state.phase)}
|
||||
>
|
||||
<div>
|
||||
{ content }
|
||||
</div>
|
||||
<div>{content}</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createRef } from 'react';
|
||||
import FileSaver from 'file-saver';
|
||||
import React, { createRef } from "react";
|
||||
import FileSaver from "file-saver";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
|
||||
import { TrustInfo } from "matrix-js-sdk/src/crypto/backup";
|
||||
|
@ -24,14 +24,14 @@ import { CrossSigningKeys } from "matrix-js-sdk/src/matrix";
|
|||
import { IRecoveryKey } from "matrix-js-sdk/src/crypto/api";
|
||||
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
|
||||
|
||||
import { MatrixClientPeg } from '../../../../MatrixClientPeg';
|
||||
import { _t, _td } from '../../../../languageHandler';
|
||||
import Modal from '../../../../Modal';
|
||||
import { promptForBackupPassphrase } from '../../../../SecurityManager';
|
||||
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
|
||||
import { _t, _td } from "../../../../languageHandler";
|
||||
import Modal from "../../../../Modal";
|
||||
import { promptForBackupPassphrase } from "../../../../SecurityManager";
|
||||
import { copyNode } from "../../../../utils/strings";
|
||||
import { SSOAuthEntry } from "../../../../components/views/auth/InteractiveAuthEntryComponents";
|
||||
import PassphraseField from "../../../../components/views/auth/PassphraseField";
|
||||
import StyledRadioButton from '../../../../components/views/elements/StyledRadioButton';
|
||||
import StyledRadioButton from "../../../../components/views/elements/StyledRadioButton";
|
||||
import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
|
||||
import DialogButtons from "../../../../components/views/elements/DialogButtons";
|
||||
import InlineSpinner from "../../../../components/views/elements/InlineSpinner";
|
||||
|
@ -40,7 +40,7 @@ import {
|
|||
getSecureBackupSetupMethods,
|
||||
isSecureBackupRequired,
|
||||
SecureBackupSetupMethod,
|
||||
} from '../../../../utils/WellKnownUtils';
|
||||
} from "../../../../utils/WellKnownUtils";
|
||||
import SecurityCustomisations from "../../../../customisations/Security";
|
||||
import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps";
|
||||
import Field from "../../../../components/views/elements/Field";
|
||||
|
@ -129,9 +129,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
|
||||
this.state = {
|
||||
phase: Phase.Loading,
|
||||
passPhrase: '',
|
||||
passPhrase: "",
|
||||
passPhraseValid: false,
|
||||
passPhraseConfirm: '',
|
||||
passPhraseConfirm: "",
|
||||
copied: false,
|
||||
downloaded: false,
|
||||
setPassphrase: false,
|
||||
|
@ -169,16 +169,15 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
this.fetchBackupInfo();
|
||||
}
|
||||
|
||||
private async fetchBackupInfo(): Promise<{ backupInfo: IKeyBackupInfo, backupSigStatus: TrustInfo }> {
|
||||
private async fetchBackupInfo(): Promise<{ backupInfo: IKeyBackupInfo; backupSigStatus: TrustInfo }> {
|
||||
try {
|
||||
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
||||
const backupSigStatus = (
|
||||
const backupSigStatus =
|
||||
// we may not have started crypto yet, in which case we definitely don't trust the backup
|
||||
MatrixClientPeg.get().isCryptoEnabled() && (await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo))
|
||||
);
|
||||
MatrixClientPeg.get().isCryptoEnabled() && (await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo));
|
||||
|
||||
const { forceReset } = this.props;
|
||||
const phase = (backupInfo && !forceReset) ? Phase.Migrate : Phase.ChooseKeyPassphrase;
|
||||
const phase = backupInfo && !forceReset ? Phase.Migrate : Phase.ChooseKeyPassphrase;
|
||||
|
||||
this.setState({
|
||||
phase,
|
||||
|
@ -207,8 +206,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
logger.log("uploadDeviceSigningKeys advertised no flows!");
|
||||
return;
|
||||
}
|
||||
const canUploadKeysWithPasswordOnly = error.data.flows.some(f => {
|
||||
return f.stages.length === 1 && f.stages[0] === 'm.login.password';
|
||||
const canUploadKeysWithPasswordOnly = error.data.flows.some((f) => {
|
||||
return f.stages.length === 1 && f.stages[0] === "m.login.password";
|
||||
});
|
||||
this.setState({
|
||||
canUploadKeysWithPasswordOnly,
|
||||
|
@ -228,8 +227,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
|
||||
private onChooseKeyPassphraseFormSubmit = async (): Promise<void> => {
|
||||
if (this.state.passPhraseKeySelected === SecureBackupSetupMethod.Key) {
|
||||
this.recoveryKey =
|
||||
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase();
|
||||
this.recoveryKey = await MatrixClientPeg.get().createRecoveryKeyFromPassphrase();
|
||||
this.setState({
|
||||
copied: false,
|
||||
downloaded: false,
|
||||
|
@ -265,9 +263,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
|
||||
private onDownloadClick = (): void => {
|
||||
const blob = new Blob([this.recoveryKey.encodedPrivateKey], {
|
||||
type: 'text/plain;charset=us-ascii',
|
||||
type: "text/plain;charset=us-ascii",
|
||||
});
|
||||
FileSaver.saveAs(blob, 'security-key.txt');
|
||||
FileSaver.saveAs(blob, "security-key.txt");
|
||||
|
||||
this.setState({
|
||||
downloaded: true,
|
||||
|
@ -277,9 +275,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
private doBootstrapUIAuth = async (makeRequest: (authData: any) => Promise<{}>): Promise<void> => {
|
||||
if (this.state.canUploadKeysWithPasswordOnly && this.state.accountPassword) {
|
||||
await makeRequest({
|
||||
type: 'm.login.password',
|
||||
type: "m.login.password",
|
||||
identifier: {
|
||||
type: 'm.id.user',
|
||||
type: "m.id.user",
|
||||
user: MatrixClientPeg.get().getUserId(),
|
||||
},
|
||||
// TODO: Remove `user` once servers support proper UIA
|
||||
|
@ -367,7 +365,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
} catch (e) {
|
||||
if (this.state.canUploadKeysWithPasswordOnly && e.httpStatus === 401 && e.data.flows) {
|
||||
this.setState({
|
||||
accountPassword: '',
|
||||
accountPassword: "",
|
||||
accountPasswordCorrect: false,
|
||||
phase: Phase.Migrate,
|
||||
});
|
||||
|
@ -385,20 +383,22 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
private restoreBackup = async (): Promise<void> => {
|
||||
// It's possible we'll need the backup key later on for bootstrapping,
|
||||
// so let's stash it here, rather than prompting for it twice.
|
||||
const keyCallback = k => this.backupKey = k;
|
||||
const keyCallback = (k) => (this.backupKey = k);
|
||||
|
||||
const { finished } = Modal.createDialog(RestoreKeyBackupDialog, {
|
||||
showSummary: false,
|
||||
keyCallback,
|
||||
}, null, /* priority = */ false, /* static = */ false);
|
||||
const { finished } = Modal.createDialog(
|
||||
RestoreKeyBackupDialog,
|
||||
{
|
||||
showSummary: false,
|
||||
keyCallback,
|
||||
},
|
||||
null,
|
||||
/* priority = */ false,
|
||||
/* static = */ false,
|
||||
);
|
||||
|
||||
await finished;
|
||||
const { backupSigStatus } = await this.fetchBackupInfo();
|
||||
if (
|
||||
backupSigStatus.usable &&
|
||||
this.state.canUploadKeysWithPasswordOnly &&
|
||||
this.state.accountPassword
|
||||
) {
|
||||
if (backupSigStatus.usable && this.state.canUploadKeysWithPasswordOnly && this.state.accountPassword) {
|
||||
this.bootstrapSecretStorage();
|
||||
}
|
||||
};
|
||||
|
@ -439,8 +439,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
|
||||
if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
|
||||
|
||||
this.recoveryKey =
|
||||
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase);
|
||||
this.recoveryKey = await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase);
|
||||
this.setState({
|
||||
copied: false,
|
||||
downloaded: false,
|
||||
|
@ -451,9 +450,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
|
||||
private onSetAgainClick = (): void => {
|
||||
this.setState({
|
||||
passPhrase: '',
|
||||
passPhrase: "",
|
||||
passPhraseValid: false,
|
||||
passPhraseConfirm: '',
|
||||
passPhraseConfirm: "",
|
||||
phase: Phase.Passphrase,
|
||||
});
|
||||
};
|
||||
|
@ -494,9 +493,13 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
>
|
||||
<div className="mx_CreateSecretStorageDialog_optionTitle">
|
||||
<span className="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_secureBackup" />
|
||||
{ _t("Generate a Security Key") }
|
||||
{_t("Generate a Security Key")}
|
||||
</div>
|
||||
<div>
|
||||
{_t(
|
||||
"We'll generate a Security Key for you to store somewhere safe, like a password manager or a safe.",
|
||||
)}
|
||||
</div>
|
||||
<div>{ _t("We'll generate a Security Key for you to store somewhere safe, like a password manager or a safe.") }</div>
|
||||
</StyledRadioButton>
|
||||
);
|
||||
}
|
||||
|
@ -513,9 +516,11 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
>
|
||||
<div className="mx_CreateSecretStorageDialog_optionTitle">
|
||||
<span className="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_securePhrase" />
|
||||
{ _t("Enter a Security Phrase") }
|
||||
{_t("Enter a Security Phrase")}
|
||||
</div>
|
||||
<div>
|
||||
{_t("Use a secret phrase only you know, and optionally save a Security Key to use for backup.")}
|
||||
</div>
|
||||
<div>{ _t("Use a secret phrase only you know, and optionally save a Security Key to use for backup.") }</div>
|
||||
</StyledRadioButton>
|
||||
);
|
||||
}
|
||||
|
@ -527,22 +532,26 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
? this.renderOptionPassphrase()
|
||||
: null;
|
||||
|
||||
return <form onSubmit={this.onChooseKeyPassphraseFormSubmit}>
|
||||
<p className="mx_CreateSecretStorageDialog_centeredBody">{ _t(
|
||||
"Safeguard against losing access to encrypted messages & data by " +
|
||||
"backing up encryption keys on your server.",
|
||||
) }</p>
|
||||
<div className="mx_CreateSecretStorageDialog_primaryContainer" role="radiogroup">
|
||||
{ optionKey }
|
||||
{ optionPassphrase }
|
||||
</div>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Continue")}
|
||||
onPrimaryButtonClick={this.onChooseKeyPassphraseFormSubmit}
|
||||
onCancel={this.onCancelClick}
|
||||
hasCancel={this.state.canSkip}
|
||||
/>
|
||||
</form>;
|
||||
return (
|
||||
<form onSubmit={this.onChooseKeyPassphraseFormSubmit}>
|
||||
<p className="mx_CreateSecretStorageDialog_centeredBody">
|
||||
{_t(
|
||||
"Safeguard against losing access to encrypted messages & data by " +
|
||||
"backing up encryption keys on your server.",
|
||||
)}
|
||||
</p>
|
||||
<div className="mx_CreateSecretStorageDialog_primaryContainer" role="radiogroup">
|
||||
{optionKey}
|
||||
{optionPassphrase}
|
||||
</div>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Continue")}
|
||||
onPrimaryButtonClick={this.onChooseKeyPassphraseFormSubmit}
|
||||
onCancel={this.onCancelClick}
|
||||
hasCancel={this.state.canSkip}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhaseMigrate(): JSX.Element {
|
||||
|
@ -555,83 +564,94 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
let authPrompt;
|
||||
let nextCaption = _t("Next");
|
||||
if (this.state.canUploadKeysWithPasswordOnly) {
|
||||
authPrompt = <div>
|
||||
<div>{ _t("Enter your account password to confirm the upgrade:") }</div>
|
||||
<div><Field
|
||||
type="password"
|
||||
label={_t("Password")}
|
||||
value={this.state.accountPassword}
|
||||
onChange={this.onAccountPasswordChange}
|
||||
forceValidity={this.state.accountPasswordCorrect === false ? false : null}
|
||||
autoFocus={true}
|
||||
/></div>
|
||||
</div>;
|
||||
authPrompt = (
|
||||
<div>
|
||||
<div>{_t("Enter your account password to confirm the upgrade:")}</div>
|
||||
<div>
|
||||
<Field
|
||||
type="password"
|
||||
label={_t("Password")}
|
||||
value={this.state.accountPassword}
|
||||
onChange={this.onAccountPasswordChange}
|
||||
forceValidity={this.state.accountPasswordCorrect === false ? false : null}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (!this.state.backupSigStatus.usable) {
|
||||
authPrompt = <div>
|
||||
<div>{ _t("Restore your key backup to upgrade your encryption") }</div>
|
||||
</div>;
|
||||
authPrompt = (
|
||||
<div>
|
||||
<div>{_t("Restore your key backup to upgrade your encryption")}</div>
|
||||
</div>
|
||||
);
|
||||
nextCaption = _t("Restore");
|
||||
} else {
|
||||
authPrompt = <p>
|
||||
{ _t("You'll need to authenticate with the server to confirm the upgrade.") }
|
||||
</p>;
|
||||
authPrompt = <p>{_t("You'll need to authenticate with the server to confirm the upgrade.")}</p>;
|
||||
}
|
||||
|
||||
return <form onSubmit={this.onMigrateFormSubmit}>
|
||||
<p>{ _t(
|
||||
"Upgrade this session to allow it to verify other sessions, " +
|
||||
"granting them access to encrypted messages and marking them " +
|
||||
"as trusted for other users.",
|
||||
) }</p>
|
||||
<div>{ authPrompt }</div>
|
||||
<DialogButtons
|
||||
primaryButton={nextCaption}
|
||||
onPrimaryButtonClick={this.onMigrateFormSubmit}
|
||||
hasCancel={false}
|
||||
primaryDisabled={this.state.canUploadKeysWithPasswordOnly && !this.state.accountPassword}
|
||||
>
|
||||
<button type="button" className="danger" onClick={this.onCancelClick}>
|
||||
{ _t('Skip') }
|
||||
</button>
|
||||
</DialogButtons>
|
||||
</form>;
|
||||
return (
|
||||
<form onSubmit={this.onMigrateFormSubmit}>
|
||||
<p>
|
||||
{_t(
|
||||
"Upgrade this session to allow it to verify other sessions, " +
|
||||
"granting them access to encrypted messages and marking them " +
|
||||
"as trusted for other users.",
|
||||
)}
|
||||
</p>
|
||||
<div>{authPrompt}</div>
|
||||
<DialogButtons
|
||||
primaryButton={nextCaption}
|
||||
onPrimaryButtonClick={this.onMigrateFormSubmit}
|
||||
hasCancel={false}
|
||||
primaryDisabled={this.state.canUploadKeysWithPasswordOnly && !this.state.accountPassword}
|
||||
>
|
||||
<button type="button" className="danger" onClick={this.onCancelClick}>
|
||||
{_t("Skip")}
|
||||
</button>
|
||||
</DialogButtons>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhasePassPhrase(): JSX.Element {
|
||||
return <form onSubmit={this.onPassPhraseNextClick}>
|
||||
<p>{ _t(
|
||||
"Enter a security phrase only you know, as it's used to safeguard your data. " +
|
||||
"To be secure, you shouldn't re-use your account password.",
|
||||
) }</p>
|
||||
return (
|
||||
<form onSubmit={this.onPassPhraseNextClick}>
|
||||
<p>
|
||||
{_t(
|
||||
"Enter a security phrase only you know, as it's used to safeguard your data. " +
|
||||
"To be secure, you shouldn't re-use your account password.",
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
|
||||
<PassphraseField
|
||||
className="mx_CreateSecretStorageDialog_passPhraseField"
|
||||
onChange={this.onPassPhraseChange}
|
||||
minScore={PASSWORD_MIN_SCORE}
|
||||
value={this.state.passPhrase}
|
||||
onValidate={this.onPassPhraseValidate}
|
||||
fieldRef={this.passphraseField}
|
||||
autoFocus={true}
|
||||
label={_td("Enter a Security Phrase")}
|
||||
labelEnterPassword={_td("Enter a Security Phrase")}
|
||||
labelStrongPassword={_td("Great! This Security Phrase looks strong enough.")}
|
||||
labelAllowedButUnsafe={_td("Great! This Security Phrase looks strong enough.")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
|
||||
<PassphraseField
|
||||
className="mx_CreateSecretStorageDialog_passPhraseField"
|
||||
onChange={this.onPassPhraseChange}
|
||||
minScore={PASSWORD_MIN_SCORE}
|
||||
value={this.state.passPhrase}
|
||||
onValidate={this.onPassPhraseValidate}
|
||||
fieldRef={this.passphraseField}
|
||||
autoFocus={true}
|
||||
label={_td("Enter a Security Phrase")}
|
||||
labelEnterPassword={_td("Enter a Security Phrase")}
|
||||
labelStrongPassword={_td("Great! This Security Phrase looks strong enough.")}
|
||||
labelAllowedButUnsafe={_td("Great! This Security Phrase looks strong enough.")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DialogButtons
|
||||
primaryButton={_t('Continue')}
|
||||
onPrimaryButtonClick={this.onPassPhraseNextClick}
|
||||
hasCancel={false}
|
||||
disabled={!this.state.passPhraseValid}
|
||||
>
|
||||
<button type="button"
|
||||
onClick={this.onCancelClick}
|
||||
className="danger"
|
||||
>{ _t("Cancel") }</button>
|
||||
</DialogButtons>
|
||||
</form>;
|
||||
<DialogButtons
|
||||
primaryButton={_t("Continue")}
|
||||
onPrimaryButtonClick={this.onPassPhraseNextClick}
|
||||
hasCancel={false}
|
||||
disabled={!this.state.passPhraseValid}
|
||||
>
|
||||
<button type="button" onClick={this.onCancelClick} className="danger">
|
||||
{_t("Cancel")}
|
||||
</button>
|
||||
</DialogButtons>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhasePassPhraseConfirm(): JSX.Element {
|
||||
|
@ -654,166 +674,188 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
|
||||
let passPhraseMatch = null;
|
||||
if (matchText) {
|
||||
passPhraseMatch = <div>
|
||||
<div>{ matchText }</div>
|
||||
<AccessibleButton kind="link" onClick={this.onSetAgainClick}>
|
||||
{ changeText }
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
}
|
||||
return <form onSubmit={this.onPassPhraseConfirmNextClick}>
|
||||
<p>{ _t(
|
||||
"Enter your Security Phrase a second time to confirm it.",
|
||||
) }</p>
|
||||
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
|
||||
<Field
|
||||
type="password"
|
||||
onChange={this.onPassPhraseConfirmChange}
|
||||
value={this.state.passPhraseConfirm}
|
||||
className="mx_CreateSecretStorageDialog_passPhraseField"
|
||||
label={_t("Confirm your Security Phrase")}
|
||||
autoFocus={true}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
<div className="mx_CreateSecretStorageDialog_passPhraseMatch">
|
||||
{ passPhraseMatch }
|
||||
passPhraseMatch = (
|
||||
<div>
|
||||
<div>{matchText}</div>
|
||||
<AccessibleButton kind="link" onClick={this.onSetAgainClick}>
|
||||
{changeText}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>
|
||||
<DialogButtons
|
||||
primaryButton={_t('Continue')}
|
||||
onPrimaryButtonClick={this.onPassPhraseConfirmNextClick}
|
||||
hasCancel={false}
|
||||
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
|
||||
>
|
||||
<button type="button"
|
||||
onClick={this.onCancelClick}
|
||||
className="danger"
|
||||
>{ _t("Skip") }</button>
|
||||
</DialogButtons>
|
||||
</form>;
|
||||
);
|
||||
}
|
||||
return (
|
||||
<form onSubmit={this.onPassPhraseConfirmNextClick}>
|
||||
<p>{_t("Enter your Security Phrase a second time to confirm it.")}</p>
|
||||
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
|
||||
<Field
|
||||
type="password"
|
||||
onChange={this.onPassPhraseConfirmChange}
|
||||
value={this.state.passPhraseConfirm}
|
||||
className="mx_CreateSecretStorageDialog_passPhraseField"
|
||||
label={_t("Confirm your Security Phrase")}
|
||||
autoFocus={true}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
<div className="mx_CreateSecretStorageDialog_passPhraseMatch">{passPhraseMatch}</div>
|
||||
</div>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Continue")}
|
||||
onPrimaryButtonClick={this.onPassPhraseConfirmNextClick}
|
||||
hasCancel={false}
|
||||
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
|
||||
>
|
||||
<button type="button" onClick={this.onCancelClick} className="danger">
|
||||
{_t("Skip")}
|
||||
</button>
|
||||
</DialogButtons>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhaseShowKey(): JSX.Element {
|
||||
let continueButton;
|
||||
if (this.state.phase === Phase.ShowKey) {
|
||||
continueButton = <DialogButtons primaryButton={_t("Continue")}
|
||||
disabled={!this.state.downloaded && !this.state.copied && !this.state.setPassphrase}
|
||||
onPrimaryButtonClick={this.onShowKeyContinueClick}
|
||||
hasCancel={false}
|
||||
/>;
|
||||
continueButton = (
|
||||
<DialogButtons
|
||||
primaryButton={_t("Continue")}
|
||||
disabled={!this.state.downloaded && !this.state.copied && !this.state.setPassphrase}
|
||||
onPrimaryButtonClick={this.onShowKeyContinueClick}
|
||||
hasCancel={false}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
continueButton = <div className="mx_CreateSecretStorageDialog_continueSpinner">
|
||||
<InlineSpinner />
|
||||
</div>;
|
||||
continueButton = (
|
||||
<div className="mx_CreateSecretStorageDialog_continueSpinner">
|
||||
<InlineSpinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <div>
|
||||
<p>{ _t(
|
||||
"Store your Security Key somewhere safe, like a password manager or a safe, " +
|
||||
"as it's used to safeguard your encrypted data.",
|
||||
) }</p>
|
||||
<div className="mx_CreateSecretStorageDialog_primaryContainer mx_CreateSecretStorageDialog_recoveryKeyPrimarycontainer">
|
||||
<div className="mx_CreateSecretStorageDialog_recoveryKeyContainer">
|
||||
<div className="mx_CreateSecretStorageDialog_recoveryKey">
|
||||
<code ref={this.recoveryKeyNode}>{ this.recoveryKey.encodedPrivateKey }</code>
|
||||
</div>
|
||||
<div className="mx_CreateSecretStorageDialog_recoveryKeyButtons">
|
||||
<AccessibleButton kind='primary'
|
||||
className="mx_Dialog_primary"
|
||||
onClick={this.onDownloadClick}
|
||||
disabled={this.state.phase === Phase.Storing}
|
||||
>
|
||||
{ _t("Download") }
|
||||
</AccessibleButton>
|
||||
<span>{ _t("%(downloadButton)s or %(copyButton)s", {
|
||||
downloadButton: "",
|
||||
copyButton: "",
|
||||
}) }</span>
|
||||
<AccessibleButton
|
||||
kind='primary'
|
||||
className="mx_Dialog_primary mx_CreateSecretStorageDialog_recoveryKeyButtons_copyBtn"
|
||||
onClick={this.onCopyClick}
|
||||
disabled={this.state.phase === Phase.Storing}
|
||||
>
|
||||
{ this.state.copied ? _t("Copied!") : _t("Copy") }
|
||||
</AccessibleButton>
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{_t(
|
||||
"Store your Security Key somewhere safe, like a password manager or a safe, " +
|
||||
"as it's used to safeguard your encrypted data.",
|
||||
)}
|
||||
</p>
|
||||
<div className="mx_CreateSecretStorageDialog_primaryContainer mx_CreateSecretStorageDialog_recoveryKeyPrimarycontainer">
|
||||
<div className="mx_CreateSecretStorageDialog_recoveryKeyContainer">
|
||||
<div className="mx_CreateSecretStorageDialog_recoveryKey">
|
||||
<code ref={this.recoveryKeyNode}>{this.recoveryKey.encodedPrivateKey}</code>
|
||||
</div>
|
||||
<div className="mx_CreateSecretStorageDialog_recoveryKeyButtons">
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
className="mx_Dialog_primary"
|
||||
onClick={this.onDownloadClick}
|
||||
disabled={this.state.phase === Phase.Storing}
|
||||
>
|
||||
{_t("Download")}
|
||||
</AccessibleButton>
|
||||
<span>
|
||||
{_t("%(downloadButton)s or %(copyButton)s", {
|
||||
downloadButton: "",
|
||||
copyButton: "",
|
||||
})}
|
||||
</span>
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
className="mx_Dialog_primary mx_CreateSecretStorageDialog_recoveryKeyButtons_copyBtn"
|
||||
onClick={this.onCopyClick}
|
||||
disabled={this.state.phase === Phase.Storing}
|
||||
>
|
||||
{this.state.copied ? _t("Copied!") : _t("Copy")}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{continueButton}
|
||||
</div>
|
||||
{ continueButton }
|
||||
</div>;
|
||||
);
|
||||
}
|
||||
|
||||
private renderBusyPhase(): JSX.Element {
|
||||
return <div>
|
||||
<Spinner />
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhaseLoadError(): JSX.Element {
|
||||
return <div>
|
||||
<p>{ _t("Unable to query secret storage status") }</p>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<DialogButtons primaryButton={_t('Retry')}
|
||||
onPrimaryButtonClick={this.onLoadRetryClick}
|
||||
hasCancel={this.state.canSkip}
|
||||
onCancel={this.onCancel}
|
||||
/>
|
||||
return (
|
||||
<div>
|
||||
<p>{_t("Unable to query secret storage status")}</p>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<DialogButtons
|
||||
primaryButton={_t("Retry")}
|
||||
onPrimaryButtonClick={this.onLoadRetryClick}
|
||||
hasCancel={this.state.canSkip}
|
||||
onCancel={this.onCancel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhaseSkipConfirm(): JSX.Element {
|
||||
return <div>
|
||||
<p>{ _t(
|
||||
"If you cancel now, you may lose encrypted messages & data if you lose access to your logins.",
|
||||
) }</p>
|
||||
<p>{ _t(
|
||||
"You can also set up Secure Backup & manage your keys in Settings.",
|
||||
) }</p>
|
||||
<DialogButtons primaryButton={_t('Go back')}
|
||||
onPrimaryButtonClick={this.onGoBackClick}
|
||||
hasCancel={false}
|
||||
>
|
||||
<button type="button" className="danger" onClick={this.onCancel}>{ _t('Cancel') }</button>
|
||||
</DialogButtons>
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{_t("If you cancel now, you may lose encrypted messages & data if you lose access to your logins.")}
|
||||
</p>
|
||||
<p>{_t("You can also set up Secure Backup & manage your keys in Settings.")}</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Go back")}
|
||||
onPrimaryButtonClick={this.onGoBackClick}
|
||||
hasCancel={false}
|
||||
>
|
||||
<button type="button" className="danger" onClick={this.onCancel}>
|
||||
{_t("Cancel")}
|
||||
</button>
|
||||
</DialogButtons>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private titleForPhase(phase: Phase): string {
|
||||
switch (phase) {
|
||||
case Phase.ChooseKeyPassphrase:
|
||||
return _t('Set up Secure Backup');
|
||||
return _t("Set up Secure Backup");
|
||||
case Phase.Migrate:
|
||||
return _t('Upgrade your encryption');
|
||||
return _t("Upgrade your encryption");
|
||||
case Phase.Passphrase:
|
||||
return _t('Set a Security Phrase');
|
||||
return _t("Set a Security Phrase");
|
||||
case Phase.PassphraseConfirm:
|
||||
return _t('Confirm Security Phrase');
|
||||
return _t("Confirm Security Phrase");
|
||||
case Phase.ConfirmSkip:
|
||||
return _t('Are you sure?');
|
||||
return _t("Are you sure?");
|
||||
case Phase.ShowKey:
|
||||
return _t('Save your Security Key');
|
||||
return _t("Save your Security Key");
|
||||
case Phase.Storing:
|
||||
return _t('Setting up keys');
|
||||
return _t("Setting up keys");
|
||||
default:
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
let content;
|
||||
if (this.state.error) {
|
||||
content = <div>
|
||||
<p>{ _t("Unable to set up secret storage") }</p>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<DialogButtons primaryButton={_t('Retry')}
|
||||
onPrimaryButtonClick={this.bootstrapSecretStorage}
|
||||
hasCancel={this.state.canSkip}
|
||||
onCancel={this.onCancel}
|
||||
/>
|
||||
content = (
|
||||
<div>
|
||||
<p>{_t("Unable to set up secret storage")}</p>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<DialogButtons
|
||||
primaryButton={_t("Retry")}
|
||||
onPrimaryButtonClick={this.bootstrapSecretStorage}
|
||||
hasCancel={this.state.canSkip}
|
||||
onCancel={this.onCancel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
} else {
|
||||
switch (this.state.phase) {
|
||||
case Phase.Loading:
|
||||
|
@ -851,32 +893,31 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
case Phase.Passphrase:
|
||||
case Phase.PassphraseConfirm:
|
||||
titleClass = [
|
||||
'mx_CreateSecretStorageDialog_titleWithIcon',
|
||||
'mx_CreateSecretStorageDialog_securePhraseTitle',
|
||||
"mx_CreateSecretStorageDialog_titleWithIcon",
|
||||
"mx_CreateSecretStorageDialog_securePhraseTitle",
|
||||
];
|
||||
break;
|
||||
case Phase.ShowKey:
|
||||
titleClass = [
|
||||
'mx_CreateSecretStorageDialog_titleWithIcon',
|
||||
'mx_CreateSecretStorageDialog_secureBackupTitle',
|
||||
"mx_CreateSecretStorageDialog_titleWithIcon",
|
||||
"mx_CreateSecretStorageDialog_secureBackupTitle",
|
||||
];
|
||||
break;
|
||||
case Phase.ChooseKeyPassphrase:
|
||||
titleClass = 'mx_CreateSecretStorageDialog_centeredTitle';
|
||||
titleClass = "mx_CreateSecretStorageDialog_centeredTitle";
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseDialog className='mx_CreateSecretStorageDialog'
|
||||
<BaseDialog
|
||||
className="mx_CreateSecretStorageDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title={this.titleForPhase(this.state.phase)}
|
||||
titleClass={titleClass}
|
||||
hasCancel={this.props.hasCancel && [Phase.Passphrase].includes(this.state.phase)}
|
||||
fixedWidth={false}
|
||||
>
|
||||
<div>
|
||||
{ content }
|
||||
</div>
|
||||
<div>{content}</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,13 +15,13 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import FileSaver from 'file-saver';
|
||||
import React from 'react';
|
||||
import { MatrixClient } from 'matrix-js-sdk/src/client';
|
||||
import FileSaver from "file-saver";
|
||||
import React from "react";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { _t } from '../../../../languageHandler';
|
||||
import * as MegolmExportEncryption from '../../../../utils/MegolmExportEncryption';
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import * as MegolmExportEncryption from "../../../../utils/MegolmExportEncryption";
|
||||
import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps";
|
||||
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
|
||||
import Field from "../../../../components/views/elements/Field";
|
||||
|
@ -68,11 +68,11 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
|
|||
|
||||
const passphrase = this.state.passphrase1;
|
||||
if (passphrase !== this.state.passphrase2) {
|
||||
this.setState({ errStr: _t('Passphrases must match') });
|
||||
this.setState({ errStr: _t("Passphrases must match") });
|
||||
return false;
|
||||
}
|
||||
if (!passphrase) {
|
||||
this.setState({ errStr: _t('Passphrase must not be empty') });
|
||||
this.setState({ errStr: _t("Passphrase must not be empty") });
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -83,29 +83,31 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
|
|||
private startExport(passphrase: string): void {
|
||||
// extra Promise.resolve() to turn synchronous exceptions into
|
||||
// asynchronous ones.
|
||||
Promise.resolve().then(() => {
|
||||
return this.props.matrixClient.exportRoomKeys();
|
||||
}).then((k) => {
|
||||
return MegolmExportEncryption.encryptMegolmKeyFile(
|
||||
JSON.stringify(k), passphrase,
|
||||
);
|
||||
}).then((f) => {
|
||||
const blob = new Blob([f], {
|
||||
type: 'text/plain;charset=us-ascii',
|
||||
Promise.resolve()
|
||||
.then(() => {
|
||||
return this.props.matrixClient.exportRoomKeys();
|
||||
})
|
||||
.then((k) => {
|
||||
return MegolmExportEncryption.encryptMegolmKeyFile(JSON.stringify(k), passphrase);
|
||||
})
|
||||
.then((f) => {
|
||||
const blob = new Blob([f], {
|
||||
type: "text/plain;charset=us-ascii",
|
||||
});
|
||||
FileSaver.saveAs(blob, "element-keys.txt");
|
||||
this.props.onFinished(true);
|
||||
})
|
||||
.catch((e) => {
|
||||
logger.error("Error exporting e2e keys:", e);
|
||||
if (this.unmounted) {
|
||||
return;
|
||||
}
|
||||
const msg = e.friendlyText || _t("Unknown error");
|
||||
this.setState({
|
||||
errStr: msg,
|
||||
phase: Phase.Edit,
|
||||
});
|
||||
});
|
||||
FileSaver.saveAs(blob, 'element-keys.txt');
|
||||
this.props.onFinished(true);
|
||||
}).catch((e) => {
|
||||
logger.error("Error exporting e2e keys:", e);
|
||||
if (this.unmounted) {
|
||||
return;
|
||||
}
|
||||
const msg = e.friendlyText || _t('Unknown error');
|
||||
this.setState({
|
||||
errStr: msg,
|
||||
phase: Phase.Edit,
|
||||
});
|
||||
});
|
||||
|
||||
this.setState({
|
||||
errStr: null,
|
||||
|
@ -126,54 +128,53 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
|
|||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
const disableForm = (this.state.phase === Phase.Exporting);
|
||||
const disableForm = this.state.phase === Phase.Exporting;
|
||||
|
||||
return (
|
||||
<BaseDialog className='mx_exportE2eKeysDialog'
|
||||
<BaseDialog
|
||||
className="mx_exportE2eKeysDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title={_t("Export room keys")}
|
||||
>
|
||||
<form onSubmit={this.onPassphraseFormSubmit}>
|
||||
<div className="mx_Dialog_content">
|
||||
<p>
|
||||
{ _t(
|
||||
'This process allows you to export the keys for messages ' +
|
||||
'you have received in encrypted rooms to a local file. You ' +
|
||||
'will then be able to import the file into another Matrix ' +
|
||||
'client in the future, so that client will also be able to ' +
|
||||
'decrypt these messages.',
|
||||
) }
|
||||
{_t(
|
||||
"This process allows you to export the keys for messages " +
|
||||
"you have received in encrypted rooms to a local file. You " +
|
||||
"will then be able to import the file into another Matrix " +
|
||||
"client in the future, so that client will also be able to " +
|
||||
"decrypt these messages.",
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{ _t(
|
||||
'The exported file will allow anyone who can read it to decrypt ' +
|
||||
'any encrypted messages that you can see, so you should be ' +
|
||||
'careful to keep it secure. To help with this, you should enter ' +
|
||||
'a passphrase below, which will be used to encrypt the exported ' +
|
||||
'data. It will only be possible to import the data by using the ' +
|
||||
'same passphrase.',
|
||||
) }
|
||||
{_t(
|
||||
"The exported file will allow anyone who can read it to decrypt " +
|
||||
"any encrypted messages that you can see, so you should be " +
|
||||
"careful to keep it secure. To help with this, you should enter " +
|
||||
"a passphrase below, which will be used to encrypt the exported " +
|
||||
"data. It will only be possible to import the data by using the " +
|
||||
"same passphrase.",
|
||||
)}
|
||||
</p>
|
||||
<div className='error'>
|
||||
{ this.state.errStr }
|
||||
</div>
|
||||
<div className='mx_E2eKeysDialog_inputTable'>
|
||||
<div className='mx_E2eKeysDialog_inputRow'>
|
||||
<div className="error">{this.state.errStr}</div>
|
||||
<div className="mx_E2eKeysDialog_inputTable">
|
||||
<div className="mx_E2eKeysDialog_inputRow">
|
||||
<Field
|
||||
label={_t("Enter passphrase")}
|
||||
value={this.state.passphrase1}
|
||||
onChange={e => this.onPassphraseChange(e, "passphrase1")}
|
||||
onChange={(e) => this.onPassphraseChange(e, "passphrase1")}
|
||||
autoFocus={true}
|
||||
size={64}
|
||||
type="password"
|
||||
disabled={disableForm}
|
||||
/>
|
||||
</div>
|
||||
<div className='mx_E2eKeysDialog_inputRow'>
|
||||
<div className="mx_E2eKeysDialog_inputRow">
|
||||
<Field
|
||||
label={_t("Confirm passphrase")}
|
||||
value={this.state.passphrase2}
|
||||
onChange={e => this.onPassphraseChange(e, "passphrase2")}
|
||||
onChange={(e) => this.onPassphraseChange(e, "passphrase2")}
|
||||
size={64}
|
||||
type="password"
|
||||
disabled={disableForm}
|
||||
|
@ -181,15 +182,15 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mx_Dialog_buttons'>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<input
|
||||
className='mx_Dialog_primary'
|
||||
type='submit'
|
||||
value={_t('Export')}
|
||||
className="mx_Dialog_primary"
|
||||
type="submit"
|
||||
value={_t("Export")}
|
||||
disabled={disableForm}
|
||||
/>
|
||||
<button onClick={this.onCancelClick} disabled={disableForm}>
|
||||
{ _t("Cancel") }
|
||||
{_t("Cancel")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -15,12 +15,12 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createRef } from 'react';
|
||||
import { MatrixClient } from 'matrix-js-sdk/src/client';
|
||||
import React, { createRef } from "react";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import * as MegolmExportEncryption from '../../../../utils/MegolmExportEncryption';
|
||||
import { _t } from '../../../../languageHandler';
|
||||
import * as MegolmExportEncryption from "../../../../utils/MegolmExportEncryption";
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps";
|
||||
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
|
||||
import Field from "../../../../components/views/elements/Field";
|
||||
|
@ -75,7 +75,7 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
|
|||
private onFormChange = (): void => {
|
||||
const files = this.file.current.files || [];
|
||||
this.setState({
|
||||
enableSubmit: (this.state.passphrase !== "" && files.length > 0),
|
||||
enableSubmit: this.state.passphrase !== "" && files.length > 0,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -97,26 +97,28 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
|
|||
phase: Phase.Importing,
|
||||
});
|
||||
|
||||
return readFileAsArrayBuffer(file).then((arrayBuffer) => {
|
||||
return MegolmExportEncryption.decryptMegolmKeyFile(
|
||||
arrayBuffer, passphrase,
|
||||
);
|
||||
}).then((keys) => {
|
||||
return this.props.matrixClient.importRoomKeys(JSON.parse(keys));
|
||||
}).then(() => {
|
||||
// TODO: it would probably be nice to give some feedback about what we've imported here.
|
||||
this.props.onFinished(true);
|
||||
}).catch((e) => {
|
||||
logger.error("Error importing e2e keys:", e);
|
||||
if (this.unmounted) {
|
||||
return;
|
||||
}
|
||||
const msg = e.friendlyText || _t('Unknown error');
|
||||
this.setState({
|
||||
errStr: msg,
|
||||
phase: Phase.Edit,
|
||||
return readFileAsArrayBuffer(file)
|
||||
.then((arrayBuffer) => {
|
||||
return MegolmExportEncryption.decryptMegolmKeyFile(arrayBuffer, passphrase);
|
||||
})
|
||||
.then((keys) => {
|
||||
return this.props.matrixClient.importRoomKeys(JSON.parse(keys));
|
||||
})
|
||||
.then(() => {
|
||||
// TODO: it would probably be nice to give some feedback about what we've imported here.
|
||||
this.props.onFinished(true);
|
||||
})
|
||||
.catch((e) => {
|
||||
logger.error("Error importing e2e keys:", e);
|
||||
if (this.unmounted) {
|
||||
return;
|
||||
}
|
||||
const msg = e.friendlyText || _t("Unknown error");
|
||||
this.setState({
|
||||
errStr: msg,
|
||||
phase: Phase.Edit,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private onCancelClick = (ev: React.MouseEvent): boolean => {
|
||||
|
@ -126,50 +128,48 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
|
|||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
const disableForm = (this.state.phase !== Phase.Edit);
|
||||
const disableForm = this.state.phase !== Phase.Edit;
|
||||
|
||||
return (
|
||||
<BaseDialog className='mx_importE2eKeysDialog'
|
||||
<BaseDialog
|
||||
className="mx_importE2eKeysDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title={_t("Import room keys")}
|
||||
>
|
||||
<form onSubmit={this.onFormSubmit}>
|
||||
<div className="mx_Dialog_content">
|
||||
<p>
|
||||
{ _t(
|
||||
'This process allows you to import encryption keys ' +
|
||||
'that you had previously exported from another Matrix ' +
|
||||
'client. You will then be able to decrypt any ' +
|
||||
'messages that the other client could decrypt.',
|
||||
) }
|
||||
{_t(
|
||||
"This process allows you to import encryption keys " +
|
||||
"that you had previously exported from another Matrix " +
|
||||
"client. You will then be able to decrypt any " +
|
||||
"messages that the other client could decrypt.",
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{ _t(
|
||||
'The export file will be protected with a passphrase. ' +
|
||||
'You should enter the passphrase here, to decrypt the file.',
|
||||
) }
|
||||
{_t(
|
||||
"The export file will be protected with a passphrase. " +
|
||||
"You should enter the passphrase here, to decrypt the file.",
|
||||
)}
|
||||
</p>
|
||||
<div className='error'>
|
||||
{ this.state.errStr }
|
||||
</div>
|
||||
<div className='mx_E2eKeysDialog_inputTable'>
|
||||
<div className='mx_E2eKeysDialog_inputRow'>
|
||||
<div className='mx_E2eKeysDialog_inputLabel'>
|
||||
<label htmlFor='importFile'>
|
||||
{ _t("File to import") }
|
||||
</label>
|
||||
<div className="error">{this.state.errStr}</div>
|
||||
<div className="mx_E2eKeysDialog_inputTable">
|
||||
<div className="mx_E2eKeysDialog_inputRow">
|
||||
<div className="mx_E2eKeysDialog_inputLabel">
|
||||
<label htmlFor="importFile">{_t("File to import")}</label>
|
||||
</div>
|
||||
<div className='mx_E2eKeysDialog_inputCell'>
|
||||
<div className="mx_E2eKeysDialog_inputCell">
|
||||
<input
|
||||
ref={this.file}
|
||||
id='importFile'
|
||||
type='file'
|
||||
id="importFile"
|
||||
type="file"
|
||||
autoFocus={true}
|
||||
onChange={this.onFormChange}
|
||||
disabled={disableForm} />
|
||||
disabled={disableForm}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mx_E2eKeysDialog_inputRow'>
|
||||
<div className="mx_E2eKeysDialog_inputRow">
|
||||
<Field
|
||||
label={_t("Enter passphrase")}
|
||||
value={this.state.passphrase}
|
||||
|
@ -181,15 +181,15 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mx_Dialog_buttons'>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<input
|
||||
className='mx_Dialog_primary'
|
||||
type='submit'
|
||||
value={_t('Import')}
|
||||
className="mx_Dialog_primary"
|
||||
type="submit"
|
||||
value={_t("Import")}
|
||||
disabled={!this.state.enableSubmit || disableForm}
|
||||
/>
|
||||
<button onClick={this.onCancelClick} disabled={disableForm}>
|
||||
{ _t("Cancel") }
|
||||
{_t("Cancel")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -18,7 +18,7 @@ limitations under the License.
|
|||
import React from "react";
|
||||
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
|
||||
|
||||
import { MatrixClientPeg } from '../../../../MatrixClientPeg';
|
||||
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
|
||||
import dis from "../../../../dispatcher/dispatcher";
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import Modal from "../../../../Modal";
|
||||
|
@ -43,61 +43,66 @@ export default class NewRecoveryMethodDialog extends React.PureComponent<IProps>
|
|||
};
|
||||
|
||||
private onSetupClick = async (): Promise<void> => {
|
||||
Modal.createDialog(RestoreKeyBackupDialog, {
|
||||
onFinished: this.props.onFinished,
|
||||
}, null, /* priority = */ false, /* static = */ true);
|
||||
Modal.createDialog(
|
||||
RestoreKeyBackupDialog,
|
||||
{
|
||||
onFinished: this.props.onFinished,
|
||||
},
|
||||
null,
|
||||
/* priority = */ false,
|
||||
/* static = */ true,
|
||||
);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
const title = <span className="mx_KeyBackupFailedDialog_title">
|
||||
{ _t("New Recovery Method") }
|
||||
</span>;
|
||||
const title = <span className="mx_KeyBackupFailedDialog_title">{_t("New Recovery Method")}</span>;
|
||||
|
||||
const newMethodDetected = <p>{ _t(
|
||||
"A new Security Phrase and key for Secure Messages have been detected.",
|
||||
) }</p>;
|
||||
const newMethodDetected = <p>{_t("A new Security Phrase and key for Secure Messages have been detected.")}</p>;
|
||||
|
||||
const hackWarning = <p className="warning">{ _t(
|
||||
"If you didn't set the new recovery method, an " +
|
||||
"attacker may be trying to access your account. " +
|
||||
"Change your account password and set a new recovery " +
|
||||
"method immediately in Settings.",
|
||||
) }</p>;
|
||||
const hackWarning = (
|
||||
<p className="warning">
|
||||
{_t(
|
||||
"If you didn't set the new recovery method, an " +
|
||||
"attacker may be trying to access your account. " +
|
||||
"Change your account password and set a new recovery " +
|
||||
"method immediately in Settings.",
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
|
||||
let content;
|
||||
if (MatrixClientPeg.get().getKeyBackupEnabled()) {
|
||||
content = <div>
|
||||
{ newMethodDetected }
|
||||
<p>{ _t(
|
||||
"This session is encrypting history using the new recovery method.",
|
||||
) }</p>
|
||||
{ hackWarning }
|
||||
<DialogButtons
|
||||
primaryButton={_t("OK")}
|
||||
onPrimaryButtonClick={this.onOkClick}
|
||||
cancelButton={_t("Go to Settings")}
|
||||
onCancel={this.onGoToSettingsClick}
|
||||
/>
|
||||
</div>;
|
||||
content = (
|
||||
<div>
|
||||
{newMethodDetected}
|
||||
<p>{_t("This session is encrypting history using the new recovery method.")}</p>
|
||||
{hackWarning}
|
||||
<DialogButtons
|
||||
primaryButton={_t("OK")}
|
||||
onPrimaryButtonClick={this.onOkClick}
|
||||
cancelButton={_t("Go to Settings")}
|
||||
onCancel={this.onGoToSettingsClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
content = <div>
|
||||
{ newMethodDetected }
|
||||
{ hackWarning }
|
||||
<DialogButtons
|
||||
primaryButton={_t("Set up Secure Messages")}
|
||||
onPrimaryButtonClick={this.onSetupClick}
|
||||
cancelButton={_t("Go to Settings")}
|
||||
onCancel={this.onGoToSettingsClick}
|
||||
/>
|
||||
</div>;
|
||||
content = (
|
||||
<div>
|
||||
{newMethodDetected}
|
||||
{hackWarning}
|
||||
<DialogButtons
|
||||
primaryButton={_t("Set up Secure Messages")}
|
||||
onPrimaryButtonClick={this.onSetupClick}
|
||||
cancelButton={_t("Go to Settings")}
|
||||
onCancel={this.onGoToSettingsClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseDialog className="mx_KeyBackupFailedDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title={title}
|
||||
>
|
||||
{ content }
|
||||
<BaseDialog className="mx_KeyBackupFailedDialog" onFinished={this.props.onFinished} title={title}>
|
||||
{content}
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -37,36 +37,40 @@ export default class RecoveryMethodRemovedDialog extends React.PureComponent<IPr
|
|||
this.props.onFinished();
|
||||
Modal.createDialogAsync(
|
||||
import("./CreateKeyBackupDialog") as unknown as Promise<ComponentType<{}>>,
|
||||
null, null, /* priority = */ false, /* static = */ true,
|
||||
null,
|
||||
null,
|
||||
/* priority = */ false,
|
||||
/* static = */ true,
|
||||
);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
const title = <span className="mx_KeyBackupFailedDialog_title">
|
||||
{ _t("Recovery Method Removed") }
|
||||
</span>;
|
||||
const title = <span className="mx_KeyBackupFailedDialog_title">{_t("Recovery Method Removed")}</span>;
|
||||
|
||||
return (
|
||||
<BaseDialog className="mx_KeyBackupFailedDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title={title}
|
||||
>
|
||||
<BaseDialog className="mx_KeyBackupFailedDialog" onFinished={this.props.onFinished} title={title}>
|
||||
<div>
|
||||
<p>{ _t(
|
||||
"This session has detected that your Security Phrase and key " +
|
||||
"for Secure Messages have been removed.",
|
||||
) }</p>
|
||||
<p>{ _t(
|
||||
"If you did this accidentally, you can setup Secure Messages on " +
|
||||
"this session which will re-encrypt this session's message " +
|
||||
"history with a new recovery method.",
|
||||
) }</p>
|
||||
<p className="warning">{ _t(
|
||||
"If you didn't remove the recovery method, an " +
|
||||
"attacker may be trying to access your account. " +
|
||||
"Change your account password and set a new recovery " +
|
||||
"method immediately in Settings.",
|
||||
) }</p>
|
||||
<p>
|
||||
{_t(
|
||||
"This session has detected that your Security Phrase and key " +
|
||||
"for Secure Messages have been removed.",
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{_t(
|
||||
"If you did this accidentally, you can setup Secure Messages on " +
|
||||
"this session which will re-encrypt this session's message " +
|
||||
"history with a new recovery method.",
|
||||
)}
|
||||
</p>
|
||||
<p className="warning">
|
||||
{_t(
|
||||
"If you didn't remove the recovery method, an " +
|
||||
"attacker may be trying to access your account. " +
|
||||
"Change your account password and set a new recovery " +
|
||||
"method immediately in Settings.",
|
||||
)}
|
||||
</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Set up Secure Messages")}
|
||||
onPrimaryButtonClick={this.onSetupClick}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue