Update report room dialog to match designs (#29669)

* Rework for designs

* Update report room position

* lint

* Improve test coverage
This commit is contained in:
Will Hunt 2025-04-03 14:25:19 +01:00 committed by GitHub
parent 1f9db9fa1a
commit cb657d6848
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 280 additions and 186 deletions

View File

@ -136,13 +136,30 @@ test.describe("RightPanel", () => {
});
test.describe("room reporting", () => {
test.skip(isDendrite, "Dendrite does not implement room reporting");
test("should handle reporting a room", async ({ page, app }) => {
test("should handle reporting a room", { tag: "@screenshot" }, async ({ page, app }) => {
await viewRoomSummaryByName(page, app, ROOM_NAME);
await page.getByRole("menuitem", { name: "Report room" }).click();
const dialog = await page.getByRole("dialog", { name: "Report Room" });
await dialog.getByLabel("reason").fill("This room should be reported");
await expect(dialog).toMatchScreenshot("room-report-dialog.png");
await dialog.getByRole("button", { name: "Send report" }).click();
await expect(page.getByText("Your report was sent.")).toBeVisible();
// Dialog should have gone
await expect(page.locator(".mx_Dialog")).toHaveCount(0);
});
test("should handle reporting a room and leaving the room", async ({ page, app }) => {
await viewRoomSummaryByName(page, app, ROOM_NAME);
await page.getByRole("menuitem", { name: "Report room" }).click();
const dialog = await page.getByRole("dialog", { name: "Report room" });
await dialog.getByRole("switch", { name: "Leave room" }).click();
await dialog.getByLabel("reason").fill("This room should be reported");
await dialog.getByRole("button", { name: "Send report" }).click();
await page.getByRole("dialog", { name: "Leave room" }).getByRole("button", { name: "Leave" }).click();
// Dialog should have gone
await expect(page.locator(".mx_Dialog")).toHaveCount(0);
});
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -13,4 +13,9 @@ Please see LICENSE files in the repository root for full details.
border-radius: 0.5rem;
padding: var(--cpd-space-3x) var(--cpd-space-4x);
}
label {
color: var(--cpd-color-text-primary);
font-weight: var(--cpd-font-weight-semibold);
}
}

View File

@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX, type ChangeEventHandler, useCallback, useState } from "react";
import { Root, Field, Label, InlineSpinner, ErrorMessage } from "@vector-im/compound-web";
import { Root, Field, Label, InlineSpinner, ErrorMessage, HelpMessage } from "@vector-im/compound-web";
import { _t } from "../../../languageHandler";
import SdkConfig from "../../../SdkConfig";
@ -14,10 +14,11 @@ import Markdown from "../../../Markdown";
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
interface IProps {
roomId: string;
onFinished(complete: boolean): void;
onFinished(leave: boolean): void;
}
/*
@ -27,27 +28,26 @@ interface IProps {
export const ReportRoomDialog: React.FC<IProps> = function ({ roomId, onFinished }) {
const [error, setErr] = useState<string>();
const [busy, setBusy] = useState(false);
const [sent, setSent] = useState(false);
const [reason, setReason] = useState("");
const [leaveRoom, setLeaveRoom] = useState(false);
const client = MatrixClientPeg.safeGet();
const onReasonChange = useCallback<ChangeEventHandler<HTMLTextAreaElement>>((e) => setReason(e.target.value), []);
const onCancel = useCallback(() => onFinished(sent), [sent, onFinished]);
const onCancel = useCallback(() => onFinished(false), [onFinished]);
const onSubmit = useCallback(async () => {
setBusy(true);
try {
await client.reportRoom(roomId, reason);
setSent(true);
onFinished(leaveRoom);
} catch (ex) {
setBusy(false);
if (ex instanceof Error) {
setErr(ex.message);
} else {
setErr("Unknown error");
}
} finally {
setBusy(false);
}
}, [roomId, reason, client]);
}, [roomId, reason, client, leaveRoom, onFinished]);
const adminMessageMD = SdkConfig.getObject("report_event")?.get("admin_message_md", "adminMessageMD");
let adminMessage: JSX.Element | undefined;
@ -59,37 +59,39 @@ export const ReportRoomDialog: React.FC<IProps> = function ({ roomId, onFinished
return (
<BaseDialog
className="mx_ReportRoomDialog"
onFinished={() => onFinished(sent)}
title={_t("report_room|title")}
onFinished={onCancel}
title={_t("action|report_room")}
contentId="mx_ReportEventDialog"
>
{sent && <p>{_t("report_room|sent")}</p>}
{!sent && (
<Root id="mx_ReportEventDialog" onSubmit={onSubmit}>
<p>{_t("report_room|description")}</p>
{adminMessage}
<Field name="reason">
<Label htmlFor="mx_ReportRoomDialog_reason">{_t("room_settings|permissions|ban_reason")}</Label>
<textarea
id="mx_ReportRoomDialog_reason"
placeholder={_t("report_room|reason_placeholder")}
rows={5}
onChange={onReasonChange}
value={reason}
disabled={busy}
/>
{error ? <ErrorMessage>{error}</ErrorMessage> : null}
</Field>
{busy ? <InlineSpinner /> : null}
<DialogButtons
primaryButton={_t("action|send_report")}
onPrimaryButtonClick={onSubmit}
focus={true}
onCancel={onCancel}
<Root id="mx_ReportEventDialog" onSubmit={onSubmit}>
<Field name="reason">
<Label htmlFor="mx_ReportRoomDialog_reason">{_t("report_room|reason_label")}</Label>
<textarea
id="mx_ReportRoomDialog_reason"
rows={5}
onChange={onReasonChange}
value={reason}
disabled={busy}
/>
</Root>
)}
{error ? <ErrorMessage>{error}</ErrorMessage> : null}
<HelpMessage>{_t("report_room|description")}</HelpMessage>
</Field>
{adminMessage}
{busy ? <InlineSpinner /> : null}
<LabelledToggleSwitch
label={_t("room_list|more_options|leave_room")}
value={leaveRoom}
onChange={setLeaveRoom}
/>
<DialogButtons
primaryButton={_t("action|send_report")}
onPrimaryButtonClick={onSubmit}
focus={true}
onCancel={onCancel}
primaryButtonClass="danger"
primaryDisabled={busy || !reason}
/>
</Root>
</BaseDialog>
);
};

View File

@ -233,10 +233,16 @@ const RoomSummaryCard: React.FC<IProps> = ({
room_id: room.roomId,
});
};
const onReportRoomClick = (): void => {
Modal.createDialog(ReportRoomDialog, {
const onReportRoomClick = async (): Promise<void> => {
const [leave] = await Modal.createDialog(ReportRoomDialog, {
roomId: room.roomId,
});
}).finished;
if (leave) {
defaultDispatcher.dispatch({
action: "leave_room",
room_id: room.roomId,
});
}
};
const isRoomEncrypted = useIsEncrypted(cli, room);
@ -447,6 +453,12 @@ const RoomSummaryCard: React.FC<IProps> = ({
<Separator />
<div className="mx_RoomSummaryCard_bottomOptions">
<MenuItem
Icon={ErrorIcon}
kind="critical"
label={_t("action|report_room")}
onSelect={onReportRoomClick}
/>
<MenuItem
className="mx_RoomSummaryCard_leave"
Icon={LeaveIcon}
@ -454,12 +466,6 @@ const RoomSummaryCard: React.FC<IProps> = ({
label={_t("action|leave_room")}
onSelect={onLeaveRoomClick}
/>
<MenuItem
Icon={ErrorIcon}
kind="critical"
label={_t("action|report_room")}
onSelect={onReportRoomClick}
/>
</div>
</div>
</BaseCard>

View File

@ -1827,10 +1827,8 @@
"toxic_behaviour": "Toxic Behaviour"
},
"report_room": {
"description": "Report this room to your homeserver admin. This will send the room's unique ID, but if messages are encrypted, the administrator won't be able to read them or view shared files.",
"reason_placeholder": " Reason for reporting...",
"sent": "Your report was sent.",
"title": "Report Room"
"description": "Report this room to your account provider. If the messages are encrypted, your admin will not be able to read them.",
"reason_label": "Describe the reason"
},
"restore_key_backup_dialog": {
"count_of_decryption_failures": "Failed to decrypt %(failedCount)s sessions!",

View File

@ -14,6 +14,7 @@ import SdkConfig from "../../../../../src/SdkConfig";
import { stubClient } from "../../../../test-utils";
const ROOM_ID = "!foo:bar";
const REASON = "This room is bad!";
describe("ReportRoomDialog", () => {
const onFinished: jest.Mock<any, any> = jest.fn();
@ -48,19 +49,24 @@ This doesn't actually go **anywhere**.`,
expect(container).toMatchSnapshot();
});
it("can submit a report", async () => {
const REASON = "This room is bad!";
const { getByLabelText, getByText, getByRole } = render(
<ReportRoomDialog roomId={ROOM_ID} onFinished={onFinished} />,
);
it("can submit a report without leaving the room", async () => {
const { getByLabelText, getByRole } = render(<ReportRoomDialog roomId={ROOM_ID} onFinished={onFinished} />);
await userEvent.type(getByLabelText("Reason"), REASON);
await userEvent.type(getByLabelText("Describe the reason"), REASON);
await userEvent.click(getByRole("button", { name: "Send report" }));
expect(reportRoom).toHaveBeenCalledWith(ROOM_ID, REASON);
expect(getByText("Your report was sent.")).toBeInTheDocument();
expect(onFinished).toHaveBeenCalledWith(false);
});
await userEvent.click(getByRole("button", { name: "Close dialog" }));
it("can submit a report and leave the room", async () => {
const { getByLabelText, getByRole } = render(<ReportRoomDialog roomId={ROOM_ID} onFinished={onFinished} />);
await userEvent.type(getByLabelText("Describe the reason"), REASON);
await userEvent.click(getByRole("switch", { name: "Leave room" }));
await userEvent.click(getByRole("button", { name: "Send report" }));
expect(reportRoom).toHaveBeenCalledWith(ROOM_ID, REASON);
expect(onFinished).toHaveBeenCalledWith(true);
});
});

View File

@ -21,16 +21,33 @@ exports[`ReportRoomDialog displays admin message 1`] = `
class="mx_Heading_h3 mx_Dialog_title"
id="mx_BaseDialog_title"
>
Report Room
Report room
</h1>
</div>
<form
class="_root_19upo_16"
id="mx_ReportEventDialog"
>
<p>
Report this room to your homeserver admin. This will send the room's unique ID, but if messages are encrypted, the administrator won't be able to read them or view shared files.
</p>
<div
class="_field_19upo_26"
>
<label
class="_label_19upo_59"
for="mx_ReportRoomDialog_reason"
>
Describe the reason
</label>
<textarea
id="mx_ReportRoomDialog_reason"
rows="5"
/>
<span
class="_message_19upo_85 _help-message_19upo_91"
id="radix-:r7:"
>
Report this room to your account provider. If the messages are encrypted, your admin will not be able to read them.
</span>
</div>
<p>
<h1>
You should know
@ -48,19 +65,29 @@ exports[`ReportRoomDialog displays admin message 1`] = `
</p>
<div
class="_field_19upo_26"
class="mx_SettingsFlag"
>
<label
class="_label_19upo_59"
for="mx_ReportRoomDialog_reason"
<span
class="mx_SettingsFlag_label"
>
Reason
</label>
<textarea
id="mx_ReportRoomDialog_reason"
placeholder=" Reason for reporting..."
rows="5"
/>
<div
id="mx_LabelledToggleSwitch_undefined"
>
Leave room
</div>
</span>
<div
aria-checked="false"
aria-disabled="false"
aria-labelledby="mx_LabelledToggleSwitch_undefined"
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_enabled"
role="switch"
tabindex="0"
>
<div
class="mx_ToggleSwitch_ball"
/>
</div>
</div>
<div
class="mx_Dialog_buttons"
@ -75,8 +102,9 @@ exports[`ReportRoomDialog displays admin message 1`] = `
Cancel
</button>
<button
class="mx_Dialog_primary"
class="mx_Dialog_primary danger"
data-testid="dialog-primary-button"
disabled=""
type="button"
>
Send report

View File

@ -23,7 +23,7 @@ import * as settingsHooks from "../../../../../src/hooks/useSettings";
import Modal from "../../../../../src/Modal";
import RightPanelStore from "../../../../../src/stores/right-panel/RightPanelStore";
import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases";
import { flushPromises, stubClient } from "../../../../test-utils";
import { flushPromises, stubClient, untilDispatch } from "../../../../test-utils";
import { PollHistoryDialog } from "../../../../../src/components/views/dialogs/PollHistoryDialog";
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
import { _t } from "../../../../../src/languageHandler";
@ -32,6 +32,7 @@ import { DefaultTagID } from "../../../../../src/stores/room-list/models";
import { Action } from "../../../../../src/dispatcher/actions";
import { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
import { ReportRoomDialog } from "../../../../../src/components/views/dialogs/ReportRoomDialog.tsx";
jest.mock("../../../../../src/utils/room/tagRoom");
@ -277,6 +278,37 @@ describe("<RoomSummaryCard />", () => {
);
});
it("dispatches leave room on button click", async () => {
jest.spyOn(Modal, "createDialog").mockReturnValueOnce({
finished: Promise.resolve([true]),
close: () => {},
});
const { getByText } = getComponent();
fireEvent.click(getByText(_t("room_list|more_options|leave_room")));
await untilDispatch("leave_room", defaultDispatcher);
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
action: "leave_room",
room_id: room.roomId,
});
});
it("opens report dialog on button click", async () => {
jest.spyOn(Modal, "createDialog").mockReturnValueOnce({
finished: Promise.resolve([true]),
close: () => {},
});
const { getByText } = getComponent();
fireEvent.click(getByText(_t("action|report_room")));
expect(Modal.createDialog).toHaveBeenCalledWith(ReportRoomDialog, { roomId: room.roomId });
await untilDispatch("leave_room", defaultDispatcher);
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
action: "leave_room",
room_id: room.roomId,
});
});
describe("pinning", () => {
it("renders pins options", () => {
const { getByText } = getComponent();

View File

@ -612,6 +612,43 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
<div
class="mx_RoomSummaryCard_bottomOptions"
>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-kind="critical"
role="menuitem"
>
<svg
aria-hidden="true"
class="_icon_dyt4i_50"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12t2.325 5.675T12 20"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_dyt4i_34"
>
Report room
</span>
<svg
aria-hidden="true"
class="_nav-hint_dyt4i_59"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
<button
class="mx_RoomSummaryCard_leave _item_dyt4i_8 _interactive_dyt4i_26"
data-kind="critical"
@ -652,43 +689,6 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
/>
</svg>
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-kind="critical"
role="menuitem"
>
<svg
aria-hidden="true"
class="_icon_dyt4i_50"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12t2.325 5.675T12 20"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_dyt4i_34"
>
Report room
</span>
<svg
aria-hidden="true"
class="_nav-hint_dyt4i_59"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
</div>
</div>
</div>
@ -1271,6 +1271,43 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
<div
class="mx_RoomSummaryCard_bottomOptions"
>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-kind="critical"
role="menuitem"
>
<svg
aria-hidden="true"
class="_icon_dyt4i_50"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12t2.325 5.675T12 20"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_dyt4i_34"
>
Report room
</span>
<svg
aria-hidden="true"
class="_nav-hint_dyt4i_59"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
<button
class="mx_RoomSummaryCard_leave _item_dyt4i_8 _interactive_dyt4i_26"
data-kind="critical"
@ -1311,43 +1348,6 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
/>
</svg>
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-kind="critical"
role="menuitem"
>
<svg
aria-hidden="true"
class="_icon_dyt4i_50"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12t2.325 5.675T12 20"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_dyt4i_34"
>
Report room
</span>
<svg
aria-hidden="true"
class="_nav-hint_dyt4i_59"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
</div>
</div>
</div>
@ -1967,6 +1967,43 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
<div
class="mx_RoomSummaryCard_bottomOptions"
>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-kind="critical"
role="menuitem"
>
<svg
aria-hidden="true"
class="_icon_dyt4i_50"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12t2.325 5.675T12 20"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_dyt4i_34"
>
Report room
</span>
<svg
aria-hidden="true"
class="_nav-hint_dyt4i_59"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
<button
class="mx_RoomSummaryCard_leave _item_dyt4i_8 _interactive_dyt4i_26"
data-kind="critical"
@ -2007,43 +2044,6 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
/>
</svg>
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-kind="critical"
role="menuitem"
>
<svg
aria-hidden="true"
class="_icon_dyt4i_50"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12t2.325 5.675T12 20"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_dyt4i_34"
>
Report room
</span>
<svg
aria-hidden="true"
class="_nav-hint_dyt4i_59"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
</div>
</div>
</div>