From 040ae52fe613e27605694205afc1ee65f38ff83e Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 8 Oct 2024 13:01:15 +0100 Subject: [PATCH] WIP encyption user settings tab --- .../views/dialogs/UserSettingsDialog.tsx | 14 +++ src/components/views/dialogs/UserTab.ts | 1 + .../tabs/user/EncryptionUserSettingsTab.tsx | 31 +++++ .../settings/tabs/user/KeyBackupPanel.tsx | 107 ++++++++++++++++++ src/i18n/strings/en_EN.json | 10 ++ 5 files changed, 163 insertions(+) create mode 100644 src/components/views/settings/tabs/user/EncryptionUserSettingsTab.tsx create mode 100644 src/components/views/settings/tabs/user/KeyBackupPanel.tsx diff --git a/src/components/views/dialogs/UserSettingsDialog.tsx b/src/components/views/dialogs/UserSettingsDialog.tsx index 8ae7a302ac..30bf6060e8 100644 --- a/src/components/views/dialogs/UserSettingsDialog.tsx +++ b/src/components/views/dialogs/UserSettingsDialog.tsx @@ -18,6 +18,7 @@ import KeyboardIcon from "@vector-im/compound-design-tokens/assets/web/icons/key import SidebarIcon from "@vector-im/compound-design-tokens/assets/web/icons/sidebar"; import MicOnIcon from "@vector-im/compound-design-tokens/assets/web/icons/mic-on"; import LockIcon from "@vector-im/compound-design-tokens/assets/web/icons/lock"; +import KeyIcon from "@vector-im/compound-design-tokens/assets/web/icons/key"; import LabsIcon from "@vector-im/compound-design-tokens/assets/web/icons/labs"; import BlockIcon from "@vector-im/compound-design-tokens/assets/web/icons/block"; import HelpIcon from "@vector-im/compound-design-tokens/assets/web/icons/help"; @@ -44,6 +45,7 @@ import { NonEmptyArray } from "../../../@types/common"; import { SDKContext, SdkContextClass } from "../../../contexts/SDKContext"; import { useSettingValue } from "../../../hooks/useSettings"; import { ToastContext, useActiveToast } from "../../../contexts/ToastContext"; +import EncryptionUserSettingsTab from "../settings/tabs/user/EncryptionUserSettingsTab"; interface IProps { initialTabId?: UserTab; @@ -75,6 +77,8 @@ function titleForTabID(tabId: UserTab): React.ReactNode { return _t("settings|voip|dialog_title", undefined, subs); case UserTab.Security: return _t("settings|security|dialog_title", undefined, subs); + case UserTab.Encryption: + return _t("settings|encryption|dialog_title", undefined, subs); case UserTab.Labs: return _t("settings|labs|dialog_title", undefined, subs); case UserTab.Mjolnir: @@ -179,6 +183,16 @@ export default function UserSettingsDialog(props: IProps): JSX.Element { ), ); + tabs.push( + new Tab( + UserTab.Encryption, + _td("room_settings|encryption|title"), + , + , + "UserSettingsEncryption", + ), + ); + if (showLabsFlags() || SettingsStore.getFeatureSettingNames().some((k) => SettingsStore.getBetaInfo(k))) { tabs.push( new Tab(UserTab.Labs, _td("common|labs"), , , "UserSettingsLabs"), diff --git a/src/components/views/dialogs/UserTab.ts b/src/components/views/dialogs/UserTab.ts index 467a67cd9f..99c349ee4b 100644 --- a/src/components/views/dialogs/UserTab.ts +++ b/src/components/views/dialogs/UserTab.ts @@ -15,6 +15,7 @@ export enum UserTab { Sidebar = "USER_SIDEBAR_TAB", Voice = "USER_VOICE_TAB", Security = "USER_SECURITY_TAB", + Encryption = "USER_ENCRYPTION_TAB", Labs = "USER_LABS_TAB", Mjolnir = "USER_MJOLNIR_TAB", Help = "USER_HELP_TAB", diff --git a/src/components/views/settings/tabs/user/EncryptionUserSettingsTab.tsx b/src/components/views/settings/tabs/user/EncryptionUserSettingsTab.tsx new file mode 100644 index 0000000000..a44e229aae --- /dev/null +++ b/src/components/views/settings/tabs/user/EncryptionUserSettingsTab.tsx @@ -0,0 +1,31 @@ +/* +Copyright 2024 New Vector Ltd. +Copyright 2019-2023 The Matrix.org Foundation C.I.C. + +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 from "react"; +import { logger } from "matrix-js-sdk/src/logger"; + +import SettingsTab from "../SettingsTab"; +import { SettingsSection } from "../../shared/SettingsSection"; +import KeyBackupPanel from "./KeyBackupPanel"; +import { _t } from "../../../../../languageHandler"; + +interface Props { + closeSettingsFn: () => void; +} + +const EncryptionUserSettingsTab: React.FC = () => { + return ( + + + + + + ); +}; + +export default EncryptionUserSettingsTab; diff --git a/src/components/views/settings/tabs/user/KeyBackupPanel.tsx b/src/components/views/settings/tabs/user/KeyBackupPanel.tsx new file mode 100644 index 0000000000..b5d4eb249a --- /dev/null +++ b/src/components/views/settings/tabs/user/KeyBackupPanel.tsx @@ -0,0 +1,107 @@ +/* +Copyright 2024 New Vector Ltd. +Copyright 2019, 2020 The Matrix.org Foundation C.I.C. +Copyright 2018 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, { useCallback, useEffect, useState } from "react"; +import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api"; +import { logger } from "matrix-js-sdk/src/logger"; + +import { _t } from "../../../../../languageHandler"; +import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; +import { useMatrixClientContext } from "../../../../../contexts/MatrixClientContext"; +import InlineSpinner from "../../../elements/InlineSpinner"; +import Modal from "../../../../../Modal"; +import CreateKeyBackupDialog from "../../../../../async-components/views/dialogs/security/CreateKeyBackupDialog"; + +const KeyBackupPanel: React.FC = () => { + const cli = useMatrixClientContext(); + + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + const [backupInfo, setBackupInfo] = useState(null); + const [activeBackupVersion, setActiveBackupVersion] = useState(null); + + const loadBackupStatus = useCallback(async () => { + setLoading(true); + setError(false); + try { + const backupInfo = await cli.getKeyBackupVersion(); + //const backupTrustInfo = backupInfo ? await cli.getCrypto()?.isKeyBackupTrusted(backupInfo) : undefined; + + const activeBackupVersion = (await cli.getCrypto()?.getActiveSessionBackupVersion()) ?? null; + + setLoading(false); + setBackupInfo(backupInfo); + setActiveBackupVersion(activeBackupVersion); + } catch (e) { + logger.log("Unable to fetch key backup status", e); + setError(true); + } finally { + setLoading(false); + } + }, [cli]); + + const startNewBackup = useCallback(() => { + Modal.createDialogAsync( + import( + "../../../../../async-components/views/dialogs/security/CreateKeyBackupDialog" + ) as unknown as Promise, + { + onFinished: () => { + loadBackupStatus(); + }, + }, + undefined, + /* priority = */ false, + /* static = */ true, + ); + }, [loadBackupStatus]); + + useEffect(() => { + // async, but handles its own exceptions + loadBackupStatus(); + }, [loadBackupStatus]); + + const onAllowKeyStorageChange = useCallback( + (checked: boolean) => { + if (checked) { + startNewBackup(); + } + }, + [startNewBackup], + ); + + if (loading) { + return ; + } + + if (error) { + return

{_t("settings|encryption|error_loading_key_backup_status")}

; + } + + return ( + <> +

+ {_t("settings|encryption|key_storage_body", undefined, { + a: (sub: string) => ( + + {sub} + + ), + })} +

+ + + ); +}; + +export default KeyBackupPanel; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 21619b99d7..a29ad90f0a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2171,6 +2171,9 @@ "title": "Bridges" }, "delete_avatar_label": "Delete avatar", + "encryption": { + "title": "Encryption" + }, "general": { "alias_field_has_domain_invalid": "Missing domain separator e.g. (:domain.org)", "alias_field_has_localpart_invalid": "Missing room name or separator e.g. (my-room:domain.org)", @@ -2477,6 +2480,13 @@ "emoji_autocomplete": "Enable Emoji suggestions while typing", "enable_markdown": "Enable Markdown", "enable_markdown_description": "Start messages with /plain to send without markdown.", + "encryption": { + "allow_key_storage": "Allow key storage", + "dialog_title": "Settings: Encryption", + "error_loading_key_backup_status": "Unable to load key backup status", + "key_storage_body": "Store your cryptographic identity and message keys securely on the server. This will allow you to view your message history on any new devices. Learn more", + "key_storage_title": "Key storage" + }, "general": { "account_management_section": "Account management", "account_section": "Account",