From b5d11336f72701c41521af8a59ae18a70f170bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 24 Jan 2022 13:48:35 +0100 Subject: [PATCH] Add ability to switch between voice & video in calls (#7155) --- res/css/views/voip/_CallViewHeader.scss | 27 +++-------- src/components/views/voip/CallView.tsx | 44 +++++++++--------- .../views/voip/CallView/CallViewHeader.tsx | 46 +++++-------------- src/components/views/voip/PipView.tsx | 1 - src/i18n/strings/en_EN.json | 4 +- 5 files changed, 42 insertions(+), 80 deletions(-) diff --git a/res/css/views/voip/_CallViewHeader.scss b/res/css/views/voip/_CallViewHeader.scss index 94971b2a9b7..358357f1343 100644 --- a/res/css/views/voip/_CallViewHeader.scss +++ b/res/css/views/voip/_CallViewHeader.scss @@ -21,10 +21,13 @@ limitations under the License. align-items: center; justify-content: left; flex-shrink: 0; - cursor: pointer; + + &.mx_CallViewHeader_pip { + cursor: pointer; + } } -.mx_CallViewHeader_callType { +.mx_CallViewHeader_text { font-size: 1.2rem; font-weight: bold; vertical-align: middle; @@ -93,18 +96,7 @@ limitations under the License. margin-left: 4px; } -.mx_CallViewHeader_callTypeSmall { - font-size: 12px; - color: $secondary-content; - line-height: initial; - height: 15px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - max-width: 240px; -} - -.mx_CallViewHeader_callTypeIcon { +.mx_CallViewHeader_icon { display: inline-block; margin-right: 6px; height: 16px; @@ -122,13 +114,6 @@ limitations under the License. mask-repeat: no-repeat; mask-size: contain; mask-position: center; - } - - &.mx_CallViewHeader_callTypeIcon_voice::before { mask-image: url('$(res)/img/element-icons/call/voice-call.svg'); } - - &.mx_CallViewHeader_callTypeIcon_video::before { - mask-image: url('$(res)/img/element-icons/call/video-call.svg'); - } } diff --git a/src/components/views/voip/CallView.tsx b/src/components/views/voip/CallView.tsx index cb0a0d40818..f9ed742057d 100644 --- a/src/components/views/voip/CallView.tsx +++ b/src/components/views/voip/CallView.tsx @@ -17,7 +17,7 @@ limitations under the License. */ import React, { createRef, CSSProperties } from 'react'; -import { CallEvent, CallState, CallType, MatrixCall } from 'matrix-js-sdk/src/webrtc/call'; +import { CallEvent, CallState, MatrixCall } from 'matrix-js-sdk/src/webrtc/call'; import classNames from 'classnames'; import { CallFeed } from 'matrix-js-sdk/src/webrtc/callFeed'; import { SDPStreamMetadataPurpose } from 'matrix-js-sdk/src/webrtc/callEventTypes'; @@ -339,35 +339,38 @@ export default class CallView extends React.Component { }; private renderCallControls(): JSX.Element { - // We don't support call upgrades (yet) so hide the video mute button in voice calls - const vidMuteButtonShown = this.props.call.type === CallType.Video; + const { call, pipMode } = this.props; + const { primaryFeed, callState, micMuted, vidMuted, screensharing, sidebarShown } = this.state; + + // If SDPStreamMetadata isn't supported don't show video mute button in voice calls + const vidMuteButtonShown = call.opponentSupportsSDPStreamMetadata() || call.hasLocalUserMediaVideoTrack; // Screensharing is possible, if we can send a second stream and // identify it using SDPStreamMetadata or if we can replace the already // existing usermedia track by a screensharing track. We also need to be // connected to know the state of the other side const screensharingButtonShown = ( - (this.props.call.opponentSupportsSDPStreamMetadata() || this.props.call.type === CallType.Video) && - this.props.call.state === CallState.Connected + (call.opponentSupportsSDPStreamMetadata() || call.hasLocalUserMediaVideoTrack) && + call.state === CallState.Connected ); // To show the sidebar we need secondary feeds, if we don't have them, // we can hide this button. If we are in PiP, sidebar is also hidden, so // we can hide the button too const sidebarButtonShown = ( - this.state.primaryFeed?.purpose === SDPStreamMetadataPurpose.Screenshare || - this.props.call.isScreensharing() + primaryFeed?.purpose === SDPStreamMetadataPurpose.Screenshare || + call.isScreensharing() ); // The dial pad & 'more' button actions are only relevant in a connected call - const contextMenuButtonShown = this.state.callState === CallState.Connected; + const contextMenuButtonShown = callState === CallState.Connected; const dialpadButtonShown = ( - this.state.callState === CallState.Connected && - this.props.call.opponentSupportsDTMF() + callState === CallState.Connected && + call.opponentSupportsDTMF() ); return ( { onVidMuteClick: this.onVidMuteClick, }} buttonsState={{ - micMuted: this.state.micMuted, - vidMuted: this.state.vidMuted, - sidebarShown: this.state.sidebarShown, - screensharing: this.state.screensharing, + micMuted: micMuted, + vidMuted: vidMuted, + sidebarShown: sidebarShown, + screensharing: screensharing, }} buttonsVisibility={{ vidMute: vidMuteButtonShown, @@ -406,7 +409,7 @@ export default class CallView extends React.Component { const someoneIsScreensharing = this.props.call.getFeeds().some((feed) => { return feed.purpose === SDPStreamMetadataPurpose.Screenshare; }); - const isVideoCall = this.props.call.type === CallType.Video; + const call = this.props.call; let contentView: React.ReactNode; let holdTransferContent; @@ -461,7 +464,7 @@ export default class CallView extends React.Component { !isOnHold && !transfereeCall && sidebarShown && - (isVideoCall || someoneIsScreensharing) + (call.hasLocalUserMediaVideoTrack || someoneIsScreensharing) ) { sidebar = ( { // This is a bit messy. I can't see a reason to have two onHold/transfer screens if (isOnHold || transfereeCall) { - if (isVideoCall) { + if (call.hasLocalUserMediaVideoTrack || call.hasRemoteUserMediaVideoTrack) { const containerClasses = classNames({ mx_CallView_content: true, mx_CallView_video: true, @@ -569,7 +572,7 @@ export default class CallView extends React.Component { let text = isScreensharing ? _t("You are presenting") : _t('%(sharerName)s is presenting', { sharerName }); - if (!this.state.sidebarShown && isVideoCall) { + if (!this.state.sidebarShown) { text += " • " + (this.props.call.isLocalVideoMuted() ? _t("Your camera is turned off") : _t("Your camera is still enabled")); @@ -613,7 +616,6 @@ export default class CallView extends React.Component { { contentView } diff --git a/src/components/views/voip/CallView/CallViewHeader.tsx b/src/components/views/voip/CallView/CallViewHeader.tsx index 7a76ca7b7be..28baa68f314 100644 --- a/src/components/views/voip/CallView/CallViewHeader.tsx +++ b/src/components/views/voip/CallView/CallViewHeader.tsx @@ -14,25 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { CallType } from 'matrix-js-sdk/src/webrtc/call'; import { Room } from 'matrix-js-sdk/src/models/room'; import React from 'react'; -import classNames from 'classnames'; -import { _t, _td } from '../../../../languageHandler'; +import { _t } from '../../../../languageHandler'; import RoomAvatar from '../../avatars/RoomAvatar'; import dis from '../../../../dispatcher/dispatcher'; import { Action } from '../../../../dispatcher/actions'; import AccessibleTooltipButton from '../../elements/AccessibleTooltipButton'; -const callTypeTranslationByType: Record = { - [CallType.Video]: _td("Video Call"), - [CallType.Voice]: _td("Voice Call"), -}; - interface CallViewHeaderProps { pipMode: boolean; - type?: CallType; callRooms?: Room[]; onPipMouseDown: (event: React.MouseEvent) => void; } @@ -51,10 +43,10 @@ const onExpandClick = (roomId: string) => { }); }; -type CallControlsProps = Pick & { +type CallControlsProps = Pick & { roomId: string; }; -const CallViewHeaderControls: React.FC = ({ pipMode = false, type, roomId }) => { +const CallViewHeaderControls: React.FC = ({ pipMode = false, roomId }) => { return
{ !pipMode && = ({ callRoom }) => { ; }; -const CallTypeIcon: React.FC<{ type: CallType }> = ({ type }) => { - const classes = classNames({ - 'mx_CallViewHeader_callTypeIcon': true, - 'mx_CallViewHeader_callTypeIcon_video': type === CallType.Video, - 'mx_CallViewHeader_callTypeIcon_voice': type === CallType.Voice, - }); - return
; -}; - const CallViewHeader: React.FC = ({ - type, pipMode = false, callRooms = [], onPipMouseDown, }) => { const [callRoom, onHoldCallRoom] = callRooms; - const callTypeText = type ? _t(callTypeTranslationByType[type]) : _t("Widget"); - const callRoomName = callRoom?.name; - const roomId = callRoom?.roomId; + const callRoomName = callRoom.name; + const { roomId } = callRoom; if (!pipMode) { return
- - { callTypeText } - +
+ { _t("Call") } +
; } return (
-
{ callRoomName }
-
- { callTypeText } - { onHoldCallRoom && } -
+
{ callRoomName }
+ { onHoldCallRoom && }
- +
); }; diff --git a/src/components/views/voip/PipView.tsx b/src/components/views/voip/PipView.tsx index a4093d063aa..3ba608e2eb8 100644 --- a/src/components/views/voip/PipView.tsx +++ b/src/components/views/voip/PipView.tsx @@ -303,7 +303,6 @@ export default class PipView extends React.Component { pipContent = ({ onStartMoving, _onResize }) =>
{ onStartMoving(event); this.onStartMoving.bind(this)(); }} pipMode={pipMode} callRooms={[roomForWidget]} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d8bcb2465ff..38b3a8453ed 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1012,12 +1012,10 @@ "Show sidebar": "Show sidebar", "More": "More", "Hangup": "Hangup", - "Video Call": "Video Call", - "Voice Call": "Voice Call", "Fill Screen": "Fill Screen", "Return to call": "Return to call", "%(name)s on hold": "%(name)s on hold", - "Widget": "Widget", + "Call": "Call", "The other party cancelled the verification.": "The other party cancelled the verification.", "Verified!": "Verified!", "You've successfully verified this user.": "You've successfully verified this user.",