From 916c5a0232662d0cd503f84ff42271ec6fb25ad1 Mon Sep 17 00:00:00 2001 From: ElementRobot Date: Wed, 3 Dec 2025 17:29:41 +0100 Subject: [PATCH 1/2] Add option to pick call options for voice calls. (#31407) (#31413) * Add option to pick call options for voice calls. * hook on the right thing * Fix wrong call being disabled * update snaps * Add tests for menus * more snaps * snap snap (cherry picked from commit a352a3838e4b53bbe87423337dbe78a85e544deb) Co-authored-by: Will Hunt <2072976+Half-Shot@users.noreply.github.com> --- .../views/rooms/RoomHeader/RoomHeader.tsx | 120 +++++++++++++----- src/i18n/strings/en_EN.json | 1 + .../__snapshots__/RoomView-test.tsx.snap | 10 +- .../rooms/RoomHeader/RoomHeader-test.tsx | 41 +++++- .../__snapshots__/RoomHeader-test.tsx.snap | 8 +- 5 files changed, 135 insertions(+), 45 deletions(-) diff --git a/src/components/views/rooms/RoomHeader/RoomHeader.tsx b/src/components/views/rooms/RoomHeader/RoomHeader.tsx index 02fbf678a3..e2cc00abda 100644 --- a/src/components/views/rooms/RoomHeader/RoomHeader.tsx +++ b/src/components/views/rooms/RoomHeader/RoomHeader.tsx @@ -1,4 +1,5 @@ /* +Copyright (C) 2025 Element Creations Ltd Copyright 2024 New Vector Ltd. Copyright 2023 The Matrix.org Foundation C.I.C. @@ -6,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import React, { type JSX, useCallback, useMemo, useState } from "react"; +import React, { type JSX, useCallback, useState } from "react"; import { Text, Button, IconButton, Menu, MenuItem, Tooltip } from "@vector-im/compound-web"; import VideoCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/video-call-solid"; import VoiceCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/voice-call-solid"; @@ -29,7 +30,6 @@ import { _t } from "../../../../languageHandler.tsx"; import { getPlatformCallTypeProps, useRoomCall } from "../../../../hooks/room/useRoomCall.tsx"; import { useRoomThreadNotifications } from "../../../../hooks/room/useRoomThreadNotifications.ts"; import { useGlobalNotificationState } from "../../../../hooks/useGlobalNotificationState.ts"; -import SdkConfig from "../../../../SdkConfig.ts"; import { useFeatureEnabled } from "../../../../hooks/useSettings.ts"; import { useEncryptionStatus } from "../../../../hooks/useEncryptionStatus.ts"; import { E2EStatus } from "../../../../utils/ShieldUtils.ts"; @@ -78,16 +78,6 @@ function RoomHeaderButtons({ showVoiceCallButton, showVideoCallButton, } = useRoomCall(room); - - const groupCallsEnabled = useFeatureEnabled("feature_group_calls"); - /** - * A special mode where only Element Call is used. In this case we want to - * hide the voice call button - */ - const useElementCallExclusively = useMemo(() => { - return SdkConfig.get("element_call").use_exclusively && groupCallsEnabled; - }, [groupCallsEnabled]); - const threadNotifications = useRoomThreadNotifications(room); const globalNotificationState = useGlobalNotificationState(); @@ -101,6 +91,11 @@ function RoomHeaderButtons({ [callOptions, videoCallClick], ); + const voiceClick = useCallback( + (ev: React.MouseEvent) => voiceCallClick(ev, callOptions[0]), + [callOptions, voiceCallClick], + ); + const toggleCallButton = ( @@ -126,35 +121,50 @@ function RoomHeaderButtons({ ); - const callIconWithTooltip = ( + const videoCallIconWithTooltip = ( ); - const [menuOpen, setMenuOpen] = useState(false); + const voiceCallIconWithTooltip = ( + + + + ); - const onOpenChange = useCallback( + const [videoMenuOpen, setVideoMenuOpen] = useState(false); + + const onVideoOpenChange = useCallback( (newOpen: boolean) => { - if (!videoCallDisabledReason) setMenuOpen(newOpen); + if (!videoCallDisabledReason) setVideoMenuOpen(newOpen); }, [videoCallDisabledReason], ); + const [voiceMenuOpen, setVoiceMenuOpen] = useState(false); + + const onVoiceOpenChange = useCallback( + (newOpen: boolean) => { + if (!voiceCallDisabledReason) setVoiceMenuOpen(newOpen); + }, + [voiceCallDisabledReason], + ); + const startVideoCallButton = ( <> {/* Can be either a menu or just a button depending on the number of call options.*/} {callOptions.length > 1 ? ( - {callIconWithTooltip} + {videoCallIconWithTooltip} } side="left" @@ -170,7 +180,7 @@ function RoomHeaderButtons({ children={children} className="mx_RoomHeader_videoCallOption" onClick={(ev) => { - setMenuOpen(false); + setVideoMenuOpen(false); videoCallClick(ev, option); }} Icon={VideoCallIcon} @@ -185,25 +195,61 @@ function RoomHeaderButtons({ aria-label={videoCallDisabledReason ?? _t("voip|video_call")} onClick={videoClick} > - {callIconWithTooltip} + {videoCallIconWithTooltip} )} ); - let voiceCallButton: JSX.Element | undefined = ( - - voiceCallClick(ev, callOptions[0])} - > - - - + const startVoiceCallButton = ( + <> + {/* Can be either a menu or just a button depending on the number of call options.*/} + {callOptions.length > 1 ? ( + + {voiceCallIconWithTooltip} + + } + side="left" + align="start" + > + {callOptions.map((option) => { + const { label, children } = getPlatformCallTypeProps(option); + return ( + { + setVoiceMenuOpen(false); + voiceCallClick(ev, option); + }} + Icon={VoiceCallIcon} + onSelect={() => {} /* Dummy handler since we want the click event.*/} + /> + ); + })} + + ) : ( + + {voiceCallIconWithTooltip} + + )} + ); + const closeLobbyButton = ( @@ -212,15 +258,19 @@ function RoomHeaderButtons({ ); let videoCallButton: JSX.Element | undefined = startVideoCallButton; + let voiceCallButton: JSX.Element | undefined = startVoiceCallButton; if (isConnectedToCall) { videoCallButton = toggleCallButton; + voiceCallButton = undefined; } else if (isViewingCall) { videoCallButton = closeLobbyButton; + voiceCallButton = undefined; } if (!showVideoCallButton) { videoCallButton = undefined; } + if (!showVoiceCallButton) { voiceCallButton = undefined; } @@ -258,7 +308,7 @@ function RoomHeaderButtons({ ) : ( <> {!isVideoRoom && videoCallButton} - {!useElementCallExclusively && !isVideoRoom && voiceCallButton} + {!isVideoRoom && voiceCallButton} )} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f1f4f4dc7c..968fefae10 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -4103,6 +4103,7 @@ "video_call_using": "Video call using:", "voice_call": "Voice call", "voice_call_incoming": "Incoming voice call", + "voice_call_using": "Voice call using:", "you_are_presenting": "You are presenting" }, "web_default_device_name": "%(appName)s: %(browserName)s on %(osName)s", diff --git a/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap b/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap index ac25954916..e1d66077cc 100644 --- a/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap +++ b/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap @@ -879,7 +879,6 @@ exports[`RoomView should hide the composer when hideComposer=true 1`] = `