Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Add RoomKnocksBar to RoomHeader
Browse files Browse the repository at this point in the history
Signed-off-by: Charly Nguyen <[email protected]>
  • Loading branch information
Charly Nguyen committed Dec 21, 2023
1 parent 0a881e2 commit b9c37d1
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 139 deletions.
282 changes: 144 additions & 138 deletions src/components/views/rooms/RoomHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import { Linkify, topicToHtml } from "../../../HtmlUtils";
import PosthogTrackers from "../../../PosthogTrackers";
import { VideoRoomChatButton } from "./RoomHeader/VideoRoomChatButton";
import { RoomKnocksBar } from "./RoomKnocksBar";

/**
* A helper to transform a notification color to the what the Compound Icon Button
Expand Down Expand Up @@ -115,165 +116,170 @@ export default function RoomHeader({
[roomTopic?.html, roomTopic?.text],
);

const askToJoinEnabled = useFeatureEnabled("feature_ask_to_join");

return (
<Flex as="header" align="center" gap="var(--cpd-space-3x)" className="mx_RoomHeader light-panel">
<button
aria-label={_t("right_panel|room_summary_card|title")}
tabIndex={0}
onClick={() => {
RightPanelStore.instance.showOrHidePanel(RightPanelPhases.RoomSummary);
}}
className="mx_RoomHeader_infoWrapper"
>
<RoomAvatar room={room} size="40px" />
<Box flex="1" className="mx_RoomHeader_info">
<BodyText
as="div"
size="lg"
weight="semibold"
dir="auto"
role="heading"
aria-level={1}
className="mx_RoomHeader_heading"
>
<span className="mx_RoomHeader_truncated mx_lineClamp">{roomName}</span>
<>
<Flex as="header" align="center" gap="var(--cpd-space-3x)" className="mx_RoomHeader light-panel">
<button
aria-label={_t("right_panel|room_summary_card|title")}
tabIndex={0}
onClick={() => {
RightPanelStore.instance.showOrHidePanel(RightPanelPhases.RoomSummary);
}}
className="mx_RoomHeader_infoWrapper"
>
<RoomAvatar room={room} size="40px" />
<Box flex="1" className="mx_RoomHeader_info">
<BodyText
as="div"
size="lg"
weight="semibold"
dir="auto"
role="heading"
aria-level={1}
className="mx_RoomHeader_heading"
>
<span className="mx_RoomHeader_truncated mx_lineClamp">{roomName}</span>

{!isDirectMessage && roomState.getJoinRule() === JoinRule.Public && (
<Tooltip label={_t("common|public_room")} side="right">
<PublicIcon
width="16px"
height="16px"
className="mx_RoomHeader_icon text-secondary"
aria-label={_t("common|public_room")}
/>
</Tooltip>
)}
{!isDirectMessage && roomState.getJoinRule() === JoinRule.Public && (
<Tooltip label={_t("common|public_room")} side="right">
<PublicIcon
width="16px"
height="16px"
className="mx_RoomHeader_icon text-secondary"
aria-label={_t("common|public_room")}
/>
</Tooltip>
)}

{isDirectMessage && e2eStatus === E2EStatus.Verified && (
<Tooltip label={_t("common|verified")} side="right">
<VerifiedIcon
width="16px"
height="16px"
className="mx_RoomHeader_icon mx_Verified"
aria-label={_t("common|verified")}
/>
</Tooltip>
{isDirectMessage && e2eStatus === E2EStatus.Verified && (
<Tooltip label={_t("common|verified")} side="right">
<VerifiedIcon
width="16px"
height="16px"
className="mx_RoomHeader_icon mx_Verified"
aria-label={_t("common|verified")}
/>
</Tooltip>
)}

{isDirectMessage && e2eStatus === E2EStatus.Warning && (
<Tooltip label={_t("room|header_untrusted_label")} side="right">
<ErrorIcon
width="16px"
height="16px"
className="mx_RoomHeader_icon mx_Untrusted"
aria-label={_t("room|header_untrusted_label")}
/>
</Tooltip>
)}
</BodyText>
{roomTopic && (
<BodyText
as="div"
size="sm"
className="mx_RoomHeader_topic mx_RoomHeader_truncated mx_lineClamp"
>
<Linkify>{roomTopicBody}</Linkify>
</BodyText>
)}
</Box>
</button>
<Flex align="center" gap="var(--cpd-space-2x)">
{additionalButtons?.map((props) => {
const label = props.label();

{isDirectMessage && e2eStatus === E2EStatus.Warning && (
<Tooltip label={_t("room|header_untrusted_label")} side="right">
<ErrorIcon
width="16px"
height="16px"
className="mx_RoomHeader_icon mx_Untrusted"
aria-label={_t("room|header_untrusted_label")}
/>
return (
<Tooltip label={label} key={props.id}>
<IconButton
aria-label={label}
onClick={(event) => {
event.stopPropagation();
props.onClick();
}}
>
{typeof props.icon === "function" ? props.icon() : props.icon}
</IconButton>
</Tooltip>
)}
</BodyText>
{roomTopic && (
<BodyText
as="div"
size="sm"
className="mx_RoomHeader_topic mx_RoomHeader_truncated mx_lineClamp"
);
})}
<Tooltip label={!videoCallDisabledReason ? _t("voip|video_call") : videoCallDisabledReason!}>
<IconButton
disabled={!!videoCallDisabledReason}
aria-label={!videoCallDisabledReason ? _t("voip|video_call") : videoCallDisabledReason!}
onClick={videoCallClick}
>
<Linkify>{roomTopicBody}</Linkify>
</BodyText>
)}
</Box>
</button>
<Flex align="center" gap="var(--cpd-space-2x)">
{additionalButtons?.map((props) => {
const label = props.label();

return (
<Tooltip label={label} key={props.id}>
<VideoCallIcon />
</IconButton>
</Tooltip>
{!useElementCallExclusively && (
<Tooltip label={!voiceCallDisabledReason ? _t("voip|voice_call") : voiceCallDisabledReason!}>
<IconButton
aria-label={label}
onClick={(event) => {
event.stopPropagation();
props.onClick();
}}
disabled={!!voiceCallDisabledReason}
aria-label={!voiceCallDisabledReason ? _t("voip|voice_call") : voiceCallDisabledReason!}
onClick={voiceCallClick}
>
{typeof props.icon === "function" ? props.icon() : props.icon}
<VoiceCallIcon />
</IconButton>
</Tooltip>
);
})}
<Tooltip label={!videoCallDisabledReason ? _t("voip|video_call") : videoCallDisabledReason!}>
<IconButton
disabled={!!videoCallDisabledReason}
aria-label={!videoCallDisabledReason ? _t("voip|video_call") : videoCallDisabledReason!}
onClick={videoCallClick}
>
<VideoCallIcon />
</IconButton>
</Tooltip>
{!useElementCallExclusively && (
<Tooltip label={!voiceCallDisabledReason ? _t("voip|voice_call") : voiceCallDisabledReason!}>
<IconButton
disabled={!!voiceCallDisabledReason}
aria-label={!voiceCallDisabledReason ? _t("voip|voice_call") : voiceCallDisabledReason!}
onClick={voiceCallClick}
>
<VoiceCallIcon />
</IconButton>
</Tooltip>
)}
)}

{/* Renders nothing when room is not a video room */}
<VideoRoomChatButton room={room} />
{/* Renders nothing when room is not a video room */}
<VideoRoomChatButton room={room} />

