Show "Bob shared this message" on messages shared via MSC4268 (#31684)
* Factor out E2ePadlock to its own file * Show "Bob shared this message" on messages shared via MSC4268 If we received the keys for a given message from another user, indicate that in the timeline, rather than just saying "authenticity not guaranteed" * Apply suggestions from code review Co-authored-by: Florian Duros <florianduros@element.io> * Address review comments * Move E2ePadlock to shared-components * update snapshots * Revert "update snapshots" This reverts commit 751e31f9db901fda085143c98e5dffa3d2b4c099. * Revert "Move E2ePadlock to shared-components" This reverts commit 172ef9f70ab26fd36b0ac854379cfd3371d22c18. --------- Co-authored-by: Florian Duros <florianduros@element.io>
This commit is contained in:
co-authored by
Florian Duros
parent
d9a4858b1d
commit
92a6db5912
@@ -35,12 +35,7 @@ import {
|
|||||||
} from "matrix-js-sdk/src/crypto-api";
|
} from "matrix-js-sdk/src/crypto-api";
|
||||||
import { Tooltip } from "@vector-im/compound-web";
|
import { Tooltip } from "@vector-im/compound-web";
|
||||||
import { uniqueId } from "lodash";
|
import { uniqueId } from "lodash";
|
||||||
import {
|
import { CircleIcon, CheckCircleIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
CircleIcon,
|
|
||||||
ErrorSolidIcon,
|
|
||||||
InfoIcon,
|
|
||||||
CheckCircleIcon,
|
|
||||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
|
||||||
|
|
||||||
import ReplyChain from "../elements/ReplyChain";
|
import ReplyChain from "../elements/ReplyChain";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
@@ -89,6 +84,8 @@ import PinningUtils from "../../../utils/PinningUtils";
|
|||||||
import { PinnedMessageBadge } from "../messages/PinnedMessageBadge";
|
import { PinnedMessageBadge } from "../messages/PinnedMessageBadge";
|
||||||
import { EventPreview } from "./EventPreview";
|
import { EventPreview } from "./EventPreview";
|
||||||
import { ElementCallEventType } from "../../../call-types";
|
import { ElementCallEventType } from "../../../call-types";
|
||||||
|
import { E2eMessageSharedIcon } from "./EventTile/E2eMessageSharedIcon.tsx";
|
||||||
|
import { E2ePadlock, E2ePadlockIcon } from "./EventTile/E2ePadlock.tsx";
|
||||||
|
|
||||||
export type GetRelationsForEvent = (
|
export type GetRelationsForEvent = (
|
||||||
eventId: string,
|
eventId: string,
|
||||||
@@ -740,6 +737,14 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.state.shieldReason === EventShieldReason.AUTHENTICITY_NOT_GUARANTEED) {
|
||||||
|
// This may happen if the message was forwarded to us by another user, in which case we can show a better message
|
||||||
|
const forwarder = this.props.mxEvent.getKeyForwardingUser();
|
||||||
|
if (forwarder) {
|
||||||
|
return <E2eMessageSharedIcon keyForwardingUserId={forwarder} roomId={ev.getRoomId()!} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.state.shieldColour !== EventShieldColour.NONE) {
|
if (this.state.shieldColour !== EventShieldColour.NONE) {
|
||||||
let shieldReasonMessage: string;
|
let shieldReasonMessage: string;
|
||||||
switch (this.state.shieldReason) {
|
switch (this.state.shieldReason) {
|
||||||
@@ -1513,58 +1518,12 @@ const SafeEventTile = (props: EventTileProps): JSX.Element => {
|
|||||||
};
|
};
|
||||||
export default SafeEventTile;
|
export default SafeEventTile;
|
||||||
|
|
||||||
function E2ePadlockUnencrypted(props: Omit<IE2ePadlockProps, "title" | "icon">): JSX.Element {
|
function E2ePadlockUnencrypted(): JSX.Element {
|
||||||
return <E2ePadlock title={_t("common|unencrypted")} icon={E2ePadlockIcon.Warning} {...props} />;
|
return <E2ePadlock title={_t("common|unencrypted")} icon={E2ePadlockIcon.Warning} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function E2ePadlockDecryptionFailure(props: Omit<IE2ePadlockProps, "title" | "icon">): JSX.Element {
|
function E2ePadlockDecryptionFailure(): JSX.Element {
|
||||||
return (
|
return <E2ePadlock title={_t("timeline|undecryptable_tooltip")} icon={E2ePadlockIcon.DecryptionFailure} />;
|
||||||
<E2ePadlock title={_t("timeline|undecryptable_tooltip")} icon={E2ePadlockIcon.DecryptionFailure} {...props} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum E2ePadlockIcon {
|
|
||||||
/** Compound Info icon in grey */
|
|
||||||
Normal = "normal",
|
|
||||||
|
|
||||||
/** Compound ErrorSolid icon in red */
|
|
||||||
Warning = "warning",
|
|
||||||
|
|
||||||
/** Compound ErrorSolid icon in grey */
|
|
||||||
DecryptionFailure = "decryption_failure",
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IE2ePadlockProps {
|
|
||||||
icon: E2ePadlockIcon;
|
|
||||||
title: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
class E2ePadlock extends React.Component<IE2ePadlockProps> {
|
|
||||||
private static icons: Record<E2ePadlockIcon, JSX.Element> = {
|
|
||||||
[E2ePadlockIcon.Normal]: <InfoIcon color="var(--cpd-color-icon-tertiary)" />,
|
|
||||||
[E2ePadlockIcon.Warning]: <ErrorSolidIcon color="var(--cpd-color-icon-critical-primary)" />,
|
|
||||||
[E2ePadlockIcon.DecryptionFailure]: <ErrorSolidIcon color="var(--cpd-color-icon-tertiary)" />,
|
|
||||||
};
|
|
||||||
|
|
||||||
public constructor(props: IE2ePadlockProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
hover: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): ReactNode {
|
|
||||||
// We specify isTriggerInteractive=true and make the div interactive manually as a workaround for
|
|
||||||
// https://github.com/element-hq/compound/issues/294
|
|
||||||
return (
|
|
||||||
<Tooltip label={this.props.title} isTriggerInteractive={true}>
|
|
||||||
<div className="mx_EventTile_e2eIcon" tabIndex={0} aria-label={_t("timeline|e2e_state")}>
|
|
||||||
{E2ePadlock.icons[this.props.icon]}
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ISentReceiptProps {
|
interface ISentReceiptProps {
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Element Creations 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 React, { type JSX } from "react";
|
||||||
|
import { EventTimeline } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext.tsx";
|
||||||
|
import { _t } from "../../../../languageHandler.tsx";
|
||||||
|
import { E2ePadlock, E2ePadlockIcon } from "./E2ePadlock.tsx";
|
||||||
|
|
||||||
|
/** The React properties of an {@link E2eMessageSharedIcon}. */
|
||||||
|
interface E2eMessageSharedIconParams {
|
||||||
|
/** The ID of the user who shared the keys. */
|
||||||
|
keyForwardingUserId: string;
|
||||||
|
|
||||||
|
/** The ID of the room that contains the event whose keys were shared. Used to find the displayname of the user who shared the keys. */
|
||||||
|
roomId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A small icon with tooltip, used as part of an {@link EventTile}, which indicates that the key to this event
|
||||||
|
* was shared with us by another user.
|
||||||
|
*
|
||||||
|
* An alternative to the {@link E2ePadlock} component, which is used for UTD events and other error cases.
|
||||||
|
*/
|
||||||
|
export function E2eMessageSharedIcon(props: E2eMessageSharedIconParams): JSX.Element {
|
||||||
|
const { roomId, keyForwardingUserId } = props;
|
||||||
|
const client = useMatrixClientContext();
|
||||||
|
|
||||||
|
const roomState = client.getRoom(roomId)?.getLiveTimeline()?.getState(EventTimeline.FORWARDS);
|
||||||
|
const forwardingMember = roomState?.getMember(keyForwardingUserId);
|
||||||
|
|
||||||
|
// We always disambiguate the user, since we need to prevent users from forging a disambiguation, and
|
||||||
|
// the ToolTip component doesn't support putting styling inside a label.
|
||||||
|
const tooltip = _t("encryption|message_shared_by", {
|
||||||
|
displayName: forwardingMember?.rawDisplayName ?? keyForwardingUserId,
|
||||||
|
userId: keyForwardingUserId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return <E2ePadlock icon={E2ePadlockIcon.Normal} title={tooltip} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 Vector Creations Ltd.
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
Copyright 2015-2023 The Matrix.org Foundation C.I.C.
|
||||||
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
|
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 React, { type ReactNode } from "react";
|
||||||
|
import { ErrorSolidIcon, InfoIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
|
import { Tooltip } from "@vector-im/compound-web";
|
||||||
|
|
||||||
|
import { _t } from "../../../../languageHandler.tsx";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The icon to display in an {@link E2ePadlock}.
|
||||||
|
*/
|
||||||
|
export enum E2ePadlockIcon {
|
||||||
|
/** Compound Info icon in grey */
|
||||||
|
Normal = "normal",
|
||||||
|
|
||||||
|
/** Compound ErrorSolid icon in red */
|
||||||
|
Warning = "warning",
|
||||||
|
|
||||||
|
/** Compound ErrorSolid icon in grey */
|
||||||
|
DecryptionFailure = "decryption_failure",
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IE2ePadlockProps {
|
||||||
|
/** The icon to display. */
|
||||||
|
icon: E2ePadlockIcon;
|
||||||
|
|
||||||
|
/** The tooltip for the icon, displayed on hover. */
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
[E2ePadlockIcon.Normal]: <InfoIcon color="var(--cpd-color-icon-tertiary)" />,
|
||||||
|
[E2ePadlockIcon.Warning]: <ErrorSolidIcon color="var(--cpd-color-icon-critical-primary)" />,
|
||||||
|
[E2ePadlockIcon.DecryptionFailure]: <ErrorSolidIcon color="var(--cpd-color-icon-tertiary)" />,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A small icon with tooltip, used in the left margin of an {@link EventTile}, which indicates a problem
|
||||||
|
* with an encrypted event.
|
||||||
|
*
|
||||||
|
* The icon is rendered with `data-testid="e2e-padlock"`.
|
||||||
|
*/
|
||||||
|
export function E2ePadlock(props: IE2ePadlockProps): ReactNode {
|
||||||
|
// We specify isTriggerInteractive=true and make the div interactive manually as a workaround for
|
||||||
|
// https://github.com/element-hq/compound/issues/294
|
||||||
|
return (
|
||||||
|
<Tooltip label={props.title} isTriggerInteractive={true}>
|
||||||
|
<div
|
||||||
|
data-testid="e2e-padlock"
|
||||||
|
className="mx_EventTile_e2eIcon"
|
||||||
|
tabIndex={0}
|
||||||
|
aria-label={_t("timeline|e2e_state")}
|
||||||
|
>
|
||||||
|
{icons[props.icon]}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -978,6 +978,7 @@
|
|||||||
"import_invalid_passphrase": "Authentication check failed: incorrect password?",
|
"import_invalid_passphrase": "Authentication check failed: incorrect password?",
|
||||||
"key_storage_out_of_sync": "Your key storage is out of sync.",
|
"key_storage_out_of_sync": "Your key storage is out of sync.",
|
||||||
"key_storage_out_of_sync_description": "Confirm your recovery key to maintain access to your key storage and message history.",
|
"key_storage_out_of_sync_description": "Confirm your recovery key to maintain access to your key storage and message history.",
|
||||||
|
"message_shared_by": "%(displayName)s (%(userId)s) shared this message since you were not in the room when it was sent.",
|
||||||
"messages_not_secure": {
|
"messages_not_secure": {
|
||||||
"cause_1": "Your homeserver",
|
"cause_1": "Your homeserver",
|
||||||
"cause_2": "The homeserver the user you're verifying is connected to",
|
"cause_2": "The homeserver the user you're verifying is connected to",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
EventShieldReason,
|
EventShieldReason,
|
||||||
} from "matrix-js-sdk/src/crypto-api";
|
} from "matrix-js-sdk/src/crypto-api";
|
||||||
import { mkEncryptedMatrixEvent } from "matrix-js-sdk/src/testing";
|
import { mkEncryptedMatrixEvent } from "matrix-js-sdk/src/testing";
|
||||||
|
import { getByTestId } from "@testing-library/dom";
|
||||||
|
|
||||||
import EventTile, { type EventTileProps } from "../../../../../src/components/views/rooms/EventTile";
|
import EventTile, { type EventTileProps } from "../../../../../src/components/views/rooms/EventTile";
|
||||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||||
@@ -333,6 +334,28 @@ describe("EventTile", () => {
|
|||||||
expect(e2eIcons[0]).toHaveAccessibleName(expectedText);
|
expect(e2eIcons[0]).toHaveAccessibleName(expectedText);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("shows the correct reason code for a forwarded message", async () => {
|
||||||
|
mxEvent = await mkEncryptedMatrixEvent({
|
||||||
|
plainContent: { msgtype: "m.text", body: "msg1" },
|
||||||
|
plainType: "m.room.message",
|
||||||
|
sender: "@alice:example.org",
|
||||||
|
roomId: room.roomId,
|
||||||
|
});
|
||||||
|
// @ts-ignore assignment to private member
|
||||||
|
mxEvent.keyForwardedBy = "@bob:example.org";
|
||||||
|
eventToEncryptionInfoMap.set(mxEvent.getId()!, {
|
||||||
|
shieldColour: EventShieldColour.GREY,
|
||||||
|
shieldReason: EventShieldReason.AUTHENTICITY_NOT_GUARANTEED,
|
||||||
|
} as EventEncryptionInfo);
|
||||||
|
|
||||||
|
const { container } = getComponent();
|
||||||
|
|
||||||
|
const e2eIcon = await waitFor(() => getByTestId(container, "e2e-padlock"));
|
||||||
|
expect(e2eIcon).toHaveAccessibleName(
|
||||||
|
"@bob:example.org (@bob:example.org) shared this message since you were not in the room when it was sent.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
describe("undecryptable event", () => {
|
describe("undecryptable event", () => {
|
||||||
filterConsole("Error decrypting event");
|
filterConsole("Error decrypting event");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Element Creations 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 { render } from "jest-matrix-react";
|
||||||
|
import React from "react";
|
||||||
|
import { mocked } from "jest-mock";
|
||||||
|
import { type RoomMember, type RoomState } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import { E2eMessageSharedIcon } from "../../../../../../src/components/views/rooms/EventTile/E2eMessageSharedIcon.tsx";
|
||||||
|
import { createTestClient, mkStubRoom, withClientContextRenderOptions } from "../../../../../test-utils";
|
||||||
|
|
||||||
|
describe("E2eMessageSharedIcon", () => {
|
||||||
|
it("renders correctly for a known user", () => {
|
||||||
|
const mockClient = createTestClient();
|
||||||
|
const mockMember = { rawDisplayName: "Bob" } as RoomMember;
|
||||||
|
const mockState = {
|
||||||
|
getMember: (userId) => {
|
||||||
|
expect(userId).toEqual("@bob:example.com");
|
||||||
|
return mockMember;
|
||||||
|
},
|
||||||
|
} as RoomState;
|
||||||
|
const mockRoom = mkStubRoom("!roomId", undefined, mockClient, mockState);
|
||||||
|
mocked(mockClient.getRoom).mockImplementation((roomId) => {
|
||||||
|
expect(roomId).toEqual("!roomId");
|
||||||
|
return mockRoom;
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = render(
|
||||||
|
<E2eMessageSharedIcon keyForwardingUserId="@bob:example.com" roomId="!roomId" />,
|
||||||
|
withClientContextRenderOptions(mockClient),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.container).toMatchSnapshot();
|
||||||
|
expect(result.container.firstChild).toHaveAccessibleName(
|
||||||
|
"Bob (@bob:example.com) shared this message since you were not in the room when it was sent.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders correctly for an unknown user", () => {
|
||||||
|
const mockClient = createTestClient();
|
||||||
|
const result = render(
|
||||||
|
<E2eMessageSharedIcon keyForwardingUserId="@bob:example.com" roomId="!roomId" />,
|
||||||
|
withClientContextRenderOptions(mockClient),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.container).toMatchSnapshot();
|
||||||
|
expect(result.container.firstChild).toHaveAccessibleName(
|
||||||
|
"@bob:example.com (@bob:example.com) shared this message since you were not in the room when it was sent.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Element Creations 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 { render } from "jest-matrix-react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { E2ePadlock, E2ePadlockIcon } from "../../../../../../src/components/views/rooms/EventTile/E2ePadlock.tsx";
|
||||||
|
|
||||||
|
describe("E2ePadlock", () => {
|
||||||
|
it("renders a 'Normal' icon", () => {
|
||||||
|
const result = render(<E2ePadlock icon={E2ePadlockIcon.Normal} title="Test tooltip" />);
|
||||||
|
expect(result.asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders a 'Warning' icon", () => {
|
||||||
|
const result = render(<E2ePadlock icon={E2ePadlockIcon.Warning} title="Bad" />);
|
||||||
|
expect(result.asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders a 'DecryptionFailure' icon", () => {
|
||||||
|
const result = render(<E2ePadlock icon={E2ePadlockIcon.DecryptionFailure} title="UTD" />);
|
||||||
|
expect(result.asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||||
|
|
||||||
|
exports[`E2eMessageSharedIcon renders correctly for a known user 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
aria-label="State of the end-to-end encryption"
|
||||||
|
aria-labelledby="_r_0_"
|
||||||
|
class="mx_EventTile_e2eIcon"
|
||||||
|
data-testid="e2e-padlock"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
color="var(--cpd-color-icon-tertiary)"
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
clip-rule="evenodd"
|
||||||
|
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"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`E2eMessageSharedIcon renders correctly for an unknown user 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
aria-label="State of the end-to-end encryption"
|
||||||
|
aria-labelledby="_r_6_"
|
||||||
|
class="mx_EventTile_e2eIcon"
|
||||||
|
data-testid="e2e-padlock"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
color="var(--cpd-color-icon-tertiary)"
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
clip-rule="evenodd"
|
||||||
|
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"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||||
|
|
||||||
|
exports[`E2ePadlock renders a 'DecryptionFailure' icon 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
aria-label="State of the end-to-end encryption"
|
||||||
|
aria-labelledby="_r_c_"
|
||||||
|
class="mx_EventTile_e2eIcon"
|
||||||
|
data-testid="e2e-padlock"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
color="var(--cpd-color-icon-tertiary)"
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`E2ePadlock renders a 'Normal' icon 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
aria-label="State of the end-to-end encryption"
|
||||||
|
aria-labelledby="_r_0_"
|
||||||
|
class="mx_EventTile_e2eIcon"
|
||||||
|
data-testid="e2e-padlock"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
color="var(--cpd-color-icon-tertiary)"
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
clip-rule="evenodd"
|
||||||
|
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"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`E2ePadlock renders a 'Warning' icon 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
aria-label="State of the end-to-end encryption"
|
||||||
|
aria-labelledby="_r_6_"
|
||||||
|
class="mx_EventTile_e2eIcon"
|
||||||
|
data-testid="e2e-padlock"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
color="var(--cpd-color-icon-critical-primary)"
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
||||||
Reference in New Issue
Block a user