Prompt users to set up recovery (#30075)
* Show indicator in settings dialog when user doesn't have recovery set up * Update settings headers to use red dot for recommended settings * update recovery setup toast and remember if the user dismisses it * update playwright snapshots * use typed event emitters * reverse logic for the account data flag * fix comment and type
This commit is contained in:
@@ -29,6 +29,7 @@ export class Tab<T extends string> {
|
||||
* @param {string|JSX.Element} icon An SVG element to use for the tab icon. Can also be a string for legacy icons, in which case it is the class for the tab icon. This should be a simple mask.
|
||||
* @param {JSX.Element} body The JSX for the tab container.
|
||||
* @param {string} screenName The screen name to report to Posthog.
|
||||
* @param {string} labelClassName Additional class to add to the tab label.
|
||||
*/
|
||||
public constructor(
|
||||
public readonly id: T,
|
||||
@@ -36,6 +37,7 @@ export class Tab<T extends string> {
|
||||
public readonly icon: string | JSX.Element | null,
|
||||
public readonly body: JSX.Element,
|
||||
public readonly screenName?: ScreenName,
|
||||
public readonly labelClassName?: string,
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -85,7 +87,7 @@ interface ITabLabelProps<T extends string> {
|
||||
}
|
||||
|
||||
function TabLabel<T extends string>({ tab, isActive, showToolip, onClick }: ITabLabelProps<T>): JSX.Element {
|
||||
const classes = classNames("mx_TabbedView_tabLabel", {
|
||||
const classes = classNames("mx_TabbedView_tabLabel", tab.labelClassName, {
|
||||
mx_TabbedView_tabLabel_active: isActive,
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { ClientEvent, type MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { Toast } from "@vector-im/compound-web";
|
||||
import React, { type JSX, useState } from "react";
|
||||
import UserProfileIcon from "@vector-im/compound-design-tokens/assets/web/icons/user-profile";
|
||||
@@ -44,6 +45,7 @@ import { UserTab } from "./UserTab";
|
||||
import { type NonEmptyArray } from "../../../@types/common";
|
||||
import { SDKContext, type SdkContextClass } from "../../../contexts/SDKContext";
|
||||
import { useSettingValue } from "../../../hooks/useSettings";
|
||||
import { NoChange, useEventEmitterAsyncState, type AsyncStateCallbackResult } from "../../../hooks/useEventEmitter";
|
||||
import { ToastContext, useActiveToast } from "../../../contexts/ToastContext";
|
||||
import { EncryptionUserSettingsTab, type State } from "../settings/tabs/user/EncryptionUserSettingsTab";
|
||||
|
||||
@@ -100,6 +102,26 @@ export default function UserSettingsDialog(props: IProps): JSX.Element {
|
||||
const [showMsc4108QrCode, setShowMsc4108QrCode] = useState(props.showMsc4108QrCode);
|
||||
const [initialEncryptionState, setInitialEncryptionState] = useState(props.initialEncryptionState);
|
||||
|
||||
// If the user doesn't have Recovery set up (no default Secret Storage key),
|
||||
// we show an indicator on the Encryption tab.
|
||||
const showSetupRecoveryIndicator = useEventEmitterAsyncState(
|
||||
props.sdkContext.client,
|
||||
ClientEvent.AccountData,
|
||||
async (event?: MatrixEvent): AsyncStateCallbackResult<boolean> => {
|
||||
if (event === undefined || event.getType() === "m.secret_storage.default_key") {
|
||||
const client = props.sdkContext.client;
|
||||
if (!client) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !(await client.secretStorage.getDefaultKeyId());
|
||||
}
|
||||
return new NoChange();
|
||||
},
|
||||
[],
|
||||
false,
|
||||
);
|
||||
|
||||
const getTabs = (): NonEmptyArray<Tab<UserTab>> => {
|
||||
const tabs: Tab<UserTab>[] = [];
|
||||
|
||||
@@ -196,6 +218,7 @@ export default function UserSettingsDialog(props: IProps): JSX.Element {
|
||||
<KeyIcon />,
|
||||
<EncryptionUserSettingsTab initialState={initialEncryptionState} />,
|
||||
"UserSettingsEncryption",
|
||||
showSetupRecoveryIndicator ? "mx_SettingsDialog_tabLabelsAlert" : undefined,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
*/
|
||||
|
||||
import React, { type JSX } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Heading } from "@vector-im/compound-web";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
/**
|
||||
* The heading for a settings section.
|
||||
*/
|
||||
@@ -25,9 +24,12 @@ interface SettingsHeaderProps {
|
||||
}
|
||||
|
||||
export function SettingsHeader({ hasRecommendedTag = false, label }: SettingsHeaderProps): JSX.Element {
|
||||
const classes = classNames("mx_SettingsHeader", {
|
||||
mx_SettingsHeader_recommended: hasRecommendedTag,
|
||||
});
|
||||
return (
|
||||
<Heading className="mx_SettingsHeader" as="h2" size="sm" weight="semibold">
|
||||
{label} {hasRecommendedTag && <span>{_t("common|recommended")}</span>}
|
||||
<Heading className={classes} as="h2" size="sm" weight="semibold">
|
||||
{label}
|
||||
</Heading>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import { initialiseDehydrationIfEnabled } from "../../../../utils/device/dehydra
|
||||
import { withSecretStorageKeyCache } from "../../../../SecurityManager";
|
||||
import { EncryptionCardButtons } from "./EncryptionCardButtons";
|
||||
import { logErrorAndShowErrorDialog } from "../../../../utils/ErrorUtils.tsx";
|
||||
import { RECOVERY_ACCOUNT_DATA_KEY } from "../../../../DeviceListener";
|
||||
|
||||
/**
|
||||
* The possible states of the component.
|
||||
@@ -131,6 +132,10 @@ export function ChangeRecoveryKey({
|
||||
});
|
||||
await initialiseDehydrationIfEnabled(matrixClient, { createNewKey: true });
|
||||
});
|
||||
|
||||
// Record the fact that the user explicitly enabled recovery.
|
||||
await matrixClient.setAccountData(RECOVERY_ACCOUNT_DATA_KEY, { enabled: true });
|
||||
|
||||
onFinish();
|
||||
} catch (e) {
|
||||
logErrorAndShowErrorDialog("Failed to set up secret storage", e);
|
||||
|
||||
Reference in New Issue
Block a user