{_t(
"Your recovery key is a safety net - you can use it to restore " +
@@ -572,12 +593,12 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
{this._encodedRecoveryKey}
{content}
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 0486ce764c..b4b38d7617 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -89,12 +89,15 @@ export const VIEWS = {
// showing flow to trust this new device with cross-signing
COMPLETE_SECURITY: 6,
+ // flow to setup SSSS / cross-signing on this account
+ E2E_SETUP: 7,
+
// we are logged in with an active matrix client.
- LOGGED_IN: 7,
+ LOGGED_IN: 8,
// We are logged out (invalid token) but have our local state again. The user
// should log back in to rehydrate the client.
- SOFT_LOGOUT: 8,
+ SOFT_LOGOUT: 9,
};
// Actions that are redirected through the onboarding process prior to being
@@ -657,7 +660,9 @@ export default createReactClass({
if (
!Lifecycle.isSoftLogout() &&
this.state.view !== VIEWS.LOGIN &&
- this.state.view !== VIEWS.COMPLETE_SECURITY
+ this.state.view !== VIEWS.REGISTER &&
+ this.state.view !== VIEWS.COMPLETE_SECURITY &&
+ this.state.view !== VIEWS.E2E_SETUP
) {
this._onLoggedIn();
}
@@ -1724,6 +1729,11 @@ export default createReactClass({
this.showScreen("forgot_password");
},
+ onRegisterFlowComplete: function(credentials) {
+ this.onUserCompletedLoginFlow();
+ return this.onRegistered(credentials);
+ },
+
// returns a promise which resolves to the new MatrixClient
onRegistered: function(credentials) {
return Lifecycle.setLoggedIn(credentials);
@@ -1847,12 +1857,18 @@ export default createReactClass({
if (masterKeyInStorage) {
this.setStateForNewView({ view: VIEWS.COMPLETE_SECURITY });
+ } else if (SettingsStore.isFeatureEnabled("feature_cross_signing")) {
+ // This will only work if the feature is set to 'enable' in the config,
+ // since it's too early in the lifecycle for users to have turned the
+ // labs flag on.
+ this.setStateForNewView({ view: VIEWS.E2E_SETUP });
} else {
this._onLoggedIn();
}
},
- onCompleteSecurityFinished() {
+ // complete security / e2e setup has finished
+ onCompleteSecurityE2eSetupFinished() {
this._onLoggedIn();
},
@@ -1872,7 +1888,14 @@ export default createReactClass({
const CompleteSecurity = sdk.getComponent('structures.auth.CompleteSecurity');
view = (
+ );
+ } else if (this.state.view === VIEWS.E2E_SETUP) {
+ const E2eSetup = sdk.getComponent('structures.auth.E2eSetup');
+ view = (
+
);
} else if (this.state.view === VIEWS.POST_REGISTRATION) {
@@ -1939,7 +1962,7 @@ export default createReactClass({
email={this.props.startingFragmentQueryParams.email}
brand={this.props.config.brand}
makeRegistrationUrl={this._makeRegistrationUrl}
- onLoggedIn={this.onRegistered}
+ onLoggedIn={this.onRegisterFlowComplete}
onLoginClick={this.onLoginClick}
onServerConfigChange={this.onServerConfigChange}
{...this.getServerProperties()}
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 5c243f04bc..60fff5f1e3 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -766,7 +766,7 @@ export default createReactClass({
onUserVerificationChanged: function(userId, _trustStatus) {
const room = this.state.room;
- if (!room.currentState.getMember(userId)) {
+ if (!room || !room.currentState.getMember(userId)) {
return;
}
this._updateE2EStatus(room);
diff --git a/src/components/structures/auth/E2eSetup.js b/src/components/structures/auth/E2eSetup.js
new file mode 100644
index 0000000000..a5f4ff933b
--- /dev/null
+++ b/src/components/structures/auth/E2eSetup.js
@@ -0,0 +1,48 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import AsyncWrapper from '../../../AsyncWrapper';
+import * as sdk from '../../../index';
+
+export default class E2eSetup extends React.Component {
+ static propTypes = {
+ onFinished: PropTypes.func.isRequired,
+ };
+
+ constructor() {
+ super();
+ // awkwardly indented because https://github.com/eslint/eslint/issues/11310
+ this._createStorageDialogPromise =
+ import("../../../async-components/views/dialogs/secretstorage/CreateSecretStorageDialog");
+ }
+
+ render() {
+ const AuthPage = sdk.getComponent("auth.AuthPage");
+ const AuthBody = sdk.getComponent("auth.AuthBody");
+ return (
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/components/views/auth/AuthBody.js b/src/components/views/auth/AuthBody.js
index fe20d76afb..b74b7d866a 100644
--- a/src/components/views/auth/AuthBody.js
+++ b/src/components/views/auth/AuthBody.js
@@ -33,6 +33,10 @@ export default class AuthBody extends React.PureComponent {
const classes = {
'mx_AuthBody': true,
'mx_AuthBody_noHeader': !this.props.header,
+ // XXX The login pages all use a smaller fonts size but we don't want this
+ // for subsequent auth screens like the e2e setup. Doing this a terrible way
+ // for now.
+ 'mx_AuthBody_loginRegister': this.props.header,
};
return
diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js
index 77fdee5e8a..0c432ba542 100644
--- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js
+++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js
@@ -16,6 +16,7 @@ limitations under the License.
*/
import React from 'react';
+import PropTypes from 'prop-types';
import * as sdk from '../../../../index';
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
import { MatrixClient } from 'matrix-js-sdk';
@@ -32,6 +33,16 @@ const RESTORE_TYPE_SECRET_STORAGE = 2;
* Dialog for restoring e2e keys from a backup and the user's recovery key
*/
export default class RestoreKeyBackupDialog extends React.PureComponent {
+ static propTypes = {
+ // if false, will close the dialog as soon as the restore completes succesfully
+ // default: true
+ showSummary: PropTypes.bool,
+ };
+
+ defaultProps = {
+ showSummary: true,
+ };
+
constructor(props) {
super(props);
this.state = {
@@ -96,6 +107,10 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithPassword(
this.state.passPhrase, undefined, undefined, this.state.backupInfo,
);
+ if (!this.props.showSummary) {
+ this.props.onFinished(true);
+ return;
+ }
this.setState({
loading: false,
recoverInfo,
@@ -119,6 +134,10 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithRecoveryKey(
this.state.recoveryKey, undefined, undefined, this.state.backupInfo,
);
+ if (!this.props.showSummary) {
+ this.props.onFinished(true);
+ return;
+ }
this.setState({
loading: false,
recoverInfo,
@@ -253,6 +272,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
title = _t("Error");
content = _t("No backup found!");
} else if (this.state.recoverInfo) {
+ const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
title = _t("Backup Restored");
let failedToDecrypt;
if (this.state.recoverInfo.total > this.state.recoverInfo.imported) {
@@ -264,6 +284,11 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
content =