-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Add key storage toggle to Encryption settings #29310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
92 commits
Select commit
Hold shift + click to select a range
5a74298
Add key storage toggle to Encryption settings
dbkr 98950de
Keys in the acceptable order
dbkr 4eb07d4
Fix some tests
dbkr 97057de
Fix import
dbkr 4ec09f0
Fix toast showing condition
dbkr 4db196e
Fix import order
dbkr 98114c8
Fix playwright tests
dbkr 6a20703
Merge remote-tracking branch 'origin/develop' into dbkr/key_storage_t…
dbkr b7b2ea3
Fix bits lost in merge
dbkr e408715
Add key storage delete confirm screen
dbkr aa6de76
Fix hardcoded Element string
dbkr 5ac2004
Fix type imports
dbkr 1304587
Fix tests
dbkr a26efc5
Tests for key storage delete panel
dbkr 1b99071
Fix test
dbkr 6b238d1
Type import
dbkr 87d44a7
Test for the view model
dbkr 4ea6a33
Merge remote-tracking branch 'origin/develop' into dbkr/key_storage_t…
dbkr df4c23b
Fix type import
dbkr 4a3a373
Actually fix type imports
dbkr e70afdb
Test updating
dbkr 40f9bd9
Add playwright test & clarify slightly confusing comment
dbkr 16c76cb
Show the advnced section whatever the state of key storage
dbkr f818d6e
Update screenshots
dbkr fc9bc09
Copy css to its own file
dbkr 25f8fe2
Add missing doc & merge loading states
dbkr 1178d77
Add tsdoc & loading alt text to spinner
dbkr 7c2d9f4
Turn comments into proper tsdoc
dbkr cfd55a6
Switch to TypedEventEmitter and remove unnecessary loading state
dbkr f586c43
Add screenshot
dbkr e8483e0
Use higher level interface
dbkr 8ca4a8b
Merge the two hooks in EncryptionUserSettingsTab
dbkr 2ef05c5
Remove unused import
dbkr 7149b3d
Don't check key backup enabled state separately
dbkr 64f84cb
Update snapshot
dbkr 0c5f2b0
Merge branch 'develop' into dbkr/key_storage_toggle
dbkr 9c4625d
Use fixed recovery key function
dbkr f0d9e05
Amalgamate duplicated CSS files
dbkr de18311
Merge remote-tracking branch 'origin/develop' into dbkr/key_storage_t…
dbkr c4525b9
Have "key storage disabled" as a separate state
dbkr d1aef9f
Update snapshot
dbkr 96a70f2
Fix... bad merge?
dbkr 98edffa
Add backup enabled mock to more tests
dbkr a5cec9e
More snapshots
dbkr 0162c82
Use defer util
dbkr 446fca2
Merge branch 'dbkr/key_storage_toggle' into dbkr/key_storage_toggle_2
dbkr 2c5e81f
Update to use EncryptionCardButtons
dbkr c0e54d9
Update snapshots
dbkr 8c86402
Merge remote-tracking branch 'origin/develop' into dbkr/key_storage_t…
dbkr 91c0adc
Use EncryptionCardEmphasisedContent
dbkr d7683e1
Update snapshots
dbkr 2f8fb06
Update snapshot
dbkr 819868c
Try screenshot from CI playwright
dbkr c83fcfd
Try playwright screenshots again
dbkr 1b8cc7c
More screenshots
dbkr 0108ba6
Rename to match files
dbkr a015d75
Test that 4S secrets are deleted
dbkr 9d3e419
Make description clearer
dbkr 90b5632
Fix typo & move related states together
dbkr 4eebdb9
Add comment
dbkr ee925b3
More comments
dbkr 2ff4a7a
Fix hook docs
dbkr defd1c7
restoreAllMocks
dbkr 10487ea
Merge branch 'develop' into dbkr/key_storage_toggle_2
dbkr e7f36ed
Update snapshot
dbkr e6aa2da
Switch icon
dbkr bf5a22a
Update snapshot
dbkr fa7ee98
Missing copyright
dbkr 1baaa10
Re-order states
dbkr b4750ae
Remove phantom space
dbkr 5b0ccf7
Clarify 'button'
dbkr 2c8bff1
Clarify docs more
dbkr f5d94dc
Explain thinking behind updating
dbkr dbd8743
Merge branch 'develop' into dbkr/key_storage_toggle_2
dbkr 33a8edb
Switch to getActiveBackupVersion
dbkr 9e6aa26
Add use of Key Storage Panel
dbkr 7b379a6
Change key storage panel to be consistent
dbkr f68276c
Add tsdoc
dbkr cf7fe04
Use BACKUP_DISABLED_ACCOUNT_DATA_KEY in more places
dbkr 9cb8a55
Expand doc
dbkr 24c5431
Undo random yarn lock change
dbkr 63d19f1
Use aggregate method for disabling key storage
dbkr 054a6bd
Merge branch 'develop' into dbkr/key_storage_toggle_2
dbkr 5cb8129
Fix tests
dbkr d803193
Use key backup status event to update
dbkr 19c718c
Comment formatting
dbkr a03a0e3
Fix comment & put check inside if statement
dbkr 63b0f0e
Add comment
dbkr 5cac058
Prettier
dbkr 75adfa8
Fix comment
dbkr 3be9d0c
Merge branch 'develop' into dbkr/key_storage_toggle_2
dbkr da5a15f
Update snapshot
dbkr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file modified
BIN
-49 Bytes
(100%)
...hots/settings/encryption-user-tab/advanced.spec.ts/encryption-details-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
+0 Bytes
(100%)
...ngs/encryption-user-tab/advanced.spec.ts/reset-cryptographic-identity-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
+10.1 KB
(120%)
...shots/settings/encryption-user-tab/encryption-tab.spec.ts/default-tab-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+53.8 KB
...encryption-user-tab/encryption-tab.spec.ts/delete-key-storage-confirm-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
-33 Bytes
(100%)
...pshots/settings/encryption-user-tab/recovery.spec.ts/default-recovery-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
-1 Byte
(100%)
...apshots/settings/encryption-user-tab/recovery.spec.ts/set-up-recovery-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
res/css/components/views/settings/encryption/_KeyStoragePanel.pcss
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| /* | ||
| * Copyright 2025 New Vector Ltd. | ||
| * | ||
| * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial | ||
| * Please see LICENSE files in the repository root for full details. | ||
| */ | ||
|
|
||
| .mx_KeyStoragePanel_toggleRow { | ||
| flex-direction: row; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| /* | ||
| Copyright 2025 New Vector Ltd. | ||
|
|
||
| SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial | ||
| Please see LICENSE files in the repository root for full details. | ||
| */ | ||
|
|
||
| import { useCallback, useEffect, useState } from "react"; | ||
| import { logger } from "matrix-js-sdk/src/logger"; | ||
|
|
||
| import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext"; | ||
| import DeviceListener, { BACKUP_DISABLED_ACCOUNT_DATA_KEY } from "../../../../DeviceListener"; | ||
|
|
||
| interface KeyStoragePanelState { | ||
| /** | ||
| * Whether the app's "key storage" option should show as enabled to the user, | ||
| * or 'undefined' if the state is still loading. | ||
| */ | ||
| isEnabled: boolean | undefined; | ||
|
|
||
| /** | ||
| * A function that can be called to enable or disable key storage. | ||
| * @param enable True to turn key storage on or false to turn it off | ||
| */ | ||
| setEnabled: (enable: boolean) => void; | ||
|
|
||
| /** | ||
| * True if the state is still loading for the first time | ||
| */ | ||
| loading: boolean; | ||
|
|
||
| /** | ||
| * True if the status is in the process of being changed | ||
| */ | ||
| busy: boolean; | ||
| } | ||
|
|
||
| /** Returns a ViewModel for use in {@link KeyStoragePanel} and {@link DeleteKeyStoragePanel}. */ | ||
| export function useKeyStoragePanelViewModel(): KeyStoragePanelState { | ||
|
dbkr marked this conversation as resolved.
|
||
| const [isEnabled, setIsEnabled] = useState<boolean | undefined>(undefined); | ||
| const [loading, setLoading] = useState(true); | ||
| // Whilst the change is being made, the toggle will reflect the pending value rather than the actual state | ||
| const [pendingValue, setPendingValue] = useState<boolean | undefined>(undefined); | ||
|
|
||
| const matrixClient = useMatrixClientContext(); | ||
|
|
||
| const checkStatus = useCallback(async () => { | ||
| const crypto = matrixClient.getCrypto(); | ||
| if (!crypto) { | ||
| logger.error("Can't check key backup status: no crypto module available"); | ||
| return; | ||
| } | ||
| // The toggle is enabled only if this device will upload megolm keys to the backup. | ||
| // This is consistent with EX. | ||
| const activeBackupVersion = await crypto.getActiveSessionBackupVersion(); | ||
| setIsEnabled(activeBackupVersion !== null); | ||
| }, [matrixClient]); | ||
|
|
||
| useEffect(() => { | ||
| (async () => { | ||
| await checkStatus(); | ||
| setLoading(false); | ||
| })(); | ||
| }, [checkStatus]); | ||
|
|
||
| const setEnabled = useCallback( | ||
| async (enable: boolean) => { | ||
| setPendingValue(enable); | ||
| try { | ||
| // stop the device listener since enabling or (especially) disabling key storage must be | ||
| // done with a sequence of API calls that will put the account in a slightly different | ||
| // state each time, so suppress any warning toasts until the process is finished (when | ||
| // we'll turn it back on again.) | ||
| DeviceListener.sharedInstance().stop(); | ||
|
|
||
| const crypto = matrixClient.getCrypto(); | ||
| if (!crypto) { | ||
| logger.error("Can't change key backup status: no crypto module available"); | ||
| return; | ||
| } | ||
| if (enable) { | ||
| // If there is no existing key backup on the server, create one. | ||
| // `resetKeyBackup` will delete any existing backup, so we only do this if there is no existing backup. | ||
| const currentKeyBackup = await crypto.checkKeyBackupAndEnable(); | ||
| if (currentKeyBackup === null) { | ||
| await crypto.resetKeyBackup(); | ||
|
|
||
| // resetKeyBackup fires this off in the background without waiting, so we need to do it | ||
| // explicitly and wait for it, otherwise it won't be enabled yet when we check again. | ||
| await crypto.checkKeyBackupAndEnable(); | ||
| } | ||
|
|
||
| // Set the flag so that EX no longer thinks the user wants backup disabled | ||
| await matrixClient.setAccountData(BACKUP_DISABLED_ACCOUNT_DATA_KEY, { disabled: false }); | ||
| } else { | ||
| // This method will delete the key backup as well as server side recovery keys and other | ||
| // server-side crypto data. | ||
| await crypto.disableKeyStorage(); | ||
|
|
||
| // Set a flag to say that the user doesn't want key backup. | ||
| // Element X uses this to determine whether to set up automatically, | ||
| // so this will stop EX turning it back on spontaneously. | ||
| await matrixClient.setAccountData(BACKUP_DISABLED_ACCOUNT_DATA_KEY, { disabled: true }); | ||
| } | ||
|
|
||
| await checkStatus(); | ||
| } finally { | ||
| setPendingValue(undefined); | ||
| DeviceListener.sharedInstance().start(matrixClient); | ||
| } | ||
| }, | ||
| [setPendingValue, checkStatus, matrixClient], | ||
| ); | ||
|
|
||
| return { isEnabled: pendingValue ?? isEnabled, setEnabled, loading, busy: pendingValue !== undefined }; | ||
| } | ||
79 changes: 79 additions & 0 deletions
79
src/components/views/settings/encryption/DeleteKeyStoragePanel.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| /* | ||
| * Copyright 2025 New Vector Ltd. | ||
| * | ||
| * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial | ||
| * Please see LICENSE files in the repository root for full details. | ||
| */ | ||
|
|
||
| import { Breadcrumb, Button, VisualList, VisualListItem } from "@vector-im/compound-web"; | ||
| import CrossIcon from "@vector-im/compound-design-tokens/assets/web/icons/close"; | ||
| import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error-solid"; | ||
| import React, { useCallback, useState } from "react"; | ||
|
|
||
| import { _t } from "../../../../languageHandler"; | ||
| import { EncryptionCard } from "./EncryptionCard"; | ||
| import { useKeyStoragePanelViewModel } from "../../../viewmodels/settings/encryption/KeyStoragePanelViewModel"; | ||
| import SdkConfig from "../../../../SdkConfig"; | ||
| import { EncryptionCardButtons } from "./EncryptionCardButtons"; | ||
| import { EncryptionCardEmphasisedContent } from "./EncryptionCardEmphasisedContent"; | ||
|
|
||
| interface Props { | ||
| /** | ||
| * Called when the user either cancels the operation or key storage has been disabled | ||
| */ | ||
| onFinish: () => void; | ||
| } | ||
|
|
||
| /** | ||
| * Confirms that the user really wants to turn off and delete their key storage. Part of the "Encryption" settings tab. | ||
| */ | ||
| export function DeleteKeyStoragePanel({ onFinish }: Props): JSX.Element { | ||
| const { setEnabled } = useKeyStoragePanelViewModel(); | ||
| const [busy, setBusy] = useState(false); | ||
|
|
||
| const onDeleteClick = useCallback(async () => { | ||
| setBusy(true); | ||
| try { | ||
| await setEnabled(false); | ||
| } finally { | ||
| setBusy(false); | ||
| } | ||
| onFinish(); | ||
| }, [setEnabled, onFinish]); | ||
|
|
||
| return ( | ||
| <> | ||
| <Breadcrumb | ||
| backLabel={_t("action|back")} | ||
| onBackClick={onFinish} | ||
| pages={[_t("settings|encryption|title"), _t("settings|encryption|delete_key_storage|breadcrumb_page")]} | ||
| onPageClick={onFinish} | ||
| /> | ||
| <EncryptionCard | ||
| Icon={ErrorIcon} | ||
| destructive={true} | ||
| title={_t("settings|encryption|delete_key_storage|title")} | ||
| > | ||
| <EncryptionCardEmphasisedContent> | ||
| {_t("settings|encryption|delete_key_storage|description")} | ||
| <VisualList> | ||
| <VisualListItem Icon={CrossIcon} destructive={true}> | ||
| {_t("settings|encryption|delete_key_storage|list_first")} | ||
| </VisualListItem> | ||
| <VisualListItem Icon={CrossIcon} destructive={true}> | ||
| {_t("settings|encryption|delete_key_storage|list_second", { brand: SdkConfig.get().brand })} | ||
| </VisualListItem> | ||
| </VisualList> | ||
| </EncryptionCardEmphasisedContent> | ||
| <EncryptionCardButtons> | ||
| <Button destructive={true} onClick={onDeleteClick} disabled={busy}> | ||
| {_t("settings|encryption|delete_key_storage|confirm")} | ||
| </Button> | ||
| <Button kind="tertiary" onClick={onFinish}> | ||
| {_t("action|cancel")} | ||
| </Button> | ||
| </EncryptionCardButtons> | ||
| </EncryptionCard> | ||
| </> | ||
| ); | ||
| } |
75 changes: 75 additions & 0 deletions
75
src/components/views/settings/encryption/KeyStoragePanel.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| /* | ||
| * Copyright 2025 New Vector Ltd. | ||
| * | ||
| * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial | ||
| * Please see LICENSE files in the repository root for full details. | ||
| */ | ||
|
|
||
| import React, { useCallback } from "react"; | ||
| import { InlineField, InlineSpinner, Label, Root, ToggleControl } from "@vector-im/compound-web"; | ||
|
|
||
| import type { FormEvent } from "react"; | ||
| import { SettingsSection } from "../shared/SettingsSection"; | ||
| import { _t } from "../../../../languageHandler"; | ||
| import { SettingsHeader } from "../SettingsHeader"; | ||
| import { useKeyStoragePanelViewModel } from "../../../viewmodels/settings/encryption/KeyStoragePanelViewModel"; | ||
|
|
||
| interface Props { | ||
| /** | ||
| * Called when the user turns off the "allow key storage" toggle | ||
| */ | ||
| onKeyStorageDisableClick: () => void; | ||
| } | ||
|
|
||
| /** | ||
| * This component allows the user to set up or change their recovery key. | ||
|
dbkr marked this conversation as resolved.
|
||
| * | ||
| * It is used within the "Encryption" settings tab. | ||
| */ | ||
| export const KeyStoragePanel: React.FC<Props> = ({ onKeyStorageDisableClick }) => { | ||
| const { isEnabled, setEnabled, loading, busy } = useKeyStoragePanelViewModel(); | ||
|
|
||
| const onKeyBackupChange = useCallback( | ||
| (e: FormEvent<HTMLInputElement>) => { | ||
| if (e.currentTarget.checked) { | ||
| setEnabled(true); | ||
| } else { | ||
| onKeyStorageDisableClick(); | ||
| } | ||
| }, | ||
| [setEnabled, onKeyStorageDisableClick], | ||
| ); | ||
|
|
||
| if (loading) { | ||
| return <InlineSpinner aria-label={_t("common|loading")} />; | ||
| } | ||
|
|
||
| return ( | ||
| <SettingsSection | ||
| legacy={false} | ||
| heading={ | ||
| <SettingsHeader | ||
| hasRecommendedTag={isEnabled === false} | ||
| label={_t("settings|encryption|key_storage|title")} | ||
| /> | ||
| } | ||
| subHeading={_t("settings|encryption|key_storage|description", undefined, { | ||
| a: (sub) => ( | ||
| <a href="https://element.io/help#encryption5" target="_blank" rel="noreferrer noopener"> | ||
| {sub} | ||
| </a> | ||
| ), | ||
| })} | ||
| > | ||
| <Root className="mx_KeyStoragePanel_toggleRow"> | ||
| <InlineField | ||
| name="keyStorage" | ||
| control={<ToggleControl name="keyStorage" checked={isEnabled} onChange={onKeyBackupChange} />} | ||
| > | ||
| <Label>{_t("settings|encryption|key_storage|allow_key_storage")}</Label> | ||
| </InlineField> | ||
| {busy && <InlineSpinner />} | ||
| </Root> | ||
| </SettingsSection> | ||
| ); | ||
| }; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.