{_t(
+ "Without completing security on this session, it won’t have " +
+ "access to encrypted messages.",
+ )}
+
+
+ {_t("Skip")}
+
+
+ {_t("Go Back")}
+
+
+
+ );
+ } else if (phase === PHASE_BUSY) {
+ const Spinner = sdk.getComponent('views.elements.Spinner');
+ return ;
+ } else {
+ console.log(`SetupEncryptionBody: Unknown phase ${phase}`);
+ }
+ }
+}
diff --git a/src/components/views/dialogs/SetupEncryptionDialog.js b/src/components/views/dialogs/SetupEncryptionDialog.js
new file mode 100644
index 0000000000..f32a289a29
--- /dev/null
+++ b/src/components/views/dialogs/SetupEncryptionDialog.js
@@ -0,0 +1,29 @@
+/*
+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 SetupEncryptionBody from '../../structures/auth/SetupEncryptionBody';
+import BaseDialog from './BaseDialog';
+import { _t } from '../../../languageHandler';
+
+export default function SetupEncryptionDialog({onFinished}) {
+ return
+
+ ;
+}
diff --git a/src/components/views/toasts/SetupEncryptionToast.js b/src/components/views/toasts/SetupEncryptionToast.js
index 9016e4c6d7..ad6488a9bb 100644
--- a/src/components/views/toasts/SetupEncryptionToast.js
+++ b/src/components/views/toasts/SetupEncryptionToast.js
@@ -18,7 +18,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import * as sdk from "../../../index";
import { _t } from '../../../languageHandler';
+import Modal from '../../../Modal';
import DeviceListener from '../../../DeviceListener';
+import SetupEncryptionDialog from "../dialogs/SetupEncryptionDialog";
import { accessSecretStorage } from '../../../CrossSigningManager';
export default class SetupEncryptionToast extends React.PureComponent {
@@ -32,7 +34,12 @@ export default class SetupEncryptionToast extends React.PureComponent {
};
_onSetupClick = async () => {
- accessSecretStorage();
+ if (this.props.kind === "verify_this_session") {
+ Modal.createTrackedDialog('Verify session', 'Verify session', SetupEncryptionDialog,
+ {}, null, /* priority = */ false, /* static = */ true);
+ } else {
+ accessSecretStorage();
+ }
};
getDescription() {
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 12bd462937..5d923e0a24 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -2010,14 +2010,7 @@
"Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other",
"Could not load user profile": "Could not load user profile",
"Complete security": "Complete security",
- "Open an existing session & use it to verify this one, granting it access to encrypted messages.": "Open an existing session & use it to verify this one, granting it access to encrypted messages.",
- "Waiting…": "Waiting…",
- "If you can’t access one, ": "If you can’t access one, ",
"Session verified": "Session verified",
- "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.",
- "Your new session is now verified. Other users will see it as trusted.": "Your new session is now verified. Other users will see it as trusted.",
- "Without completing security on this session, it won’t have access to encrypted messages.": "Without completing security on this session, it won’t have access to encrypted messages.",
- "Go Back": "Go Back",
"Failed to send email": "Failed to send email",
"The email address linked to your account must be entered.": "The email address linked to your account must be entered.",
"A new password must be entered.": "A new password must be entered.",
@@ -2067,6 +2060,13 @@
"You can now close this window or log in to your new account.": "You can now close this window or log in to your new account.",
"Registration Successful": "Registration Successful",
"Create your account": "Create your account",
+ "Open an existing session & use it to verify this one, granting it access to encrypted messages.": "Open an existing session & use it to verify this one, granting it access to encrypted messages.",
+ "Waiting…": "Waiting…",
+ "If you can’t access one, ": "If you can’t access one, ",
+ "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.",
+ "Your new session is now verified. Other users will see it as trusted.": "Your new session is now verified. Other users will see it as trusted.",
+ "Without completing security on this session, it won’t have access to encrypted messages.": "Without completing security on this session, it won’t have access to encrypted messages.",
+ "Go Back": "Go Back",
"Failed to re-authenticate due to a homeserver problem": "Failed to re-authenticate due to a homeserver problem",
"Failed to re-authenticate": "Failed to re-authenticate",
"Regain access to your account and recover encryption keys stored in this session. Without them, you won’t be able to read all of your secure messages in any session.": "Regain access to your account and recover encryption keys stored in this session. Without them, you won’t be able to read all of your secure messages in any session.",
diff --git a/src/stores/SetupEncryptionStore.js b/src/stores/SetupEncryptionStore.js
new file mode 100644
index 0000000000..7b42e1552d
--- /dev/null
+++ b/src/stores/SetupEncryptionStore.js
@@ -0,0 +1,144 @@
+/*
+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 EventEmitter from 'events';
+import { MatrixClientPeg } from '../MatrixClientPeg';
+import { accessSecretStorage, AccessCancelledError } from '../CrossSigningManager';
+
+export const PHASE_INTRO = 0;
+export const PHASE_BUSY = 1;
+export const PHASE_DONE = 2; //final done stage, but still showing UX
+export const PHASE_CONFIRM_SKIP = 3;
+export const PHASE_FINISHED = 4; //UX can be closed
+
+export class SetupEncryptionStore extends EventEmitter {
+ static sharedInstance() {
+ if (!global.mx_SetupEncryptionStore) global.mx_SetupEncryptionStore = new SetupEncryptionStore();
+ return global.mx_SetupEncryptionStore;
+ }
+
+ start() {
+ if (this._started) {
+ return;
+ }
+ this._started = true;
+ this.phase = PHASE_INTRO;
+ this.verificationRequest = null;
+ this.backupInfo = null;
+ MatrixClientPeg.get().on("crypto.verification.request", this.onVerificationRequest);
+ }
+
+ stop() {
+ if (!this._started) {
+ return;
+ }
+ this._started = false;
+ if (this.verificationRequest) {
+ this.verificationRequest.off("change", this.onVerificationRequestChange);
+ }
+ if (MatrixClientPeg.get()) {
+ MatrixClientPeg.get().removeListener("crypto.verification.request", this.onVerificationRequest);
+ }
+ }
+
+ async usePassPhrase() {
+ this.phase = PHASE_BUSY;
+ this.emit("update");
+ const cli = MatrixClientPeg.get();
+ try {
+ const backupInfo = await cli.getKeyBackupVersion();
+ this.backupInfo = backupInfo;
+ this.emit("update");
+ // The control flow is fairly twisted here...
+ // For the purposes of completing security, we only wait on getting
+ // as far as the trust check and then show a green shield.
+ // We also begin the key backup restore as well, which we're
+ // awaiting inside `accessSecretStorage` only so that it keeps your
+ // passphase cached for that work. This dialog itself will only wait
+ // on the first trust check, and the key backup restore will happen
+ // in the background.
+ await new Promise((resolve, reject) => {
+ try {
+ accessSecretStorage(async () => {
+ await cli.checkOwnCrossSigningTrust();
+ resolve();
+ if (backupInfo) {
+ // A complete restore can take many minutes for large
+ // accounts / slow servers, so we allow the dialog
+ // to advance before this.
+ await cli.restoreKeyBackupWithSecretStorage(backupInfo);
+ }
+ }).catch(reject);
+ } catch (e) {
+ console.error(e);
+ reject(e);
+ }
+ });
+
+ if (cli.getCrossSigningId()) {
+ this.phase = PHASE_DONE;
+ this.emit("update");
+ }
+ } catch (e) {
+ if (!(e instanceof AccessCancelledError)) {
+ console.log(e);
+ }
+ // this will throw if the user hits cancel, so ignore
+ this.phase = PHASE_INTRO;
+ this.emit("update");
+ }
+ }
+
+ onVerificationRequest = async (request) => {
+ if (request.otherUserId !== MatrixClientPeg.get().getUserId()) return;
+
+ if (this.verificationRequest) {
+ this.verificationRequest.off("change", this.onVerificationRequestChange);
+ }
+ this.verificationRequest = request;
+ await request.accept();
+ request.on("change", this.onVerificationRequestChange);
+ this.emit("update");
+ }
+
+ onVerificationRequestChange = () => {
+ if (this.verificationRequest.cancelled) {
+ this.verificationRequest.off("change", this.onVerificationRequestChange);
+ this.verificationRequest = null;
+ this.emit("update");
+ }
+ }
+
+ skip() {
+ this.phase = PHASE_CONFIRM_SKIP;
+ this.emit("update");
+ }
+
+ skipConfirm() {
+ this.phase = PHASE_FINISHED;
+ this.emit("update");
+ }
+
+ returnAfterSkip() {
+ this.phase = PHASE_INTRO;
+ this.emit("update");
+ }
+
+ done() {
+ this.phase = PHASE_FINISHED;
+ this.emit("update");
+ }
+}