<Tooltip label={_t("common|threads")}>
<IconButton
indicator={notificationColorToIndicator(threadNotifications)}
onClick={(evt) => {
evt.stopPropagation();
RightPanelStore.instance.showOrHidePanel(RightPanelPhases.ThreadPanel);
PosthogTrackers.trackInteraction("WebRoomHeaderButtonsThreadsButton", evt);
}}
aria-label={_t("common|threads")}
>
<ThreadsIcon />
</IconButton>
</Tooltip>
{notificationsEnabled && (
<Tooltip label={_t("notifications|enable_prompt_toast_title")}>
<Tooltip label={_t("common|threads")}>
<IconButton
indicator={notificationColorToIndicator(globalNotificationState.color)}
indicator={notificationColorToIndicator(threadNotifications)}
onClick={(evt) => {
evt.stopPropagation();
RightPanelStore.instance.showOrHidePanel(RightPanelPhases.NotificationPanel);
RightPanelStore.instance.showOrHidePanel(RightPanelPhases.ThreadPanel);
PosthogTrackers.trackInteraction("WebRoomHeaderButtonsThreadsButton", evt);
}}
aria-label={_t("notifications|enable_prompt_toast_title")}
aria-label={_t("common|threads")}
>
<NotificationsIcon />
<ThreadsIcon />
</IconButton>
</Tooltip>
{notificationsEnabled && (
<Tooltip label={_t("notifications|enable_prompt_toast_title")}>
<IconButton
indicator={notificationColorToIndicator(globalNotificationState.color)}
onClick={(evt) => {
evt.stopPropagation();
RightPanelStore.instance.showOrHidePanel(RightPanelPhases.NotificationPanel);
}}
aria-label={_t("notifications|enable_prompt_toast_title")}
>
<NotificationsIcon />
</IconButton>
</Tooltip>
)}
</Flex>
{!isDirectMessage && (
<BodyText
as="div"
size="sm"
weight="medium"
aria-label={_t("common|n_members", { count: memberCount })}
onClick={(e: React.MouseEvent) => {
RightPanelStore.instance.showOrHidePanel(RightPanelPhases.RoomMemberList);
e.stopPropagation();
}}
>
<FacePile
className="mx_RoomHeader_members"
members={members.slice(0, 3)}
size="20px"
overflow={false}
viewUserOnClick={false}
>
{formatCount(memberCount)}
</FacePile>
</BodyText>
)}
</Flex>
{!isDirectMessage && (
<BodyText
as="div"
size="sm"
weight="medium"
aria-label={_t("common|n_members", { count: memberCount })}
onClick={(e: React.MouseEvent) => {
RightPanelStore.instance.showOrHidePanel(RightPanelPhases.RoomMemberList);
e.stopPropagation();
}}
>
<FacePile
className="mx_RoomHeader_members"
members={members.slice(0, 3)}
size="20px"
overflow={false}
viewUserOnClick={false}
>
{formatCount(memberCount)}
</FacePile>
</BodyText>
)}
</Flex>
{askToJoinEnabled && <RoomKnocksBar room={room} />}
</>
);
}
29 changes: 28 additions & 1 deletion test/components/views/rooms/RoomHeader-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ limitations under the License.

