Global configuration flag for media previews (#29582)
* Modify useMediaVisible to take a room. * Add initial support for a account data level key. * Update controls. * Update settings * Lint and fixes * make some tests go happy * lint * i18n * update preferences * prettier * Update settings tab. * update screenshot * Update docs * Rewrite controller * Rewrite tons of tests * Rewrite RoomAvatar to be a functional component This is so we can use hooks to determine the setting state. * lint * lint * Tidy up comments * Apply media visible hook to inline images. * Move conditionals. * copyright all the things * Review changes * Update html utils to properly discard media. * Types fix * Fixing tests that break settings getValue expectations * Fix logic around media preview calculation * Fix room header tests * Fixup tests for timelinePanel * Clear settings in matrixchat * Update tests to use SettingsStore where possible. * fix bug * revert changes to client.ts * copyright years * Add header * Add a test for MediaPreviewAccountSettingsTab * Mark initMatrixClient as optional * Improve on types * Ensure we do not set the account data twice. * lint * Review changes * Ensure we include the client on rendered messages. * Fix test * update labels * clean designs * update settings tab * update snapshot * copyright * prevent mutation
This commit is contained in:
@@ -12,6 +12,7 @@ import parse from "html-react-parser";
|
||||
|
||||
import { bodyToHtml, bodyToNode, formatEmojis, topicToHtml } from "../../src/HtmlUtils";
|
||||
import SettingsStore from "../../src/settings/SettingsStore";
|
||||
import { getMockClientWithEventEmitter } from "../test-utils";
|
||||
import { SettingLevel } from "../../src/settings/SettingLevel";
|
||||
import SdkConfig from "../../src/SdkConfig";
|
||||
|
||||
@@ -231,6 +232,37 @@ describe("bodyToNode", () => {
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it.each([[true], [false]])("should handle inline media when mediaIsVisible is %s", (mediaIsVisible) => {
|
||||
const cli = getMockClientWithEventEmitter({
|
||||
mxcUrlToHttp: jest.fn().mockReturnValue("https://example.org/img"),
|
||||
});
|
||||
const { className, formattedBody } = bodyToNode(
|
||||
{
|
||||
"body": " Hello there",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": `<img src="mxc://going/knowwhere">foo</img> Hello there`,
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
event_id: "$eventId",
|
||||
},
|
||||
},
|
||||
"msgtype": "m.text",
|
||||
},
|
||||
[],
|
||||
{
|
||||
mediaIsVisible,
|
||||
},
|
||||
);
|
||||
|
||||
const { asFragment } = render(
|
||||
<span className={className} dir="auto" dangerouslySetInnerHTML={{ __html: formattedBody! }} />,
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
// We do not want to download untrusted media.
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
expect(cli.mxcUrlToHttp).toHaveBeenCalledTimes(mediaIsVisible ? 1 : 0);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
@@ -64,3 +64,30 @@ exports[`bodyToNode should generate big emoji for an emoji-only reply to a messa
|
||||
</span>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`bodyToNode should handle inline media when mediaIsVisible is false 1`] = `
|
||||
<DocumentFragment>
|
||||
<span
|
||||
class="mx_EventTile_body markdown-body translate"
|
||||
dir="auto"
|
||||
>
|
||||
<img />
|
||||
foo Hello there
|
||||
</span>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`bodyToNode should handle inline media when mediaIsVisible is true 1`] = `
|
||||
<DocumentFragment>
|
||||
<span
|
||||
class="mx_EventTile_body markdown-body translate"
|
||||
dir="auto"
|
||||
>
|
||||
<img
|
||||
src="https://example.org/img"
|
||||
style="max-width:800px;max-height:600px"
|
||||
/>
|
||||
foo Hello there
|
||||
</span>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
@@ -17,6 +17,7 @@ import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { LocalRoom } from "../../../../../src/models/LocalRoom";
|
||||
import * as AvatarModule from "../../../../../src/Avatar";
|
||||
import { DirectoryMember } from "../../../../../src/utils/direct-messages";
|
||||
import { MediaPreviewValue } from "../../../../../src/@types/media_preview";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
|
||||
|
||||
@@ -37,18 +38,18 @@ describe("RoomAvatar", () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
SettingsStore.setValue(
|
||||
"mediaPreviewConfig",
|
||||
null,
|
||||
SettingLevel.ACCOUNT,
|
||||
SettingsStore.getDefaultValue("mediaPreviewConfig"),
|
||||
);
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mocked(DMRoomMap.shared().getUserIdForRoomId).mockReset();
|
||||
mocked(AvatarModule.defaultAvatarUrlForString).mockClear();
|
||||
SettingsStore.setValue(
|
||||
"showAvatarsOnInvites",
|
||||
null,
|
||||
SettingLevel.ACCOUNT,
|
||||
SettingsStore.getDefaultValue("showAvatarsOnInvites"),
|
||||
);
|
||||
});
|
||||
|
||||
it("should render as expected for a Room", () => {
|
||||
@@ -74,7 +75,6 @@ describe("RoomAvatar", () => {
|
||||
expect(render(<RoomAvatar room={localRoom} />).container).toMatchSnapshot();
|
||||
});
|
||||
it("should render an avatar for a room the user is invited to", () => {
|
||||
SettingsStore.setValue("showAvatarsOnInvites", null, SettingLevel.ACCOUNT, true);
|
||||
const room = new Room("!room:example.com", client, client.getSafeUserId());
|
||||
jest.spyOn(room, "getMxcAvatarUrl").mockImplementation(() => "mxc://example.com/foobar");
|
||||
room.name = "test room";
|
||||
@@ -93,7 +93,9 @@ describe("RoomAvatar", () => {
|
||||
expect(render(<RoomAvatar room={room} />).container).toMatchSnapshot();
|
||||
});
|
||||
it("should not render an invite avatar if the user has disabled it", () => {
|
||||
SettingsStore.setValue("showAvatarsOnInvites", null, SettingLevel.ACCOUNT, false);
|
||||
SettingsStore.setValue("mediaPreviewConfig", null, SettingLevel.ACCOUNT, {
|
||||
invite_avatars: MediaPreviewValue.Off,
|
||||
});
|
||||
const room = new Room("!room:example.com", client, client.getSafeUserId());
|
||||
room.name = "test room";
|
||||
room.updateMyMembership("invite");
|
||||
|
||||
@@ -8,20 +8,20 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React from "react";
|
||||
import { fireEvent, render, screen } from "jest-matrix-react";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixEvent, type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { HideActionButton } from "../../../../../src/components/views/messages/HideActionButton";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
|
||||
import type { Settings } from "../../../../../src/settings/Settings";
|
||||
import { MediaPreviewValue } from "../../../../../src/@types/media_preview";
|
||||
import { getMockClientWithEventEmitter, withClientContextRenderOptions } from "../../../../test-utils";
|
||||
import type { MockedObject } from "jest-mock";
|
||||
|
||||
function mockSetting(
|
||||
showImages: Settings["showImages"]["default"],
|
||||
showMediaEventIds: Settings["showMediaEventIds"]["default"],
|
||||
) {
|
||||
function mockSetting(mediaPreviews: MediaPreviewValue, showMediaEventIds: Settings["showMediaEventIds"]["default"]) {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName) => {
|
||||
if (settingName === "showImages") {
|
||||
return showImages;
|
||||
if (settingName === "mediaPreviewConfig") {
|
||||
return { media_previews: mediaPreviews, invite_avatars: MediaPreviewValue.Off };
|
||||
} else if (settingName === "showMediaEventIds") {
|
||||
return showMediaEventIds;
|
||||
}
|
||||
@@ -29,8 +29,10 @@ function mockSetting(
|
||||
});
|
||||
}
|
||||
|
||||
const EVENT_ID = "$foo:bar";
|
||||
|
||||
const event = new MatrixEvent({
|
||||
event_id: "$foo:bar",
|
||||
event_id: EVENT_ID,
|
||||
room_id: "!room:id",
|
||||
sender: "@user:id",
|
||||
type: "m.room.message",
|
||||
@@ -42,32 +44,38 @@ const event = new MatrixEvent({
|
||||
});
|
||||
|
||||
describe("HideActionButton", () => {
|
||||
let cli: MockedObject<MatrixClient>;
|
||||
beforeEach(() => {
|
||||
cli = getMockClientWithEventEmitter({
|
||||
getRoom: jest.fn(),
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
it("should show button when event is visible by showMediaEventIds setting", async () => {
|
||||
mockSetting(false, { "$foo:bar": true });
|
||||
render(<HideActionButton mxEvent={event} />);
|
||||
mockSetting(MediaPreviewValue.Off, { [EVENT_ID]: true });
|
||||
render(<HideActionButton mxEvent={event} />, withClientContextRenderOptions(cli));
|
||||
expect(screen.getByRole("button")).toBeVisible();
|
||||
});
|
||||
it("should show button when event is visible by showImages setting", async () => {
|
||||
mockSetting(true, {});
|
||||
render(<HideActionButton mxEvent={event} />);
|
||||
it("should show button when event is visible by mediaPreviewConfig setting", async () => {
|
||||
mockSetting(MediaPreviewValue.On, {});
|
||||
render(<HideActionButton mxEvent={event} />, withClientContextRenderOptions(cli));
|
||||
expect(screen.getByRole("button")).toBeVisible();
|
||||
});
|
||||
it("should hide button when event is hidden by showMediaEventIds setting", async () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue({ "$foo:bar": false });
|
||||
render(<HideActionButton mxEvent={event} />);
|
||||
mockSetting(MediaPreviewValue.Off, { [EVENT_ID]: false });
|
||||
render(<HideActionButton mxEvent={event} />, withClientContextRenderOptions(cli));
|
||||
expect(screen.queryByRole("button")).toBeNull();
|
||||
});
|
||||
it("should hide button when event is hidden by showImages setting", async () => {
|
||||
mockSetting(false, {});
|
||||
render(<HideActionButton mxEvent={event} />);
|
||||
mockSetting(MediaPreviewValue.Off, {});
|
||||
render(<HideActionButton mxEvent={event} />, withClientContextRenderOptions(cli));
|
||||
expect(screen.queryByRole("button")).toBeNull();
|
||||
});
|
||||
it("should store event as hidden when clicked", async () => {
|
||||
const spy = jest.spyOn(SettingsStore, "setValue");
|
||||
render(<HideActionButton mxEvent={event} />);
|
||||
render(<HideActionButton mxEvent={event} />, withClientContextRenderOptions(cli));
|
||||
fireEvent.click(screen.getByRole("button"));
|
||||
expect(spy).toHaveBeenCalledWith("showMediaEventIds", null, SettingLevel.DEVICE, { "$foo:bar": false });
|
||||
// Button should be hidden after the setting is set.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024, 2025 New Vector Ltd.
|
||||
Copyright 2022 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 React, { act } from "react";
|
||||
import React from "react";
|
||||
import { fireEvent, render, screen, waitFor, waitForElementToBeRemoved } from "jest-matrix-react";
|
||||
import { EventType, getHttpUriForMxc, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
@@ -24,10 +24,11 @@ import {
|
||||
mockClientMethodsDevice,
|
||||
mockClientMethodsServer,
|
||||
mockClientMethodsUser,
|
||||
withClientContextRenderOptions,
|
||||
} from "../../../../test-utils";
|
||||
import { MediaEventHelper } from "../../../../../src/utils/MediaEventHelper";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
|
||||
import { MediaPreviewValue } from "../../../../../src/@types/media_preview";
|
||||
|
||||
jest.mock("matrix-encrypt-attachment", () => ({
|
||||
decryptAttachment: jest.fn(),
|
||||
@@ -42,6 +43,7 @@ describe("<MImageBody/>", () => {
|
||||
...mockClientMethodsDevice(deviceId),
|
||||
...mockClientMethodsCrypto(),
|
||||
getRooms: jest.fn().mockReturnValue([]),
|
||||
getRoom: jest.fn(),
|
||||
getIgnoredUsers: jest.fn(),
|
||||
getVersions: jest.fn().mockResolvedValue({
|
||||
unstable_features: {
|
||||
@@ -85,6 +87,7 @@ describe("<MImageBody/>", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
SettingsStore.reset();
|
||||
mocked(encrypt.decryptAttachment).mockReset();
|
||||
});
|
||||
|
||||
@@ -97,6 +100,7 @@ describe("<MImageBody/>", () => {
|
||||
mxEvent={encryptedMediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)}
|
||||
/>,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
|
||||
// thumbnail with dimensions present
|
||||
@@ -112,6 +116,7 @@ describe("<MImageBody/>", () => {
|
||||
mxEvent={encryptedMediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)}
|
||||
/>,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(url);
|
||||
@@ -129,6 +134,7 @@ describe("<MImageBody/>", () => {
|
||||
mxEvent={encryptedMediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)}
|
||||
/>,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
|
||||
await screen.findByText("Error decrypting image");
|
||||
@@ -136,25 +142,12 @@ describe("<MImageBody/>", () => {
|
||||
|
||||
describe("with image previews/thumbnails disabled", () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
SettingsStore.setValue("showImages", null, SettingLevel.DEVICE, false);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => {
|
||||
SettingsStore.setValue(
|
||||
"showImages",
|
||||
null,
|
||||
SettingLevel.DEVICE,
|
||||
SettingsStore.getDefaultValue("showImages"),
|
||||
);
|
||||
SettingsStore.setValue(
|
||||
"showMediaEventIds",
|
||||
null,
|
||||
SettingLevel.DEVICE,
|
||||
SettingsStore.getDefaultValue("showMediaEventIds"),
|
||||
);
|
||||
const origFn = SettingsStore.getValue;
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting, ...args) => {
|
||||
if (setting === "mediaPreviewConfig") {
|
||||
return { invite_avatars: MediaPreviewValue.Off, media_previews: MediaPreviewValue.Off };
|
||||
}
|
||||
return origFn(setting, ...args);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -167,6 +160,7 @@ describe("<MImageBody/>", () => {
|
||||
mxEvent={encryptedMediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)}
|
||||
/>,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
|
||||
expect(screen.getByText("Show image")).toBeInTheDocument();
|
||||
@@ -183,6 +177,7 @@ describe("<MImageBody/>", () => {
|
||||
mxEvent={encryptedMediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)}
|
||||
/>,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
|
||||
expect(screen.getByText("Show image")).toBeInTheDocument();
|
||||
@@ -220,6 +215,7 @@ describe("<MImageBody/>", () => {
|
||||
|
||||
const { container } = render(
|
||||
<MImageBody {...props} mxEvent={event} mediaEventHelper={new MediaEventHelper(event)} />,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
|
||||
const img = container.querySelector(".mx_MImageBody_thumbnail")!;
|
||||
@@ -273,6 +269,7 @@ describe("<MImageBody/>", () => {
|
||||
|
||||
const { container } = render(
|
||||
<MImageBody {...props} mxEvent={event} mediaEventHelper={new MediaEventHelper(event)} />,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
|
||||
// Wait for spinners to go away
|
||||
@@ -298,6 +295,7 @@ describe("<MImageBody/>", () => {
|
||||
|
||||
const { container } = render(
|
||||
<MImageBody {...props} mxEvent={event} mediaEventHelper={new MediaEventHelper(event)} />,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
|
||||
const img = container.querySelector(".mx_MImageBody_thumbnail")!;
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
mockClientMethodsDevice,
|
||||
mockClientMethodsServer,
|
||||
mockClientMethodsUser,
|
||||
withClientContextRenderOptions,
|
||||
} from "../../../../test-utils";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import MStickerBody from "../../../../../src/components/views/messages/MStickerBody";
|
||||
@@ -31,6 +32,7 @@ describe("<MStickerBody/>", () => {
|
||||
...mockClientMethodsServer(),
|
||||
...mockClientMethodsDevice(deviceId),
|
||||
...mockClientMethodsCrypto(),
|
||||
getRoom: jest.fn(),
|
||||
getRooms: jest.fn().mockReturnValue([]),
|
||||
getIgnoredUsers: jest.fn(),
|
||||
getVersions: jest.fn().mockResolvedValue({
|
||||
@@ -76,7 +78,7 @@ describe("<MStickerBody/>", () => {
|
||||
it("should show a tooltip on hover", async () => {
|
||||
fetchMock.getOnce(url, { status: 200 });
|
||||
|
||||
render(<MStickerBody {...props} mxEvent={mediaEvent} />);
|
||||
render(<MStickerBody {...props} mxEvent={mediaEvent} />, withClientContextRenderOptions(cli));
|
||||
|
||||
expect(screen.queryByRole("tooltip")).toBeNull();
|
||||
await userEvent.hover(screen.getByRole("img"));
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024, 2025 New Vector Ltd.
|
||||
Copyright 2022 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 React, { act } from "react";
|
||||
import { EventType, getHttpUriForMxc, type IContent, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import React from "react";
|
||||
import { EventType, getHttpUriForMxc, type IContent, type MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { fireEvent, render, screen, type RenderResult } from "jest-matrix-react";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
import { type MockedObject } from "jest-mock";
|
||||
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import { type RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||
@@ -20,11 +21,12 @@ import {
|
||||
mockClientMethodsDevice,
|
||||
mockClientMethodsServer,
|
||||
mockClientMethodsUser,
|
||||
withClientContextRenderOptions,
|
||||
} from "../../../../test-utils";
|
||||
import MVideoBody from "../../../../../src/components/views/messages/MVideoBody";
|
||||
import type { IBodyProps } from "../../../../../src/components/views/messages/IBodyProps";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { MediaPreviewValue } from "../../../../../src/@types/media_preview";
|
||||
|
||||
// Needed so we don't throw an error about failing to decrypt.
|
||||
jest.mock("matrix-encrypt-attachment", () => ({
|
||||
@@ -36,13 +38,15 @@ describe("MVideoBody", () => {
|
||||
const deviceId = "DEADB33F";
|
||||
|
||||
const thumbUrl = "https://server/_matrix/media/v3/download/server/encrypted-poster";
|
||||
let cli: MockedObject<MatrixClient>;
|
||||
|
||||
beforeEach(() => {
|
||||
const cli = getMockClientWithEventEmitter({
|
||||
cli = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(userId),
|
||||
...mockClientMethodsServer(),
|
||||
...mockClientMethodsDevice(deviceId),
|
||||
...mockClientMethodsCrypto(),
|
||||
getRoom: jest.fn(),
|
||||
getRooms: jest.fn().mockReturnValue([]),
|
||||
getIgnoredUsers: jest.fn(),
|
||||
getVersions: jest.fn().mockResolvedValue({
|
||||
@@ -65,6 +69,7 @@ describe("MVideoBody", () => {
|
||||
room_id: "!room:server",
|
||||
sender: userId,
|
||||
type: EventType.RoomMessage,
|
||||
event_id: "$foo:bar",
|
||||
content: {
|
||||
body: "alt for a test video",
|
||||
info: {
|
||||
@@ -93,32 +98,25 @@ describe("MVideoBody", () => {
|
||||
fetchMock.getOnce(thumbUrl, { status: 200 });
|
||||
const { asFragment } = render(
|
||||
<MVideoBody mxEvent={encryptedMediaEvent} mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)} />,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("with video previews/thumbnails disabled", () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
SettingsStore.setValue("showImages", null, SettingLevel.DEVICE, false);
|
||||
const origFn = SettingsStore.getValue;
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting, ...args) => {
|
||||
if (setting === "mediaPreviewConfig") {
|
||||
return { invite_avatars: MediaPreviewValue.Off, media_previews: MediaPreviewValue.Off };
|
||||
}
|
||||
return origFn(setting, ...args);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => {
|
||||
SettingsStore.setValue(
|
||||
"showImages",
|
||||
null,
|
||||
SettingLevel.DEVICE,
|
||||
SettingsStore.getDefaultValue("showImages"),
|
||||
);
|
||||
SettingsStore.setValue(
|
||||
"showMediaEventIds",
|
||||
null,
|
||||
SettingLevel.DEVICE,
|
||||
SettingsStore.getDefaultValue("showMediaEventIds"),
|
||||
);
|
||||
});
|
||||
SettingsStore.reset();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("should not download video", async () => {
|
||||
@@ -129,6 +127,7 @@ describe("MVideoBody", () => {
|
||||
mxEvent={encryptedMediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)}
|
||||
/>,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
|
||||
expect(screen.getByText("Show video")).toBeInTheDocument();
|
||||
@@ -144,6 +143,7 @@ describe("MVideoBody", () => {
|
||||
mxEvent={encryptedMediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)}
|
||||
/>,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
|
||||
const placeholderButton = screen.getByRole("button", { name: "Show video" });
|
||||
@@ -191,6 +191,7 @@ function makeMVideoBody(w: number, h: number): RenderResult {
|
||||
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
mxcUrlToHttp: jest.fn(),
|
||||
getRoom: jest.fn(),
|
||||
});
|
||||
|
||||
return render(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024, 2025 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -44,9 +44,13 @@ describe("RoomPreviewCard", () => {
|
||||
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
||||
|
||||
enabledFeatures = [];
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName): any =>
|
||||
enabledFeatures.includes(settingName) ? true : undefined,
|
||||
);
|
||||
const origFn = SettingsStore.getValue;
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName): any => {
|
||||
if (enabledFeatures.includes(settingName)) {
|
||||
return true;
|
||||
}
|
||||
return origFn(settingName);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { render } from "jest-matrix-react";
|
||||
import React from "react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { type MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { MediaPreviewAccountSettings } from "../../../../../../../src/components/views/settings/tabs/user/MediaPreviewAccountSettings";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
mockClientMethodsServer,
|
||||
mockClientMethodsUser,
|
||||
} from "../../../../../../test-utils";
|
||||
import MatrixClientBackedController from "../../../../../../../src/settings/controllers/MatrixClientBackedController";
|
||||
import MatrixClientBackedSettingsHandler from "../../../../../../../src/settings/handlers/MatrixClientBackedSettingsHandler";
|
||||
import type { MockedObject } from "jest-mock";
|
||||
import {
|
||||
MEDIA_PREVIEW_ACCOUNT_DATA_TYPE,
|
||||
type MediaPreviewConfig,
|
||||
MediaPreviewValue,
|
||||
} from "../../../../../../../src/@types/media_preview";
|
||||
import MediaPreviewConfigController from "../../../../../../../src/settings/controllers/MediaPreviewConfigController";
|
||||
|
||||
describe("MediaPreviewAccountSettings", () => {
|
||||
let client: MockedObject<MatrixClient>;
|
||||
beforeEach(() => {
|
||||
client = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
...mockClientMethodsUser(),
|
||||
getRoom: jest.fn(),
|
||||
setAccountData: jest.fn(),
|
||||
isVersionSupported: jest.fn().mockResolvedValue(true),
|
||||
});
|
||||
MatrixClientBackedController.matrixClient = client;
|
||||
MatrixClientBackedSettingsHandler.matrixClient = client;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("should render", () => {
|
||||
const { getByLabelText } = render(<MediaPreviewAccountSettings />);
|
||||
// Defaults
|
||||
expect(getByLabelText("Hide avatars of room and inviter")).not.toBeChecked();
|
||||
expect(getByLabelText("Always hide")).not.toBeChecked();
|
||||
expect(getByLabelText("In private rooms")).not.toBeChecked();
|
||||
expect(getByLabelText("Always show")).toBeChecked();
|
||||
});
|
||||
|
||||
it("should be able to toggle hide avatar", async () => {
|
||||
const { getByLabelText } = render(<MediaPreviewAccountSettings />);
|
||||
// Defaults
|
||||
const element = getByLabelText("Hide avatars of room and inviter");
|
||||
await userEvent.click(element);
|
||||
expect(client.setAccountData).toHaveBeenCalledWith(MEDIA_PREVIEW_ACCOUNT_DATA_TYPE, {
|
||||
invite_avatars: MediaPreviewValue.Off,
|
||||
media_previews: MediaPreviewValue.On,
|
||||
});
|
||||
// Ensure we don't double set the account data.
|
||||
expect(client.setAccountData).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// Skip the default.
|
||||
it.each([
|
||||
["Always hide", MediaPreviewValue.Off],
|
||||
["In private rooms", MediaPreviewValue.Private],
|
||||
["Always show", MediaPreviewValue.On],
|
||||
])("should be able to toggle media preview option %s", async (key, value) => {
|
||||
if (value === MediaPreviewConfigController.default.media_previews) {
|
||||
// This is the default, so switch away first.
|
||||
client.getAccountData.mockImplementation((type) => {
|
||||
if (type === MEDIA_PREVIEW_ACCOUNT_DATA_TYPE) {
|
||||
return new MatrixEvent({
|
||||
content: {
|
||||
media_previews: MediaPreviewValue.Off,
|
||||
} satisfies Partial<MediaPreviewConfig>,
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
const { getByLabelText } = render(<MediaPreviewAccountSettings />);
|
||||
|
||||
const element = getByLabelText(key);
|
||||
await userEvent.click(element);
|
||||
expect(client.setAccountData).toHaveBeenCalledWith(MEDIA_PREVIEW_ACCOUNT_DATA_TYPE, {
|
||||
invite_avatars: MediaPreviewValue.On,
|
||||
media_previews: value,
|
||||
});
|
||||
// Ensure we don't double set the account data.
|
||||
expect(client.setAccountData).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -95,33 +95,6 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_QgU2PomxwKpa"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show avatars of rooms you have been invited to
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show avatars of rooms you have been invited to"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_QgU2PomxwKpa"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -144,7 +117,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_6hpi3YEetmBG"
|
||||
for="mx_SettingsFlag_QgU2PomxwKpa"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -162,7 +135,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Show all rooms in Home"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_6hpi3YEetmBG"
|
||||
id="mx_SettingsFlag_QgU2PomxwKpa"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -212,7 +185,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_4yVCeEefiPqp"
|
||||
for="mx_SettingsFlag_6hpi3YEetmBG"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -225,7 +198,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Use Ctrl + F to search timeline"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_4yVCeEefiPqp"
|
||||
id="mx_SettingsFlag_6hpi3YEetmBG"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -285,7 +258,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_MRMwbPDmfGtm"
|
||||
for="mx_SettingsFlag_4yVCeEefiPqp"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -298,6 +271,33 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Show timestamps in 12 hour format (e.g. 2:30pm)"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_4yVCeEefiPqp"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_MRMwbPDmfGtm"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Always show message timestamps
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-label="Always show message timestamps"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_MRMwbPDmfGtm"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
@@ -313,33 +313,6 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_GQvdMWe954DV"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Always show message timestamps
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-label="Always show message timestamps"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_GQvdMWe954DV"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_IAu5CsiHRD7n"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -352,7 +325,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Publish timezone on public profile"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_IAu5CsiHRD7n"
|
||||
id="mx_SettingsFlag_GQvdMWe954DV"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -392,7 +365,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_yrA2ohjWVJIP"
|
||||
for="mx_SettingsFlag_IAu5CsiHRD7n"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -405,7 +378,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Send read receipts"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_yrA2ohjWVJIP"
|
||||
id="mx_SettingsFlag_IAu5CsiHRD7n"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -419,7 +392,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_auy1OmnTidX4"
|
||||
for="mx_SettingsFlag_yrA2ohjWVJIP"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -432,7 +405,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Send typing notifications"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_auy1OmnTidX4"
|
||||
id="mx_SettingsFlag_yrA2ohjWVJIP"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -463,7 +436,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_ePDS0OpWwAHG"
|
||||
for="mx_SettingsFlag_auy1OmnTidX4"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -476,7 +449,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Automatically replace plain text Emoji"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_ePDS0OpWwAHG"
|
||||
id="mx_SettingsFlag_auy1OmnTidX4"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -490,7 +463,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_75JNTNkNU64r"
|
||||
for="mx_SettingsFlag_ePDS0OpWwAHG"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -514,6 +487,33 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Enable Markdown"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_ePDS0OpWwAHG"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_75JNTNkNU64r"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Enable Emoji suggestions while typing
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Enable Emoji suggestions while typing"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_75JNTNkNU64r"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
@@ -533,14 +533,14 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Enable Emoji suggestions while typing
|
||||
Use Ctrl + Enter to send a message
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-label="Enable Emoji suggestions while typing"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
aria-label="Use Ctrl + Enter to send a message"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_aTLcRsQRlYy7"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
@@ -560,13 +560,13 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Use Ctrl + Enter to send a message
|
||||
Surround selected text when typing special characters
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-label="Use Ctrl + Enter to send a message"
|
||||
aria-label="Surround selected text when typing special characters"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_5nfv5bOEPN1s"
|
||||
role="switch"
|
||||
@@ -587,14 +587,14 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Surround selected text when typing special characters
|
||||
Show stickers button
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Surround selected text when typing special characters"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
aria-label="Show stickers button"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_u1JYVtOyR5kb"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
@@ -610,33 +610,6 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_u3pEwuLn9Enn"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show stickers button
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show stickers button"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_u3pEwuLn9Enn"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_YuxfFEpOsztW"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -649,7 +622,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Insert a trailing colon after user mentions at the start of a message"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_YuxfFEpOsztW"
|
||||
id="mx_SettingsFlag_u3pEwuLn9Enn"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -680,7 +653,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_hQkBerF1ejc4"
|
||||
for="mx_SettingsFlag_YuxfFEpOsztW"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -693,6 +666,33 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Enable automatic language detection for syntax highlighting"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_YuxfFEpOsztW"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_hQkBerF1ejc4"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Expand code blocks by default
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-label="Expand code blocks by default"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_hQkBerF1ejc4"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
@@ -708,33 +708,6 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_GFes1UFzOK2n"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Expand code blocks by default
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-label="Expand code blocks by default"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_GFes1UFzOK2n"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_vfGFMldL2r2v"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -747,7 +720,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Show line numbers in code blocks"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_vfGFMldL2r2v"
|
||||
id="mx_SettingsFlag_GFes1UFzOK2n"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -778,7 +751,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_bsSwicmKUiOB"
|
||||
for="mx_SettingsFlag_vfGFMldL2r2v"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -791,6 +764,33 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Enable inline URL previews by default"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_vfGFMldL2r2v"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_bsSwicmKUiOB"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Autoplay GIFs
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-label="Autoplay GIFs"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_bsSwicmKUiOB"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
@@ -806,33 +806,6 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_dvqsxEaZtl3A"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Autoplay GIFs
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-label="Autoplay GIFs"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_dvqsxEaZtl3A"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_NIiWzqsApP1c"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -845,34 +818,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Autoplay videos"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_NIiWzqsApP1c"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_q1SIAPqLMVXh"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show previews/thumbnails for images
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show previews/thumbnails for images"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_q1SIAPqLMVXh"
|
||||
id="mx_SettingsFlag_dvqsxEaZtl3A"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -903,7 +849,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_dXFDGgBsKXay"
|
||||
for="mx_SettingsFlag_NIiWzqsApP1c"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -916,6 +862,60 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Show typing notifications"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_NIiWzqsApP1c"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_q1SIAPqLMVXh"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show a placeholder for removed messages
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show a placeholder for removed messages"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_q1SIAPqLMVXh"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_dXFDGgBsKXay"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show read receipts sent by other users
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show read receipts sent by other users"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_dXFDGgBsKXay"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
@@ -935,13 +935,13 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show a placeholder for removed messages
|
||||
Show join/leave messages (invites/removes/bans unaffected)
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show a placeholder for removed messages"
|
||||
aria-label="Show join/leave messages (invites/removes/bans unaffected)"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_7Az0xw4Bs4Tt"
|
||||
role="switch"
|
||||
@@ -962,13 +962,13 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show read receipts sent by other users
|
||||
Show display name changes
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show read receipts sent by other users"
|
||||
aria-label="Show display name changes"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_8jmzPIlPoBCv"
|
||||
role="switch"
|
||||
@@ -989,13 +989,13 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show join/leave messages (invites/removes/bans unaffected)
|
||||
Show chat effects (animations when receiving e.g. confetti)
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show join/leave messages (invites/removes/bans unaffected)"
|
||||
aria-label="Show chat effects (animations when receiving e.g. confetti)"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_enFRaTjdsFou"
|
||||
role="switch"
|
||||
@@ -1016,13 +1016,13 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show display name changes
|
||||
Show profile picture changes
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show display name changes"
|
||||
aria-label="Show profile picture changes"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_bfwnd5rz4XNX"
|
||||
role="switch"
|
||||
@@ -1043,13 +1043,13 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show chat effects (animations when receiving e.g. confetti)
|
||||
Show avatars in user, room and event mentions
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show chat effects (animations when receiving e.g. confetti)"
|
||||
aria-label="Show avatars in user, room and event mentions"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_gs5uWEzYzZrS"
|
||||
role="switch"
|
||||
@@ -1070,13 +1070,13 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show profile picture changes
|
||||
Enable big emoji in chat
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show profile picture changes"
|
||||
aria-label="Enable big emoji in chat"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_qWg7OgID1yRR"
|
||||
role="switch"
|
||||
@@ -1097,13 +1097,13 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show avatars in user, room and event mentions
|
||||
Jump to the bottom of the timeline when you send a message
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Show avatars in user, room and event mentions"
|
||||
aria-label="Jump to the bottom of the timeline when you send a message"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_pOPewl7rtMbV"
|
||||
role="switch"
|
||||
@@ -1124,14 +1124,14 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Enable big emoji in chat
|
||||
Show current profile picture and name for users in message history
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-label="Enable big emoji in chat"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
aria-label="Show current profile picture and name for users in message history"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_cmt3PZSyNp3v"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
@@ -1141,62 +1141,170 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_dJJz3lHUv9XX"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Jump to the bottom of the timeline when you send a message
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Jump to the bottom of the timeline when you send a message"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_dJJz3lHUv9XX"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_SBSSOZDRlzlA"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Show current profile picture and name for users in message history
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-label="Show current profile picture and name for users in message history"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_SBSSOZDRlzlA"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection mx_SettingsSubsection_newUi"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Moderation and safety
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
|
||||
>
|
||||
<form
|
||||
class="_root_19upo_16 mx_MediaPreviewAccountSetting_Form"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsFlag mx_MediaPreviewAccountSetting_ToggleSwitch"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_label"
|
||||
>
|
||||
<div
|
||||
id="mx_LabelledToggleSwitch_«r8»"
|
||||
>
|
||||
Hide avatars of room and inviter
|
||||
</div>
|
||||
</span>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="false"
|
||||
aria-labelledby="mx_LabelledToggleSwitch_«r8»"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_enabled"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Show media in timeline"
|
||||
class="_field_19upo_26"
|
||||
id="mx_media_previews"
|
||||
role="radiogroup"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="radix-«r9»"
|
||||
>
|
||||
Show media in timeline
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91 mx_MediaPreviewAccountSetting_RadioHelp"
|
||||
id="radix-«ra»"
|
||||
>
|
||||
A hidden media can always be shown by tapping on it
|
||||
</span>
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_MediaPreviewAccountSetting_Radio"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_1e0uz_10"
|
||||
>
|
||||
<input
|
||||
class="_input_1e0uz_18"
|
||||
id="mx_media_previews_off"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1e0uz_19"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_media_previews_off"
|
||||
>
|
||||
Always hide
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_MediaPreviewAccountSetting_Radio"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_1e0uz_10"
|
||||
>
|
||||
<input
|
||||
class="_input_1e0uz_18"
|
||||
id="mx_media_previews_private"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1e0uz_19"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_media_previews_private"
|
||||
>
|
||||
In private rooms
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_MediaPreviewAccountSetting_Radio"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_1e0uz_10"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_1e0uz_18"
|
||||
id="mx_media_previews_on"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1e0uz_19"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_media_previews_on"
|
||||
>
|
||||
Always show
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div
|
||||
class="_separator_7ckbw_8"
|
||||
data-kind="primary"
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
>
|
||||
@@ -1217,7 +1325,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_FLEpLCb0jpp6"
|
||||
for="mx_SettingsFlag_dJJz3lHUv9XX"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -1230,7 +1338,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Show NSFW content"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch"
|
||||
id="mx_SettingsFlag_FLEpLCb0jpp6"
|
||||
id="mx_SettingsFlag_dJJz3lHUv9XX"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -1261,7 +1369,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_NQFWldEwbV3q"
|
||||
for="mx_SettingsFlag_SBSSOZDRlzlA"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
@@ -1274,7 +1382,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
aria-disabled="true"
|
||||
aria-label="Prompt before sending invites to potentially invalid matrix IDs"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
id="mx_SettingsFlag_NQFWldEwbV3q"
|
||||
id="mx_SettingsFlag_SBSSOZDRlzlA"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
|
||||
@@ -6,48 +6,74 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { act, renderHook, waitFor } from "jest-matrix-react";
|
||||
import { JoinRule, type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { useMediaVisible } from "../../../src/hooks/useMediaVisible";
|
||||
import { createTestClient, mkStubRoom, withClientContextRenderOptions } from "../../test-utils";
|
||||
import { type MediaPreviewConfig, MediaPreviewValue } from "../../../src/@types/media_preview";
|
||||
import MediaPreviewConfigController from "../../../src/settings/controllers/MediaPreviewConfigController";
|
||||
import SettingsStore from "../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../src/settings/SettingLevel";
|
||||
|
||||
const EVENT_ID = "$fibble:example.org";
|
||||
|
||||
function render() {
|
||||
return renderHook(() => useMediaVisible(EVENT_ID));
|
||||
}
|
||||
const ROOM_ID = "!foobar:example.org";
|
||||
|
||||
describe("useMediaVisible", () => {
|
||||
afterEach(() => {
|
||||
// Using act here as otherwise React warns about state updates not being wrapped.
|
||||
act(() => {
|
||||
SettingsStore.setValue(
|
||||
"showMediaEventIds",
|
||||
null,
|
||||
SettingLevel.DEVICE,
|
||||
SettingsStore.getDefaultValue("showMediaEventIds"),
|
||||
);
|
||||
SettingsStore.setValue(
|
||||
"showImages",
|
||||
null,
|
||||
SettingLevel.DEVICE,
|
||||
SettingsStore.getDefaultValue("showImages"),
|
||||
);
|
||||
let matrixClient: MatrixClient;
|
||||
let room: Room;
|
||||
const mediaPreviewConfig: MediaPreviewConfig = MediaPreviewConfigController.default;
|
||||
|
||||
function render() {
|
||||
return renderHook(() => useMediaVisible(EVENT_ID, ROOM_ID), withClientContextRenderOptions(matrixClient));
|
||||
}
|
||||
beforeEach(() => {
|
||||
matrixClient = createTestClient();
|
||||
room = mkStubRoom(ROOM_ID, undefined, matrixClient);
|
||||
matrixClient.getRoom = jest.fn().mockReturnValue(room);
|
||||
const origFn = SettingsStore.getValue;
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting, ...args) => {
|
||||
if (setting === "mediaPreviewConfig") {
|
||||
return mediaPreviewConfig;
|
||||
}
|
||||
return origFn(setting, ...args);
|
||||
});
|
||||
});
|
||||
|
||||
it("should display images by default", async () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("should display media by default", async () => {
|
||||
const { result } = render();
|
||||
expect(result.current[0]).toEqual(true);
|
||||
});
|
||||
|
||||
it("should hide images when the default is changed", async () => {
|
||||
SettingsStore.setValue("showImages", null, SettingLevel.DEVICE, false);
|
||||
it("should hide media when media previews are Off", async () => {
|
||||
mediaPreviewConfig.media_previews = MediaPreviewValue.Off;
|
||||
const { result } = render();
|
||||
expect(result.current[0]).toEqual(false);
|
||||
});
|
||||
|
||||
it("should hide images after function is called", async () => {
|
||||
it.each([[JoinRule.Invite], [JoinRule.Knock], [JoinRule.Restricted]])(
|
||||
"should display media when media previews are Private and the join rule is %s",
|
||||
async (rule) => {
|
||||
mediaPreviewConfig.media_previews = MediaPreviewValue.Private;
|
||||
room.currentState.getJoinRule = jest.fn().mockReturnValue(rule);
|
||||
const { result } = render();
|
||||
expect(result.current[0]).toEqual(true);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([[JoinRule.Public], ["anything_else"]])(
|
||||
"should hide media when media previews are Private and the join rule is %s",
|
||||
async (rule) => {
|
||||
mediaPreviewConfig.media_previews = MediaPreviewValue.Private;
|
||||
room.currentState.getJoinRule = jest.fn().mockReturnValue(rule);
|
||||
const { result } = render();
|
||||
expect(result.current[0]).toEqual(false);
|
||||
},
|
||||
);
|
||||
|
||||
it("should hide media after function is called", async () => {
|
||||
const { result } = render();
|
||||
expect(result.current[0]).toEqual(true);
|
||||
act(() => {
|
||||
@@ -57,8 +83,8 @@ describe("useMediaVisible", () => {
|
||||
expect(result.current[0]).toEqual(false);
|
||||
});
|
||||
});
|
||||
it("should show images after function is called", async () => {
|
||||
SettingsStore.setValue("showImages", null, SettingLevel.DEVICE, false);
|
||||
it("should show media after function is called", async () => {
|
||||
mediaPreviewConfig.media_previews = MediaPreviewValue.Off;
|
||||
const { result } = render();
|
||||
expect(result.current[0]).toEqual(false);
|
||||
act(() => {
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright 2025 New Vector 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 { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import MatrixClientBackedController from "../../../../src/settings/controllers/MatrixClientBackedController";
|
||||
import MediaPreviewConfigController from "../../../../src/settings/controllers/MediaPreviewConfigController";
|
||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||
import { getMockClientWithEventEmitter, mockClientMethodsServer } from "../../../test-utils";
|
||||
import { MEDIA_PREVIEW_ACCOUNT_DATA_TYPE, MediaPreviewValue } from "../../../../src/@types/media_preview";
|
||||
|
||||
describe("MediaPreviewConfigController", () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const ROOM_ID = "!room:example.org";
|
||||
|
||||
it("gets the default settings when none are specified.", () => {
|
||||
const controller = new MediaPreviewConfigController();
|
||||
|
||||
MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest.fn().mockReturnValue(null),
|
||||
});
|
||||
|
||||
const value = controller.getValueOverride(SettingLevel.ACCOUNT, null);
|
||||
expect(value).toEqual(MediaPreviewConfigController.default);
|
||||
});
|
||||
|
||||
it("gets the default settings when the setting is empty.", () => {
|
||||
const controller = new MediaPreviewConfigController();
|
||||
|
||||
MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest
|
||||
.fn()
|
||||
.mockReturnValue(new MatrixEvent({ type: MEDIA_PREVIEW_ACCOUNT_DATA_TYPE, content: {} })),
|
||||
});
|
||||
|
||||
const value = controller.getValueOverride(SettingLevel.ACCOUNT, null);
|
||||
expect(value).toEqual(MediaPreviewConfigController.default);
|
||||
});
|
||||
|
||||
it.each([["media_previews"], ["invite_avatars"]])("gets the correct value for %s at the global level", (key) => {
|
||||
const controller = new MediaPreviewConfigController();
|
||||
|
||||
MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest.fn().mockReturnValue(
|
||||
new MatrixEvent({
|
||||
type: MEDIA_PREVIEW_ACCOUNT_DATA_TYPE,
|
||||
content: {
|
||||
[key]: MediaPreviewValue.Off,
|
||||
},
|
||||
}),
|
||||
),
|
||||
getRoom: jest.fn().mockReturnValue({
|
||||
getAccountData: jest.fn().mockReturnValue(null),
|
||||
}),
|
||||
});
|
||||
|
||||
const globalValue = controller.getValueOverride(SettingLevel.ACCOUNT, null);
|
||||
expect(globalValue[key]).toEqual(MediaPreviewValue.Off);
|
||||
|
||||
// Should follow the global value.
|
||||
const roomValue = controller.getValueOverride(SettingLevel.ROOM_ACCOUNT, ROOM_ID);
|
||||
expect(roomValue[key]).toEqual(MediaPreviewValue.Off);
|
||||
});
|
||||
|
||||
it.each([["media_previews"], ["invite_avatars"]])("gets the correct value for %s at the room level", (key) => {
|
||||
const controller = new MediaPreviewConfigController();
|
||||
|
||||
MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest.fn().mockReturnValue(null),
|
||||
getRoom: jest.fn().mockReturnValue({
|
||||
getAccountData: jest.fn().mockReturnValue(
|
||||
new MatrixEvent({
|
||||
type: MEDIA_PREVIEW_ACCOUNT_DATA_TYPE,
|
||||
content: {
|
||||
[key]: MediaPreviewValue.Off,
|
||||
},
|
||||
}),
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
const globalValue = controller.getValueOverride(SettingLevel.ACCOUNT, null);
|
||||
expect(globalValue[key]).toEqual(MediaPreviewValue.On);
|
||||
|
||||
// Should follow the global value.
|
||||
const roomValue = controller.getValueOverride(SettingLevel.ROOM_ACCOUNT, ROOM_ID);
|
||||
expect(roomValue[key]).toEqual(MediaPreviewValue.Off);
|
||||
});
|
||||
|
||||
it.each([["media_previews"], ["invite_avatars"]])(
|
||||
"uses defaults when an invalid value is set on the global level",
|
||||
(key) => {
|
||||
const controller = new MediaPreviewConfigController();
|
||||
|
||||
MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest.fn().mockReturnValue(
|
||||
new MatrixEvent({
|
||||
type: MEDIA_PREVIEW_ACCOUNT_DATA_TYPE,
|
||||
content: {
|
||||
[key]: "bibble",
|
||||
},
|
||||
}),
|
||||
),
|
||||
getRoom: jest.fn().mockReturnValue({
|
||||
getAccountData: jest.fn().mockReturnValue(null),
|
||||
}),
|
||||
});
|
||||
|
||||
const globalValue = controller.getValueOverride(SettingLevel.ACCOUNT, null);
|
||||
expect(globalValue[key]).toEqual(MediaPreviewValue.On);
|
||||
|
||||
// Should follow the global value.
|
||||
const roomValue = controller.getValueOverride(SettingLevel.ROOM_ACCOUNT, ROOM_ID);
|
||||
expect(roomValue[key]).toEqual(MediaPreviewValue.On);
|
||||
},
|
||||
);
|
||||
it.each([["media_previews"], ["invite_avatars"]])(
|
||||
"uses global value when an invalid value is set on the room level",
|
||||
(key) => {
|
||||
const controller = new MediaPreviewConfigController();
|
||||
|
||||
MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest.fn().mockReturnValue(
|
||||
new MatrixEvent({
|
||||
type: MEDIA_PREVIEW_ACCOUNT_DATA_TYPE,
|
||||
content: {
|
||||
[key]: MediaPreviewValue.Off,
|
||||
},
|
||||
}),
|
||||
),
|
||||
getRoom: jest.fn().mockReturnValue({
|
||||
getAccountData: jest.fn().mockReturnValue(
|
||||
new MatrixEvent({
|
||||
type: MEDIA_PREVIEW_ACCOUNT_DATA_TYPE,
|
||||
content: {
|
||||
[key]: "bibble",
|
||||
},
|
||||
}),
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
const globalValue = controller.getValueOverride(SettingLevel.ACCOUNT, null);
|
||||
expect(globalValue[key]).toEqual(MediaPreviewValue.Off);
|
||||
|
||||
// Should follow the global value.
|
||||
const roomValue = controller.getValueOverride(SettingLevel.ROOM_ACCOUNT, ROOM_ID);
|
||||
expect(roomValue[key]).toEqual(MediaPreviewValue.Off);
|
||||
},
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user