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

Commit 73b1623

Browse files
toger5robintown
andauthored
Add Element call related functionality to new room header (#12091)
* New room header - add chat button during call - close lobby button in lobby - join button if session exists - allow to toggle call <-> timeline during call with call button Compound style for join button in call notify toast. Signed-off-by: Timo K <[email protected]> * dont show start call, join button in video rooms. Signed-off-by: Timo K <[email protected]> * Make active call check based on participant count Not based on available call object Signed-off-by: Timo K <[email protected]> * fix room header tests Signed-off-by: Timo K <[email protected]> * fix room header tests Signed-off-by: Timo K <[email protected]> * remove chat button test for displaying. Chat button display logic is now part of the RoomHeader. Signed-off-by: Timo K <[email protected]> * remove duplicate notification Tread icon Signed-off-by: Timo K <[email protected]> * remove obsolete jest snapshot Signed-off-by: Timo K <[email protected]> * Update src/components/views/rooms/RoomHeader.tsx Co-authored-by: Robin <[email protected]> * update isECWidget logic Signed-off-by: Timo K <[email protected]> * remove dead code Signed-off-by: Timo K <[email protected]> * refactor call options Add menu to choose if there are multiple options Signed-off-by: Timo K <[email protected]> * join ec when clicking join button (dont start jitsi) Use icon buttons don't show call icon when join button is visible Signed-off-by: Timo K <[email protected]> * refactor isViewingCall Signed-off-by: Timo K <[email protected]> * fix room header tests Signed-off-by: Timo K <[email protected]> * fix header snapshot Signed-off-by: Timo K <[email protected]> * sonar proposals Signed-off-by: Timo K <[email protected]> * fix event shiftKey may be undefined Signed-off-by: Timo K <[email protected]> * more lobby time before timeout only await sticky promise on becoming sticky. Signed-off-by: Timo K <[email protected]> * don't allow starting new calls if there is an ongoing other call. Signed-off-by: Timo K <[email protected]> * review Signed-off-by: Timo K <[email protected]> * fix translation typo Signed-off-by: Timo K <[email protected]> --------- Signed-off-by: Timo K <[email protected]> Co-authored-by: Robin <[email protected]>
1 parent 31449d6 commit 73b1623

File tree

14 files changed

+283
-161
lines changed

14 files changed

+283
-161
lines changed

src/components/structures/RoomView.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
821821
private onActiveCalls = (): void => {
822822
if (this.state.roomId === undefined) return;
823823
const activeCall = CallStore.instance.getActiveCall(this.state.roomId);
824-
825824
if (activeCall === null) {
826825
// We disconnected from the call, so stop viewing it
827826
dis.dispatch<ViewRoomPayload>(

src/components/views/rooms/RoomHeader.tsx

Lines changed: 117 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import React, { useEffect, useMemo, useState } from "react";
18-
import { Body as BodyText, IconButton, Tooltip } from "@vector-im/compound-web";
17+
import React, { useCallback, useEffect, useMemo, useState } from "react";
18+
import { Body as BodyText, Button, IconButton, Menu, MenuItem, Tooltip } from "@vector-im/compound-web";
1919
import { Icon as VideoCallIcon } from "@vector-im/compound-design-tokens/icons/video-call-solid.svg";
2020
import { Icon as VoiceCallIcon } from "@vector-im/compound-design-tokens/icons/voice-call.svg";
21+
import { Icon as CloseCallIcon } from "@vector-im/compound-design-tokens/icons/close.svg";
2122
import { Icon as ThreadsIcon } from "@vector-im/compound-design-tokens/icons/threads-solid.svg";
2223
import { Icon as NotificationsIcon } from "@vector-im/compound-design-tokens/icons/notifications-solid.svg";
2324
import { Icon as VerifiedIcon } from "@vector-im/compound-design-tokens/icons/verified.svg";
@@ -35,7 +36,7 @@ import { useRoomMemberCount, useRoomMembers } from "../../../hooks/useRoomMember
3536
import { _t } from "../../../languageHandler";
3637
import { Flex } from "../../utils/Flex";
3738
import { Box } from "../../utils/Box";
38-
import { useRoomCall } from "../../../hooks/room/useRoomCall";
39+
import { getPlatformCallTypeLabel, useRoomCall } from "../../../hooks/room/useRoomCall";
3940
import { useRoomThreadNotifications } from "../../../hooks/room/useRoomThreadNotifications";
4041
import { useGlobalNotificationState } from "../../../hooks/useGlobalNotificationState";
4142
import SdkConfig from "../../../SdkConfig";
@@ -51,6 +52,7 @@ import { Linkify, topicToHtml } from "../../../HtmlUtils";
5152
import PosthogTrackers from "../../../PosthogTrackers";
5253
import { VideoRoomChatButton } from "./RoomHeader/VideoRoomChatButton";
5354
import { RoomKnocksBar } from "./RoomKnocksBar";
55+
import { isVideoRoom } from "../../../utils/video-rooms";
5456
import { notificationLevelToIndicator } from "../../../utils/notifications";
5557

5658
export default function RoomHeader({
@@ -69,7 +71,17 @@ export default function RoomHeader({
6971
const members = useRoomMembers(room, 2500);
7072
const memberCount = useRoomMemberCount(room, { throttleWait: 2500 });
7173

72-
const { voiceCallDisabledReason, voiceCallClick, videoCallDisabledReason, videoCallClick } = useRoomCall(room);
74+
const {
75+
voiceCallDisabledReason,
76+
voiceCallClick,
77+
videoCallDisabledReason,
78+
videoCallClick,
79+
toggleCallMaximized: toggleCall,
80+
isViewingCall,
81+
isConnectedToCall,
82+
hasActiveCallSession,
83+
callOptions,
84+
} = useRoomCall(room);
7385

7486
const groupCallsEnabled = useFeatureEnabled("feature_group_calls");
7587
/**
@@ -104,6 +116,97 @@ export default function RoomHeader({
104116

105117
const askToJoinEnabled = useFeatureEnabled("feature_ask_to_join");
106118

119+
const videoClick = useCallback((ev) => videoCallClick(ev, callOptions[0]), [callOptions, videoCallClick]);
120+
121+
const toggleCallButton = (
122+
<Tooltip label={isViewingCall ? _t("voip|minimise_call") : _t("voip|maximise_call")}>
123+
<IconButton onClick={toggleCall}>
124+
<VideoCallIcon />
125+
</IconButton>
126+
</Tooltip>
127+
);
128+
const joinCallButton = (
129+
<Button
130+
size="sm"
131+
onClick={videoClick}
132+
Icon={VideoCallIcon}
133+
className="mx_RoomHeader_join_button"
134+
color="primary"
135+
>
136+
{_t("action|join")}
137+
</Button>
138+
);
139+
const [menuOpen, setMenuOpen] = useState(false);
140+
const callIconWithTooltip = (
141+
<Tooltip label={videoCallDisabledReason ?? _t("voip|video_call")}>
142+
<VideoCallIcon />
143+
</Tooltip>
144+
);
145+
const startVideoCallButton = (
146+
<>
147+
{/* Can be either a menu or just a button depending on the number of call options.*/}
148+
{callOptions.length > 1 ? (
149+
<Menu
150+
open={menuOpen}
151+
onOpenChange={setMenuOpen}
152+
title={_t("voip|video_call_using")}
153+
trigger={
154+
<IconButton
155+
disabled={!!videoCallDisabledReason}
156+
aria-label={videoCallDisabledReason ?? _t("voip|video_call")}
157+
>
158+
{callIconWithTooltip}
159+
</IconButton>
160+
}
161+
side="left"
162+
align="start"
163+
>
164+
{callOptions.map((option) => (
165+
<MenuItem
166+
key={option}
167+
label={getPlatformCallTypeLabel(option)}
168+
onClick={(ev) => videoCallClick(ev, option)}
169+
Icon={VideoCallIcon}
170+
onSelect={() => {} /* Dummy handler since we want the click event.*/}
171+
/>
172+
))}
173+
</Menu>
174+
) : (
175+
<IconButton
176+
disabled={!!videoCallDisabledReason}
177+
aria-label={videoCallDisabledReason ?? _t("voip|video_call")}
178+
onClick={videoClick}
179+
>
180+
{callIconWithTooltip}
181+
</IconButton>
182+
)}
183+
</>
184+
);
185+
const voiceCallButton = (
186+
<Tooltip label={voiceCallDisabledReason ?? _t("voip|voice_call")}>
187+
<IconButton
188+
disabled={!!voiceCallDisabledReason}
189+
aria-label={voiceCallDisabledReason ?? _t("voip|voice_call")}
190+
onClick={(ev) => voiceCallClick(ev, callOptions[0])}
191+
>
192+
<VoiceCallIcon />
193+
</IconButton>
194+
</Tooltip>
195+
);
196+
const closeLobbyButton = (
197+
<Tooltip label={_t("voip|close_lobby")}>
198+
<IconButton onClick={toggleCall}>
199+
<CloseCallIcon />
200+
</IconButton>
201+
</Tooltip>
202+
);
203+
let videoCallButton = startVideoCallButton;
204+
if (isConnectedToCall) {
205+
videoCallButton = toggleCallButton;
206+
} else if (isViewingCall) {
207+
videoCallButton = closeLobbyButton;
208+
}
209+
107210
return (
108211
<>
109212
<Flex as="header" align="center" gap="var(--cpd-space-3x)" className="mx_RoomHeader light-panel">
@@ -190,29 +293,17 @@ export default function RoomHeader({
190293
</Tooltip>
191294
);
192295
})}
193-
<Tooltip label={!videoCallDisabledReason ? _t("voip|video_call") : videoCallDisabledReason!}>
194-
<IconButton
195-
disabled={!!videoCallDisabledReason}
196-
aria-label={!videoCallDisabledReason ? _t("voip|video_call") : videoCallDisabledReason!}
197-
onClick={videoCallClick}
198-
>
199-
<VideoCallIcon />
200-
</IconButton>
201-
</Tooltip>
202-
{!useElementCallExclusively && (
203-
<Tooltip label={!voiceCallDisabledReason ? _t("voip|voice_call") : voiceCallDisabledReason!}>
204-
<IconButton
205-
disabled={!!voiceCallDisabledReason}
206-
aria-label={!voiceCallDisabledReason ? _t("voip|voice_call") : voiceCallDisabledReason!}
207-
onClick={voiceCallClick}
208-
>
209-
<VoiceCallIcon />
210-
</IconButton>
211-
</Tooltip>
212-
)}
213296

214-
{/* Renders nothing when room is not a video room */}
215-
<VideoRoomChatButton room={room} />
297+
{((isConnectedToCall && isViewingCall) || isVideoRoom(room)) && <VideoRoomChatButton room={room} />}
298+
299+
{hasActiveCallSession && !isConnectedToCall ? (
300+
joinCallButton
301+
) : (
302+
<>
303+
{!isVideoRoom(room) && videoCallButton}
304+
{!useElementCallExclusively && !isVideoRoom(room) && voiceCallButton}
305+
</>
306+
)}
216307

217308
<Tooltip label={_t("common|threads")}>
218309
<IconButton

src/components/views/rooms/RoomHeader/VideoRoomChatButton.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { Icon as ChatIcon } from "@vector-im/compound-design-tokens/icons/chat-s
1919
import { Room } from "matrix-js-sdk/src/matrix";
2020
import { IconButton, Tooltip } from "@vector-im/compound-web";
2121

22-
import { isVideoRoom as calcIsVideoRoom } from "../../../../utils/video-rooms";
2322
import { _t } from "../../../../languageHandler";
2423
import { useEventEmitterState } from "../../../../hooks/useEventEmitter";
2524
import { NotificationStateEvents } from "../../../../stores/notifications/NotificationState";
@@ -31,25 +30,18 @@ import { ButtonEvent } from "../../elements/AccessibleButton";
3130
/**
3231
* Display a button to toggle timeline for video rooms
3332
* @param room
34-
* @returns for a video room: a button to toggle timeline in the right panel
35-
* otherwise null
33+
* @returns A button to toggle timeline in the right panel.
3634
*/
3735
export const VideoRoomChatButton: React.FC<{ room: Room }> = ({ room }) => {
3836
const sdkContext = useContext(SDKContext);
3937

40-
const isVideoRoom = calcIsVideoRoom(room);
41-
42-
const notificationState = isVideoRoom ? sdkContext.roomNotificationStateStore.getRoomState(room) : undefined;
38+
const notificationState = sdkContext.roomNotificationStateStore.getRoomState(room);
4339
const notificationColor = useEventEmitterState(
4440
notificationState,
4541
NotificationStateEvents.Update,
4642
() => notificationState?.level,
4743
);
4844

49-
if (!isVideoRoom) {
50-
return null;
51-
}
52-
5345
const displayUnreadIndicator =
5446
!!notificationColor &&
5547
[NotificationLevel.Activity, NotificationLevel.Notification, NotificationLevel.Highlight].includes(

0 commit comments

Comments
 (0)