Skip to content

Commit f8922b2

Browse files
committed
prevent user from accidentally triggering multiple identity resets
1 parent 0997e0a commit f8922b2

File tree

5 files changed

+242
-6
lines changed

5 files changed

+242
-6
lines changed

src/components/views/settings/encryption/ResetIdentityPanel.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
* Please see LICENSE files in the repository root for full details.
66
*/
77

8-
import { Breadcrumb, Button, VisualList, VisualListItem } from "@vector-im/compound-web";
8+
import { Breadcrumb, Button, InlineSpinner, VisualList, VisualListItem } from "@vector-im/compound-web";
99
import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check";
1010
import InfoIcon from "@vector-im/compound-design-tokens/assets/web/icons/info";
1111
import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error-solid";
12-
import React, { type MouseEventHandler } from "react";
12+
import React, { useState, type MouseEventHandler } from "react";
1313

1414
import { _t } from "../../../../languageHandler";
1515
import { EncryptionCard } from "./EncryptionCard";
@@ -44,6 +44,10 @@ interface ResetIdentityPanelProps {
4444
export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetIdentityPanelProps): JSX.Element {
4545
const matrixClient = useMatrixClientContext();
4646

47+
// After the user clicks "Continue", we disable the button so it can't be
48+
// clicked again, and warn the user not to close the window.
49+
const [inProgress, setInProgress] = useState(false);
50+
4751
return (
4852
<>
4953
<Breadcrumb
@@ -78,18 +82,29 @@ export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetId
7882
<EncryptionCardButtons>
7983
<Button
8084
destructive={true}
85+
disabled={inProgress}
8186
onClick={async (evt) => {
87+
setInProgress(true);
8288
await matrixClient
8389
.getCrypto()
8490
?.resetEncryption((makeRequest) => uiAuthCallback(matrixClient, makeRequest));
8591
onFinish(evt);
8692
}}
8793
>
94+
{inProgress && <InlineSpinner />}
8895
{_t("action|continue")}
8996
</Button>
90-
<Button kind="tertiary" onClick={onCancelClick}>
91-
{_t("action|cancel")}
92-
</Button>
97+
{inProgress ? (
98+
<EncryptionCardEmphasisedContent>
99+
<span className="text-warning">
100+
{_t("settings|encryption|advanced|do_not_close_warning")}
101+
</span>
102+
</EncryptionCardEmphasisedContent>
103+
) : (
104+
<Button kind="tertiary" onClick={onCancelClick}>
105+
{_t("action|cancel")}
106+
</Button>
107+
)}
93108
</EncryptionCardButtons>
94109
</EncryptionCard>
95110
</>

src/i18n/strings/en_EN.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2479,6 +2479,7 @@
24792479
"breadcrumb_title_forgot": "Forgot your recovery key? You’ll need to reset your identity.",
24802480
"breadcrumb_warning": "Only do this if you believe your account has been compromised.",
24812481
"details_title": "Encryption details",
2482+
"do_not_close_warning": "Do not close this window until the reset is finished",
24822483
"export_keys": "Export keys",
24832484
"import_keys": "Import keys",
24842485
"other_people_device_description": "By default in encrypted rooms, do not send encrypted messages to anyone until you’ve verified them",

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import React from "react";
99
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
10+
import { sleep } from "matrix-js-sdk/src/utils";
1011
import { render, screen } from "jest-matrix-react";
1112
import userEvent from "@testing-library/user-event";
1213

@@ -30,7 +31,22 @@ describe("<ResetIdentityPanel />", () => {
3031
);
3132
expect(asFragment()).toMatchSnapshot();
3233

33-
await user.click(screen.getByRole("button", { name: "Continue" }));
34+
// We need to pause the reset so that we can check that it's providing
35+
// feedback to the user that something is happening.
36+
let resolveResetEncryption;
37+
const resetEncryptionPromise: Promise<void> = new Promise((resolve) => {
38+
resolveResetEncryption = resolve;
39+
});
40+
jest.spyOn(matrixClient.getCrypto()!, "resetEncryption").mockImplementation(() => {
41+
return resetEncryptionPromise;
42+
});
43+
44+
const continueButton = screen.getByRole("button", { name: "Continue" });
45+
await user.click(continueButton);
46+
expect(asFragment()).toMatchSnapshot();
47+
resolveResetEncryption!();
48+
await sleep(0);
49+
3450
expect(matrixClient.getCrypto()!.resetEncryption).toHaveBeenCalled();
3551
expect(onFinish).toHaveBeenCalled();
3652
});

test/unit-tests/components/views/settings/encryption/__snapshots__/ResetIdentityPanel-test.tsx.snap

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ exports[`<ResetIdentityPanel /> should display the 'forgot recovery key' variant
159159
class="mx_EncryptionCard_buttons"
160160
>
161161
<button
162+
aria-disabled="false"
162163
class="_button_vczzf_8 _destructive_vczzf_107"
163164
data-kind="primary"
164165
data-size="lg"
@@ -343,6 +344,7 @@ exports[`<ResetIdentityPanel /> should reset the encryption when the continue bu
343344
class="mx_EncryptionCard_buttons"
344345
>
345346
<button
347+
aria-disabled="false"
346348
class="_button_vczzf_8 _destructive_vczzf_107"
347349
data-kind="primary"
348350
data-size="lg"
@@ -364,3 +366,204 @@ exports[`<ResetIdentityPanel /> should reset the encryption when the continue bu
364366
</div>
365367
</DocumentFragment>
366368
`;
369+
370+
exports[`<ResetIdentityPanel /> should reset the encryption when the continue button is clicked 2`] = `
371+
<DocumentFragment>
372+
<nav
373+
class="_breadcrumb_1xygz_8"
374+
>
375+
<button
376+
aria-label="Back"
377+
class="_icon-button_m2erp_8 _subtle-bg_m2erp_29"
378+
role="button"
379+
style="--cpd-icon-button-size: 28px;"
380+
tabindex="0"
381+
>
382+
<div
383+
class="_indicator-icon_zr2a0_17"
384+
style="--cpd-icon-button-size: 100%;"
385+
>
386+
<svg
387+
fill="currentColor"
388+
height="1em"
389+
viewBox="0 0 24 24"
390+
width="1em"
391+
xmlns="http://www.w3.org/2000/svg"
392+
>
393+
<path
394+
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"
395+
/>
396+
</svg>
397+
</div>
398+
</button>
399+
<ol
400+
class="_pages_1xygz_17"
401+
>
402+
<li>
403+
<a
404+
class="_link_1v5rz_8"
405+
data-kind="primary"
406+
data-size="small"
407+
rel="noreferrer noopener"
408+
role="button"
409+
tabindex="0"
410+
>
411+
Encryption
412+
</a>
413+
</li>
414+
<li>
415+
<span
416+
aria-current="page"
417+
class="_last-page_1xygz_30"
418+
>
419+
Reset encryption
420+
</span>
421+
</li>
422+
</ol>
423+
</nav>
424+
<div
425+
class="mx_EncryptionCard"
426+
>
427+
<div
428+
class="mx_EncryptionCard_header"
429+
>
430+
<div
431+
class="_content_o77nw_8 _destructive_o77nw_34"
432+
data-size="large"
433+
>
434+
<svg
435+
fill="currentColor"
436+
height="1em"
437+
viewBox="0 0 24 24"
438+
width="1em"
439+
xmlns="http://www.w3.org/2000/svg"
440+
>
441+
<path
442+
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"
443+
/>
444+
</svg>
445+
</div>
446+
<h2
447+
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
448+
>
449+
Are you sure you want to reset your identity?
450+
</h2>
451+
</div>
452+
<div
453+
class="mx_Flex mx_EncryptionCard_emphasisedContent"
454+
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: normal; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x);"
455+
>
456+
<ul
457+
class="_visual-list_15wzx_8"
458+
>
459+
<li
460+
class="_visual-list-item_1ma3e_8"
461+
>
462+
<svg
463+
aria-hidden="true"
464+
class="_visual-list-item-icon_1ma3e_17 _visual-list-item-icon-success_1ma3e_22"
465+
fill="currentColor"
466+
height="24px"
467+
viewBox="0 0 24 24"
468+
width="24px"
469+
xmlns="http://www.w3.org/2000/svg"
470+
>
471+
<path
472+
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"
473+
/>
474+
</svg>
475+
Your account details, contacts, preferences, and chat list will be kept
476+
</li>
477+
<li
478+
class="_visual-list-item_1ma3e_8"
479+
>
480+
<svg
481+
aria-hidden="true"
482+
class="_visual-list-item-icon_1ma3e_17"
483+
fill="currentColor"
484+
height="24px"
485+
viewBox="0 0 24 24"
486+
width="24px"
487+
xmlns="http://www.w3.org/2000/svg"
488+
>
489+
<path
490+
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"
491+
/>
492+
<path
493+
clip-rule="evenodd"
494+
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"
495+
fill-rule="evenodd"
496+
/>
497+
</svg>
498+
You will lose any message history that’s stored only on the server
499+
</li>
500+
<li
501+
class="_visual-list-item_1ma3e_8"
502+
>
503+
<svg
504+
aria-hidden="true"
505+
class="_visual-list-item-icon_1ma3e_17"
506+
fill="currentColor"
507+
height="24px"
508+
viewBox="0 0 24 24"
509+
width="24px"
510+
xmlns="http://www.w3.org/2000/svg"
511+
>
512+
<path
513+
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"
514+
/>
515+
<path
516+
clip-rule="evenodd"
517+
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"
518+
fill-rule="evenodd"
519+
/>
520+
</svg>
521+
You will need to verify all your existing devices and contacts again
522+
</li>
523+
</ul>
524+
<span>
525+
Only do this if you believe your account has been compromised.
526+
</span>
527+
</div>
528+
<div
529+
class="mx_EncryptionCard_buttons"
530+
>
531+
<button
532+
aria-disabled="true"
533+
class="_button_vczzf_8 _destructive_vczzf_107"
534+
data-kind="primary"
535+
data-size="lg"
536+
role="button"
537+
tabindex="0"
538+
>
539+
<svg
540+
class="_icon_11k6c_18"
541+
fill="currentColor"
542+
height="1em"
543+
style="width: 20px; height: 20px;"
544+
viewBox="0 0 24 24"
545+
width="1em"
546+
xmlns="http://www.w3.org/2000/svg"
547+
>
548+
<path
549+
clip-rule="evenodd"
550+
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"
551+
fill-rule="evenodd"
552+
/>
553+
</svg>
554+
Continue
555+
</button>
556+
<div
557+
class="mx_Flex mx_EncryptionCard_emphasisedContent"
558+
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: normal; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x);"
559+
>
560+
<span
561+
class="text-warning"
562+
>
563+
Do not close this window until the reset is finished
564+
</span>
565+
</div>
566+
</div>
567+
</div>
568+
</DocumentFragment>
569+
`;

test/unit-tests/components/views/settings/tabs/user/__snapshots__/EncryptionUserSettingsTab-test.tsx.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ exports[`<EncryptionUserSettingsTab /> should display the reset identity panel w
333333
class="mx_EncryptionCard_buttons"
334334
>
335335
<button
336+
aria-disabled="false"
336337
class="_button_vczzf_8 _destructive_vczzf_107"
337338
data-kind="primary"
338339
data-size="lg"

0 commit comments

Comments
 (0)