Add ability to hide images after clicking "show image" (#29467)
* start hide * Move useSettingsValueWithSetter to useSettings * Add new setting showMediaEventIds * Add a migration path * Add an action button to hide settings. * Tweaks to MImageBody to support new setting. * Fixup and add tests * add description for migration * docs fixes * add type * i18n * appese prettier * Add tests for HideActionButton * lint * lint * Use a hook for media visibility. * Drop setting hook usage. * Fixup MImageBody test * Fixup tests * Support functional components for message body rendering. * Add a comment * Move props into IProps
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2024 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 from "react";
|
||||
import { fireEvent, render, screen } from "jest-matrix-react";
|
||||
import { MatrixEvent } 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";
|
||||
|
||||
function mockSetting(
|
||||
showImages: Settings["showImages"]["default"],
|
||||
showMediaEventIds: Settings["showMediaEventIds"]["default"],
|
||||
) {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName) => {
|
||||
if (settingName === "showImages") {
|
||||
return showImages;
|
||||
} else if (settingName === "showMediaEventIds") {
|
||||
return showMediaEventIds;
|
||||
}
|
||||
throw Error(`Unexpected setting ${settingName}`);
|
||||
});
|
||||
}
|
||||
|
||||
const event = new MatrixEvent({
|
||||
event_id: "$foo:bar",
|
||||
room_id: "!room:id",
|
||||
sender: "@user:id",
|
||||
type: "m.room.message",
|
||||
content: {
|
||||
body: "test",
|
||||
msgtype: "m.image",
|
||||
url: "mxc://matrix.org/1234",
|
||||
},
|
||||
});
|
||||
|
||||
describe("HideActionButton", () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
it("should show button when event is visible by showMediaEventIds setting", async () => {
|
||||
mockSetting(false, { "$foo:bar": true });
|
||||
render(<HideActionButton mxEvent={event} />);
|
||||
expect(screen.getByRole("button")).toBeVisible();
|
||||
});
|
||||
it("should show button when event is visible by showImages setting", async () => {
|
||||
mockSetting(true, {});
|
||||
render(<HideActionButton mxEvent={event} />);
|
||||
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} />);
|
||||
expect(screen.queryByRole("button")).toBeNull();
|
||||
});
|
||||
it("should hide button when event is hidden by showImages setting", async () => {
|
||||
mockSetting(false, {});
|
||||
render(<HideActionButton mxEvent={event} />);
|
||||
expect(screen.queryByRole("button")).toBeNull();
|
||||
});
|
||||
it("should store event as hidden when clicked", async () => {
|
||||
const spy = jest.spyOn(SettingsStore, "setValue");
|
||||
render(<HideActionButton mxEvent={event} />);
|
||||
fireEvent.click(screen.getByRole("button"));
|
||||
expect(spy).toHaveBeenCalledWith("showMediaEventIds", null, SettingLevel.DEVICE, { "$foo:bar": false });
|
||||
// Button should be hidden after the setting is set.
|
||||
expect(screen.queryByRole("button")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React, { act } from "react";
|
||||
import { fireEvent, render, screen, waitForElementToBeRemoved } from "jest-matrix-react";
|
||||
import { EventType, getHttpUriForMxc, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
} from "../../../../test-utils";
|
||||
import { MediaEventHelper } from "../../../../../src/utils/MediaEventHelper";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
|
||||
|
||||
jest.mock("matrix-encrypt-attachment", () => ({
|
||||
decryptAttachment: jest.fn(),
|
||||
@@ -57,6 +58,7 @@ describe("<MImageBody/>", () => {
|
||||
},
|
||||
);
|
||||
const encryptedMediaEvent = new MatrixEvent({
|
||||
event_id: "$foo:bar",
|
||||
room_id: "!room:server",
|
||||
sender: userId,
|
||||
type: EventType.RoomMessage,
|
||||
@@ -131,7 +133,26 @@ describe("<MImageBody/>", () => {
|
||||
|
||||
describe("with image previews/thumbnails disabled", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
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"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should not download image", async () => {
|
||||
@@ -163,7 +184,6 @@ describe("<MImageBody/>", () => {
|
||||
|
||||
fireEvent.click(screen.getByRole("button"));
|
||||
|
||||
// image fetched after clicking show image
|
||||
expect(fetchMock).toHaveFetched(url);
|
||||
|
||||
// spinner while downloading image
|
||||
|
||||
71
test/unit-tests/hooks/useMediaVisible-test.tsx
Normal file
71
test/unit-tests/hooks/useMediaVisible-test.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
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 { act, renderHook, waitFor } from "jest-matrix-react";
|
||||
|
||||
import { useMediaVisible } from "../../../src/hooks/useMediaVisible";
|
||||
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));
|
||||
}
|
||||
|
||||
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"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should display images 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);
|
||||
const { result } = render();
|
||||
expect(result.current[0]).toEqual(false);
|
||||
});
|
||||
|
||||
it("should hide images after function is called", async () => {
|
||||
const { result } = render();
|
||||
expect(result.current[0]).toEqual(true);
|
||||
act(() => {
|
||||
result.current[1](false);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current[0]).toEqual(false);
|
||||
});
|
||||
});
|
||||
it("should show images after function is called", async () => {
|
||||
SettingsStore.setValue("showImages", null, SettingLevel.DEVICE, false);
|
||||
const { result } = render();
|
||||
expect(result.current[0]).toEqual(false);
|
||||
act(() => {
|
||||
result.current[1](true);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current[0]).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user