Compare commits

...

1 Commits

Author SHA1 Message Date
Andy Balaam
f6f58a29f8 Extract ResetIdentityBody into a separate object to allow re-using it 2025-04-11 16:00:51 +01:00
3 changed files with 135 additions and 93 deletions

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2024-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 { 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 JSX, useState, type MouseEventHandler } from "react";
import { _t } from "../../../../languageHandler";
import { EncryptionCard } from "./EncryptionCard";
import { uiAuthCallback } from "../../../../CreateCrossSigning";
import { EncryptionCardButtons } from "./EncryptionCardButtons";
import { EncryptionCardEmphasisedContent } from "./EncryptionCardEmphasisedContent";
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
interface ResetIdentityBodyProps {
/**
* Called when the identity is reset.
*/
onFinish: MouseEventHandler<HTMLButtonElement>;
/**
* Called when the cancel button is clicked.
*/
onCancelClick: () => void;
/**
* The variant of the panel to show. We show more warnings in the 'compromised' variant (no use in showing a user
* this warning if they have to reset because they no longer have their key)
*/
variant: ResetIdentityBodyVariant;
}
/**
* "compromised" is shown when the user chooses 'reset' explicitly in settings, usually because they believe their
* identity has been compromised.
*
* "sync_failed" is shown when the user tried to recover their identity but the process failed, probably because
* the required information is missing from recovery.
*
* "forgot" is shown when the user has just forgotten their passphrase.
*/
export type ResetIdentityBodyVariant = "compromised" | "forgot" | "sync_failed";
/**
* User interface component allowing the user to reset their cryptographic identity.
*
* Used by {@link ResetIdentityPanel}.
*/
export function ResetIdentityBody({ onCancelClick, onFinish, variant }: ResetIdentityBodyProps): 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 (
<EncryptionCard Icon={ErrorIcon} destructive={true} title={titleForVariant(variant)}>
<EncryptionCardEmphasisedContent>
<VisualList>
<VisualListItem Icon={CheckIcon} success={true}>
{_t("settings|encryption|advanced|breadcrumb_first_description")}
</VisualListItem>
<VisualListItem Icon={InfoIcon}>
{_t("settings|encryption|advanced|breadcrumb_second_description")}
</VisualListItem>
<VisualListItem Icon={InfoIcon}>
{_t("settings|encryption|advanced|breadcrumb_third_description")}
</VisualListItem>
</VisualList>
{variant === "compromised" && <span>{_t("settings|encryption|advanced|breadcrumb_warning")}</span>}
</EncryptionCardEmphasisedContent>
<EncryptionCardButtons>
<Button
destructive={true}
disabled={inProgress}
onClick={async (evt) => {
setInProgress(true);
await matrixClient
.getCrypto()
?.resetEncryption((makeRequest) => uiAuthCallback(matrixClient, makeRequest));
onFinish(evt);
}}
>
{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>
);
}
function titleForVariant(variant: ResetIdentityBodyVariant): string {
switch (variant) {
case "compromised":
return _t("settings|encryption|advanced|breadcrumb_title");
case "sync_failed":
return _t("settings|encryption|advanced|breadcrumb_title_sync_failed");
default:
case "forgot":
return _t("settings|encryption|advanced|breadcrumb_title_forgot");
}
}

View File

