diff --git a/res/css/_components.scss b/res/css/_components.scss
index 039bcd545b..083071ef6c 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -47,6 +47,7 @@
@import "./views/dialogs/_ShareDialog.scss";
@import "./views/dialogs/_UnknownDeviceDialog.scss";
@import "./views/dialogs/keybackup/_CreateKeyBackupDialog.scss";
+@import "./views/dialogs/keybackup/_RestoreKeyBackupDialog.scss";
@import "./views/directory/_NetworkDropdown.scss";
@import "./views/elements/_AccessibleButton.scss";
@import "./views/elements/_AddressSelector.scss";
diff --git a/res/css/views/dialogs/keybackup/_RestoreKeyBackupDialog.scss b/res/css/views/dialogs/keybackup/_RestoreKeyBackupDialog.scss
new file mode 100644
index 0000000000..612c921038
--- /dev/null
+++ b/res/css/views/dialogs/keybackup/_RestoreKeyBackupDialog.scss
@@ -0,0 +1,29 @@
+/*
+Copyright 2018 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+.mx_RestoreKeyBackupDialog_primaryContainer {
+ /*FIXME: plinth colour in new theme(s). background-color: $accent-color;*/
+ padding: 20px
+}
+
+.mx_RestoreKeyBackupDialog_passPhraseInput,
+.mx_RestoreKeyBackupDialog_recoveryKeyInput {
+ width: 300px;
+ border: 1px solid $accent-color;
+ border-radius: 5px;
+ padding: 10px;
+}
+
diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js
index 9e5e61cb1a..e4250814d0 100644
--- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js
+++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js
@@ -17,6 +17,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../../index';
import MatrixClientPeg from '../../../../MatrixClientPeg';
+import Modal from '../../../../Modal';
import { _t } from '../../../../languageHandler';
@@ -33,6 +34,9 @@ export default React.createClass({
recoveryKey: "",
recoverInfo: null,
recoveryKeyValid: false,
+ forceRecoveryKey: false,
+ passPhrase: '',
+ recoveryKey: '',
};
},
@@ -48,6 +52,18 @@ export default React.createClass({
this.props.onFinished(true);
},
+ _onUseRecoveryKeyClick: function() {
+ this.setState({
+ forceRecoveryKey: true,
+ });
+ },
+
+ _onResetRecoveryClick: function() {
+ this.props.onFinished(false);
+ const CreateKeyBackupDialog = sdk.getComponent("dialogs.keybackup.CreateKeyBackupDialog");
+ Modal.createTrackedDialog('Create Key Backup', '', CreateKeyBackupDialog, {});
+ },
+
_onRecoveryKeyChange: function(e) {
this.setState({
recoveryKey: e.target.value,
@@ -55,13 +71,35 @@ export default React.createClass({
});
},
- _onRecoverClick: async function() {
+ _onPassPhraseNext: async function() {
this.setState({
loading: true,
restoreError: null,
});
try {
- const recoverInfo = await MatrixClientPeg.get().restoreKeyBackups(
+ const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithPassword(
+ this.state.passPhrase, undefined, undefined, this.state.backupInfo.version,
+ );
+ this.setState({
+ loading: false,
+ recoverInfo,
+ });
+ } catch (e) {
+ console.log("Error restoring backup", e);
+ this.setState({
+ loading: false,
+ restoreError: e,
+ });
+ }
+ },
+
+ _onRecoveryKeyNext: async function() {
+ this.setState({
+ loading: true,
+ restoreError: null,
+ });
+ try {
+ const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithRecoveryKey(
this.state.recoveryKey, undefined, undefined, this.state.backupInfo.version,
);
this.setState({
@@ -77,6 +115,24 @@ export default React.createClass({
}
},
+ _onPassPhraseChange: function(e) {
+ this.setState({
+ passPhrase: e.target.value,
+ });
+ },
+
+ _onPassPhraseKeyPress: function(e) {
+ if (e.key === "Enter") {
+ this._onPassPhraseNext();
+ }
+ },
+
+ _onRecoveryKeyKeyPress: function(e) {
+ if (e.key === "Enter" && this.state.recoveryKeyValid) {
+ this._onRecoveryKeyNext();
+ }
+ },
+
_loadBackupStatus: async function() {
this.setState({
loading: true,
@@ -102,16 +158,29 @@ export default React.createClass({
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const Spinner = sdk.getComponent("elements.Spinner");
+ const backupHasPassphrase = (
+ this.state.backupInfo &&
+ this.state.backupInfo.auth_data &&
+ this.state.backupInfo.auth_data.private_key_salt &&
+ this.state.backupInfo.auth_data.private_key_iterations
+ );
+
let content;
+ let title;
if (this.state.loading) {
+ title = _t("Loading...");
content =
{_t( @@ -123,8 +192,54 @@ export default React.createClass({
{_t("Restored %(sessionCount)s session keys", {sessionCount: this.state.recoverInfo.imported})}
{failedToDecrypt} ; - } else { + } else if (backupHasPassphrase && !this.state.forceRecoveryKey) { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + title = _t("Enter Recovery Passphrase"); + content =