Skip to content

Commit 0edccfe

Browse files
committed
Delegate to ResetIdentityDialog from SetupEncryptionBody
1 parent 4ac84fa commit 0edccfe

File tree

8 files changed

+47
-66
lines changed

8 files changed

+47
-66
lines changed

playwright/e2e/crypto/dehydration.spec.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,20 @@ test.describe("Dehydration", () => {
3535

3636
await app.closeDialog();
3737

38-
// Verify the device by resetting the key
38+
// Reset the key
3939
const settings = await app.settings.openUserSettings("Encryption");
4040
await settings.getByRole("button", { name: "Verify this device" }).click();
4141
await page.getByRole("button", { name: "Proceed with reset" }).click();
4242
await page.getByRole("button", { name: "Continue" }).click();
43-
await page.getByRole("button", { name: "Copy" }).click();
43+
44+
// Set up recovery
45+
await page.getByRole("button", { name: "Set up recovery" }).click();
46+
await page.getByRole("button", { name: "Continue" }).click();
47+
const recoveryKey = await page.getByTestId("recoveryKey").innerText();
4448
await page.getByRole("button", { name: "Continue" }).click();
45-
await page.getByRole("button", { name: "Done" }).click();
49+
await page.getByRole("textbox").fill(recoveryKey);
50+
await page.getByRole("button", { name: "Finish set up" }).click();
51+
await page.getByRole("button", { name: "Close" }).click();
4652

4753
await expectDehydratedDeviceEnabled(app);
4854

src/components/structures/auth/CompleteSecurity.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,9 @@ export default class CompleteSecurity extends React.Component<IProps, IState> {
7575
} else if (phase === Phase.ConfirmSkip) {
7676
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
7777
title = _t("common|are_you_sure");
78-
} else if (phase === Phase.Busy) {
78+
} else if (phase === Phase.Busy || phase === Phase.ConfirmReset) {
7979
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
8080
title = _t("encryption|verification|after_new_login|verify_this_device");
81-
} else if (phase === Phase.ConfirmReset) {
82-
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
83-
title = _t("encryption|verification|after_new_login|reset_confirmation");
8481
} else if (phase === Phase.Finished) {
8582
// SetupEncryptionBody will take care of calling onFinished, we don't need to do anything
8683
} else {

src/components/structures/auth/SetupEncryptionBody.tsx

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2024 New Vector Ltd.
2+
Copyright 2024, 2025 New Vector Ltd.
33
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
44
55
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
@@ -19,6 +19,7 @@ import { SetupEncryptionStore, Phase } from "../../../stores/SetupEncryptionStor
1919
import EncryptionPanel from "../../views/right_panel/EncryptionPanel";
2020
import AccessibleButton, { type ButtonEvent } from "../../views/elements/AccessibleButton";
2121
import Spinner from "../../views/elements/Spinner";
22+
import { ResetIdentityDialog } from "../../views/dialogs/ResetIdentityDialog";
2223

2324
function keyHasPassphrase(keyInfo: SecretStorageKeyDescription): boolean {
2425
return Boolean(keyInfo.passphrase && keyInfo.passphrase.salt && keyInfo.passphrase.iterations);
@@ -114,12 +115,18 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
114115
ev.preventDefault();
115116
const store = SetupEncryptionStore.sharedInstance();
116117
store.reset();
117-
};
118-
119-
private onResetConfirmClick = (): void => {
120-
this.props.onFinished();
121-
const store = SetupEncryptionStore.sharedInstance();
122-
store.resetConfirm();
118+
Modal.createDialog(ResetIdentityDialog, {
119+
onReset: () => {
120+
// The user completed the reset process - close this dialog
121+
this.props.onFinished();
122+
this.onDoneClick();
123+
},
124+
onFinished: () => {
125+
// The user cancelled the reset dialog or click away - go back a step
126+
this.onResetBackClick();
127+
},
128+
variant: "confirm",
129+
});
123130
};
124131

125132
private onResetBackClick = (): void => {
@@ -157,7 +164,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
157164
<p>{_t("encryption|verification|no_key_or_device")}</p>
158165

159166
<div className="mx_CompleteSecurity_actionRow">
160-
<AccessibleButton kind="primary" onClick={this.onResetConfirmClick}>
167+
<AccessibleButton kind="primary" onClick={this.onResetClick}>
161168
{_t("encryption|verification|reset_proceed_prompt")}
162169
</AccessibleButton>
163170
</div>
@@ -246,23 +253,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
246253
</div>
247254
</div>
248255
);
249-
} else if (phase === Phase.ConfirmReset) {
250-
return (
251-
<div>
252-
<p>{_t("encryption|verification|verify_reset_warning_1")}</p>
253-
<p>{_t("encryption|verification|verify_reset_warning_2")}</p>
254-
255-
<div className="mx_CompleteSecurity_actionRow">
256-
<AccessibleButton kind="danger_outline" onClick={this.onResetConfirmClick}>
257-
{_t("encryption|verification|reset_proceed_prompt")}
258-
</AccessibleButton>
259-
<AccessibleButton kind="primary" onClick={this.onResetBackClick}>
260-
{_t("action|go_back")}
261-
</AccessibleButton>
262-
</div>
263-
</div>
264-
);
265-
} else if (phase === Phase.Busy || phase === Phase.Loading) {
256+
} else if (phase === Phase.Busy || phase === Phase.Loading || phase == Phase.ConfirmReset) {
266257
return <Spinner />;
267258
} else {
268259
logger.log(`SetupEncryptionBody: Unknown phase ${phase}`);

src/components/views/dialogs/ResetIdentityDialog.tsx

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,11 @@ interface ResetIdentityDialogProps {
1616
* Called when the dialog closes.
1717
*/
1818
onFinished: () => void;
19+
1920
/**
20-
* Called when the identity is reset.
21-
*/
22-
onResetFinished: MouseEventHandler<HTMLButtonElement>;
23-
/**
24-
* Called when the cancel button is clicked.
21+
* Called when the identity is reset (before onFinished is called).
2522
*/
26-
onCancelClick: () => void;
23+
onReset: MouseEventHandler<HTMLButtonElement>;
2724

2825
/**
2926
* Which variant of this dialog to show.
@@ -34,28 +31,19 @@ interface ResetIdentityDialogProps {
3431
/**
3532
* The dialog for resetting the identity of the current user.
3633
*/
37-
export function ResetIdentityDialog({
38-
onFinished,
39-
onCancelClick,
40-
onResetFinished,
41-
variant,
42-
}: ResetIdentityDialogProps): JSX.Element {
34+
export function ResetIdentityDialog({ onFinished, onReset, variant }: ResetIdentityDialogProps): JSX.Element {
4335
const matrixClient = MatrixClientPeg.safeGet();
4436

4537
// Wrappers for ResetIdentityBody's callbacks so that onFinish gets called
4638
// whenever the reset is done, whether by completing successfully, or by
4739
// being cancelled
4840
const onResetWrapper: MouseEventHandler<HTMLButtonElement> = (...args) => {
41+
onReset(...args);
4942
onFinished();
50-
onResetFinished(...args);
51-
};
52-
const onCancelWrapper: () => void = () => {
53-
onFinished();
54-
onCancelClick();
5543
};
5644
return (
5745
<MatrixClientContext.Provider value={matrixClient}>
58-
<ResetIdentityBody onFinish={onResetWrapper} onCancelClick={onCancelWrapper} variant={variant} />
46+
<ResetIdentityBody onReset={onResetWrapper} onCancelClick={onFinished} variant={variant} />
5947
</MatrixClientContext.Provider>
6048
);
6149
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ interface ResetIdentityBodyProps {
2222
/**
2323
* Called when the identity is reset.
2424
*/
25-
onFinish: MouseEventHandler<HTMLButtonElement>;
25+
onReset: MouseEventHandler<HTMLButtonElement>;
26+
2627
/**
2728
* Called when the cancel button is clicked.
2829
*/
@@ -51,7 +52,7 @@ export type ResetIdentityBodyVariant = "compromised" | "forgot" | "sync_failed"
5152
/**
5253
* Reset the user's cryptographic identity. Used by ResetIdentityPanel.
5354
*/
54-
export function ResetIdentityBody({ onCancelClick, onFinish, variant }: ResetIdentityBodyProps): JSX.Element {
55+
export function ResetIdentityBody({ onCancelClick, onReset, variant }: ResetIdentityBodyProps): JSX.Element {
5556
const matrixClient = useMatrixClientContext();
5657

5758
// After the user clicks "Continue", we disable the button so it can't be
@@ -83,7 +84,7 @@ export function ResetIdentityBody({ onCancelClick, onFinish, variant }: ResetIde
8384
await matrixClient
8485
.getCrypto()
8586
?.resetEncryption((makeRequest) => uiAuthCallback(matrixClient, makeRequest));
86-
onFinish(evt);
87+
onReset(evt);
8788
}}
8889
>
8990
{inProgress ? (

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ interface ResetIdentityPanelProps {
1515
/**
1616
* Called when the identity is reset.
1717
*/
18-
onFinish: MouseEventHandler<HTMLButtonElement>;
18+
onReset: MouseEventHandler<HTMLButtonElement>;
19+
1920
/**
2021
* Called when the cancel button is clicked or when we go back in the breadcrumbs.
2122
*/
@@ -32,7 +33,7 @@ interface ResetIdentityPanelProps {
3233
*
3334
* A thin wrapper around ResetIdentityBody, just adding breadcrumbs.
3435
*/
35-
export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetIdentityPanelProps): JSX.Element {
36+
export function ResetIdentityPanel({ onCancelClick, onReset, variant }: ResetIdentityPanelProps): JSX.Element {
3637
return (
3738
<>
3839
<Breadcrumb
@@ -41,7 +42,7 @@ export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetId
4142
pages={[_t("settings|encryption|title"), _t("settings|encryption|advanced|breadcrumb_page")]}
4243
onPageClick={onCancelClick}
4344
/>
44-
<ResetIdentityBody onFinish={onFinish} onCancelClick={onCancelClick} variant={variant} />
45+
<ResetIdentityBody onReset={onReset} onCancelClick={onCancelClick} variant={variant} />
4546
</>
4647
);
4748
}

src/i18n/strings/en_EN.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,6 @@
992992
"accepting": "Accepting…",
993993
"after_new_login": {
994994
"device_verified": "Device verified",
995-
"reset_confirmation": "Really reset verification keys?",
996995
"skip_verification": "Skip verification for now",
997996
"unable_to_verify": "Unable to verify this device",
998997
"verify_this_device": "Verify this device"
@@ -1063,8 +1062,6 @@
10631062
"verify_emoji_prompt": "Verify by comparing unique emoji.",
10641063
"verify_emoji_prompt_qr": "If you can't scan the code above, verify by comparing unique emoji.",
10651064
"verify_later": "I'll verify later",
1066-
"verify_reset_warning_1": "Resetting your verification keys cannot be undone. After resetting, you won't have access to old encrypted messages, and any friends who have previously verified you will see security warnings until you re-verify with them.",
1067-
"verify_reset_warning_2": "Please only proceed if you're sure you've lost all of your other devices and your Recovery Key.",
10681065
"verify_using_device": "Verify with another device",
10691066
"verify_using_key": "Verify with Recovery Key",
10701067
"verify_using_key_or_phrase": "Verify with Recovery Key or Phrase",

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ describe("<ResetIdentityPanel />", () => {
2424
it("should reset the encryption when the continue button is clicked", async () => {
2525
const user = userEvent.setup();
2626

27-
const onFinish = jest.fn();
27+
const onReset = jest.fn();
2828
const { asFragment } = render(
29-
<ResetIdentityPanel variant="compromised" onFinish={onFinish} onCancelClick={jest.fn()} />,
29+
<ResetIdentityPanel variant="compromised" onReset={onReset} onCancelClick={jest.fn()} />,
3030
withClientContextRenderOptions(matrixClient),
3131
);
3232
expect(asFragment()).toMatchSnapshot();
@@ -43,22 +43,22 @@ describe("<ResetIdentityPanel />", () => {
4343
await sleep(0);
4444

4545
expect(matrixClient.getCrypto()!.resetEncryption).toHaveBeenCalled();
46-
expect(onFinish).toHaveBeenCalled();
46+
expect(onReset).toHaveBeenCalled();
4747
});
4848

4949
it("should display the 'forgot recovery key' variant correctly", async () => {
50-
const onFinish = jest.fn();
50+
const onReset = jest.fn();
5151
const { asFragment } = render(
52-
<ResetIdentityPanel variant="forgot" onFinish={onFinish} onCancelClick={jest.fn()} />,
52+
<ResetIdentityPanel variant="forgot" onReset={onReset} onCancelClick={jest.fn()} />,
5353
withClientContextRenderOptions(matrixClient),
5454
);
5555
expect(asFragment()).toMatchSnapshot();
5656
});
5757

5858
it("should display the 'sync failed' variant correctly", async () => {
59-
const onFinish = jest.fn();
59+
const onReset = jest.fn();
6060
const { asFragment } = render(
61-
<ResetIdentityPanel variant="sync_failed" onFinish={onFinish} onCancelClick={jest.fn()} />,
61+
<ResetIdentityPanel variant="sync_failed" onReset={onReset} onCancelClick={jest.fn()} />,
6262
withClientContextRenderOptions(matrixClient),
6363
);
6464
expect(asFragment()).toMatchSnapshot();

0 commit comments

Comments
 (0)