Add option to hide pinned message banner in room view (#31296)

* feat: add `hidePinnedMessageBanner` to room view

* test: add test for `hidePinnedMessageBanner`
This commit is contained in:
Florian Duros 2025-11-21 14:45:22 +01:00 committed by GitHub
parent 81c375007e
commit a79f6e7aa5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 421 additions and 19 deletions

View File

@ -180,6 +180,11 @@ interface IRoomProps extends RoomViewProps {
* If true, hide the right panel
*/
hideRightPanel?: boolean;
/**
* If true, hide the pinned messages banner
*/
hidePinnedMessageBanner?: boolean;
}
export { MainSplitContentType };
@ -2464,7 +2469,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
</AuxPanel>
);
const pinnedMessageBanner = (
const pinnedMessageBanner = !this.props.hidePinnedMessageBanner && (
<PinnedMessageBanner room={this.state.room} permalinkCreator={this.permalinkCreator} />
);

View File

@ -77,6 +77,7 @@ import { CallStore } from "../../../../src/stores/CallStore.ts";
import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../../src/MediaDeviceHandler.ts";
import Modal, { type ComponentProps } from "../../../../src/Modal.tsx";
import ErrorDialog from "../../../../src/components/views/dialogs/ErrorDialog.tsx";
import * as pinnedEventHooks from "../../../../src/hooks/usePinnedEvents";
// Used by group calls
jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({
@ -124,6 +125,11 @@ describe("RoomView", () => {
afterEach(() => {
unmockPlatformPeg();
jest.clearAllMocks();
// Can't jest.restoreAllMocks() because some tests will break
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockRestore();
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockRestore();
cleanup();
});
@ -298,6 +304,35 @@ describe("RoomView", () => {
expect(asFragment()).toMatchSnapshot();
});
it("should hide the pinned message banner when hidePinnedMessageBanner=true", async () => {
// Join the room
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
const pinnedEvent = new MatrixEvent({
type: EventType.RoomMessage,
sender: "@alice:example.org",
content: {
body: "First pinned message",
msgtype: "m.text",
},
room_id: room.roomId,
origin_server_ts: 0,
event_id: "$eventId",
});
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([pinnedEvent.getId()!]);
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([pinnedEvent]);
const { asFragment, rerender } = await mountRoomView(undefined);
// Check that the pinned message banner is rendered
await expect(screen.findByTestId("pinned-message-banner")).resolves.toBeTruthy();
// Now rerender with hidePinnedMessagesBanner=true
rerender(<RoomView threepidInvite={undefined} forceTimeline={false} hidePinnedMessageBanner={true} />);
// Check that the pinned message banner is not rendered
await expect(screen.findByTestId("pinned-message-banner")).rejects.toThrow();
expect(asFragment()).toMatchSnapshot();
});
describe("invites", () => {
beforeEach(() => {
const member = new RoomMember(room.roomId, cli.getSafeUserId());

View File

@ -363,7 +363,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
>
<svg
aria-label="Messages in this room are not end-to-end encrypted"
aria-labelledby="_r_gu_"
aria-labelledby="_r_jb_"
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
color="var(--cpd-color-icon-info-primary)"
fill="currentColor"
@ -1215,6 +1215,368 @@ exports[`RoomView should hide the header when hideHeader=true 1`] = `
</DocumentFragment>
`;
exports[`RoomView should hide the pinned message banner when hidePinnedMessageBanner=true 1`] = `
<DocumentFragment>
<div
class="mx_RoomView"
>
<canvas
aria-hidden="true"
height="768"
style="display: block; z-index: 999999; pointer-events: none; position: fixed; top: 0px; right: 0px;"
width="0"
/>
<div
class="mx_MainSplit"
>
<div
class="mx_RoomView_body mx_MainSplit_timeline"
data-layout="group"
>
<header
class="_flex_4dswl_9 mx_RoomHeader light-panel"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
>
<button
aria-label="Open room settings"
aria-live="off"
class="_avatar_1qbcf_8 mx_BaseAvatar _avatar-imageless_1qbcf_52"
data-color="2"
data-testid="avatar-img"
data-type="round"
role="button"
style="--cpd-avatar-size: 40px;"
tabindex="-1"
>
!
</button>
<button
aria-label="Room info"
class="mx_RoomHeader_infoWrapper"
tabindex="0"
>
<div
class="mx_RoomHeader_info _box-flex_1odfs_9"
style="--mx-box-flex: 1;"
>
<div
aria-level="1"
class="_typography_6v6n8_153 _font-body-lg-semibold_6v6n8_74 mx_RoomHeader_heading"
dir="auto"
role="heading"
>
<span
class="mx_RoomHeader_truncated mx_lineClamp"
>
!roomviewshouldhidethepinnedmessagebannerwhenhidepinnedmessagebannertrue:example.org
</span>
</div>
</div>
</button>
<button
aria-disabled="false"
aria-label="Video call"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_zr2a0_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="_r_5b_"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4"
/>
</svg>
</div>
</button>
<button
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="_r_5g_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_zr2a0_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m20.958 16.374.039 3.527q0 .427-.33.756-.33.33-.756.33a16 16 0 0 1-6.57-1.105 16.2 16.2 0 0 1-5.563-3.663 16.1 16.1 0 0 1-3.653-5.573 16.3 16.3 0 0 1-1.115-6.56q0-.427.33-.757T4.095 3l3.528.039a1.07 1.07 0 0 1 1.085.93l.543 3.954q.039.271-.039.504a1.1 1.1 0 0 1-.271.426l-1.64 1.64q.505 1.008 1.154 1.909c.433.6 1.444 1.696 1.444 1.696s1.095 1.01 1.696 1.444q.9.65 1.909 1.153l1.64-1.64q.193-.193.426-.27t.504-.04l3.954.543q.406.059.668.359t.262.727"
/>
</svg>
</div>
</button>
<button
aria-label="Threads"
aria-labelledby="_r_5l_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_zr2a0_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
class=""
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 3h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6l-2.293 2.293c-.63.63-1.707.184-1.707-.707V5a2 2 0 0 1 2-2m3 7h10q.424 0 .712-.287A.97.97 0 0 0 18 9a.97.97 0 0 0-.288-.713A.97.97 0 0 0 17 8H7a.97.97 0 0 0-.713.287A.97.97 0 0 0 6 9q0 .424.287.713Q6.576 10 7 10m0 4h6q.424 0 .713-.287A.97.97 0 0 0 14 13a.97.97 0 0 0-.287-.713A.97.97 0 0 0 13 12H7a.97.97 0 0 0-.713.287A.97.97 0 0 0 6 13q0 .424.287.713Q6.576 14 7 14"
/>
</svg>
</div>
</button>
<button
aria-label="Room info"
aria-labelledby="_r_5q_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_zr2a0_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
class=""
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16v-4a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 11a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 12v4q0 .424.287.712.288.288.713.288m0-8q.424 0 .713-.287A.97.97 0 0 0 13 8a.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 8q0 .424.287.713Q11.576 9 12 9m0 13a9.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 22"
/>
</svg>
</div>
</button>
<div
class="_typography_6v6n8_153 _font-body-sm-medium_6v6n8_41"
>
<div
aria-label="0 members"
aria-labelledby="_r_5v_"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
>
<div
class="_stacked-avatars_1qbcf_102"
/>
0
</div>
</div>
</header>
<div
class="mx_AutoHideScrollbar mx_AuxPanel"
role="region"
tabindex="-1"
>
<div />
</div>
<main
class="mx_RoomView_timeline mx_RoomView_timeline_rr_enabled"
>
<div
class="mx_AutoHideScrollbar mx_ScrollPanel mx_RoomView_messagePanel"
tabindex="-1"
>
<div
class="mx_RoomView_messageListWrapper"
>
<ol
aria-live="polite"
class="mx_RoomView_MessageList"
style="height: 400px;"
/>
</div>
</div>
</main>
<div
aria-label="Room status bar"
class="mx_RoomView_statusArea"
role="region"
>
<div
class="mx_RoomView_statusAreaBox"
>
<div
class="mx_RoomView_statusAreaBox_line"
/>
</div>
</div>
<div
aria-label="Message composer"
class="mx_MessageComposer mx_MessageComposer_e2eStatus"
role="region"
>
<div
class="mx_MessageComposer_wrapper"
>
<div
class="mx_MessageComposer_row"
>
<div
class="mx_MessageComposer_e2eIconWrapper"
>
<svg
aria-label="Messages in this room are not end-to-end encrypted"
aria-labelledby="_r_6e_"
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
color="var(--cpd-color-icon-info-primary)"
fill="currentColor"
height="12"
viewBox="0 0 24 24"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 22q-.825 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412a2 2 0 0 1 .702-.463L1.333 4.167a1 1 0 0 1 1.414-1.414L7 7.006v-.012l13 13v.012l1.247 1.247a1 1 0 1 1-1.414 1.414l-.896-.896A1.94 1.94 0 0 1 18 22zm14-4.834V10q0-.825-.587-1.412A1.93 1.93 0 0 0 18 8h-1V6q0-2.075-1.463-3.537Q14.075 1 12 1T8.463 2.463a4.9 4.9 0 0 0-1.22 1.946L9 6.166V6q0-1.25.875-2.125A2.9 2.9 0 0 1 12 3q1.25 0 2.125.875T15 6v2h-4.166z"
/>
</svg>
</div>
<div
class="mx_SendMessageComposer"
>
<div
class="mx_BasicMessageComposer"
>
<div
aria-label="Formatting"
class="mx_MessageComposerFormatBar"
role="toolbar"
>
<button
aria-label="Bold"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconBold"
role="button"
tabindex="0"
type="button"
/>
<button
aria-label="Italics"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconItalic"
role="button"
tabindex="-1"
type="button"
/>
<button
aria-label="Strikethrough"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconStrikethrough"
role="button"
tabindex="-1"
type="button"
/>
<button
aria-label="Code block"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconCode"
role="button"
tabindex="-1"
type="button"
/>
<button
aria-label="Quote"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconQuote"
role="button"
tabindex="-1"
type="button"
/>
<button
aria-label="Insert link"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconInsertLink"
role="button"
tabindex="-1"
type="button"
/>
</div>
<div
aria-autocomplete="list"
aria-disabled="false"
aria-haspopup="listbox"
aria-label="Send an unencrypted message…"
aria-multiline="true"
class="mx_BasicMessageComposer_input mx_BasicMessageComposer_input_shouldShowPillAvatar mx_BasicMessageComposer_inputEmpty"
contenteditable="true"
data-testid="basicmessagecomposer"
dir="auto"
role="textbox"
style="--placeholder: 'Send\\ an\\ unencrypted\\ message…';"
tabindex="0"
translate="no"
>
<div>
<br />
</div>
</div>
</div>
</div>
<div
class="mx_MessageComposer_actions"
>
<div
aria-label="Emoji"
class="mx_AccessibleButton mx_EmojiButton mx_MessageComposer_button mx_EmojiButton_icon"
role="button"
tabindex="0"
/>
<div
aria-label="Attachment"
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_upload"
role="button"
tabindex="0"
/>
<div
aria-label="More options"
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
role="button"
tabindex="0"
/>
<input
multiple=""
style="display: none;"
type="file"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</DocumentFragment>
`;
exports[`RoomView should hide the right panel when hideRightPanel=true 1`] = `
<DocumentFragment>
<div
@ -1649,7 +2011,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="_r_7u_"
aria-labelledby="_r_ab_"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@ -1665,7 +2027,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
<button
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="_r_83_"
aria-labelledby="_r_ag_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -1691,7 +2053,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</button>
<button
aria-label="Threads"
aria-labelledby="_r_88_"
aria-labelledby="_r_al_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -1718,7 +2080,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</button>
<button
aria-label="Room info"
aria-labelledby="_r_8d_"
aria-labelledby="_r_aq_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -1748,7 +2110,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
>
<div
aria-label="0 members"
aria-labelledby="_r_8i_"
aria-labelledby="_r_av_"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
@ -1861,7 +2223,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="_r_7u_"
aria-labelledby="_r_ab_"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@ -1877,7 +2239,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
<button
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="_r_83_"
aria-labelledby="_r_ag_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -1903,7 +2265,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</button>
<button
aria-label="Threads"
aria-labelledby="_r_88_"
aria-labelledby="_r_al_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -1930,7 +2292,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</button>
<button
aria-label="Room info"
aria-labelledby="_r_8d_"
aria-labelledby="_r_aq_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -1960,7 +2322,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
>
<div
aria-label="0 members"
aria-labelledby="_r_8i_"
aria-labelledby="_r_av_"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
@ -2028,7 +2390,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
tabindex="0"
>
<div
aria-labelledby="_r_91_"
aria-labelledby="_r_be_"
class="mx_E2EIcon mx_E2EIcon_verified mx_MessageComposer_e2eIcon"
data-testid="e2e-icon"
>
@ -2239,7 +2601,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</button>
<button
aria-label="Chat"
aria-labelledby="_r_d0_"
aria-labelledby="_r_fd_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -2266,7 +2628,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</button>
<button
aria-label="Threads"
aria-labelledby="_r_d5_"
aria-labelledby="_r_fi_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -2293,7 +2655,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</button>
<button
aria-label="Room info"
aria-labelledby="_r_da_"
aria-labelledby="_r_fn_"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@ -2323,7 +2685,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
>
<div
aria-label="0 members"
aria-labelledby="_r_df_"
aria-labelledby="_r_fs_"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
@ -2397,7 +2759,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</p>
</div>
<button
aria-labelledby="_r_do_"
aria-labelledby="_r_g5_"
class="_icon-button_1pz9o_8"
data-kind="secondary"
data-testid="base-card-close-button"
@ -2457,7 +2819,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
>
<svg
aria-label="Messages in this room are not end-to-end encrypted"
aria-labelledby="_r_e1_"
aria-labelledby="_r_ge_"
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
color="var(--cpd-color-icon-info-primary)"
fill="currentColor"