Refactor EncryptionEvent using MVVM and move to shared-components (#32531)
* Refactor EncryptionEvent using MVVM and move to shared-components * Added viewmodel and unit tests for bothe viewmodel and component. * Added test for custom-class * Update EventTileFactory and RoomView to use the new component * Clean up unused language strings from element-web * Changed how the view model is created * Make sure the initial snapshot mimics the previous component * Optimizing viewmodel initial snapshot and update * Updated playwright screenshots
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.error {
|
||||
color: var(--cpd-color-icon-critical-primary);
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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 type { Meta, StoryFn } from "@storybook/react-vite";
|
||||
import { EncryptionEventView, EncryptionEventState, type EncryptionEventViewSnapshot } from "./EncryptionEventView";
|
||||
import { useMockedViewModel } from "../../viewmodel/useMockedViewModel";
|
||||
|
||||
type EncryptionEventProps = EncryptionEventViewSnapshot;
|
||||
|
||||
const EncryptionEventViewWrapper = ({ ...rest }: EncryptionEventProps): JSX.Element => {
|
||||
const vm = useMockedViewModel(rest, {});
|
||||
|
||||
return <EncryptionEventView vm={vm} />;
|
||||
};
|
||||
|
||||
export default {
|
||||
title: "Event/EncryptionEvent",
|
||||
component: EncryptionEventViewWrapper,
|
||||
tags: ["autodocs"],
|
||||
argTypes: {
|
||||
state: {
|
||||
options: Object.entries(EncryptionEventState)
|
||||
.filter(([key, value]) => key === value)
|
||||
.map(([key]) => key),
|
||||
control: { type: "select" },
|
||||
},
|
||||
},
|
||||
args: {
|
||||
state: EncryptionEventState.ENABLED,
|
||||
encryptedStateEvents: false,
|
||||
userName: "Alice",
|
||||
className: "",
|
||||
},
|
||||
} as Meta<typeof EncryptionEventViewWrapper>;
|
||||
|
||||
const Template: StoryFn<typeof EncryptionEventViewWrapper> = (args) => <EncryptionEventViewWrapper {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
export const StateEncryptionEnabled = Template.bind({});
|
||||
StateEncryptionEnabled.args = {
|
||||
state: EncryptionEventState.ENABLED,
|
||||
encryptedStateEvents: true,
|
||||
};
|
||||
|
||||
export const ParametersChanged = Template.bind({});
|
||||
ParametersChanged.args = {
|
||||
state: EncryptionEventState.CHANGED,
|
||||
};
|
||||
|
||||
export const DisableAttempt = Template.bind({});
|
||||
DisableAttempt.args = {
|
||||
state: EncryptionEventState.DISABLE_ATTEMPT,
|
||||
};
|
||||
|
||||
export const EnabledDirectMessage = Template.bind({});
|
||||
EnabledDirectMessage.args = {
|
||||
state: EncryptionEventState.ENABLED_DM,
|
||||
userName: "Alice",
|
||||
};
|
||||
|
||||
export const EnabledLocalRoom = Template.bind({});
|
||||
EnabledLocalRoom.args = {
|
||||
state: EncryptionEventState.ENABLED_LOCAL,
|
||||
};
|
||||
|
||||
export const Unsupported = Template.bind({});
|
||||
Unsupported.args = {
|
||||
state: EncryptionEventState.UNSUPPORTED,
|
||||
};
|
||||
|
||||
export const WithTimestamp = Template.bind({});
|
||||
WithTimestamp.args = {
|
||||
state: EncryptionEventState.ENABLED,
|
||||
timestamp: <span>14:56</span>,
|
||||
};
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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, screen } from "@test-utils";
|
||||
import { composeStories } from "@storybook/react-vite";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import React from "react";
|
||||
|
||||
import { EncryptionEventState, EncryptionEventView } from "./EncryptionEventView";
|
||||
import * as stories from "./EncryptionEventView.stories";
|
||||
import { MockViewModel } from "../../viewmodel";
|
||||
|
||||
const {
|
||||
Default,
|
||||
StateEncryptionEnabled,
|
||||
ParametersChanged,
|
||||
DisableAttempt,
|
||||
EnabledDirectMessage,
|
||||
EnabledLocalRoom,
|
||||
Unsupported,
|
||||
WithTimestamp,
|
||||
} = composeStories(stories);
|
||||
|
||||
describe("EncryptionEventView", () => {
|
||||
const renderView = (
|
||||
state: EncryptionEventState,
|
||||
encryptedStateEvents?: boolean,
|
||||
userName?: string,
|
||||
className?: string,
|
||||
): void => {
|
||||
const vm = new MockViewModel({
|
||||
state,
|
||||
encryptedStateEvents,
|
||||
userName,
|
||||
className,
|
||||
});
|
||||
render(<EncryptionEventView vm={vm} />);
|
||||
};
|
||||
|
||||
it("renders Default story", () => {
|
||||
const { container } = render(<Default />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders StateEncryptionEnabled story", () => {
|
||||
const { container } = render(<StateEncryptionEnabled />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders ParametersChanged story", () => {
|
||||
const { container } = render(<ParametersChanged />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders DisableAttempt story", () => {
|
||||
const { container } = render(<DisableAttempt />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders EnabledDirectMessage story", () => {
|
||||
const { container } = render(<EnabledDirectMessage />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders EnabledLocalRoom story", () => {
|
||||
const { container } = render(<EnabledLocalRoom />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders Unsupported story", () => {
|
||||
const { container } = render(<Unsupported />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders WithTimestamp story", () => {
|
||||
const { container } = render(<WithTimestamp />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows enabled room encryption copy", () => {
|
||||
renderView(EncryptionEventState.ENABLED);
|
||||
|
||||
expect(screen.getByText("Encryption enabled")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
"Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.",
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows enabled state encryption copy", () => {
|
||||
renderView(EncryptionEventState.ENABLED, true);
|
||||
|
||||
expect(screen.getByText("Experimental state encryption enabled")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
"Messages and state events in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.",
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows changed encryption parameters copy", () => {
|
||||
renderView(EncryptionEventState.CHANGED);
|
||||
|
||||
expect(screen.getByText("Encryption enabled")).toBeInTheDocument();
|
||||
expect(screen.getByText("Some encryption parameters have been changed.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows disable attempt copy", () => {
|
||||
renderView(EncryptionEventState.DISABLE_ATTEMPT);
|
||||
|
||||
expect(screen.getByText("Encryption enabled")).toBeInTheDocument();
|
||||
expect(screen.getByText("Ignored attempt to disable encryption")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows unsupported encryption copy", () => {
|
||||
renderView(EncryptionEventState.UNSUPPORTED);
|
||||
|
||||
expect(screen.getByText("Encryption not enabled")).toBeInTheDocument();
|
||||
expect(screen.getByText("The encryption used by this room isn't supported.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows local room encryption copy", () => {
|
||||
renderView(EncryptionEventState.ENABLED_LOCAL);
|
||||
|
||||
expect(screen.getByText("Encryption enabled")).toBeInTheDocument();
|
||||
expect(screen.getByText("Messages in this chat will be end-to-end encrypted.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows dm room encryption copy with display name", () => {
|
||||
renderView(EncryptionEventState.ENABLED_DM, false, "Alice");
|
||||
|
||||
expect(screen.getByText("Encryption enabled")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
"Messages here are end-to-end encrypted. Verify Alice in their profile - tap on their profile picture.",
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders additional class name on the event tile bubble", () => {
|
||||
renderView(EncryptionEventState.ENABLED, false, undefined, "custom-class");
|
||||
|
||||
expect(screen.getByText("Encryption enabled").parentElement).toHaveClass("custom-class");
|
||||
});
|
||||
});
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 { LockSolidIcon, ErrorSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
import { type ViewModel, useViewModel } from "../../viewmodel";
|
||||
import styles from "./EncryptionEventView.module.css";
|
||||
import { useI18n } from "../../utils/i18nContext";
|
||||
import { EventTileBubble } from "../EventTileBubble";
|
||||
|
||||
export enum EncryptionEventState {
|
||||
/** Encryption settings changed while encryption stayed enabled. */
|
||||
CHANGED = "CHANGED",
|
||||
/** Someone attempted to disable encryption in an encrypted room. */
|
||||
DISABLE_ATTEMPT = "DISABLE_ATTEMPT",
|
||||
/** Encryption was enabled in a regular room. */
|
||||
ENABLED = "ENABLED",
|
||||
/** Encryption was enabled in a DM room. */
|
||||
ENABLED_DM = "ENABLED_DM",
|
||||
/** Encryption was enabled in a local room. */
|
||||
ENABLED_LOCAL = "ENABLED_LOCAL",
|
||||
/** Encryption is unavailable/unsupported for this event context. */
|
||||
UNSUPPORTED = "UNSUPPORTED",
|
||||
}
|
||||
|
||||
export type EncryptionEventViewSnapshot = {
|
||||
/** Which encryption event variant to render. */
|
||||
state: EncryptionEventState;
|
||||
/** Whether state-event encryption messaging should be shown. */
|
||||
encryptedStateEvents?: boolean;
|
||||
/** Display name for DM partner, used by ENABLED_DM subtitle text. */
|
||||
userName?: string;
|
||||
/** Optional CSS classes passed through to EventTileBubble. */
|
||||
className?: string;
|
||||
/** Optional timestamp element rendered in the EventTileBubble footer slot. */
|
||||
timestamp?: JSX.Element;
|
||||
};
|
||||
|
||||
/**
|
||||
* ViewModel contract consumed by {@link EncryptionEventView}.
|
||||
*/
|
||||
export type EncryptionEventViewModel = ViewModel<EncryptionEventViewSnapshot>;
|
||||
|
||||
export interface EncryptionEventViewProps {
|
||||
/**
|
||||
* ViewModel providing the current encryption event snapshot.
|
||||
*/
|
||||
vm: ViewModel<EncryptionEventViewSnapshot>;
|
||||
/**
|
||||
* Ref forwarded to the root DOM element.
|
||||
*/
|
||||
ref?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export function EncryptionEventView({ vm, ref }: Readonly<EncryptionEventViewProps>): JSX.Element {
|
||||
const { translate: _t } = useI18n();
|
||||
const { state, encryptedStateEvents, userName, className, timestamp } = useViewModel(vm);
|
||||
|
||||
let icon = <LockSolidIcon />;
|
||||
let title = encryptedStateEvents ? _t("common|state_encryption_enabled") : _t("common|encryption_enabled");
|
||||
let subtitle = "";
|
||||
|
||||
switch (state) {
|
||||
case EncryptionEventState.CHANGED:
|
||||
subtitle = _t("timeline|m.room.encryption|parameters_changed");
|
||||
break;
|
||||
case EncryptionEventState.DISABLE_ATTEMPT:
|
||||
title = _t("common|encryption_enabled");
|
||||
subtitle = _t("timeline|m.room.encryption|disable_attempt");
|
||||
break;
|
||||
case EncryptionEventState.ENABLED:
|
||||
subtitle = encryptedStateEvents
|
||||
? _t("timeline|m.room.encryption|state_enabled")
|
||||
: _t("timeline|m.room.encryption|enabled");
|
||||
break;
|
||||
case EncryptionEventState.ENABLED_DM:
|
||||
subtitle = _t("timeline|m.room.encryption|enabled_dm", { displayName: userName });
|
||||
break;
|
||||
case EncryptionEventState.ENABLED_LOCAL:
|
||||
subtitle = _t("timeline|m.room.encryption|enabled_local");
|
||||
break;
|
||||
case EncryptionEventState.UNSUPPORTED:
|
||||
default:
|
||||
icon = <ErrorSolidIcon className={styles.error} />;
|
||||
title = _t("timeline|m.room.encryption|disabled");
|
||||
subtitle = _t("timeline|m.room.encryption|unsupported");
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<EventTileBubble icon={icon} className={className} title={title} subtitle={subtitle} ref={ref}>
|
||||
{timestamp}
|
||||
</EventTileBubble>
|
||||
);
|
||||
}
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`EncryptionEventView > renders Default story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="container"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
Encryption enabled
|
||||
</div>
|
||||
<div
|
||||
class="subtitle"
|
||||
>
|
||||
Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EncryptionEventView > renders DisableAttempt story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="container"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
Encryption enabled
|
||||
</div>
|
||||
<div
|
||||
class="subtitle"
|
||||
>
|
||||
Ignored attempt to disable encryption
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EncryptionEventView > renders EnabledDirectMessage story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="container"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
Encryption enabled
|
||||
</div>
|
||||
<div
|
||||
class="subtitle"
|
||||
>
|
||||
Messages here are end-to-end encrypted. Verify Alice in their profile - tap on their profile picture.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EncryptionEventView > renders EnabledLocalRoom story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="container"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
Encryption enabled
|
||||
</div>
|
||||
<div
|
||||
class="subtitle"
|
||||
>
|
||||
Messages in this chat will be end-to-end encrypted.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EncryptionEventView > renders ParametersChanged story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="container"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
Encryption enabled
|
||||
</div>
|
||||
<div
|
||||
class="subtitle"
|
||||
>
|
||||
Some encryption parameters have been changed.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EncryptionEventView > renders StateEncryptionEnabled story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="container"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
Experimental state encryption enabled
|
||||
</div>
|
||||
<div
|
||||
class="subtitle"
|
||||
>
|
||||
Messages and state events in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EncryptionEventView > renders Unsupported story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="container"
|
||||
>
|
||||
<svg
|
||||
class="error"
|
||||
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
|
||||
class="title"
|
||||
>
|
||||
Encryption not enabled
|
||||
</div>
|
||||
<div
|
||||
class="subtitle"
|
||||
>
|
||||
The encryption used by this room isn't supported.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EncryptionEventView > renders WithTimestamp story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="container"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
Encryption enabled
|
||||
</div>
|
||||
<div
|
||||
class="subtitle"
|
||||
>
|
||||
Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.
|
||||
</div>
|
||||
<span>
|
||||
14:56
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export {
|
||||
EncryptionEventView,
|
||||
EncryptionEventState,
|
||||
type EncryptionEventViewSnapshot,
|
||||
type EncryptionEventViewModel,
|
||||
} from "./EncryptionEventView";
|
||||
@@ -20,7 +20,9 @@
|
||||
"start_chat": "Start chat"
|
||||
},
|
||||
"common": {
|
||||
"preferences": "Preferences"
|
||||
"encryption_enabled": "Encryption enabled",
|
||||
"preferences": "Preferences",
|
||||
"state_encryption_enabled": "Experimental state encryption enabled"
|
||||
},
|
||||
"left_panel": {
|
||||
"open_dial_pad": "Open dial pad"
|
||||
@@ -159,6 +161,16 @@
|
||||
"audio_player": "Audio player",
|
||||
"error_downloading_audio": "Error downloading audio",
|
||||
"unnamed_audio": "Unnamed audio"
|
||||
},
|
||||
"m.room.encryption": {
|
||||
"disable_attempt": "Ignored attempt to disable encryption",
|
||||
"disabled": "Encryption not enabled",
|
||||
"enabled": "Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.",
|
||||
"enabled_dm": "Messages here are end-to-end encrypted. Verify %(displayName)s in their profile - tap on their profile picture.",
|
||||
"enabled_local": "Messages in this chat will be end-to-end encrypted.",
|
||||
"parameters_changed": "Some encryption parameters have been changed.",
|
||||
"state_enabled": "Messages and state events in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.",
|
||||
"unsupported": "The encryption used by this room isn't supported."
|
||||
}
|
||||
},
|
||||
"widget": {
|
||||
|
||||
@@ -13,6 +13,7 @@ export * from "./audio/SeekBar";
|
||||
export * from "./avatar/AvatarWithDetails";
|
||||
export * from "./composer/Banner";
|
||||
export * from "./crypto/SasEmoji";
|
||||
export * from "./event-tiles/EncryptionEventView";
|
||||
export * from "./event-tiles/EventTileBubble";
|
||||
export * from "./event-tiles/TextualEventView";
|
||||
export * from "./message-body/MediaBody";
|
||||
|
||||
Reference in New Issue
Block a user