diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts
index 4f47cd7eac..e80c9402b2 100644
--- a/src/DeviceListener.ts
+++ b/src/DeviceListener.ts
@@ -292,21 +292,29 @@ export default class DeviceListener {
await crypto.getUserDeviceInfo([cli.getSafeUserId()]);
// cross signing isn't enabled - nag to enable it
- // There are 2 different toasts for:
+ // There are 3 different toasts for:
if (!(await crypto.getCrossSigningKeyId()) && (await crypto.userHasCrossSigningKeys())) {
- // Cross-signing on account but this device doesn't trust the master key (verify this session)
+ // Toast 1. Cross-signing on account but this device doesn't trust the master key (verify this session)
showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION);
this.checkKeyBackupStatus();
} else {
- // No cross-signing or key backup on account (set up encryption)
- await cli.waitForClientWellKnown();
- if (isSecureBackupRequired(cli) && isLoggedIn()) {
- // If we're meant to set up, and Secure Backup is required,
- // trigger the flow directly without a toast once logged in.
- hideSetupEncryptionToast();
- accessSecretStorage();
+ const backupInfo = await this.getKeyBackupInfo();
+ if (backupInfo) {
+ // Toast 2: Key backup is enabled but recovery (4S) is not set up: prompt user to set up recovery.
+ // Since we now enable key backup at registration time, this will be the common case for
+ // new users.
+ showSetupEncryptionToast(SetupKind.SET_UP_RECOVERY);
} else {
- showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION);
+ // Toast 3: No cross-signing or key backup on account (set up encryption)
+ await cli.waitForClientWellKnown();
+ if (isSecureBackupRequired(cli) && isLoggedIn()) {
+ // If we're meant to set up, and Secure Backup is required,
+ // trigger the flow directly without a toast once logged in.
+ hideSetupEncryptionToast();
+ accessSecretStorage();
+ } else {
+ showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION);
+ }
}
}
}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 3b4765b0ad..807264c4d2 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -912,6 +912,9 @@
"warning": "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."
},
"reset_all_button": "Forgotten or lost all recovery methods? Reset all",
+ "set_up_recovery": "Set up recovery",
+ "set_up_recovery_later": "Not now",
+ "set_up_recovery_toast_title": "Set up recovery to protect your account",
"set_up_toast_description": "Safeguard against losing access to encrypted messages & data",
"set_up_toast_title": "Set up Secure Backup",
"setup_secure_backup": {
diff --git a/src/toasts/SetupEncryptionToast.ts b/src/toasts/SetupEncryptionToast.ts
index 0dd54bb18f..8ae4830242 100644
--- a/src/toasts/SetupEncryptionToast.ts
+++ b/src/toasts/SetupEncryptionToast.ts
@@ -6,6 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
+import KeyboardIcon from "@vector-im/compound-design-tokens/assets/web/icons/key";
+import { ComponentType } from "react";
+
import Modal from "../Modal";
import { _t } from "../languageHandler";
import DeviceListener from "../DeviceListener";
@@ -23,33 +26,61 @@ const getTitle = (kind: Kind): string => {
switch (kind) {
case Kind.SET_UP_ENCRYPTION:
return _t("encryption|set_up_toast_title");
+ case Kind.SET_UP_RECOVERY:
+ return _t("encryption|set_up_recovery_toast_title");
case Kind.VERIFY_THIS_SESSION:
return _t("encryption|verify_toast_title");
}
};
-const getIcon = (kind: Kind): string => {
+const getIcon = (kind: Kind): string | undefined => {
switch (kind) {
case Kind.SET_UP_ENCRYPTION:
return "secure_backup";
+ case Kind.SET_UP_RECOVERY:
+ return undefined;
case Kind.VERIFY_THIS_SESSION:
return "verification_warning";
}
};
+// Gets the icon displayed on the prinary button
+const getPrimaryIcon = (kind: Kind): ComponentType> | undefined => {
+ switch (kind) {
+ case Kind.SET_UP_RECOVERY:
+ return KeyboardIcon;
+ default:
+ return undefined;
+ }
+};
+
const getSetupCaption = (kind: Kind): string => {
switch (kind) {
case Kind.SET_UP_ENCRYPTION:
return _t("action|continue");
+ case Kind.SET_UP_RECOVERY:
+ return _t("encryption|set_up_recovery");
case Kind.VERIFY_THIS_SESSION:
return _t("action|verify");
}
};
+const getSecondaryButtonLabel = (kind: Kind): string => {
+ switch (kind) {
+ case Kind.SET_UP_RECOVERY:
+ return _t("encryption|set_up_recovery_later");
+ case Kind.SET_UP_ENCRYPTION:
+ case Kind.VERIFY_THIS_SESSION:
+ return _t("encryption|verification|unverified_sessions_toast_reject");
+ }
+};
+
const getDescription = (kind: Kind): string => {
switch (kind) {
case Kind.SET_UP_ENCRYPTION:
return _t("encryption|set_up_toast_description");
+ case Kind.SET_UP_RECOVERY:
+ return _t("encryption|set_up_recovery_toast_title");
case Kind.VERIFY_THIS_SESSION:
return _t("encryption|verify_toast_description");
}
@@ -57,6 +88,7 @@ const getDescription = (kind: Kind): string => {
export enum Kind {
SET_UP_ENCRYPTION = "set_up_encryption",
+ SET_UP_RECOVERY = "set_up_recovery",
VERIFY_THIS_SESSION = "verify_this_session",
}
@@ -101,9 +133,9 @@ export const showToast = (kind: Kind): void => {
description: getDescription(kind),
primaryLabel: getSetupCaption(kind),
onPrimaryClick: onAccept,
- secondaryLabel: _t("encryption|verification|unverified_sessions_toast_reject"),
+ secondaryLabel: getSecondaryButtonLabel(kind),
onSecondaryClick: onReject,
- destructive: "secondary",
+ PrimaryIcon: getPrimaryIcon(kind),
},
component: GenericToast,
priority: kind === Kind.VERIFY_THIS_SESSION ? 95 : 40,