@@ -5,18 +5,11 @@
* Please see LICENSE files in the repository root for full details.
*/
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 JSX, useState, type MouseEventHandler } from "react";
import { Breadcrumb } from "@vector-im/compound-web";
import React, { type JSX, type MouseEventHandler } from "react";
import { _t } from "../../../../languageHandler";
import { EncryptionCard } from "./EncryptionCard";
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
import { uiAuthCallback } from "../../../../CreateCrossSigning";
import { EncryptionCardButtons } from "./EncryptionCardButtons";
import { EncryptionCardEmphasisedContent } from "./EncryptionCardEmphasisedContent";
import { ResetIdentityBody, type ResetIdentityBodyVariant } from "./ResetIdentityBody";
interface ResetIdentityPanelProps {
/**
@@ -29,33 +22,17 @@ interface ResetIdentityPanelProps {
onCancelClick: () => void;
/**
* The variant of the panel to show. We show more warnings in the 'compromised' variant (no use in showing a user
* this warning if they have to reset because they no longer have their key)
* Which variant of this panel to show.
*/
variant: ResetIdentityPanelVariant;
variant: ResetIdentityBodyVariant;
}
/**
* "compromised" is shown when the user chooses 'reset' explicitly in settings, usually because they believe their
* identity has been compromised.
* The Encryption Settings panel for resetting the identity of the current user.
*
* "sync_failed" is shown when the user tried to recover their identity but the process failed, probably because
* the required information is missing from recovery.
*
* "forgot" is shown when the user has just forgotten their passphrase.
*/
export type ResetIdentityPanelVariant = "compromised" | "forgot" | "sync_failed";
/**
* The panel for resetting the identity of the current user.
* A thin wrapper around {@link ResetIdentityBody}, just adding breadcrumbs.
*/
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
@@ -64,67 +41,7 @@ export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetId
pages={[_t("settings|encryption|title"), _t("settings|encryption|advanced|breadcrumb_page")]}
onPageClick={onCancelClick}
/>
<EncryptionCard Icon={ErrorIcon} destructive={true} title={titleForVariant(variant)}>
<EncryptionCardEmphasisedContent>
<VisualList>
<VisualListItem Icon={CheckIcon} success={true}>
{_t("settings|encryption|advanced|breadcrumb_first_description")}
</VisualListItem>
<VisualListItem Icon={InfoIcon}>
{_t("settings|encryption|advanced|breadcrumb_second_description")}
</VisualListItem>
<VisualListItem Icon={InfoIcon}>
{_t("settings|encryption|advanced|breadcrumb_third_description")}
</VisualListItem>
</VisualList>
{variant === "compromised" && <span>{_t("settings|encryption|advanced|breadcrumb_warning")}</span>}
</EncryptionCardEmphasisedContent>
<EncryptionCardButtons>
<Button
destructive={true}
disabled={inProgress}
onClick={async (evt) => {
setInProgress(true);
await matrixClient
.getCrypto()
?.resetEncryption((makeRequest) => uiAuthCallback(matrixClient, makeRequest));
onFinish(evt);
}}
>
{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>
<ResetIdentityBody onFinish={onFinish} onCancelClick={onCancelClick} variant={variant} />
</>
);
}
function titleForVariant(variant: ResetIdentityPanelVariant): string {
switch (variant) {
case "compromised":
return _t("settings|encryption|advanced|breadcrumb_title");
case "sync_failed":
return _t("settings|encryption|advanced|breadcrumb_title_sync_failed");
default:
case "forgot":
return _t("settings|encryption|advanced|breadcrumb_title_forgot");
}
}

View File

@@ -20,7 +20,8 @@ import SetupEncryptionDialog from "../../../dialogs/security/SetupEncryptionDial
import { SettingsSection } from "../../shared/SettingsSection";
import { SettingsSubheader } from "../../SettingsSubheader";
import { AdvancedPanel } from "../../encryption/AdvancedPanel";
import { ResetIdentityPanel, type ResetIdentityPanelVariant } from "../../encryption/ResetIdentityPanel";
import { ResetIdentityPanel } from "../../encryption/ResetIdentityPanel";
import { type ResetIdentityBodyVariant } from "../../encryption/ResetIdentityBody";
import { RecoveryPanelOutOfSync } from "../../encryption/RecoveryPanelOutOfSync";
import { useTypedEventEmitter } from "../../../../../hooks/useEventEmitter";
import { KeyStoragePanel } from "../../encryption/KeyStoragePanel";
@@ -147,7 +148,7 @@ export function EncryptionUserSettingsTab({ initialState = "loading" }: Props):
* Given what state we want the tab to be in, what variant of the
* ResetIdentityPanel do we need?
*/
function findResetVariant(state: State): ResetIdentityPanelVariant {
function findResetVariant(state: State): ResetIdentityBodyVariant {
switch (state) {
case "reset_identity_compromised":
return "compromised";