diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js
new file mode 100644
index 0000000000..a0451a9201
--- /dev/null
+++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js
@@ -0,0 +1,156 @@
+/*
+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.
+*/
+
+import Modal from '../../../../Modal';
+import React from 'react';
+import PropTypes from 'prop-types';
+import sdk from '../../../../index';
+import MatrixClientPeg from '../../../../MatrixClientPeg';
+import { formatCryptoKey } from '../../../../utils/FormattingUtils';
+import Promise from 'bluebird';
+
+import { _t, _td } from '../../../../languageHandler';
+
+/**
+ * Dialog for restoring e2e keys from a backup and the user's recovery key
+ */
+export default React.createClass({
+ getInitialState: function() {
+ return {
+ backupInfo: null,
+ loading: false,
+ loadError: null,
+ restoreError: null,
+ recoveryKey: "",
+ recoverInfo: null,
+ };
+ },
+
+ componentWillMount: function() {
+ this._loadBackupStatus();
+ },
+
+ _onCancel: function() {
+ this.props.onFinished(false);
+ },
+
+ _onDone: function() {
+ this.props.onFinished(true);
+ },
+
+ _onRecoveryKeyChange: function(e) {
+ this.setState({recoveryKey: e.target.value});
+ },
+
+ _onRecoverClick: async function() {
+ this.setState({
+ loading: true,
+ restoreError: null,
+ });
+ try {
+ const recoverInfo = await MatrixClientPeg.get().restoreKeyBackups(
+ this.state.recoveryKey.replace(/ /g, ''), 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,
+ });
+ }
+ },
+
+ _loadBackupStatus: async function() {
+ this.setState({
+ loading: true,
+ loadError: null,
+ });
+ try {
+ const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
+ this.setState({
+ loadError: null,
+ loading: false,
+ backupInfo,
+ });
+ } catch (e) {
+ console.log("Error loading backup status", e);
+ this.setState({
+ loadError: e,
+ loading: false,
+ });
+ }
+ },
+
+ render: function() {
+ const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
+ const Spinner = sdk.getComponent("elements.Spinner");
+
+ let content;
+ if (this.state.loading) {
+ content =
{_t( + "Failed to decrypt %(failedCount)s sessions!", + {failedCount: this.state.recoverInfo.total - this.state.recoverInfo.imported}, + )}
; + } + content ={_t("Restored %(sessionCount)s session keys", {sessionCount: this.state.recoverInfo.imported})}
+ {failedToDecrypt} +