Allow dismissing 'Key storage out of sync' temporarily (#31455)
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -38,6 +38,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
grid-template-columns: 22px 1fr;
|
grid-template-columns: 22px 1fr;
|
||||||
column-gap: 8px;
|
column-gap: 8px;
|
||||||
row-gap: 4px;
|
row-gap: 4px;
|
||||||
|
align-items: center;
|
||||||
padding: var(--cpd-space-3x);
|
padding: var(--cpd-space-3x);
|
||||||
|
|
||||||
&.mx_Toast_hasIcon {
|
&.mx_Toast_hasIcon {
|
||||||
@@ -47,10 +48,17 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Toast_title,
|
.mx_Toast_title {
|
||||||
.mx_Toast_body {
|
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_Toast_body {
|
||||||
|
grid-column: 2 / 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Toast_closebutton {
|
||||||
|
grid-column: 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&:not(.mx_Toast_hasIcon) {
|
&:not(.mx_Toast_hasIcon) {
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Text } from "@vector-im/compound-web";
|
import { IconButton, Text } from "@vector-im/compound-web";
|
||||||
import { type EmptyObject } from "matrix-js-sdk/src/matrix";
|
import { type EmptyObject } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { CloseIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
|
|
||||||
import ToastStore, { type IToast } from "../../stores/ToastStore";
|
import ToastStore, { type IToast } from "../../stores/ToastStore";
|
||||||
|
import { _t } from "../../languageHandler";
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
toasts: IToast<any>[];
|
toasts: IToast<any>[];
|
||||||
@@ -47,7 +49,7 @@ export default class ToastContainer extends React.Component<EmptyObject, IState>
|
|||||||
let containerClasses;
|
let containerClasses;
|
||||||
if (totalCount !== 0) {
|
if (totalCount !== 0) {
|
||||||
const topToast = this.state.toasts[0];
|
const topToast = this.state.toasts[0];
|
||||||
const { title, icon, key, component, className, bodyClassName, props } = topToast;
|
const { title, icon, key, component, className, bodyClassName, onCloseButtonClicked, props } = topToast;
|
||||||
const bodyClasses = classNames("mx_Toast_body", bodyClassName);
|
const bodyClasses = classNames("mx_Toast_body", bodyClassName);
|
||||||
const toastClasses = classNames("mx_Toast_toast", className, {
|
const toastClasses = classNames("mx_Toast_toast", className, {
|
||||||
mx_Toast_hasIcon: !!icon,
|
mx_Toast_hasIcon: !!icon,
|
||||||
@@ -61,11 +63,24 @@ export default class ToastContainer extends React.Component<EmptyObject, IState>
|
|||||||
let titleElement;
|
let titleElement;
|
||||||
if (title) {
|
if (title) {
|
||||||
titleElement = (
|
titleElement = (
|
||||||
<div className="mx_Toast_title">
|
<>
|
||||||
<Text size="lg" weight="semibold" as="h2">
|
<div className="mx_Toast_title">
|
||||||
{title}
|
<Text size="lg" weight="semibold" as="h2">
|
||||||
</Text>
|
{title}
|
||||||
</div>
|
</Text>
|
||||||
|
</div>
|
||||||
|
{onCloseButtonClicked && (
|
||||||
|
<IconButton
|
||||||
|
className="mx_Toast_closebutton"
|
||||||
|
size="28px"
|
||||||
|
onClick={onCloseButtonClicked}
|
||||||
|
tooltip={_t("action|close")}
|
||||||
|
kind="secondary"
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ export interface IToast<C extends ComponentClass> {
|
|||||||
component: C;
|
component: C;
|
||||||
className?: string;
|
className?: string;
|
||||||
bodyClassName?: string;
|
bodyClassName?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What to do if the user clicks the close button. If this is undefined, the
|
||||||
|
* close button is not displayed.
|
||||||
|
*
|
||||||
|
* Note: the close button is only displayed if the toast has a title (i.e. if {@link title} is truthy).
|
||||||
|
*/
|
||||||
|
onCloseButtonClicked?: () => void;
|
||||||
|
|
||||||
props?: Omit<React.ComponentProps<C>, "toastKey">; // toastKey is injected by ToastContainer
|
props?: Omit<React.ComponentProps<C>, "toastKey">; // toastKey is injected by ToastContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,18 @@ const getIcon = (state: DeviceStateForToast): IToast<any>["icon"] => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const shouldShowCloseButton = (state: DeviceStateForToast): boolean => {
|
||||||
|
switch (state) {
|
||||||
|
case "key_storage_out_of_sync":
|
||||||
|
case "identity_needs_reset":
|
||||||
|
return true;
|
||||||
|
case "set_up_recovery":
|
||||||
|
case "verify_this_session":
|
||||||
|
case "turn_on_key_storage":
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getSetupCaption = (state: DeviceStateForToast): string => {
|
const getSetupCaption = (state: DeviceStateForToast): string => {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "set_up_recovery":
|
case "set_up_recovery":
|
||||||
@@ -299,10 +311,15 @@ export const showToast = (state: DeviceStateForToast): void => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onCloseButtonClicked = shouldShowCloseButton(state)
|
||||||
|
? () => DeviceListener.sharedInstance().dismissEncryptionSetup()
|
||||||
|
: undefined;
|
||||||
|
|
||||||
ToastStore.sharedInstance().addOrReplaceToast({
|
ToastStore.sharedInstance().addOrReplaceToast({
|
||||||
key: TOAST_KEY,
|
key: TOAST_KEY,
|
||||||
title: getTitle(state),
|
title: getTitle(state),
|
||||||
icon: getIcon(state),
|
icon: getIcon(state),
|
||||||
|
onCloseButtonClicked,
|
||||||
props: {
|
props: {
|
||||||
description: getDescription(state),
|
description: getDescription(state),
|
||||||
primaryLabel: getSetupCaption(state),
|
primaryLabel: getSetupCaption(state),
|
||||||
|
|||||||
@@ -189,6 +189,17 @@ describe("SetupEncryptionToast", () => {
|
|||||||
props: { initialEncryptionState: "change_recovery_key" },
|
props: { initialEncryptionState: "change_recovery_key" },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should dismiss the toast when the close button is clicked", async () => {
|
||||||
|
jest.spyOn(DeviceListener.sharedInstance(), "dismissEncryptionSetup");
|
||||||
|
|
||||||
|
act(() => showToast("key_storage_out_of_sync"));
|
||||||
|
|
||||||
|
const user = userEvent.setup();
|
||||||
|
await user.click(await screen.findByRole("button", { name: "Close" }));
|
||||||
|
|
||||||
|
expect(DeviceListener.sharedInstance().dismissEncryptionSetup).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Turn on key storage", () => {
|
describe("Turn on key storage", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user