diff --git a/src/i18n/en-US.json b/src/i18n/en-US.json index 3d78c716b1a..fcacdfa6489 100644 --- a/src/i18n/en-US.json +++ b/src/i18n/en-US.json @@ -229,7 +229,17 @@ "callAccept": "Accept", "callChooseSharedScreen": "Choose a screen to share", "callChooseSharedWindow": "Choose a window to share", + "cameraStatusOn": "on", + "cameraStatusOff": "off", "callConversationAcceptOrDecline": "{{conversationName}} is calling. Press control + enter to accept the call or press control + shift + enter to decline the call.", + "startedAudioCallingAlert": "You are calling {{conversationName}}.", + "startedVideoCallingAlert": "You are calling {{conversationName}}, you camera is {{cameraStatus}}.", + "startedGroupCallingAlert": "You started a conference call with {{conversationName}}.", + "startedVideoGroupCallingAlert": "You started a conference call with {{conversationName}}, your camera is {{cameraStatus}}.", + "ongoingAudioCall": "Ongoing audio call with {{conversationName}}.", + "ongoingVideoCall": "Ongoing video call with {{conversationName}}, your camera is {{cameraStatus}}.", + "ongoingGroupAudioCall": "Ongoing conference call with {{conversationName}}.", + "ongoingGroupVideoCall": "Ongoing video conference call with {{conversationName}}, your camera is {{cameraStatus}}.", "callDecline": "Decline", "callDegradationAction": "OK", "callDegradationDescription": "The call was disconnected because {{username}} is no longer a verified contact.", diff --git a/src/script/components/TitleBar/TitleBar.tsx b/src/script/components/TitleBar/TitleBar.tsx index 122aab22608..a4c3e82d351 100644 --- a/src/script/components/TitleBar/TitleBar.tsx +++ b/src/script/components/TitleBar/TitleBar.tsx @@ -17,7 +17,7 @@ * */ -import React, {useMemo, useEffect, useCallback} from 'react'; +import React, {useMemo, useEffect, useCallback, useRef} from 'react'; import {TabIndex} from '@wireapp/react-ui-kit/lib/types/enums'; import {amplify} from 'amplify'; @@ -27,6 +27,7 @@ import {container} from 'tsyringe'; import {IconButton, IconButtonVariant, useMatchMedia} from '@wireapp/react-ui-kit'; import {WebAppEvents} from '@wireapp/webapp-events'; +import {useCallAlertState} from 'Components/calling/useCallAlertState'; import {Icon} from 'Components/Icon'; import {LegalHoldDot} from 'Components/LegalHoldDot'; import {useAppMainState, ViewType} from 'src/script/page/state'; @@ -101,9 +102,11 @@ export const TitleBar: React.FC = ({ ]); const {isActivatedAccount} = useKoSubscribableChildren(userState, ['isActivatedAccount']); - const {joinedCall} = useKoSubscribableChildren(callState, ['joinedCall']); + const {joinedCall, activeCalls} = useKoSubscribableChildren(callState, ['joinedCall', 'activeCalls']); const {isVideoCallingEnabled} = useKoSubscribableChildren(teamState, ['isVideoCallingEnabled']); + const currentFocusedElementRef = useRef(null); + const badgeLabelCopy = useMemo(() => { if (is1to1 && isRequest) { return ''; @@ -196,11 +199,22 @@ export const TitleBar: React.FC = ({ const onClickStartAudio = () => { callActions.startAudio(conversation); + showStartedCallAlert(isGroup); + if (smBreakpoint) { setLeftSidebar(); } }; + useEffect(() => { + if (!activeCalls.length && currentFocusedElementRef.current) { + currentFocusedElementRef.current.focus(); + currentFocusedElementRef.current = null; + } + }, [activeCalls.length]); + + const {showStartedCallAlert} = useCallAlertState(); + return (
    = ({ onClick={onClickDetails} title={peopleTooltip} aria-label={peopleTooltip} - onKeyDown={e => handleKeyDown(e, onClickDetails)} + onKeyDown={event => handleKeyDown(event, onClickDetails)} data-placement="bottom" role="button" tabIndex={TabIndex.FOCUSABLE} @@ -277,7 +291,11 @@ export const TitleBar: React.FC = ({ className="conversation-title-bar-icon" title={t('tooltipConversationVideoCall')} aria-label={t('tooltipConversationVideoCall')} - onClick={() => callActions.startVideo(conversation)} + onClick={event => { + currentFocusedElementRef.current = event.target as HTMLButtonElement; + callActions.startVideo(conversation); + showStartedCallAlert(isGroup, true); + }} data-uie-name="do-video-call" > @@ -289,7 +307,11 @@ export const TitleBar: React.FC = ({ className="conversation-title-bar-icon" title={t('tooltipConversationCall')} aria-label={t('tooltipConversationCall')} - onClick={() => callActions.startAudio(conversation)} + onClick={event => { + currentFocusedElementRef.current = event.target as HTMLButtonElement; + callActions.startAudio(conversation); + showStartedCallAlert(isGroup); + }} data-uie-name="do-call" > diff --git a/src/script/components/calling/CallingCell.tsx b/src/script/components/calling/CallingCell.tsx index 9efb5eb8fc9..cab7718725d 100644 --- a/src/script/components/calling/CallingCell.tsx +++ b/src/script/components/calling/CallingCell.tsx @@ -30,6 +30,7 @@ import {Avatar, AVATAR_SIZE} from 'Components/Avatar'; import {GroupAvatar} from 'Components/avatar/GroupAvatar'; import {Duration} from 'Components/calling/Duration'; import {GroupVideoGrid} from 'Components/calling/GroupVideoGrid'; +import {useCallAlertState} from 'Components/calling/useCallAlertState'; import {FadingScrollbar} from 'Components/FadingScrollbar'; import {Icon} from 'Components/Icon'; import {ClassifiedBar} from 'Components/input/ClassifiedBar'; @@ -101,6 +102,7 @@ const CallingCell: React.FC = ({ 'currentPage', 'muteState', ]); + const { isGroup, participating_user_ets: userEts, @@ -146,8 +148,10 @@ const CallingCell: React.FC = ({ const currentCallStatus = callStatus[state]; - const showNoCameraPreview = !hasAccessToCamera && call.initialType === CALL_TYPE.VIDEO && !isOngoing; - const showVideoButton = isVideoCallingEnabled && (call.initialType === CALL_TYPE.VIDEO || isOngoing); + const isVideoCall = call.initialType === CALL_TYPE.VIDEO; + + const showNoCameraPreview = !hasAccessToCamera && isVideoCall && !isOngoing; + const showVideoButton = isVideoCallingEnabled && (isVideoCall || isOngoing); const showParticipantsButton = isOngoing && isGroup; const videoGrid = useVideoGrid(call); @@ -221,6 +225,7 @@ const CallingCell: React.FC = ({ }, [isOngoing, multitasking]); const {setCurrentView} = useAppMainState(state => state.responsiveView); + const {showAlert, clearShowAlert} = useCallAlertState(); const answerCall = () => { callActions.answer(call); @@ -253,9 +258,26 @@ const CallingCell: React.FC = ({ }; } - return () => undefined; + return () => { + clearShowAlert(); + }; }, [answerOrRejectCall, isIncoming]); + const call1To1StartedAlert = t(isOutgoingVideoCall ? 'startedVideoCallingAlert' : 'startedAudioCallingAlert', { + conversationName, + cameraStatus: t(selfSharesCamera ? 'cameraStatusOn' : 'cameraStatusOff'), + }); + + const onGoingCallAlert = t(isOutgoingVideoCall ? 'ongoingVideoCall' : 'ongoingAudioCall', { + conversationName, + cameraStatus: t(selfSharesCamera ? 'cameraStatusOn' : 'cameraStatusOff'), + }); + + const callGroupStartedAlert = t(isOutgoingVideoCall ? 'startedVideoGroupCallingAlert' : 'startedGroupCallingAlert', { + conversationName, + cameraStatus: t(selfSharesCamera ? 'cameraStatusOn' : 'cameraStatusOff'), + }); + return (
    {isIncoming && ( @@ -267,7 +289,7 @@ const CallingCell: React.FC = ({ {showJoinButton && isFullUi && (