Merge branch 'develop' into florianduros/encryption-tab
|
@ -16,6 +16,11 @@ on:
|
||||||
options:
|
options:
|
||||||
- staging.element.io
|
- staging.element.io
|
||||||
- app.element.io
|
- app.element.io
|
||||||
|
skip-checks:
|
||||||
|
description: Skip CI on the tagged commit
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
concurrency: ${{ inputs.site || 'staging.element.io' }}
|
concurrency: ${{ inputs.site || 'staging.element.io' }}
|
||||||
permissions: {}
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
|
@ -75,6 +80,7 @@ jobs:
|
||||||
|
|
||||||
- name: Wait for other steps to succeed
|
- name: Wait for other steps to succeed
|
||||||
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
|
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
|
||||||
|
if: inputs.skip-checks != true
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.sha }}
|
ref: ${{ github.sha }}
|
||||||
running-workflow-name: "Deploy to Cloudflare Pages"
|
running-workflow-name: "Deploy to Cloudflare Pages"
|
||||||
|
|
10
CHANGELOG.md
|
@ -1,3 +1,13 @@
|
||||||
|
Changes in [1.11.89](https://github.com/element-hq/element-web/releases/tag/v1.11.89) (2024-12-18)
|
||||||
|
==================================================================================================
|
||||||
|
This is a patch release to fix a bug which could prevent loading stored crypto state from storage, and also to fix URL previews when switching back to a room.
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* Upgrade matrix-sdk-crypto-wasm to 1.11.0 (https://github.com/matrix-org/matrix-js-sdk/pull/4593)
|
||||||
|
* Fix url preview display ([#28766](https://github.com/element-hq/element-web/pull/28766)).
|
||||||
|
|
||||||
|
|
||||||
Changes in [1.11.88](https://github.com/element-hq/element-web/releases/tag/v1.11.88) (2024-12-17)
|
Changes in [1.11.88](https://github.com/element-hq/element-web/releases/tag/v1.11.88) (2024-12-17)
|
||||||
==================================================================================================
|
==================================================================================================
|
||||||
## ✨ Features
|
## ✨ Features
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "element-web",
|
"name": "element-web",
|
||||||
"version": "1.11.88",
|
"version": "1.11.89",
|
||||||
"description": "A feature-rich client for Matrix.org",
|
"description": "A feature-rich client for Matrix.org",
|
||||||
"author": "New Vector Ltd.",
|
"author": "New Vector Ltd.",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -282,7 +282,7 @@
|
||||||
"terser-webpack-plugin": "^5.3.9",
|
"terser-webpack-plugin": "^5.3.9",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"ts-prune": "^0.10.3",
|
"ts-prune": "^0.10.3",
|
||||||
"typescript": "5.6.3",
|
"typescript": "5.7.2",
|
||||||
"util": "^0.12.5",
|
"util": "^0.12.5",
|
||||||
"web-streams-polyfill": "^4.0.0",
|
"web-streams-polyfill": "^4.0.0",
|
||||||
"webpack": "^5.89.0",
|
"webpack": "^5.89.0",
|
||||||
|
|
|
@ -9,6 +9,8 @@ Please see LICENSE files in the repository root for full details.
|
||||||
import { type Page } from "@playwright/test";
|
import { type Page } from "@playwright/test";
|
||||||
|
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
|
import { test as masTest, registerAccountMas } from "../oidc";
|
||||||
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
|
||||||
async function expectBackupVersionToBe(page: Page, version: string) {
|
async function expectBackupVersionToBe(page: Page, version: string) {
|
||||||
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(5) td")).toHaveText(
|
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(5) td")).toHaveText(
|
||||||
|
@ -18,6 +20,32 @@ async function expectBackupVersionToBe(page: Page, version: string) {
|
||||||
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(6) td")).toHaveText(version);
|
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(6) td")).toHaveText(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
masTest.describe("Encryption state after registration", () => {
|
||||||
|
masTest.skip(isDendrite, "does not yet support MAS");
|
||||||
|
|
||||||
|
masTest("Key backup is enabled by default", async ({ page, mailhog, app }) => {
|
||||||
|
await page.goto("/#/login");
|
||||||
|
await page.getByRole("button", { name: "Continue" }).click();
|
||||||
|
await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!");
|
||||||
|
|
||||||
|
await app.settings.openUserSettings("Security & Privacy");
|
||||||
|
expect(page.getByText("This session is backing up your keys.")).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
masTest("user is prompted to set up recovery", async ({ page, mailhog, app }) => {
|
||||||
|
await page.goto("/#/login");
|
||||||
|
await page.getByRole("button", { name: "Continue" }).click();
|
||||||
|
await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!");
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Add room" }).click();
|
||||||
|
await page.getByRole("menuitem", { name: "New room" }).click();
|
||||||
|
await page.getByRole("textbox", { name: "Name" }).fill("test room");
|
||||||
|
await page.getByRole("button", { name: "Create room" }).click();
|
||||||
|
|
||||||
|
await expect(page.getByRole("heading", { name: "Set up recovery" })).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test.describe("Backups", () => {
|
test.describe("Backups", () => {
|
||||||
test.use({
|
test.use({
|
||||||
displayName: "Hanako",
|
displayName: "Hanako",
|
||||||
|
|
|
@ -8,11 +8,11 @@ Please see LICENSE files in the repository root for full details.
|
||||||
|
|
||||||
import { Locator, type Page } from "@playwright/test";
|
import { Locator, type Page } from "@playwright/test";
|
||||||
|
|
||||||
import { test as base, expect } from "../../element-web-test";
|
import { test as base, expect, Fixtures } from "../../element-web-test";
|
||||||
import { viewRoomSummaryByName } from "../right-panel/utils";
|
import { viewRoomSummaryByName } from "../right-panel/utils";
|
||||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
|
||||||
const test = base.extend({
|
const test = base.extend<Fixtures>({
|
||||||
// eslint-disable-next-line no-empty-pattern
|
// eslint-disable-next-line no-empty-pattern
|
||||||
startHomeserverOpts: async ({}, use) => {
|
startHomeserverOpts: async ({}, use) => {
|
||||||
await use("dehydration");
|
await use("dehydration");
|
||||||
|
|
|
@ -9,9 +9,9 @@ Please see LICENSE files in the repository root for full details.
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { readFile } from "node:fs/promises";
|
import { readFile } from "node:fs/promises";
|
||||||
|
|
||||||
import { expect, test as base } from "../../element-web-test";
|
import { expect, Fixtures, test as base } from "../../element-web-test";
|
||||||
|
|
||||||
const test = base.extend({
|
const test = base.extend<Fixtures>({
|
||||||
// Replace the `user` fixture with one which populates the indexeddb data before starting the app.
|
// Replace the `user` fixture with one which populates the indexeddb data before starting the app.
|
||||||
user: async ({ context, pageWithCredentials: page, credentials }, use) => {
|
user: async ({ context, pageWithCredentials: page, credentials }, use) => {
|
||||||
await page.route(`/test_indexeddb_cryptostore_dump/*`, async (route, request) => {
|
await page.route(`/test_indexeddb_cryptostore_dump/*`, async (route, request) => {
|
||||||
|
|
|
@ -71,7 +71,9 @@ test.describe("Room Header", () => {
|
||||||
|
|
||||||
// Assert the size of buttons on RoomHeader are specified and the buttons are not compressed
|
// Assert the size of buttons on RoomHeader are specified and the buttons are not compressed
|
||||||
// Note these assertions do not check the size of mx_LegacyRoomHeader_name button
|
// Note these assertions do not check the size of mx_LegacyRoomHeader_name button
|
||||||
const buttons = header.locator(".mx_Flex").getByRole("button");
|
const buttons = header.getByRole("button").filter({
|
||||||
|
has: page.locator("svg"),
|
||||||
|
});
|
||||||
await expect(buttons).toHaveCount(5);
|
await expect(buttons).toHaveCount(5);
|
||||||
|
|
||||||
for (const button of await buttons.all()) {
|
for (const button of await buttons.all()) {
|
||||||
|
|
|
@ -60,7 +60,7 @@ interface CredentialsWithDisplayName extends Credentials {
|
||||||
displayName: string;
|
displayName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const test = base.extend<{
|
export interface Fixtures {
|
||||||
axe: AxeBuilder;
|
axe: AxeBuilder;
|
||||||
checkA11y: () => Promise<void>;
|
checkA11y: () => Promise<void>;
|
||||||
|
|
||||||
|
@ -124,7 +124,9 @@ export const test = base.extend<{
|
||||||
slidingSyncProxy: ProxyInstance;
|
slidingSyncProxy: ProxyInstance;
|
||||||
labsFlags: string[];
|
labsFlags: string[];
|
||||||
webserver: Webserver;
|
webserver: Webserver;
|
||||||
}>({
|
}
|
||||||
|
|
||||||
|
export const test = base.extend<Fixtures>({
|
||||||
config: CONFIG_JSON,
|
config: CONFIG_JSON,
|
||||||
page: async ({ context, page, config, labsFlags }, use) => {
|
page: async ({ context, page, config, labsFlags }, use) => {
|
||||||
await context.route(`http://localhost:8080/config.json*`, async (route) => {
|
await context.route(`http://localhost:8080/config.json*`, async (route) => {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { randB64Bytes } from "../../utils/rand";
|
||||||
// Docker tag to use for synapse docker image.
|
// Docker tag to use for synapse docker image.
|
||||||
// We target a specific digest as every now and then a Synapse update will break our CI.
|
// We target a specific digest as every now and then a Synapse update will break our CI.
|
||||||
// This digest is updated by the playwright-image-updates.yaml workflow periodically.
|
// This digest is updated by the playwright-image-updates.yaml workflow periodically.
|
||||||
const DOCKER_TAG = "develop@sha256:ef3d491214fa380918c736d9aa720992fb58829ce5c06fa3ca36d357fa1df75d";
|
const DOCKER_TAG = "develop@sha256:c965896a4865479ab2628807ebf6d9c742586f3b6185a56f10077a408f1c7c3b";
|
||||||
|
|
||||||
async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> {
|
async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> {
|
||||||
const templateDir = path.join(__dirname, "templates", opts.template);
|
const templateDir = path.join(__dirname, "templates", opts.template);
|
||||||
|
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
@ -16,18 +16,19 @@ import { _t } from "./languageHandler";
|
||||||
import InteractiveAuthDialog from "./components/views/dialogs/InteractiveAuthDialog";
|
import InteractiveAuthDialog from "./components/views/dialogs/InteractiveAuthDialog";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the homeserver allows uploading device keys with only password auth.
|
* Determine if the homeserver allows uploading device keys with only password auth, or with no auth at
|
||||||
|
* all (ie. if the homeserver supports MSC3967).
|
||||||
* @param cli The Matrix Client to use
|
* @param cli The Matrix Client to use
|
||||||
* @returns True if the homeserver allows uploading device keys with only password auth, otherwise false
|
* @returns True if the homeserver allows uploading device keys with only password auth or with no auth
|
||||||
|
* at all, otherwise false
|
||||||
*/
|
*/
|
||||||
async function canUploadKeysWithPasswordOnly(cli: MatrixClient): Promise<boolean> {
|
async function canUploadKeysWithPasswordOnly(cli: MatrixClient): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await cli.uploadDeviceSigningKeys(undefined, {} as CrossSigningKeys);
|
await cli.uploadDeviceSigningKeys(undefined, {} as CrossSigningKeys);
|
||||||
// We should never get here: the server should always require
|
// If we get here, it's because the server is allowing us to upload keys without
|
||||||
// UI auth to upload device signing keys. If we do, we upload
|
// auth the first time due to MSC3967. Therefore, yes, we can upload keys
|
||||||
// no keys which would be a no-op.
|
// (with or without password, technically, but that's fine).
|
||||||
logger.log("uploadDeviceSigningKeys unexpectedly succeeded without UI auth!");
|
return true;
|
||||||
return false;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!(error instanceof MatrixError) || !error.data || !error.data.flows) {
|
if (!(error instanceof MatrixError) || !error.data || !error.data.flows) {
|
||||||
logger.log("uploadDeviceSigningKeys advertised no flows!");
|
logger.log("uploadDeviceSigningKeys advertised no flows!");
|
||||||
|
|
|
@ -295,13 +295,20 @@ export default class DeviceListener {
|
||||||
await crypto.getUserDeviceInfo([cli.getSafeUserId()]);
|
await crypto.getUserDeviceInfo([cli.getSafeUserId()]);
|
||||||
|
|
||||||
// cross signing isn't enabled - nag to enable it
|
// cross signing isn't enabled - nag to enable it
|
||||||
// There are 2 different toasts for:
|
// There are 3 different toasts for:
|
||||||
if (!(await crypto.getCrossSigningKeyId()) && (await crypto.userHasCrossSigningKeys())) {
|
if (!(await crypto.getCrossSigningKeyId()) && (await crypto.userHasCrossSigningKeys())) {
|
||||||
// Cross-signing on account but this device doesn't trust the master key (verify this session)
|
// Toast 1. Cross-signing on account but this device doesn't trust the master key (verify this session)
|
||||||
showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION);
|
showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION);
|
||||||
this.checkKeyBackupStatus();
|
this.checkKeyBackupStatus();
|
||||||
} else {
|
} else {
|
||||||
// No cross-signing or key backup on account (set up encryption)
|
const backupInfo = await this.getKeyBackupInfo();
|
||||||
|
if (backupInfo) {
|
||||||
|
// Toast 2: Key backup is enabled but recovery (4S) is not set up: prompt user to set up recovery.
|
||||||
|
// Since we now enable key backup at registration time, this will be the common case for
|
||||||
|
// new users.
|
||||||
|
showSetupEncryptionToast(SetupKind.SET_UP_RECOVERY);
|
||||||
|
} else {
|
||||||
|
// Toast 3: No cross-signing or key backup on account (set up encryption)
|
||||||
await cli.waitForClientWellKnown();
|
await cli.waitForClientWellKnown();
|
||||||
if (isSecureBackupRequired(cli) && isLoggedIn()) {
|
if (isSecureBackupRequired(cli) && isLoggedIn()) {
|
||||||
// If we're meant to set up, and Secure Backup is required,
|
// If we're meant to set up, and Secure Backup is required,
|
||||||
|
@ -313,6 +320,7 @@ export default class DeviceListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This needs to be done after awaiting on getUserDeviceInfo() above, so
|
// This needs to be done after awaiting on getUserDeviceInfo() above, so
|
||||||
// we make sure we get the devices after the fetch is done.
|
// we make sure we get the devices after the fetch is done.
|
||||||
|
|
|
@ -38,6 +38,9 @@ enum BackupStatus {
|
||||||
/** there is a backup on the server but we are not backing up to it */
|
/** there is a backup on the server but we are not backing up to it */
|
||||||
SERVER_BACKUP_BUT_DISABLED,
|
SERVER_BACKUP_BUT_DISABLED,
|
||||||
|
|
||||||
|
/** Key backup is set up but recovery (4s) is not */
|
||||||
|
BACKUP_NO_RECOVERY,
|
||||||
|
|
||||||
/** backup is not set up locally and there is no backup on the server */
|
/** backup is not set up locally and there is no backup on the server */
|
||||||
NO_BACKUP,
|
NO_BACKUP,
|
||||||
|
|
||||||
|
@ -104,7 +107,11 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((await crypto.getActiveSessionBackupVersion()) !== null) {
|
if ((await crypto.getActiveSessionBackupVersion()) !== null) {
|
||||||
|
if (await crypto.isSecretStorageReady()) {
|
||||||
this.setState({ backupStatus: BackupStatus.BACKUP_ACTIVE });
|
this.setState({ backupStatus: BackupStatus.BACKUP_ACTIVE });
|
||||||
|
} else {
|
||||||
|
this.setState({ backupStatus: BackupStatus.BACKUP_NO_RECOVERY });
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,13 +171,17 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a dialog prompting the user to set up key backup.
|
* Show a dialog prompting the user to set up their recovery method.
|
||||||
*
|
*
|
||||||
* Either there is no backup at all ({@link BackupStatus.NO_BACKUP}), there is a backup on the server but
|
* Either:
|
||||||
* we are not connected to it ({@link BackupStatus.SERVER_BACKUP_BUT_DISABLED}), or we were unable to pull the
|
* * There is no backup at all ({@link BackupStatus.NO_BACKUP})
|
||||||
* backup data ({@link BackupStatus.ERROR}). In all three cases, we should prompt the user to set up key backup.
|
* * There is a backup set up but recovery (4s) is not ({@link BackupStatus.BACKUP_NO_RECOVERY})
|
||||||
|
* * There is a backup on the server but we are not connected to it ({@link BackupStatus.SERVER_BACKUP_BUT_DISABLED})
|
||||||
|
* * We were unable to pull the backup data ({@link BackupStatus.ERROR}).
|
||||||
|
*
|
||||||
|
* In all four cases, we should prompt the user to set up a method of recovery.
|
||||||
*/
|
*/
|
||||||
private renderSetupBackupDialog(): React.ReactNode {
|
private renderSetupRecoveryMethod(): React.ReactNode {
|
||||||
const description = (
|
const description = (
|
||||||
<div>
|
<div>
|
||||||
<p>{_t("auth|logout_dialog|setup_secure_backup_description_1")}</p>
|
<p>{_t("auth|logout_dialog|setup_secure_backup_description_1")}</p>
|
||||||
|
@ -254,7 +265,8 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
|
||||||
case BackupStatus.NO_BACKUP:
|
case BackupStatus.NO_BACKUP:
|
||||||
case BackupStatus.SERVER_BACKUP_BUT_DISABLED:
|
case BackupStatus.SERVER_BACKUP_BUT_DISABLED:
|
||||||
case BackupStatus.ERROR:
|
case BackupStatus.ERROR:
|
||||||
return this.renderSetupBackupDialog();
|
case BackupStatus.BACKUP_NO_RECOVERY:
|
||||||
|
return this.renderSetupRecoveryMethod();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,8 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
if (!this.props.editState) {
|
if (!this.props.editState) {
|
||||||
const stoppedEditing = prevProps.editState && !this.props.editState;
|
const stoppedEditing = prevProps.editState && !this.props.editState;
|
||||||
const messageWasEdited = prevProps.replacingEventId !== this.props.replacingEventId;
|
const messageWasEdited = prevProps.replacingEventId !== this.props.replacingEventId;
|
||||||
if (messageWasEdited || stoppedEditing) {
|
const urlPreviewChanged = prevProps.showUrlPreview !== this.props.showUrlPreview;
|
||||||
|
if (messageWasEdited || stoppedEditing || urlPreviewChanged) {
|
||||||
this.applyFormatting();
|
this.applyFormatting();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -310,7 +310,7 @@ export default function RoomHeader({
|
||||||
</BodyText>
|
</BodyText>
|
||||||
</Box>
|
</Box>
|
||||||
</button>
|
</button>
|
||||||
<Flex align="center" gap="var(--cpd-space-2x)">
|
|
||||||
{additionalButtons?.map((props) => {
|
{additionalButtons?.map((props) => {
|
||||||
const label = props.label();
|
const label = props.label();
|
||||||
|
|
||||||
|
@ -340,18 +340,6 @@ export default function RoomHeader({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Tooltip label={_t("right_panel|room_summary_card|title")}>
|
|
||||||
<IconButton
|
|
||||||
onClick={(evt) => {
|
|
||||||
evt.stopPropagation();
|
|
||||||
RightPanelStore.instance.showOrHidePhase(RightPanelPhases.RoomSummary);
|
|
||||||
}}
|
|
||||||
aria-label={_t("right_panel|room_summary_card|title")}
|
|
||||||
>
|
|
||||||
<RoomInfoIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{showChatButton && <VideoRoomChatButton room={room} />}
|
{showChatButton && <VideoRoomChatButton room={room} />}
|
||||||
|
|
||||||
<Tooltip label={_t("common|threads")}>
|
<Tooltip label={_t("common|threads")}>
|
||||||
|
@ -381,7 +369,19 @@ export default function RoomHeader({
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
|
||||||
|
<Tooltip label={_t("right_panel|room_summary_card|title")}>
|
||||||
|
<IconButton
|
||||||
|
onClick={(evt) => {
|
||||||
|
evt.stopPropagation();
|
||||||
|
RightPanelStore.instance.showOrHidePhase(RightPanelPhases.RoomSummary);
|
||||||
|
}}
|
||||||
|
aria-label={_t("right_panel|room_summary_card|title")}
|
||||||
|
>
|
||||||
|
<RoomInfoIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
{!isDirectMessage && (
|
{!isDirectMessage && (
|
||||||
<BodyText as="div" size="sm" weight="medium">
|
<BodyText as="div" size="sm" weight="medium">
|
||||||
<FacePile
|
<FacePile
|
||||||
|
|
|
@ -214,7 +214,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
|
||||||
<SettingsSubsectionText>
|
<SettingsSubsectionText>
|
||||||
{this.state.enabling ? <InlineSpinner /> : _t("settings|security|message_search_failed")}
|
{this.state.enabling ? <InlineSpinner /> : _t("settings|security|message_search_failed")}
|
||||||
</SettingsSubsectionText>
|
</SettingsSubsectionText>
|
||||||
{EventIndexPeg.error && (
|
{EventIndexPeg.error ? (
|
||||||
<SettingsSubsectionText>
|
<SettingsSubsectionText>
|
||||||
<details>
|
<details>
|
||||||
<summary>{_t("common|advanced")}</summary>
|
<summary>{_t("common|advanced")}</summary>
|
||||||
|
@ -230,7 +230,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
|
||||||
</p>
|
</p>
|
||||||
</details>
|
</details>
|
||||||
</SettingsSubsectionText>
|
</SettingsSubsectionText>
|
||||||
)}
|
) : undefined}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -915,6 +915,9 @@
|
||||||
"warning": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings."
|
"warning": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings."
|
||||||
},
|
},
|
||||||
"reset_all_button": "Forgotten or lost all recovery methods? <a>Reset all</a>",
|
"reset_all_button": "Forgotten or lost all recovery methods? <a>Reset all</a>",
|
||||||
|
"set_up_recovery": "Set up recovery",
|
||||||
|
"set_up_recovery_later": "Not now",
|
||||||
|
"set_up_recovery_toast_description": "Generate a recovery key that can be used to restore your encrypted message history in case you lose access to your devices.",
|
||||||
"set_up_toast_description": "Safeguard against losing access to encrypted messages & data",
|
"set_up_toast_description": "Safeguard against losing access to encrypted messages & data",
|
||||||
"set_up_toast_title": "Set up Secure Backup",
|
"set_up_toast_title": "Set up Secure Backup",
|
||||||
"setup_secure_backup": {
|
"setup_secure_backup": {
|
||||||
|
|
|
@ -2115,7 +2115,8 @@
|
||||||
"show_less": "Pokaż mniej",
|
"show_less": "Pokaż mniej",
|
||||||
"show_n_more": {
|
"show_n_more": {
|
||||||
"one": "Pokaż %(count)s więcej",
|
"one": "Pokaż %(count)s więcej",
|
||||||
"other": "Pokaż %(count)s więcej"
|
"few": "Pokaż %(count)s więcej",
|
||||||
|
"many": "Pokaż %(count)s więcej"
|
||||||
},
|
},
|
||||||
"show_previews": "Pokazuj podgląd wiadomości",
|
"show_previews": "Pokazuj podgląd wiadomości",
|
||||||
"sort_by": "Sortuj według",
|
"sort_by": "Sortuj według",
|
||||||
|
@ -3689,7 +3690,8 @@
|
||||||
"close": "Zamknij podgląd",
|
"close": "Zamknij podgląd",
|
||||||
"show_n_more": {
|
"show_n_more": {
|
||||||
"one": "Pokaż %(count)s inny podgląd",
|
"one": "Pokaż %(count)s inny podgląd",
|
||||||
"other": "Pokaż %(count)s innych podglądów"
|
"few": "Pokaż %(count)s inne podglądy",
|
||||||
|
"many": "Pokaż %(count)s innych podglądów"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -114,8 +114,15 @@ export class InitialCryptoSetupStore extends EventEmitter {
|
||||||
this.emit("update");
|
this.emit("update");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Create the user's cross-signing keys
|
||||||
await createCrossSigning(this.client, this.isTokenLogin, this.stores.accountPasswordStore.getPassword());
|
await createCrossSigning(this.client, this.isTokenLogin, this.stores.accountPasswordStore.getPassword());
|
||||||
|
|
||||||
|
// Check for any existing backup and enable key backup if there isn't one
|
||||||
|
const currentKeyBackup = await cryptoApi.checkKeyBackupAndEnable();
|
||||||
|
if (currentKeyBackup === null) {
|
||||||
|
await cryptoApi.resetKeyBackup();
|
||||||
|
}
|
||||||
|
|
||||||
this.reset();
|
this.reset();
|
||||||
|
|
||||||
this.status = "complete";
|
this.status = "complete";
|
||||||
|
|
|
@ -23,15 +23,19 @@ const getTitle = (kind: Kind): string => {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case Kind.SET_UP_ENCRYPTION:
|
case Kind.SET_UP_ENCRYPTION:
|
||||||
return _t("encryption|set_up_toast_title");
|
return _t("encryption|set_up_toast_title");
|
||||||
|
case Kind.SET_UP_RECOVERY:
|
||||||
|
return _t("encryption|set_up_recovery");
|
||||||
case Kind.VERIFY_THIS_SESSION:
|
case Kind.VERIFY_THIS_SESSION:
|
||||||
return _t("encryption|verify_toast_title");
|
return _t("encryption|verify_toast_title");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIcon = (kind: Kind): string => {
|
const getIcon = (kind: Kind): string | undefined => {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case Kind.SET_UP_ENCRYPTION:
|
case Kind.SET_UP_ENCRYPTION:
|
||||||
return "secure_backup";
|
return "secure_backup";
|
||||||
|
case Kind.SET_UP_RECOVERY:
|
||||||
|
return undefined;
|
||||||
case Kind.VERIFY_THIS_SESSION:
|
case Kind.VERIFY_THIS_SESSION:
|
||||||
return "verification_warning";
|
return "verification_warning";
|
||||||
}
|
}
|
||||||
|
@ -41,22 +45,49 @@ const getSetupCaption = (kind: Kind): string => {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case Kind.SET_UP_ENCRYPTION:
|
case Kind.SET_UP_ENCRYPTION:
|
||||||
return _t("action|continue");
|
return _t("action|continue");
|
||||||
|
case Kind.SET_UP_RECOVERY:
|
||||||
|
return _t("action|continue");
|
||||||
case Kind.VERIFY_THIS_SESSION:
|
case Kind.VERIFY_THIS_SESSION:
|
||||||
return _t("action|verify");
|
return _t("action|verify");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getSecondaryButtonLabel = (kind: Kind): string => {
|
||||||
|
switch (kind) {
|
||||||
|
case Kind.SET_UP_RECOVERY:
|
||||||
|
return _t("encryption|set_up_recovery_later");
|
||||||
|
case Kind.SET_UP_ENCRYPTION:
|
||||||
|
case Kind.VERIFY_THIS_SESSION:
|
||||||
|
return _t("encryption|verification|unverified_sessions_toast_reject");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getDescription = (kind: Kind): string => {
|
const getDescription = (kind: Kind): string => {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case Kind.SET_UP_ENCRYPTION:
|
case Kind.SET_UP_ENCRYPTION:
|
||||||
return _t("encryption|set_up_toast_description");
|
return _t("encryption|set_up_toast_description");
|
||||||
|
case Kind.SET_UP_RECOVERY:
|
||||||
|
return _t("encryption|set_up_recovery_toast_description");
|
||||||
case Kind.VERIFY_THIS_SESSION:
|
case Kind.VERIFY_THIS_SESSION:
|
||||||
return _t("encryption|verify_toast_description");
|
return _t("encryption|verify_toast_description");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The kind of toast to show.
|
||||||
|
*/
|
||||||
export enum Kind {
|
export enum Kind {
|
||||||
|
/**
|
||||||
|
* Prompt the user to set up encryption
|
||||||
|
*/
|
||||||
SET_UP_ENCRYPTION = "set_up_encryption",
|
SET_UP_ENCRYPTION = "set_up_encryption",
|
||||||
|
/**
|
||||||
|
* Prompt the user to set up a recovery key
|
||||||
|
*/
|
||||||
|
SET_UP_RECOVERY = "set_up_recovery",
|
||||||
|
/**
|
||||||
|
* Prompt the user to verify this session
|
||||||
|
*/
|
||||||
VERIFY_THIS_SESSION = "verify_this_session",
|
VERIFY_THIS_SESSION = "verify_this_session",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +95,11 @@ const onReject = (): void => {
|
||||||
DeviceListener.sharedInstance().dismissEncryptionSetup();
|
DeviceListener.sharedInstance().dismissEncryptionSetup();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a toast prompting the user for some action related to setting up their encryption.
|
||||||
|
*
|
||||||
|
* @param kind The kind of toast to show
|
||||||
|
*/
|
||||||
export const showToast = (kind: Kind): void => {
|
export const showToast = (kind: Kind): void => {
|
||||||
if (
|
if (
|
||||||
ModuleRunner.instance.extensions.cryptoSetup.setupEncryptionNeeded({
|
ModuleRunner.instance.extensions.cryptoSetup.setupEncryptionNeeded({
|
||||||
|
@ -101,15 +137,17 @@ export const showToast = (kind: Kind): void => {
|
||||||
description: getDescription(kind),
|
description: getDescription(kind),
|
||||||
primaryLabel: getSetupCaption(kind),
|
primaryLabel: getSetupCaption(kind),
|
||||||
onPrimaryClick: onAccept,
|
onPrimaryClick: onAccept,
|
||||||
secondaryLabel: _t("encryption|verification|unverified_sessions_toast_reject"),
|
secondaryLabel: getSecondaryButtonLabel(kind),
|
||||||
onSecondaryClick: onReject,
|
onSecondaryClick: onReject,
|
||||||
destructive: "secondary",
|
|
||||||
},
|
},
|
||||||
component: GenericToast,
|
component: GenericToast,
|
||||||
priority: kind === Kind.VERIFY_THIS_SESSION ? 95 : 40,
|
priority: kind === Kind.VERIFY_THIS_SESSION ? 95 : 40,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the encryption setup toast if it is currently being shown.
|
||||||
|
*/
|
||||||
export const hideToast = (): void => {
|
export const hideToast = (): void => {
|
||||||
ToastStore.sharedInstance().dismissToast(TOAST_KEY);
|
ToastStore.sharedInstance().dismissToast(TOAST_KEY);
|
||||||
};
|
};
|
||||||
|
|
|
@ -137,6 +137,7 @@ export function createTestClient(): MatrixClient {
|
||||||
restoreKeyBackupWithPassphrase: jest.fn(),
|
restoreKeyBackupWithPassphrase: jest.fn(),
|
||||||
loadSessionBackupPrivateKeyFromSecretStorage: jest.fn(),
|
loadSessionBackupPrivateKeyFromSecretStorage: jest.fn(),
|
||||||
storeSessionBackupPrivateKey: jest.fn(),
|
storeSessionBackupPrivateKey: jest.fn(),
|
||||||
|
checkKeyBackupAndEnable: jest.fn().mockResolvedValue(null),
|
||||||
getKeyBackupInfo: jest.fn().mockResolvedValue(null),
|
getKeyBackupInfo: jest.fn().mockResolvedValue(null),
|
||||||
getEncryptionInfoForEvent: jest.fn().mockResolvedValue(null),
|
getEncryptionInfoForEvent: jest.fn().mockResolvedValue(null),
|
||||||
getCrossSigningStatus: jest.fn().mockResolvedValue({
|
getCrossSigningStatus: jest.fn().mockResolvedValue({
|
||||||
|
|
|
@ -352,13 +352,13 @@ describe("DeviceListener", () => {
|
||||||
mockCrypto!.getCrossSigningKeyId.mockResolvedValue("abc");
|
mockCrypto!.getCrossSigningKeyId.mockResolvedValue("abc");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows set up encryption toast when user has a key backup available", async () => {
|
it("shows set up recovery toast when user has a key backup available", async () => {
|
||||||
// non falsy response
|
// non falsy response
|
||||||
mockCrypto.getKeyBackupInfo.mockResolvedValue({} as unknown as KeyBackupInfo);
|
mockCrypto.getKeyBackupInfo.mockResolvedValue({} as unknown as KeyBackupInfo);
|
||||||
await createAndStart();
|
await createAndStart();
|
||||||
|
|
||||||
expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith(
|
expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith(
|
||||||
SetupEncryptionToast.Kind.SET_UP_ENCRYPTION,
|
SetupEncryptionToast.Kind.SET_UP_RECOVERY,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1003,7 +1003,9 @@ describe("<MatrixChat />", () => {
|
||||||
userHasCrossSigningKeys: jest.fn().mockResolvedValue(false),
|
userHasCrossSigningKeys: jest.fn().mockResolvedValue(false),
|
||||||
// This needs to not finish immediately because we need to test the screen appears
|
// This needs to not finish immediately because we need to test the screen appears
|
||||||
bootstrapCrossSigning: jest.fn().mockImplementation(() => bootstrapDeferred.promise),
|
bootstrapCrossSigning: jest.fn().mockImplementation(() => bootstrapDeferred.promise),
|
||||||
|
resetKeyBackup: jest.fn(),
|
||||||
isEncryptionEnabledInRoom: jest.fn().mockResolvedValue(false),
|
isEncryptionEnabledInRoom: jest.fn().mockResolvedValue(false),
|
||||||
|
checkKeyBackupAndEnable: jest.fn().mockResolvedValue(null),
|
||||||
};
|
};
|
||||||
loginClient.getCrypto.mockReturnValue(mockCrypto as any);
|
loginClient.getCrypto.mockReturnValue(mockCrypto as any);
|
||||||
});
|
});
|
||||||
|
|
|
@ -45,10 +45,6 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
class="mx_Flex"
|
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
aria-label="Video call"
|
aria-label="Video call"
|
||||||
|
@ -101,34 +97,9 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
aria-label="Room info"
|
|
||||||
aria-labelledby=":rge:"
|
|
||||||
class="_icon-button_bh2qc_17"
|
|
||||||
role="button"
|
|
||||||
style="--cpd-icon-button-size: 32px;"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="_indicator-icon_133tf_26"
|
|
||||||
style="--cpd-icon-button-size: 100%;"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
height="1em"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="1em"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":rgj:"
|
aria-labelledby=":rge:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -151,7 +122,31 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
aria-labelledby=":rgj:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||||
>
|
>
|
||||||
|
@ -263,10 +258,6 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
class="mx_Flex"
|
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
aria-label="Video call"
|
aria-label="Video call"
|
||||||
|
@ -319,34 +310,9 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
aria-label="Room info"
|
|
||||||
aria-labelledby=":rhc:"
|
|
||||||
class="_icon-button_bh2qc_17"
|
|
||||||
role="button"
|
|
||||||
style="--cpd-icon-button-size: 32px;"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="_indicator-icon_133tf_26"
|
|
||||||
style="--cpd-icon-button-size: 100%;"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
height="1em"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="1em"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":rhh:"
|
aria-labelledby=":rhc:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -369,7 +335,31 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
aria-labelledby=":rhh:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||||
>
|
>
|
||||||
|
@ -566,10 +556,6 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
class="mx_Flex"
|
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
aria-label="Video call"
|
aria-label="Video call"
|
||||||
|
@ -622,34 +608,9 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
aria-label="Room info"
|
|
||||||
aria-labelledby=":rc2:"
|
|
||||||
class="_icon-button_bh2qc_17"
|
|
||||||
role="button"
|
|
||||||
style="--cpd-icon-button-size: 32px;"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="_indicator-icon_133tf_26"
|
|
||||||
style="--cpd-icon-button-size: 100%;"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
height="1em"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="1em"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":rc7:"
|
aria-labelledby=":rc2:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -672,7 +633,31 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
aria-labelledby=":rc7:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||||
>
|
>
|
||||||
|
@ -946,10 +931,6 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
class="mx_Flex"
|
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
aria-label="Video call"
|
aria-label="Video call"
|
||||||
|
@ -1002,34 +983,9 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
aria-label="Room info"
|
|
||||||
aria-labelledby=":re8:"
|
|
||||||
class="_icon-button_bh2qc_17"
|
|
||||||
role="button"
|
|
||||||
style="--cpd-icon-button-size: 32px;"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="_indicator-icon_133tf_26"
|
|
||||||
style="--cpd-icon-button-size: 100%;"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
height="1em"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="1em"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":red:"
|
aria-labelledby=":re8:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -1052,7 +1008,31 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
aria-labelledby=":red:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||||
>
|
>
|
||||||
|
@ -1334,10 +1314,6 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
class="mx_Flex"
|
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
aria-disabled="true"
|
aria-disabled="true"
|
||||||
aria-label="There's no one here to call"
|
aria-label="There's no one here to call"
|
||||||
|
@ -1390,34 +1366,9 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
aria-label="Room info"
|
|
||||||
aria-labelledby=":r2m:"
|
|
||||||
class="_icon-button_bh2qc_17"
|
|
||||||
role="button"
|
|
||||||
style="--cpd-icon-button-size: 32px;"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="_indicator-icon_133tf_26"
|
|
||||||
style="--cpd-icon-button-size: 100%;"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
height="1em"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="1em"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":r2r:"
|
aria-labelledby=":r2m:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -1440,7 +1391,31 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
aria-labelledby=":r2r:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||||
>
|
>
|
||||||
|
@ -1545,10 +1520,6 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
class="mx_Flex"
|
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
aria-disabled="true"
|
aria-disabled="true"
|
||||||
aria-label="There's no one here to call"
|
aria-label="There's no one here to call"
|
||||||
|
@ -1601,34 +1572,9 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
aria-label="Room info"
|
|
||||||
aria-labelledby=":r2m:"
|
|
||||||
class="_icon-button_bh2qc_17"
|
|
||||||
role="button"
|
|
||||||
style="--cpd-icon-button-size: 32px;"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="_indicator-icon_133tf_26"
|
|
||||||
style="--cpd-icon-button-size: 100%;"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
height="1em"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="1em"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":r2r:"
|
aria-labelledby=":r2m:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -1651,7 +1597,31 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
aria-labelledby=":r2r:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||||
>
|
>
|
||||||
|
@ -1929,38 +1899,9 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
class="mx_Flex"
|
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
aria-label="Room info"
|
|
||||||
aria-labelledby=":r7c:"
|
|
||||||
class="_icon-button_bh2qc_17"
|
|
||||||
role="button"
|
|
||||||
style="--cpd-icon-button-size: 32px;"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="_indicator-icon_133tf_26"
|
|
||||||
style="--cpd-icon-button-size: 100%;"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
height="1em"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="1em"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
aria-label="Chat"
|
aria-label="Chat"
|
||||||
aria-labelledby=":r7h:"
|
aria-labelledby=":r7c:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -1985,7 +1926,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":r7m:"
|
aria-labelledby=":r7h:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -2008,7 +1949,31 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
aria-labelledby=":r7m:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||||
>
|
>
|
||||||
|
|
|
@ -42,12 +42,20 @@ describe("LogoutDialog", () => {
|
||||||
expect(rendered.container).toMatchSnapshot();
|
expect(rendered.container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows a regular dialog if backups are working", async () => {
|
it("shows a regular dialog if backups and recovery are working", async () => {
|
||||||
mockCrypto.getActiveSessionBackupVersion.mockResolvedValue("1");
|
mockCrypto.getActiveSessionBackupVersion.mockResolvedValue("1");
|
||||||
|
mockCrypto.isSecretStorageReady.mockResolvedValue(true);
|
||||||
const rendered = renderComponent();
|
const rendered = renderComponent();
|
||||||
await rendered.findByText("Are you sure you want to sign out?");
|
await rendered.findByText("Are you sure you want to sign out?");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prompts user to set up recovery if backups are enabled but recovery isn't", async () => {
|
||||||
|
mockCrypto.getActiveSessionBackupVersion.mockResolvedValue("1");
|
||||||
|
mockCrypto.isSecretStorageReady.mockResolvedValue(false);
|
||||||
|
const rendered = renderComponent();
|
||||||
|
await rendered.findByText("You'll lose access to your encrypted messages");
|
||||||
|
});
|
||||||
|
|
||||||
it("Prompts user to connect backup if there is a backup on the server", async () => {
|
it("Prompts user to connect backup if there is a backup on the server", async () => {
|
||||||
mockCrypto.getKeyBackupInfo.mockResolvedValue({} as KeyBackupInfo);
|
mockCrypto.getKeyBackupInfo.mockResolvedValue({} as KeyBackupInfo);
|
||||||
const rendered = renderComponent();
|
const rendered = renderComponent();
|
||||||
|
|
|
@ -375,10 +375,12 @@ describe("<TextualBody />", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders url previews correctly", () => {
|
describe("url preview", () => {
|
||||||
languageHandler.setMissingEntryGenerator((key) => key.split("|", 2)[1]);
|
let matrixClient: MatrixClient;
|
||||||
|
|
||||||
const matrixClient = getMockClientWithEventEmitter({
|
beforeEach(() => {
|
||||||
|
languageHandler.setMissingEntryGenerator((key) => key.split("|", 2)[1]);
|
||||||
|
matrixClient = getMockClientWithEventEmitter({
|
||||||
getRoom: () => mkStubRoom("room_id", "room name", undefined),
|
getRoom: () => mkStubRoom("room_id", "room name", undefined),
|
||||||
getAccountData: (): MatrixClient | undefined => undefined,
|
getAccountData: (): MatrixClient | undefined => undefined,
|
||||||
getUrlPreview: (url: string) => new Promise(() => {}),
|
getUrlPreview: (url: string) => new Promise(() => {}),
|
||||||
|
@ -386,7 +388,9 @@ describe("<TextualBody />", () => {
|
||||||
mxcUrlToHttp: (s: string) => s,
|
mxcUrlToHttp: (s: string) => s,
|
||||||
});
|
});
|
||||||
DMRoomMap.makeShared(defaultMatrixClient);
|
DMRoomMap.makeShared(defaultMatrixClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders url previews correctly", () => {
|
||||||
const ev = mkRoomTextMessage("Visit https://matrix.org/");
|
const ev = mkRoomTextMessage("Visit https://matrix.org/");
|
||||||
const { container, rerender } = getComponent(
|
const { container, rerender } = getComponent(
|
||||||
{ mxEvent: ev, showUrlPreview: true, onHeightChanged: jest.fn() },
|
{ mxEvent: ev, showUrlPreview: true, onHeightChanged: jest.fn() },
|
||||||
|
@ -426,4 +430,18 @@ describe("<TextualBody />", () => {
|
||||||
expect(node).toHaveAttribute("href", links[index]);
|
expect(node).toHaveAttribute("href", links[index]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should listen to showUrlPreview change", () => {
|
||||||
|
const ev = mkRoomTextMessage("Visit https://matrix.org/");
|
||||||
|
|
||||||
|
const { container, rerender } = getComponent(
|
||||||
|
{ mxEvent: ev, showUrlPreview: false, onHeightChanged: jest.fn() },
|
||||||
|
matrixClient,
|
||||||
|
);
|
||||||
|
expect(container.querySelector(".mx_LinkPreviewGroup")).toBeNull();
|
||||||
|
|
||||||
|
getComponent({ mxEvent: ev, showUrlPreview: true, onHeightChanged: jest.fn() }, matrixClient, rerender);
|
||||||
|
expect(container.querySelector(".mx_LinkPreviewGroup")).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -42,10 +42,6 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
class="mx_Flex"
|
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
aria-labelledby=":r16c:"
|
aria-labelledby=":r16c:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
|
@ -96,34 +92,9 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
aria-label="Room info"
|
|
||||||
aria-labelledby=":r16m:"
|
|
||||||
class="_icon-button_bh2qc_17"
|
|
||||||
role="button"
|
|
||||||
style="--cpd-icon-button-size: 32px;"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="_indicator-icon_133tf_26"
|
|
||||||
style="--cpd-icon-button-size: 100%;"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
height="1em"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="1em"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":r16r:"
|
aria-labelledby=":r16m:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -146,7 +117,31 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
aria-labelledby=":r16r:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
</button>
|
||||||
</header>
|
</header>
|
||||||
</DocumentFragment>
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -11,7 +11,6 @@ import { fireEvent, render, screen, waitFor, within } from "jest-matrix-react";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||||
import { SDKContext, SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
|
||||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||||
import { UIFeature } from "../../../../../src/settings/UIFeature";
|
import { UIFeature } from "../../../../../src/settings/UIFeature";
|
||||||
import {
|
import {
|
||||||
|
@ -35,13 +34,9 @@ describe("SetIntegrationManager", () => {
|
||||||
deleteThreePid: jest.fn(),
|
deleteThreePid: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let stores!: SdkContextClass;
|
|
||||||
|
|
||||||
const getComponent = () => (
|
const getComponent = () => (
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
<MatrixClientContext.Provider value={mockClient}>
|
||||||
<SDKContext.Provider value={stores}>
|
|
||||||
<SetIntegrationManager />
|
<SetIntegrationManager />
|
||||||
</SDKContext.Provider>
|
|
||||||
</MatrixClientContext.Provider>
|
</MatrixClientContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { render, screen } from "jest-matrix-react";
|
||||||
|
|
||||||
|
import ToastContainer from "../../../src/components/structures/ToastContainer";
|
||||||
|
import { Kind, showToast } from "../../../src/toasts/SetupEncryptionToast";
|
||||||
|
|
||||||
|
describe("SetupEncryptionToast", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
render(<ToastContainer />);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render the se up recovery toast", async () => {
|
||||||
|
showToast(Kind.SET_UP_RECOVERY);
|
||||||
|
|
||||||
|
await expect(screen.findByText("Set up recovery")).resolves.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
14
yarn.lock
|
@ -8595,9 +8595,9 @@ murmurhash-js@^1.0.0:
|
||||||
integrity sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==
|
integrity sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==
|
||||||
|
|
||||||
nanoid@^3.3.7:
|
nanoid@^3.3.7:
|
||||||
version "3.3.7"
|
version "3.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf"
|
||||||
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==
|
||||||
|
|
||||||
natural-compare@^1.4.0:
|
natural-compare@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
|
@ -11539,10 +11539,10 @@ typed-array-length@^1.0.6:
|
||||||
possible-typed-array-names "^1.0.0"
|
possible-typed-array-names "^1.0.0"
|
||||||
reflect.getprototypeof "^1.0.6"
|
reflect.getprototypeof "^1.0.6"
|
||||||
|
|
||||||
typescript@5.6.3:
|
typescript@5.7.2:
|
||||||
version "5.6.3"
|
version "5.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
|
||||||
integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
|
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
|
||||||
|
|
||||||
ua-parser-js@^1.0.2:
|
ua-parser-js@^1.0.2:
|
||||||
version "1.0.39"
|
version "1.0.39"
|
||||||
|
|