Prevent user from accidentally triggering multiple identity resets (#29388)

* prevent user from accidentally triggering multiple identity resets

* apply changes from review and update to latest design

* Use a CSS class and compound variable

* update snapshot

* Update test/unit-tests/components/views/settings/encryption/ResetIdentityPanel-test.tsx

---------

Co-authored-by: Richard van der Hoff <richard@matrix.org>
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
This commit is contained in:
Hubert Chathi
2025-03-04 11:40:35 -05:00
committed by GitHub
parent 9d8efacede
commit 56c7fc1948
7 changed files with 256 additions and 7 deletions

View File

@@ -362,6 +362,7 @@
@import "./views/settings/encryption/_EncryptionCard.pcss";
@import "./views/settings/encryption/_EncryptionCardEmphasisedContent.pcss";
@import "./views/settings/encryption/_RecoveryPanelOutOfSync.pcss";
@import "./views/settings/encryption/_ResetIdentityPanel.pcss";
@import "./views/settings/tabs/_SettingsBanner.pcss";
@import "./views/settings/tabs/_SettingsIndent.pcss";
@import "./views/settings/tabs/_SettingsSection.pcss";

View File

@@ -0,0 +1,11 @@
/*
* Copyright 2024 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.
*/
// Red text for the "Do not close this window" warning
.mx_ResetIdentityPanel_warning {
color: var(--cpd-color-text-critical-primary);
}

View File

@@ -5,11 +5,11 @@
* Please see LICENSE files in the repository root for full details.
*/
import { Breadcrumb, Button, VisualList, VisualListItem } from "@vector-im/compound-web";
import { Breadcrumb, Button, InlineSpinner, VisualList, VisualListItem } from "@vector-im/compound-web";
import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check";
import InfoIcon from "@vector-im/compound-design-tokens/assets/web/icons/info";
import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error-solid";
import React, { type MouseEventHandler } from "react";
import React, { useState, type MouseEventHandler } from "react";
import { _t } from "../../../../languageHandler";
import { EncryptionCard } from "./EncryptionCard";
@@ -44,6 +44,10 @@ interface ResetIdentityPanelProps {
export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetIdentityPanelProps): JSX.Element {
const matrixClient = useMatrixClientContext();
// After the user clicks "Continue", we disable the button so it can't be
// clicked again, and warn the user not to close the window.
const [inProgress, setInProgress] = useState(false);
return (
<>
<Breadcrumb
@@ -78,18 +82,34 @@ export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetId
<EncryptionCardButtons>
<Button
destructive={true}
disabled={inProgress}
onClick={async (evt) => {
setInProgress(true);
await matrixClient
.getCrypto()
?.resetEncryption((makeRequest) => uiAuthCallback(matrixClient, makeRequest));
onFinish(evt);
}}
>
{_t("action|continue")}
</Button>
<Button kind="tertiary" onClick={onCancelClick}>
{_t("action|cancel")}
{inProgress ? (
<>
<InlineSpinner /> {_t("settings|encryption|advanced|reset_in_progress")}
</>
) : (
_t("action|continue")
)}
</Button>
{inProgress ? (
<EncryptionCardEmphasisedContent>
<span className="mx_ResetIdentityPanel_warning">
{_t("settings|encryption|advanced|do_not_close_warning")}
</span>
</EncryptionCardEmphasisedContent>
) : (
<Button kind="tertiary" onClick={onCancelClick}>
{_t("action|cancel")}
</Button>
)}
</EncryptionCardButtons>
</EncryptionCard>
</>

View File

@@ -2479,12 +2479,14 @@
"breadcrumb_title_forgot": "Forgot your recovery key? Youll need to reset your identity.",
"breadcrumb_warning": "Only do this if you believe your account has been compromised.",
"details_title": "Encryption details",
"do_not_close_warning": "Do not close this window until the reset is finished",
"export_keys": "Export keys",
"import_keys": "Import keys",
"other_people_device_description": "By default in encrypted rooms, do not send encrypted messages to anyone until youve verified them",
"other_people_device_label": "Never send encrypted messages to unverified devices",
"other_people_device_title": "Other peoples devices",
"reset_identity": "Reset cryptographic identity",
"reset_in_progress": "Reset in progress...",
"session_id": "Session ID:",
"session_key": "Session key:",
"title": "Advanced"

View File

@@ -7,6 +7,7 @@
import React from "react";
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { sleep, defer } from "matrix-js-sdk/src/utils";
import { render, screen } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
@@ -30,7 +31,17 @@ describe("<ResetIdentityPanel />", () => {
);
expect(asFragment()).toMatchSnapshot();
await user.click(screen.getByRole("button", { name: "Continue" }));
// We need to pause the reset so that we can check that it's providing
// feedback to the user that something is happening.
const { promise: resetEncryptionPromise, resolve: resolveResetEncryption } = defer();
jest.spyOn(matrixClient.getCrypto()!, "resetEncryption").mockReturnValue(resetEncryptionPromise);
const continueButton = screen.getByRole("button", { name: "Continue" });
await user.click(continueButton);
expect(asFragment()).toMatchSnapshot();
resolveResetEncryption!();
await sleep(0);
expect(matrixClient.getCrypto()!.resetEncryption).toHaveBeenCalled();
expect(onFinish).toHaveBeenCalled();
});

View File

@@ -159,6 +159,7 @@ exports[`<ResetIdentityPanel /> should display the 'forgot recovery key' variant
class="mx_EncryptionCard_buttons"
>
<button
aria-disabled="false"
class="_button_vczzf_8 _destructive_vczzf_107"
data-kind="primary"
data-size="lg"
@@ -343,6 +344,7 @@ exports[`<ResetIdentityPanel /> should reset the encryption when the continue bu
class="mx_EncryptionCard_buttons"
>
<button
aria-disabled="false"
class="_button_vczzf_8 _destructive_vczzf_107"
data-kind="primary"
data-size="lg"
@@ -364,3 +366,204 @@ exports[`<ResetIdentityPanel /> should reset the encryption when the continue bu
</div>
</DocumentFragment>
`;
exports[`<ResetIdentityPanel /> should reset the encryption when the continue button is clicked 2`] = `
<DocumentFragment>
<nav
class="_breadcrumb_1xygz_8"
>
<button
aria-label="Back"
class="_icon-button_m2erp_8 _subtle-bg_m2erp_29"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_zr2a0_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</div>
</button>
<ol
class="_pages_1xygz_17"
>
<li>
<a
class="_link_1v5rz_8"
data-kind="primary"
data-size="small"
rel="noreferrer noopener"
role="button"
tabindex="0"
>
Encryption
</a>
</li>
<li>
<span
aria-current="page"
class="_last-page_1xygz_30"
>
Reset encryption
</span>
</li>
</ol>
</nav>
<div
class="mx_EncryptionCard"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_o77nw_8 _destructive_o77nw_34"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Are you sure you want to reset your identity?
</h2>
</div>
<div
class="mx_Flex mx_EncryptionCard_emphasisedContent"
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: normal; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x);"
>
<ul
class="_visual-list_15wzx_8"
>
<li
class="_visual-list-item_1ma3e_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1ma3e_17 _visual-list-item-icon-success_1ma3e_22"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
/>
</svg>
Your account details, contacts, preferences, and chat list will be kept
</li>
<li
class="_visual-list-item_1ma3e_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1ma3e_17"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
/>
<path
clip-rule="evenodd"
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
fill-rule="evenodd"
/>
</svg>
You will lose any message history thats stored only on the server
</li>
<li
class="_visual-list-item_1ma3e_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1ma3e_17"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
/>
<path
clip-rule="evenodd"
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
fill-rule="evenodd"
/>
</svg>
You will need to verify all your existing devices and contacts again
</li>
</ul>
<span>
Only do this if you believe your account has been compromised.
</span>
</div>
<div
class="mx_EncryptionCard_buttons"
>
<button
aria-disabled="true"
class="_button_vczzf_8 _destructive_vczzf_107"
data-kind="primary"
data-size="lg"
role="button"
tabindex="0"
>
<svg
class="_icon_11k6c_18"
fill="currentColor"
height="1em"
style="width: 20px; height: 20px;"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M12 4.031a8 8 0 1 0 8 8 1 1 0 0 1 2 0c0 5.523-4.477 10-10 10s-10-4.477-10-10 4.477-10 10-10a1 1 0 1 1 0 2"
fill-rule="evenodd"
/>
</svg>
Reset in progress...
</button>
<div
class="mx_Flex mx_EncryptionCard_emphasisedContent"
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: normal; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x);"
>
<span
class="mx_ResetIdentityPanel_warning"
>
Do not close this window until the reset is finished
</span>
</div>
</div>
</div>
</DocumentFragment>
`;

View File

@@ -333,6 +333,7 @@ exports[`<EncryptionUserSettingsTab /> should display the reset identity panel w
class="mx_EncryptionCard_buttons"
>
<button
aria-disabled="false"
class="_button_vczzf_8 _destructive_vczzf_107"
data-kind="primary"
data-size="lg"