signal-desktop/ts/components/CallManager.tsx

268 lines
7.3 KiB
TypeScript
Raw Normal View History

2020-10-30 20:34:04 +00:00
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
2020-11-13 19:57:55 +00:00
import React, { useCallback } from 'react';
2020-10-01 19:09:15 +00:00
import { CallNeedPermissionScreen } from './CallNeedPermissionScreen';
import { CallScreen } from './CallScreen';
2020-11-17 15:07:53 +00:00
import { CallingLobby } from './CallingLobby';
import { CallingParticipantsList } from './CallingParticipantsList';
import { CallingPip } from './CallingPip';
import { IncomingCallBar } from './IncomingCallBar';
import {
2020-11-17 15:07:53 +00:00
CallEndedReason,
2020-11-13 19:57:55 +00:00
CallMode,
CallState,
GroupCallJoinState,
2020-11-17 15:07:53 +00:00
VideoFrameSource,
2020-11-13 19:57:55 +00:00
} from '../types/Calling';
import { ConversationType } from '../state/ducks/conversations';
import {
AcceptCallType,
ActiveCallType,
2020-11-13 19:57:55 +00:00
CancelCallType,
DeclineCallType,
DirectCallStateType,
HangUpType,
2020-11-13 19:57:55 +00:00
SetLocalAudioType,
SetLocalPreviewType,
SetLocalVideoType,
SetRendererCanvasType,
2020-11-13 19:57:55 +00:00
StartCallType,
} from '../state/ducks/calling';
import { LocalizerType } from '../types/Util';
import { ColorType } from '../types/Colors';
2020-11-13 19:57:55 +00:00
import { missingCaseError } from '../util/missingCaseError';
2020-06-04 18:16:19 +00:00
2020-11-13 19:57:55 +00:00
export interface PropsType {
activeCall?: ActiveCallType;
availableCameras: Array<MediaDeviceInfo>;
2020-11-13 19:57:55 +00:00
cancelCall: (_: CancelCallType) => void;
2020-10-01 19:09:15 +00:00
closeNeedPermissionScreen: () => void;
2020-11-13 19:57:55 +00:00
getGroupCallVideoFrameSource: (
conversationId: string,
demuxId: number
) => VideoFrameSource;
incomingCall?: {
call: DirectCallStateType;
2020-11-13 19:57:55 +00:00
conversation: ConversationType;
};
2020-08-27 00:03:42 +00:00
renderDeviceSelection: () => JSX.Element;
startCall: (payload: StartCallType) => void;
2020-10-08 01:25:33 +00:00
toggleParticipants: () => void;
acceptCall: (_: AcceptCallType) => void;
declineCall: (_: DeclineCallType) => void;
i18n: LocalizerType;
me: {
avatarPath?: string;
color?: ColorType;
name?: string;
phoneNumber?: string;
profileName?: string;
title: string;
};
setLocalAudio: (_: SetLocalAudioType) => void;
setLocalVideo: (_: SetLocalVideoType) => void;
setLocalPreview: (_: SetLocalPreviewType) => void;
setRendererCanvas: (_: SetRendererCanvasType) => void;
hangUp: (_: HangUpType) => void;
togglePip: () => void;
toggleSettings: () => void;
}
2020-06-04 18:16:19 +00:00
2020-11-13 19:57:55 +00:00
interface ActiveCallManagerPropsType extends PropsType {
activeCall: ActiveCallType;
}
const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
activeCall,
availableCameras,
2020-10-08 01:25:33 +00:00
cancelCall,
2020-10-01 19:09:15 +00:00
closeNeedPermissionScreen,
2020-06-04 18:16:19 +00:00
hangUp,
i18n,
2020-11-13 19:57:55 +00:00
getGroupCallVideoFrameSource,
me,
2020-08-27 00:03:42 +00:00
renderDeviceSelection,
2020-06-04 18:16:19 +00:00
setLocalAudio,
2020-08-27 00:03:42 +00:00
setLocalPreview,
2020-06-04 18:16:19 +00:00
setLocalVideo,
2020-08-27 00:03:42 +00:00
setRendererCanvas,
2020-10-08 01:25:33 +00:00
startCall,
toggleParticipants,
2020-10-01 00:43:05 +00:00
togglePip,
2020-08-27 00:03:42 +00:00
toggleSettings,
2020-11-13 19:57:55 +00:00
}) => {
const {
2020-11-17 15:07:53 +00:00
call,
activeCallState,
conversation,
groupCallParticipants,
} = activeCall;
const {
2020-11-13 19:57:55 +00:00
hasLocalAudio,
hasLocalVideo,
2020-11-17 15:07:53 +00:00
joinedAt,
2020-11-13 19:57:55 +00:00
pip,
2020-11-17 15:07:53 +00:00
settingsDialogOpen,
showParticipantsList,
2020-11-13 19:57:55 +00:00
} = activeCallState;
const cancelActiveCall = useCallback(() => {
cancelCall({ conversationId: conversation.id });
}, [cancelCall, conversation.id]);
const joinActiveCall = useCallback(() => {
startCall({
callMode: call.callMode,
conversationId: conversation.id,
hasLocalAudio,
hasLocalVideo,
2020-11-13 19:57:55 +00:00
});
}, [startCall, call.callMode, conversation.id, hasLocalAudio, hasLocalVideo]);
const getGroupCallVideoFrameSourceForActiveCall = useCallback(
(demuxId: number) => {
return getGroupCallVideoFrameSource(conversation.id, demuxId);
},
[getGroupCallVideoFrameSource, conversation.id]
);
let showCallLobby: boolean;
2020-11-13 19:57:55 +00:00
switch (call.callMode) {
case CallMode.Direct: {
const { callState, callEndedReason } = call;
const ended = callState === CallState.Ended;
if (
ended &&
callEndedReason === CallEndedReason.RemoteHangupNeedPermission
) {
return (
<CallNeedPermissionScreen
close={closeNeedPermissionScreen}
conversation={conversation}
i18n={i18n}
/>
);
}
2020-11-13 19:57:55 +00:00
showCallLobby = !callState;
break;
}
2020-11-13 19:57:55 +00:00
case CallMode.Group: {
showCallLobby = call.joinState === GroupCallJoinState.NotJoined;
break;
2020-10-01 00:43:05 +00:00
}
2020-11-13 19:57:55 +00:00
default:
throw missingCaseError(call);
}
2020-10-01 00:43:05 +00:00
2020-11-13 19:57:55 +00:00
if (showCallLobby) {
2020-11-17 15:07:53 +00:00
const participantNames = groupCallParticipants.map(participant =>
participant.isSelf
? i18n('you')
: participant.firstName || participant.title
);
2020-06-04 18:16:19 +00:00
return (
2020-08-27 00:03:42 +00:00
<>
2020-11-13 19:57:55 +00:00
<CallingLobby
availableCameras={availableCameras}
conversation={conversation}
2020-08-27 00:03:42 +00:00
hasLocalAudio={hasLocalAudio}
hasLocalVideo={hasLocalVideo}
i18n={i18n}
2020-11-17 15:07:53 +00:00
isGroupCall={call.callMode === CallMode.Group}
me={me}
2020-11-13 19:57:55 +00:00
onCallCanceled={cancelActiveCall}
onJoinCall={joinActiveCall}
2020-11-17 15:07:53 +00:00
participantNames={participantNames}
2020-08-27 00:03:42 +00:00
setLocalPreview={setLocalPreview}
setLocalAudio={setLocalAudio}
setLocalVideo={setLocalVideo}
2020-11-20 19:39:50 +00:00
showParticipantsList={showParticipantsList}
2020-11-13 19:57:55 +00:00
toggleParticipants={toggleParticipants}
2020-08-27 00:03:42 +00:00
toggleSettings={toggleSettings}
/>
{settingsDialogOpen && renderDeviceSelection()}
2020-11-17 15:07:53 +00:00
{showParticipantsList && call.callMode === CallMode.Group ? (
<CallingParticipantsList
i18n={i18n}
onClose={toggleParticipants}
participants={groupCallParticipants}
/>
) : null}
2020-08-27 00:03:42 +00:00
</>
2020-06-04 18:16:19 +00:00
);
}
2020-11-17 15:07:53 +00:00
if (pip) {
2020-11-13 19:57:55 +00:00
return (
<CallingPip
activeCall={activeCall}
2020-11-17 15:07:53 +00:00
getGroupCallVideoFrameSource={getGroupCallVideoFrameSourceForActiveCall}
2020-11-13 19:57:55 +00:00
hangUp={hangUp}
hasLocalVideo={hasLocalVideo}
i18n={i18n}
setLocalPreview={setLocalPreview}
setRendererCanvas={setRendererCanvas}
togglePip={togglePip}
/>
);
}
return (
<>
<CallScreen
activeCall={activeCall}
2020-11-13 19:57:55 +00:00
getGroupCallVideoFrameSource={getGroupCallVideoFrameSourceForActiveCall}
hangUp={hangUp}
hasLocalAudio={hasLocalAudio}
hasLocalVideo={hasLocalVideo}
i18n={i18n}
joinedAt={joinedAt}
me={me}
setLocalPreview={setLocalPreview}
setRendererCanvas={setRendererCanvas}
setLocalAudio={setLocalAudio}
setLocalVideo={setLocalVideo}
2020-11-17 15:07:53 +00:00
stickyControls={showParticipantsList}
toggleParticipants={toggleParticipants}
2020-11-13 19:57:55 +00:00
togglePip={togglePip}
toggleSettings={toggleSettings}
/>
{settingsDialogOpen && renderDeviceSelection()}
2020-11-17 15:07:53 +00:00
{showParticipantsList && call.callMode === CallMode.Group ? (
<CallingParticipantsList
i18n={i18n}
onClose={toggleParticipants}
participants={groupCallParticipants}
/>
) : null}
2020-11-13 19:57:55 +00:00
</>
);
};
export const CallManager: React.FC<PropsType> = props => {
const { activeCall, incomingCall, acceptCall, declineCall, i18n } = props;
if (activeCall) {
// `props` should logically have an `activeCall` at this point, but TypeScript can't
// figure that out, so we pass it in again.
return <ActiveCallManager {...props} activeCall={activeCall} />;
}
// In the future, we may want to show the incoming call bar when a call is active.
if (incomingCall) {
2020-06-04 18:16:19 +00:00
return (
<IncomingCallBar
acceptCall={acceptCall}
declineCall={declineCall}
i18n={i18n}
call={incomingCall.call}
conversation={incomingCall.conversation}
2020-06-04 18:16:19 +00:00
/>
);
}
return null;
};