import React from "react";
import { CallType, MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import { EventType, JoinRule, MatrixClient, MatrixEvent, PendingEventOrdering, Room } from "matrix-js-sdk/src/matrix";
import {
EventType,
JoinRule,
MatrixClient,
MatrixEvent,
PendingEventOrdering,
Room,
RoomMember,
} from "matrix-js-sdk/src/matrix";
import {
createEvent,
fireEvent,
Expand Down Expand Up @@ -562,6 +570,25 @@ describe("RoomHeader", () => {
expect(callback).toHaveBeenCalled();
expect(event.stopPropagation).toHaveBeenCalled();
});

describe("ask to join disabled", () => {
it("does not render the RoomKnocksBar", () => {
render(<RoomHeader room={room} />, withClientContextRenderOptions(MatrixClientPeg.get()!));
expect(screen.queryByRole("heading", { name: "Asking to join" })).not.toBeInTheDocument();
});
});

describe("ask to join enabled", () => {
it("does render the RoomKnocksBar", () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((feature) => feature === "feature_ask_to_join");
jest.spyOn(room, "canInvite").mockReturnValue(true);
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Knock);
jest.spyOn(room, "getMembersWithMembership").mockReturnValue([new RoomMember(room.roomId, "@foo")]);

render(<RoomHeader room={room} />, withClientContextRenderOptions(MatrixClientPeg.get()!));
expect(screen.getByRole("heading", { name: "Asking to join" })).toBeInTheDocument();
});
});
});

/**
Expand Down

0 comments on commit b9c37d1

Please sign in to comment.