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:
Will Hunt
2025-04-22 10:37:47 +01:00
committed by GitHub
parent da6ac36f11
commit 75d9898dff
44 changed files with 1427 additions and 422 deletions
@@ -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.