diff --git a/res/css/_common.pcss b/res/css/_common.pcss
index 74328af39b2..9964ec8e508 100644
--- a/res/css/_common.pcss
+++ b/res/css/_common.pcss
@@ -596,7 +596,9 @@ legend {
.mx_Dialog
button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not(
.mx_UserProfileSettings button
- ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button),
+ ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not(
+ .mx_EncryptionUserSettingsTab button
+ ),
.mx_Dialog input[type="submit"],
.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton),
.mx_Dialog_buttons input[type="submit"] {
@@ -616,8 +618,8 @@ legend {
.mx_Dialog
button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not(
.mx_UserProfileSettings button
- ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(
- .mx_ShareDialog button
+ ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not(
+ .mx_EncryptionUserSettingsTab button
):last-child {
margin-right: 0px;
}
@@ -625,7 +627,9 @@ legend {
.mx_Dialog
button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not(
.mx_UserProfileSettings button
- ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):focus,
+ ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not(
+ .mx_EncryptionUserSettingsTab button
+ ):focus,
.mx_Dialog input[type="submit"]:focus,
.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):focus,
.mx_Dialog_buttons input[type="submit"]:focus {
@@ -637,7 +641,9 @@ legend {
.mx_Dialog_buttons
button.mx_Dialog_primary:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):not(
.mx_UserProfileSettings button
- ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button),
+ ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not(
+ .mx_EncryptionUserSettingsTab button
+ ),
.mx_Dialog_buttons input[type="submit"].mx_Dialog_primary {
color: var(--cpd-color-text-on-solid-primary);
background-color: var(--cpd-color-bg-action-primary-rest);
@@ -650,7 +656,7 @@ legend {
.mx_Dialog_buttons
button.danger:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):not(.mx_UserProfileSettings button):not(
.mx_ThemeChoicePanel_CustomTheme button
- ):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button),
+ ):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not(.mx_EncryptionUserSettingsTab button),
.mx_Dialog_buttons input[type="submit"].danger {
background-color: var(--cpd-color-bg-critical-primary);
border: solid 1px var(--cpd-color-bg-critical-primary);
@@ -666,7 +672,9 @@ legend {
.mx_Dialog
button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not(
.mx_UserProfileSettings button
- ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):disabled,
+ ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not(
+ .mx_EncryptionUserSettingsTab button
+ ):disabled,
.mx_Dialog input[type="submit"]:disabled,
.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):disabled,
.mx_Dialog_buttons input[type="submit"]:disabled {
diff --git a/res/css/_components.pcss b/res/css/_components.pcss
index 7426f407990..b424c1e3c1a 100644
--- a/res/css/_components.pcss
+++ b/res/css/_components.pcss
@@ -351,6 +351,9 @@
@import "./views/settings/_ThemeChoicePanel.pcss";
@import "./views/settings/_UpdateCheckButton.pcss";
@import "./views/settings/_UserProfileSettings.pcss";
+@import "./views/settings/encryption/_ChangeRecoveryKey.pcss";
+@import "./views/settings/encryption/_RecoveryPanel.pcss";
+@import "./views/settings/encryption/_EncryptionCard.pcss";
@import "./views/settings/tabs/_SettingsBanner.pcss";
@import "./views/settings/tabs/_SettingsIndent.pcss";
@import "./views/settings/tabs/_SettingsSection.pcss";
diff --git a/res/css/views/settings/encryption/_ChangeRecoveryKey.pcss b/res/css/views/settings/encryption/_ChangeRecoveryKey.pcss
new file mode 100644
index 00000000000..2d8a8299f6c
--- /dev/null
+++ b/res/css/views/settings/encryption/_ChangeRecoveryKey.pcss
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2024 New Vector Ltd.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
+ * Please see LICENSE files in the repository root for full details.
+ */
+
+.mx_ChangeRecoveryKey_header {
+ display: flex;
+ flex-direction: column;
+ gap: var(--cpd-space-4x);
+ align-items: center;
+
+ > h2 {
+ margin: 0;
+ }
+
+ > span {
+ color: var(--cpd-color-text-secondary);
+ text-align: center;
+ }
+}
+
+.mx_ChangeRecoveryKey_content {
+ display: grid;
+ grid-template:
+ "header button" auto
+ "content button" auto / 1fr 36px;
+ column-gap: var(--cpd-space-3x);
+ row-gap: var(--cpd-space-1x);
+ align-items: center;
+
+ > span {
+ grid-area: header;
+ }
+
+ > div {
+ grid-area: content;
+ display: flex;
+ flex-direction: column;
+ gap: var(--cpd-space-2x);
+ color: var(--cpd-color-text-secondary);
+
+ > code {
+ background-color: var(--cpd-color-bg-subtle-secondary);
+ border-radius: var(--cpd-space-2x);
+ padding: var(--cpd-space-3x) var(--cpd-space-4x);
+ }
+ }
+
+ > button {
+ margin: 0 var(--cpd-space-1x);
+ color: var(--cpd-color-bg-subtle-secondary);
+ grid-area: button;
+ }
+}
+
+.mx_ChangeRecoveryKey_action {
+ display: flex;
+ flex-direction: column;
+ gap: var(--cpd-space-4x);
+ justify-content: center;
+}
diff --git a/res/css/views/settings/encryption/_EncryptionCard.pcss b/res/css/views/settings/encryption/_EncryptionCard.pcss
new file mode 100644
index 00000000000..d017f74e688
--- /dev/null
+++ b/res/css/views/settings/encryption/_EncryptionCard.pcss
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 New Vector Ltd.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
+ * Please see LICENSE files in the repository root for full details.
+ */
+
+.mx_EncryptionCard {
+ display: flex;
+ flex-direction: column;
+ gap: var(--cpd-space-8x);
+ padding: var(--cpd-space-10x);
+ border-radius: var(--cpd-space-4x);
+ /* From figma */
+ box-shadow: 0 1.2px 2.4px 0 rgba(27, 29, 34, 0.15);
+ border: 1px solid var(--cpd-color-gray-400);
+}
diff --git a/res/css/views/settings/encryption/_RecoveryPanel.pcss b/res/css/views/settings/encryption/_RecoveryPanel.pcss
new file mode 100644
index 00000000000..0ecc51187d4
--- /dev/null
+++ b/res/css/views/settings/encryption/_RecoveryPanel.pcss
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2024 New Vector Ltd.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
+ * Please see LICENSE files in the repository root for full details.
+ */
+
+.mx_RecoveryPanel {
+ .mx_RecoveryPanel_Subheader {
+ display: flex;
+ flex-direction: column;
+ gap: var(--cpd-space-2x);
+
+ > span {
+ display: flex;
+ align-items: center;
+ gap: var(--cpd-space-2x);
+ font: var(--cpd-font-body-sm-medium);
+ color: var(--cpd-color-text-success-primary);
+ }
+ }
+}
diff --git a/src/components/views/settings/encryption/ChangeRecoveryKey.tsx b/src/components/views/settings/encryption/ChangeRecoveryKey.tsx
new file mode 100644
index 00000000000..afe6f415006
--- /dev/null
+++ b/src/components/views/settings/encryption/ChangeRecoveryKey.tsx
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2024 New Vector Ltd.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
+ * Please see LICENSE files in the repository root for full details.
+ */
+
+import React, { JSX } from "react";
+import { Breadcrumb, Heading, BigIcon, IconButton, Text, Button } from "@vector-im/compound-web";
+import KeyIcon from "@vector-im/compound-design-tokens/assets/web/icons/key-solid";
+import CopyIcon from "@vector-im/compound-design-tokens/assets/web/icons/copy";
+
+import { _t } from "../../../../languageHandler.tsx";
+import { EncryptionCard } from "./EncryptionCard.tsx";
+
+interface ChangeRecoveryKeyProps {
+ onBackClick: () => void;
+}
+
+export function ChangeRecoveryKey({ onBackClick }: ChangeRecoveryKeyProps): JSX.Element {
+ return (
+ <>
+ Text text text text text text text text text text text text text text text
+
/plain
to send without markdown.",
"encryption": {
"dialog_title": "Settings: Encryption",
+ "recovery": {
+ "change_recovery_key": "Change recovery key",
+ "change_recovery_key_content_description": "Do not share this with anyone!",
+ "change_recovery_key_content_title": "Recovery key",
+ "change_recovery_key_description": "Get a new recovery key if you've lost your existing one. After changing your recovery key, your old one will no longer work.",
+ "change_recovery_key_title": "Change recovery key?",
+ "description": "Recover your cryptographic identity and message history with a recovery key if you’ve lost all your existing devices.",
+ "key_active": "Recovery key active",
+ "set_up_recovery": "Set up recovery",
+ "title": "Recovery"
+ },
"title": "Encryption"
},
"general": {