diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bb802ef03e..ad1804bfc5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -16,6 +16,7 @@ /src/components/views/dialogs/devtools/Crypto.tsx @element-hq/element-crypto-web-reviewers /playwright/e2e/crypto/ @element-hq/element-crypto-web-reviewers /playwright/e2e/settings/encryption-user-tab/ @element-hq/element-crypto-web-reviewers +/packages/shared-components/src/crypto/ @element-hq/element-crypto-web-reviewers /src/models/Call.ts @element-hq/element-call-reviewers diff --git a/package.json b/package.json index 2b8c1e243f..92cf154988 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,6 @@ "@matrix-org/analytics-events": "^0.31.0", "@matrix-org/emojibase-bindings": "^1.5.0", "@matrix-org/react-sdk-module-api": "^2.4.0", - "@matrix-org/spec": "^1.7.0", "@sentry/browser": "^10.0.0", "@types/png-chunks-extract": "^1.0.2", "@vector-im/compound-design-tokens": "6.9.0", diff --git a/packages/shared-components/__vis__/linux/__baselines__/crypto/SasEmoji/SasEmoji.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/crypto/SasEmoji/SasEmoji.stories.tsx/default-auto.png new file mode 100644 index 0000000000..8e27242e47 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/crypto/SasEmoji/SasEmoji.stories.tsx/default-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/crypto/SasEmoji/SasEmoji.stories.tsx/worst-case-albanian-auto.png b/packages/shared-components/__vis__/linux/__baselines__/crypto/SasEmoji/SasEmoji.stories.tsx/worst-case-albanian-auto.png new file mode 100644 index 0000000000..c845bdeabe Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/crypto/SasEmoji/SasEmoji.stories.tsx/worst-case-albanian-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/crypto/SasEmoji/SasEmoji.stories.tsx/worst-case-german-auto.png b/packages/shared-components/__vis__/linux/__baselines__/crypto/SasEmoji/SasEmoji.stories.tsx/worst-case-german-auto.png new file mode 100644 index 0000000000..6565cf86b7 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/crypto/SasEmoji/SasEmoji.stories.tsx/worst-case-german-auto.png differ diff --git a/packages/shared-components/package.json b/packages/shared-components/package.json index 5987d81dbd..0ed297872e 100644 --- a/packages/shared-components/package.json +++ b/packages/shared-components/package.json @@ -50,6 +50,7 @@ }, "dependencies": { "@element-hq/element-web-module-api": "^1.8.0", + "@matrix-org/spec": "^1.7.0", "@vector-im/compound-design-tokens": "^6.4.3", "classnames": "^2.5.1", "counterpart": "^0.18.6", diff --git a/packages/shared-components/src/crypto/SasEmoji/SasEmoji.module.css b/packages/shared-components/src/crypto/SasEmoji/SasEmoji.module.css new file mode 100644 index 0000000000..b763ef6664 --- /dev/null +++ b/packages/shared-components/src/crypto/SasEmoji/SasEmoji.module.css @@ -0,0 +1,33 @@ +/* + * 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. + */ + +.container { + display: flex; + flex-wrap: wrap; + gap: var(--cpd-space-2x); + justify-content: space-evenly; +} + +.segment { + display: inline-block; + margin-bottom: var(--cpd-space-4x); + text-align: center; + /* Allow maximum 4 per line, accounting for 8px gap */ + min-width: calc(25% - 8px); +} + +.emoji { + /* Use the Twemoji font for consistency with other clients */ + font-family: Twemoji, var(--cpd-font-family-sans); + font-size: var(--cpd-font-size-heading-xl); +} + +.label { + font-weight: var(--cpd-font-weight-regular); + font-size: var(--cpd-font-size-body-lg); + color: var(--cpd-color-text-secondary); +} diff --git a/packages/shared-components/src/crypto/SasEmoji/SasEmoji.stories.tsx b/packages/shared-components/src/crypto/SasEmoji/SasEmoji.stories.tsx new file mode 100644 index 0000000000..9c0fcc3f40 --- /dev/null +++ b/packages/shared-components/src/crypto/SasEmoji/SasEmoji.stories.tsx @@ -0,0 +1,48 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type Meta, type StoryObj } from "@storybook/react-vite"; + +import { SasEmoji } from "./SasEmoji"; + +const meta = { + title: "Crypto/SasEmoji", + component: SasEmoji, + tags: ["autodocs"], + args: { + emoji: ["🍕", "🌽", "🚀", "🔒", "🔧", "🍓", "⌛"], + }, + parameters: { + design: { + type: "figma", + url: "https://www.figma.com/design/XLWIAB5n8yObYvU0INKPK1/Verification-by-Emoji?node-id=1-2935&t=NrV9JnuItrAyyh53-4", + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const WorstCaseAlbanian: Story = { + globals: { + language: "sq", + }, + args: { + emoji: ["🎅", "🎅", "🎅", "🎅", "🎅", "🎅", "🎅"], + }, +}; + +export const WorstCaseGerman: Story = { + globals: { + language: "de", + }, + args: { + emoji: ["🔧", "🔧", "🔧", "🔧", "🔧", "🔧", "🔧"], + }, +}; diff --git a/packages/shared-components/src/crypto/SasEmoji/SasEmoji.test.tsx b/packages/shared-components/src/crypto/SasEmoji/SasEmoji.test.tsx new file mode 100644 index 0000000000..c9946d52a0 --- /dev/null +++ b/packages/shared-components/src/crypto/SasEmoji/SasEmoji.test.tsx @@ -0,0 +1,19 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import React from "react"; +import { describe, it, expect } from "vitest"; +import { render } from "@test-utils"; + +import { SasEmoji } from "./SasEmoji"; + +describe("", () => { + it("should match snapshot", () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); + }); +}); diff --git a/packages/shared-components/src/crypto/SasEmoji/SasEmoji.tsx b/packages/shared-components/src/crypto/SasEmoji/SasEmoji.tsx new file mode 100644 index 0000000000..6701cb37c9 --- /dev/null +++ b/packages/shared-components/src/crypto/SasEmoji/SasEmoji.tsx @@ -0,0 +1,43 @@ +/* + * 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 classNames from "classnames"; + +import { type SasEmoji, tEmoji } from "./SasEmojiTranslate.ts"; +import styles from "./SasEmoji.module.css"; +import { useI18n } from "../../utils/i18nContext.ts"; + +export type Props = { + /** + * The emoji to render + */ + emoji: [SasEmoji, SasEmoji, SasEmoji, SasEmoji, SasEmoji, SasEmoji, SasEmoji]; + /** + * Optional className to apply to the container + */ + className?: string; +}; + +/** + * Renders the 7 emoji used for SAS verification. + * The component is responsive so can be rendered in any context, dialog, side panel. + */ +export function SasEmoji({ emoji, className }: Props): JSX.Element { + const { language } = useI18n(); + + const emojiBlocks = emoji.map((emoji, i) => ( +
+
+ {emoji} +
+
{tEmoji(emoji, language)}
+
+ )); + + return
{emojiBlocks}
; +} diff --git a/packages/shared-components/src/crypto/SasEmoji/SasEmojiTranslate.test.ts b/packages/shared-components/src/crypto/SasEmoji/SasEmojiTranslate.test.ts new file mode 100644 index 0000000000..8251e52ea4 --- /dev/null +++ b/packages/shared-components/src/crypto/SasEmoji/SasEmojiTranslate.test.ts @@ -0,0 +1,26 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { describe, it, expect } from "vitest"; + +import { tEmoji, type SasEmoji } from "./SasEmojiTranslate.ts"; + +describe("tEmoji", () => { + it.each([ + ["🐶", "en-GB", "Dog"], + ["🐶", "en", "Dog"], + ["🐶", "de-DE", "Hund"], + ["🐶", "pt", "Cachorro"], + ["🔧", "de-DE", "Schraubenschlüssel"], + ["🎅", "sq", "Babagjyshi i Vitit të Ri"], + ] as [emoji: SasEmoji, locale: string, expectation: string][])( + "should handle locale %s", + (emoji, locale, expectation) => { + expect(tEmoji(emoji, locale)).toEqual(expectation); + }, + ); +}); diff --git a/packages/shared-components/src/crypto/SasEmoji/SasEmojiTranslate.ts b/packages/shared-components/src/crypto/SasEmoji/SasEmojiTranslate.ts new file mode 100644 index 0000000000..da977a98e0 --- /dev/null +++ b/packages/shared-components/src/crypto/SasEmoji/SasEmojiTranslate.ts @@ -0,0 +1,122 @@ +/* + * 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 SasEmojiJson from "@matrix-org/spec/sas-emoji.json"; +import { getNormalizedLanguageKeys } from "matrix-web-i18n"; + +// Type as specified in https://spec.matrix.org/v1.17/client-server-api/#sas-method-emoji +export type SasEmoji = + | "🐶" + | "🐱" + | "🦁" + | "🐎" + | "🦄" + | "🐷" + | "🐘" + | "🐰" + | "🐼" + | "🐓" + | "🐧" + | "🐢" + | "🐟" + | "🐙" + | "🦋" + | "🌷" + | "🌳" + | "🌵" + | "🍄" + | "🌏" + | "🌙" + | "☁" + | "🔥" + | "🍌" + | "🍎" + | "🍓" + | "🌽" + | "🍕" + | "🎂" + | "❤" + | "😀" + | "🤖" + | "🎩" + | "👓" + | "🔧" + | "🎅" + | "👍" + | "☂" + | "⌛" + | "⏰" + | "🎁" + | "💡" + | "📕" + | "✏" + | "📎" + | "✂" + | "🔒" + | "🔑" + | "🔨" + | "☎" + | "🏁" + | "🚂" + | "🚲" + | "✈" + | "🚀" + | "🏆" + | "⚽" + | "🎸" + | "🎺" + | "🔔" + | "⚓" + | "🎧" + | "📁" + | "📌"; + +const SasEmojiMap = new Map< + SasEmoji, + [ + description: string, + translations: { + [normalizedLanguageKey: string]: string; + }, + ] +>( + SasEmojiJson.map(({ emoji, description, translated_descriptions: translations }) => [ + emoji as SasEmoji, + [ + description, + // Normalize the translation keys + Object.keys(translations).reduce>((o, k) => { + for (const key of getNormalizedLanguageKeys(k)) { + o[key] = translations[k as keyof typeof translations]!; + } + return o; + }, {}), + ], + ]), +); + +/** + * Translate given SAS emoji into the target locale + * @param emoji - the SAS emoji to translate + * @param locale - the BCP 47 locale to translate to, will fall back to English as the base locale for Matrix SAS Emoji. + */ +export function tEmoji(emoji: SasEmoji, locale: string): string { + const mapping = SasEmojiMap.get(emoji); + if (!mapping) { + throw new Error(`Emoji mapping not found for emoji ${emoji}`); + } + + const [description, translations] = mapping; + + for (const key of getNormalizedLanguageKeys(locale)) { + if (translations[key]) { + return translations[key]; + } + } + + return description; +} diff --git a/packages/shared-components/src/crypto/SasEmoji/__snapshots__/SasEmoji.test.tsx.snap b/packages/shared-components/src/crypto/SasEmoji/__snapshots__/SasEmoji.test.tsx.snap new file mode 100644 index 0000000000..27f53cb870 --- /dev/null +++ b/packages/shared-components/src/crypto/SasEmoji/__snapshots__/SasEmoji.test.tsx.snap @@ -0,0 +1,115 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[` > should match snapshot 1`] = ` + +
+
+ +
+ Butterfly +
+
+
+ +
+ Mushroom +
+
+
+ +
+ Ball +
+
+
+ +
+ Globe +
+
+
+ +
+ Unicorn +
+
+
+ +
+ Rocket +
+
+
+ +
+ Spanner +
+
+
+
+`; diff --git a/packages/shared-components/src/crypto/SasEmoji/index.ts b/packages/shared-components/src/crypto/SasEmoji/index.ts new file mode 100644 index 0000000000..5c6dc18fb7 --- /dev/null +++ b/packages/shared-components/src/crypto/SasEmoji/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { SasEmoji } from "./SasEmoji.tsx"; diff --git a/packages/shared-components/src/i18n/strings/hu.json b/packages/shared-components/src/i18n/strings/hu.json index 559dec09d8..3ea03a5aef 100644 --- a/packages/shared-components/src/i18n/strings/hu.json +++ b/packages/shared-components/src/i18n/strings/hu.json @@ -43,12 +43,15 @@ } }, "room_list": { + "appearance": "Megjelenítés", "open_space_menu": "Tér menü megnyitása", "room_options": "Szobabeállítások", + "show_message_previews": "Üzenetelőnézetek megjelenítése", "sort": "Rendezés", "sort_type": { "activity": "Tevékenység", - "atoz": "A–Z" + "atoz": "A–Z", + "unread_first": "Olvasatlan elöl" }, "space_menu": { "home": "Kezdő tér", diff --git a/packages/shared-components/src/index.ts b/packages/shared-components/src/index.ts index ae7770e4d7..370f7d038b 100644 --- a/packages/shared-components/src/index.ts +++ b/packages/shared-components/src/index.ts @@ -12,6 +12,7 @@ export * from "./audio/PlayPauseButton"; export * from "./audio/SeekBar"; export * from "./avatar/AvatarWithDetails"; export * from "./composer/Banner"; +export * from "./crypto/SasEmoji"; export * from "./event-tiles/TextualEventView"; export * from "./message-body/MediaBody"; export * from "./pill-input/Pill"; diff --git a/packages/shared-components/yarn.lock b/packages/shared-components/yarn.lock index 2341936db5..62e161d50d 100644 --- a/packages/shared-components/yarn.lock +++ b/packages/shared-components/yarn.lock @@ -667,6 +667,11 @@ dependencies: "@babel/runtime" "^7.17.9" +"@matrix-org/spec@^1.7.0": + version "1.16.0" + resolved "https://registry.yarnpkg.com/@matrix-org/spec/-/spec-1.16.0.tgz#c88f4ed521e4c0bd3a4c108bcaf13f25173a0fdc" + integrity sha512-xUKHkwGXXISMCfTrx6JW6uGEK5O8IeZVOjBm7FX1h/ihpK6l50nlSIMRYdtz4V6q3pvOVBOCft4hPYTJVeTZDA== + "@mdx-js/react@^3.0.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-3.1.1.tgz#24bda7fffceb2fe256f954482123cda1be5f5fef" diff --git a/playwright/e2e/crypto/utils.ts b/playwright/e2e/crypto/utils.ts index 8b677ed4cb..4aab27f51a 100644 --- a/playwright/e2e/crypto/utils.ts +++ b/playwright/e2e/crypto/utils.ts @@ -290,7 +290,7 @@ export async function doTwoWaySasVerification(page: Page, verifier: JSHandle div"); await expect(emojiBlocks).toHaveCount(emojis.length); // then, check that our application shows an emoji panel with the same emojis. diff --git a/playwright/testcontainers/mas.ts b/playwright/testcontainers/mas.ts index 4fcaa93ce6..4f1a86aa83 100644 --- a/playwright/testcontainers/mas.ts +++ b/playwright/testcontainers/mas.ts @@ -10,7 +10,7 @@ import { type StartedPostgreSqlContainer, } from "@element-hq/element-web-playwright-common/lib/testcontainers"; -const TAG = "main@sha256:a3d8276ff5878ce109f184fd2f0e27fc50e1cb4ec409ad5217022074b19a7fd9"; +const TAG = "main@sha256:d0d03f9067c7977807131a9c739c9ed9f081063d1a1c21bee66204e40c44aa50"; /** * MatrixAuthenticationServiceContainer which freezes the docker digest to diff --git a/res/css/views/verification/_VerificationShowSas.pcss b/res/css/views/verification/_VerificationShowSas.pcss index 9e4d1f138b..57fae76788 100644 --- a/res/css/views/verification/_VerificationShowSas.pcss +++ b/res/css/views/verification/_VerificationShowSas.pcss @@ -20,40 +20,9 @@ Please see LICENSE files in the repository root for full details. } .mx_VerificationShowSas_emojiSas { - text-align: center; - display: flex; - flex-wrap: wrap; - justify-content: center; margin: 25px 0; } -.mx_VerificationShowSas_emojiSas_block { - display: inline-block; - margin-bottom: 16px; - position: relative; - width: 52px; -} - -.mx_Dialog .mx_VerificationShowSas_emojiSas_block, -.mx_AuthPage_modal .mx_VerificationShowSas_emojiSas_block { - width: 60px; -} - -.mx_VerificationShowSas_emojiSas_emoji { - font-size: $font-32px; - /* Use the Twemoji font for consistency with other clients */ - font-family: Twemoji, var(--cpd-font-family-sans); -} - -.mx_VerificationShowSas_emojiSas_label { - font-size: $font-12px; - word-break: break-word; -} - -.mx_VerificationShowSas_emojiSas_break { - flex-basis: 100%; -} - .mx_VerificationShowSas_buttonRow { text-align: center; display: flex; diff --git a/src/components/views/rooms/RoomHeader/RoomHeader.tsx b/src/components/views/rooms/RoomHeader/RoomHeader.tsx index 2434d5900e..b6c0086800 100644 --- a/src/components/views/rooms/RoomHeader/RoomHeader.tsx +++ b/src/components/views/rooms/RoomHeader/RoomHeader.tsx @@ -22,7 +22,7 @@ import { HistoryVisibility, JoinRule, type Room } from "matrix-js-sdk/src/matrix import { type ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle"; import { Flex, Box } from "@element-hq/web-shared-components"; import { CallType } from "matrix-js-sdk/src/webrtc/call"; -import { HistoryIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { HistoryIcon, UserProfileSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { useRoomName } from "../../../../hooks/useRoomName.ts"; import { RightPanelPhases } from "../../../../stores/right-panel/RightPanelStorePhases.ts"; @@ -391,6 +391,40 @@ function RoomHeaderButtons({ ); } +/** Create an icon to warn the user about shared history visibility, in encrypted rooms. + * + * Note that we use the same icon as in the room summary card and elsewhere, to aid user recognition. + */ +function historyVisibilityIcon(historyVisibility: HistoryVisibility): JSX.Element | null { + if (historyVisibility === HistoryVisibility.Shared) { + return ( + + + + ); + } else if (historyVisibility === HistoryVisibility.WorldReadable) { + return ( + + + + ); + } else { + return null; + } +} + export default function RoomHeader({ room, additionalButtons, @@ -490,20 +524,7 @@ export default function RoomHeader({ )} - {isRoomEncrypted && - historySharingEnabled && - (historyVisibility === HistoryVisibility.Shared || - historyVisibility === HistoryVisibility.WorldReadable) && ( - - - - )} + {isRoomEncrypted && historySharingEnabled && historyVisibilityIcon(historyVisibility)} diff --git a/src/components/views/verification/VerificationShowSas.tsx b/src/components/views/verification/VerificationShowSas.tsx index 0f6272a885..84490e556b 100644 --- a/src/components/views/verification/VerificationShowSas.tsx +++ b/src/components/views/verification/VerificationShowSas.tsx @@ -6,12 +6,12 @@ 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 React from "react"; +import React, { type ComponentProps } from "react"; import { type Device } from "matrix-js-sdk/src/matrix"; -import { type GeneratedSas, type EmojiMapping } from "matrix-js-sdk/src/crypto-api"; -import SasEmoji from "@matrix-org/spec/sas-emoji.json"; +import { type GeneratedSas } from "matrix-js-sdk/src/crypto-api"; +import { SasEmoji } from "@element-hq/web-shared-components"; -import { _t, getNormalizedLanguageKeys, getUserLanguage } from "../../../languageHandler"; +import { _t } from "../../../languageHandler"; import { PendingActionSpinner } from "../right_panel/EncryptionInfo"; import AccessibleButton from "../elements/AccessibleButton"; @@ -34,52 +34,6 @@ interface IState { cancelling?: boolean; } -const SasEmojiMap = new Map< - string, // lowercase - { - description: string; - translations: { - [normalizedLanguageKey: string]: string; - }; - } ->( - SasEmoji.map(({ description, translated_descriptions: translations }) => [ - description.toLowerCase(), - { - description, - // Normalize the translation keys - translations: Object.keys(translations).reduce>((o, k) => { - for (const key of getNormalizedLanguageKeys(k)) { - o[key] = translations[k as keyof typeof translations]!; - } - return o; - }, {}), - }, - ]), -); - -/** - * Translate given EmojiMapping into the target locale - * @param mapping - the given EmojiMapping to translate - * @param locale - the BCP 47 locale to translate to, will fall back to English as the base locale for Matrix SAS Emoji. - */ -export function tEmoji(mapping: EmojiMapping, locale: string): string { - const name = mapping[1]; - const emoji = SasEmojiMap.get(name.toLowerCase()); - if (!emoji) { - console.warn("Emoji not found for translation", name); - return name; - } - - for (const key of getNormalizedLanguageKeys(locale)) { - if (!!emoji.translations[key]) { - return emoji.translations[key]; - } - } - - return emoji.description; -} - export default class VerificationShowSas extends React.Component { public constructor(props: IProps) { super(props); @@ -100,25 +54,14 @@ export default class VerificationShowSas extends React.Component }; public render(): React.ReactNode { - const locale = getUserLanguage(); - let sasDisplay; let sasCaption; if (this.props.sas.emoji) { - const emojiBlocks = this.props.sas.emoji.map((emoji, i) => ( -
-
- {emoji[0]} -
-
{tEmoji(emoji, locale)}
-
- )); sasDisplay = ( -
- {emojiBlocks.slice(0, 4)} -
- {emojiBlocks.slice(4)} -
+ e[0]) as ComponentProps["emoji"]} + /> ); sasCaption = this.props.isSelf ? _t("encryption|verification|confirm_the_emojis") diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e899fec982..7749614825 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2020,7 +2020,8 @@ "other": "%(count)s people asking to join" }, "room_is_public": "This room is public", - "shared_history_tooltip": "New members see history" + "shared_history_tooltip": "New members see history", + "world_readable_history_tooltip": "Anyone can see history" }, "header_avatar_open_settings_label": "Open room settings", "header_face_pile_tooltip": "People", diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index bc0c8adfb8..295397a1df 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2017,7 +2017,8 @@ "Csatlakozást kér": "one", "%(count)s csatlakozást kérő ember": "other" }, - "room_is_public": "Ez egy nyilvános szoba" + "room_is_public": "Ez egy nyilvános szoba", + "shared_history_tooltip": "Az új tagok látják az előzményeket" }, "header_avatar_open_settings_label": "Szobabeállítások megnyitása", "header_face_pile_tooltip": "Taglista váltása", diff --git a/test/unit-tests/components/views/VerificationShowSas-test.tsx b/test/unit-tests/components/views/VerificationShowSas-test.tsx deleted file mode 100644 index f9312e906e..0000000000 --- a/test/unit-tests/components/views/VerificationShowSas-test.tsx +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2023 The Matrix.org Foundation C.I.C. - -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 { type EmojiMapping } from "matrix-js-sdk/src/crypto-api"; - -import { tEmoji } from "../../../../src/components/views/verification/VerificationShowSas"; - -describe("tEmoji", () => { - it.each([ - ["en-GB", "Dog"], - ["en", "Dog"], - ["de-DE", "Hund"], - ["pt", "Cachorro"], - ])("should handle locale %s", (locale, expectation) => { - const emoji: EmojiMapping = ["🐶", "Dog"]; - expect(tEmoji(emoji, locale)).toEqual(expectation); - }); -}); diff --git a/test/unit-tests/components/views/dialogs/IncomingSasDialog-test.tsx b/test/unit-tests/components/views/dialogs/IncomingSasDialog-test.tsx index cf9e103ee6..2a7ed61f13 100644 --- a/test/unit-tests/components/views/dialogs/IncomingSasDialog-test.tsx +++ b/test/unit-tests/components/views/dialogs/IncomingSasDialog-test.tsx @@ -34,7 +34,7 @@ describe("IncomingSasDialog", () => { it("should show some emojis once keys are exchanged", () => { const mockVerifier = makeMockVerifier(); - const { container } = renderComponent(mockVerifier); + const { getAllByText } = renderComponent(mockVerifier); // fire the ShowSas event const sasEvent = makeMockSasCallbacks(); @@ -42,11 +42,8 @@ describe("IncomingSasDialog", () => { mockVerifier.emit(VerifierEvent.ShowSas, sasEvent); }); - const emojis = container.getElementsByClassName("mx_VerificationShowSas_emojiSas_block"); - expect(emojis.length).toEqual(7); - for (const emoji of emojis) { - expect(emoji).toHaveTextContent("🦄Unicorn"); - } + expect(getAllByText("🦄")).toHaveLength(7); + expect(getAllByText("Unicorn")).toHaveLength(7); }); }); diff --git a/test/unit-tests/components/views/dialogs/__snapshots__/VerificationRequestDialog-test.tsx.snap b/test/unit-tests/components/views/dialogs/__snapshots__/VerificationRequestDialog-test.tsx.snap index 79737988d0..79d21cf19f 100644 --- a/test/unit-tests/components/views/dialogs/__snapshots__/VerificationRequestDialog-test.tsx.snap +++ b/test/unit-tests/components/views/dialogs/__snapshots__/VerificationRequestDialog-test.tsx.snap @@ -456,41 +456,38 @@ exports[`VerificationRequestDialog When other device accepted emoji, displays em Confirm that the emojis below match those shown on your other device.

Dog
Cat
-

", () => { }); it("should show some emojis once keys are exchanged", () => { - const { container } = renderComponent({ + const { getAllByText } = renderComponent({ request: mockRequest, phase: Phase.Started, }); @@ -117,11 +117,8 @@ describe("", () => { mockVerifier.emit(VerifierEvent.ShowSas, sasEvent); }); - const emojis = container.getElementsByClassName("mx_VerificationShowSas_emojiSas_block"); - expect(emojis.length).toEqual(7); - for (const emoji of emojis) { - expect(emoji).toHaveTextContent("🦄Unicorn"); - } + expect(getAllByText("🦄")).toHaveLength(7); + expect(getAllByText("Unicorn")).toHaveLength(7); }); describe("'Verify own device' flow", () => { diff --git a/test/unit-tests/components/views/rooms/RoomHeader/RoomHeader-test.tsx b/test/unit-tests/components/views/rooms/RoomHeader/RoomHeader-test.tsx index 7c1b5429cd..7de539ed54 100644 --- a/test/unit-tests/components/views/rooms/RoomHeader/RoomHeader-test.tsx +++ b/test/unit-tests/components/views/rooms/RoomHeader/RoomHeader-test.tsx @@ -744,6 +744,29 @@ describe("RoomHeader", () => { expect(queryByLabelText(document.body, "New members see history")).not.toBeInTheDocument(); }); + it("shows a user icon if the room is encrypted and has world readable history", async () => { + mocked(client.getCrypto()!).isEncryptionEnabledInRoom.mockResolvedValue(true); + await room.addLiveEvents( + [ + new MatrixEvent({ + type: "m.room.history_visibility", + content: { history_visibility: "world_readable" }, + sender: MatrixClientPeg.get()!.getSafeUserId(), + state_key: "", + room_id: room.roomId, + }), + ], + { addToState: true }, + ); + const featureEnabled = true; + jest.spyOn(SettingsStore, "getValue").mockImplementation( + (flag) => flag === "feature_share_history_on_invite" && featureEnabled, + ); + + render(, getWrapper()); + await waitFor(() => getByLabelText(document.body, "Anyone can see history")); + }); + describe("dm", () => { beforeEach(() => { // Make the mocked room a DM diff --git a/test/unit-tests/components/views/rooms/RoomHeader/__snapshots__/RoomHeader-test.tsx.snap b/test/unit-tests/components/views/rooms/RoomHeader/__snapshots__/RoomHeader-test.tsx.snap index 404edd3c6a..e3ff02aeaf 100644 --- a/test/unit-tests/components/views/rooms/RoomHeader/__snapshots__/RoomHeader-test.tsx.snap +++ b/test/unit-tests/components/views/rooms/RoomHeader/__snapshots__/RoomHeader-test.tsx.snap @@ -56,7 +56,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = ` style="--cpd-icon-button-size: 100%;" >