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

Commit

Permalink
Add Element call related functionality to new room header (#12091)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
toger5 and robintown authored Jan 31, 2024
1 parent 31449d6 commit 73b1623
Show file tree
Hide file tree
Showing 14 changed files with 283 additions and 161 deletions.
1 change: 0 additions & 1 deletion src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
private onActiveCalls = (): void => {
if (this.state.roomId === undefined) return;
const activeCall = CallStore.instance.getActiveCall(this.state.roomId);

if (activeCall === null) {
// We disconnected from the call, so stop viewing it
dis.dispatch<ViewRoomPayload>(
Expand Down
143 changes: 117 additions & 26 deletions src/components/views/rooms/RoomHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React, { useEffect, useMemo, useState } from "react";
import { Body as BodyText, IconButton, Tooltip } from "@vector-im/compound-web";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Body as BodyText, Button, IconButton, Menu, MenuItem, Tooltip } from "@vector-im/compound-web";
import { Icon as VideoCallIcon } from "@vector-im/compound-design-tokens/icons/video-call-solid.svg";
import { Icon as VoiceCallIcon } from "@vector-im/compound-design-tokens/icons/voice-call.svg";
import { Icon as CloseCallIcon } from "@vector-im/compound-design-tokens/icons/close.svg";
import { Icon as ThreadsIcon } from "@vector-im/compound-design-tokens/icons/threads-solid.svg";
import { Icon as NotificationsIcon } from "@vector-im/compound-design-tokens/icons/notifications-solid.svg";
import { Icon as VerifiedIcon } from "@vector-im/compound-design-tokens/icons/verified.svg";
Expand All @@ -35,7 +36,7 @@ import { useRoomMemberCount, useRoomMembers } from "../../../hooks/useRoomMember
import { _t } from "../../../languageHandler";
import { Flex } from "../../utils/Flex";
import { Box } from "../../utils/Box";
import { useRoomCall } from "../../../hooks/room/useRoomCall";
import { getPlatformCallTypeLabel, useRoomCall } from "../../../hooks/room/useRoomCall";
import { useRoomThreadNotifications } from "../../../hooks/room/useRoomThreadNotifications";
import { useGlobalNotificationState } from "../../../hooks/useGlobalNotificationState";
import SdkConfig from "../../../SdkConfig";
Expand All @@ -51,6 +52,7 @@ import { Linkify, topicToHtml } from "../../../HtmlUtils";
import PosthogTrackers from "../../../PosthogTrackers";
import { VideoRoomChatButton } from "./RoomHeader/VideoRoomChatButton";
import { RoomKnocksBar } from "./RoomKnocksBar";
import { isVideoRoom } from "../../../utils/video-rooms";
import { notificationLevelToIndicator } from "../../../utils/notifications";

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

const { voiceCallDisabledReason, voiceCallClick, videoCallDisabledReason, videoCallClick } = useRoomCall(room);
const {
voiceCallDisabledReason,
voiceCallClick,
videoCallDisabledReason,
videoCallClick,
toggleCallMaximized: toggleCall,
isViewingCall,
isConnectedToCall,
hasActiveCallSession,
callOptions,
} = useRoomCall(room);

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

const askToJoinEnabled = useFeatureEnabled("feature_ask_to_join");

const videoClick = useCallback((ev) => videoCallClick(ev, callOptions[0]), [callOptions, videoCallClick]);

const toggleCallButton = (
<Tooltip label={isViewingCall ? _t("voip|minimise_call") : _t("voip|maximise_call")}>
<IconButton onClick={toggleCall}>
<VideoCallIcon />
</IconButton>
</Tooltip>
);
const joinCallButton = (
<Button
size="sm"
onClick={videoClick}
Icon={VideoCallIcon}
className="mx_RoomHeader_join_button"
color="primary"
>
{_t("action|join")}
</Button>
);
const [menuOpen, setMenuOpen] = useState(false);
const callIconWithTooltip = (
<Tooltip label={videoCallDisabledReason ?? _t("voip|video_call")}>
<VideoCallIcon />
</Tooltip>
);
const startVideoCallButton = (
<>
{/* Can be either a menu or just a button depending on the number of call options.*/}
{callOptions.length > 1 ? (
<Menu
open={menuOpen}
onOpenChange={setMenuOpen}
title={_t("voip|video_call_using")}
trigger={
<IconButton
disabled={!!videoCallDisabledReason}
aria-label={videoCallDisabledReason ?? _t("voip|video_call")}
>
{callIconWithTooltip}
</IconButton>
}
side="left"
align="start"
>
{callOptions.map((option) => (
<MenuItem
key={option}
label={getPlatformCallTypeLabel(option)}
onClick={(ev) => videoCallClick(ev, option)}
Icon={VideoCallIcon}
onSelect={() => {} /* Dummy handler since we want the click event.*/}
/>
))}
</Menu>
) : (
<IconButton
disabled={!!videoCallDisabledReason}
aria-label={videoCallDisabledReason ?? _t("voip|video_call")}
onClick={videoClick}
>
{callIconWithTooltip}
</IconButton>
)}
</>
);
const voiceCallButton = (
<Tooltip label={voiceCallDisabledReason ?? _t("voip|voice_call")}>
<IconButton
disabled={!!voiceCallDisabledReason}
aria-label={voiceCallDisabledReason ?? _t("voip|voice_call")}
onClick={(ev) => voiceCallClick(ev, callOptions[0])}
>
<VoiceCallIcon />
</IconButton>
</Tooltip>
);
const closeLobbyButton = (
<Tooltip label={_t("voip|close_lobby")}>
<IconButton onClick={toggleCall}>
<CloseCallIcon />
</IconButton>
</Tooltip>
);
let videoCallButton = startVideoCallButton;
if (isConnectedToCall) {
videoCallButton = toggleCallButton;
} else if (isViewingCall) {
videoCallButton = closeLobbyButton;
}

return (
<>
<Flex as="header" align="center" gap="var(--cpd-space-3x)" className="mx_RoomHeader light-panel">
Expand Down Expand Up @@ -190,29 +293,17 @@ export default function RoomHeader({
</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} />
{((isConnectedToCall && isViewingCall) || isVideoRoom(room)) && <VideoRoomChatButton room={room} />}

{hasActiveCallSession && !isConnectedToCall ? (
joinCallButton
) : (
<>
{!isVideoRoom(room) && videoCallButton}
{!useElementCallExclusively && !isVideoRoom(room) && voiceCallButton}
</>
)}

<Tooltip label={_t("common|threads")}>
<IconButton
Expand Down
12 changes: 2 additions & 10 deletions src/components/views/rooms/RoomHeader/VideoRoomChatButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { Icon as ChatIcon } from "@vector-im/compound-design-tokens/icons/chat-s
import { Room } from "matrix-js-sdk/src/matrix";
import { IconButton, Tooltip } from "@vector-im/compound-web";

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

const isVideoRoom = calcIsVideoRoom(room);

const notificationState = isVideoRoom ? sdkContext.roomNotificationStateStore.getRoomState(room) : undefined;
const notificationState = sdkContext.roomNotificationStateStore.getRoomState(room);
const notificationColor = useEventEmitterState(
notificationState,
NotificationStateEvents.Update,
() => notificationState?.level,
);

if (!isVideoRoom) {
return null;
}

const displayUnreadIndicator =
!!notificationColor &&
[NotificationLevel.Activity, NotificationLevel.Notification, NotificationLevel.Highlight].includes(
Expand Down
Loading

0 comments on commit 73b1623

Please sign in to comment.