2020-10-30 20:34:04 +00:00
|
|
|
// Copyright 2020 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2020-08-27 00:03:42 +00:00
|
|
|
import React from 'react';
|
2020-06-04 18:16:19 +00:00
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import { mapDispatchToProps } from '../actions';
|
|
|
|
import { CallManager } from '../../components/CallManager';
|
2020-11-13 19:57:55 +00:00
|
|
|
import { calling as callingService } from '../../services/calling';
|
2020-11-06 17:36:37 +00:00
|
|
|
import { getMe, getConversationSelector } from '../selectors/conversations';
|
2020-11-17 15:07:53 +00:00
|
|
|
import { getActiveCall, GroupCallParticipantInfoType } from '../ducks/calling';
|
2020-11-13 19:57:55 +00:00
|
|
|
import { getIncomingCall } from '../selectors/calling';
|
2020-11-20 20:14:07 +00:00
|
|
|
import {
|
|
|
|
CallMode,
|
|
|
|
GroupCallPeekedParticipantType,
|
|
|
|
GroupCallRemoteParticipantType,
|
|
|
|
} from '../../types/Calling';
|
2020-06-04 18:16:19 +00:00
|
|
|
import { StateType } from '../reducer';
|
|
|
|
|
|
|
|
import { getIntl } from '../selectors/user';
|
|
|
|
|
2020-08-27 00:03:42 +00:00
|
|
|
import { SmartCallingDeviceSelection } from './CallingDeviceSelection';
|
|
|
|
|
|
|
|
function renderDeviceSelection(): JSX.Element {
|
2020-10-26 21:32:46 +00:00
|
|
|
return <SmartCallingDeviceSelection />;
|
2020-08-27 00:03:42 +00:00
|
|
|
}
|
|
|
|
|
2020-11-13 19:57:55 +00:00
|
|
|
const getGroupCallVideoFrameSource = callingService.getGroupCallVideoFrameSource.bind(
|
|
|
|
callingService
|
|
|
|
);
|
|
|
|
|
2020-11-06 17:36:37 +00:00
|
|
|
const mapStateToActiveCallProp = (state: StateType) => {
|
2020-10-08 01:25:33 +00:00
|
|
|
const { calling } = state;
|
2020-11-06 17:36:37 +00:00
|
|
|
const { activeCallState } = calling;
|
|
|
|
|
|
|
|
if (!activeCallState) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const call = getActiveCall(calling);
|
|
|
|
if (!call) {
|
|
|
|
window.log.error(
|
|
|
|
'There was an active call state but no corresponding call'
|
|
|
|
);
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
2020-11-17 15:07:53 +00:00
|
|
|
const conversationSelector = getConversationSelector(state);
|
|
|
|
const conversation = conversationSelector(activeCallState.conversationId);
|
2020-11-06 17:36:37 +00:00
|
|
|
if (!conversation) {
|
|
|
|
window.log.error('The active call has no corresponding conversation');
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
2020-11-20 20:14:07 +00:00
|
|
|
// TODO: The way we deal with remote participants isn't ideal. See DESKTOP-949.
|
|
|
|
let isCallFull = false;
|
|
|
|
const groupCallPeekedParticipants: Array<GroupCallPeekedParticipantType> = [];
|
2020-11-17 15:07:53 +00:00
|
|
|
const groupCallParticipants: Array<GroupCallRemoteParticipantType> = [];
|
2020-11-20 20:14:07 +00:00
|
|
|
if (call.callMode === CallMode.Group) {
|
|
|
|
isCallFull = call.peekInfo.deviceCount >= call.peekInfo.maxDevices;
|
|
|
|
|
|
|
|
call.peekInfo.conversationIds.forEach((conversationId: string) => {
|
|
|
|
const peekedConversation = conversationSelector(conversationId);
|
|
|
|
|
|
|
|
if (!peekedConversation) {
|
|
|
|
window.log.error(
|
|
|
|
'Peeked participant has no corresponding conversation'
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
groupCallPeekedParticipants.push({
|
|
|
|
avatarPath: peekedConversation.avatarPath,
|
|
|
|
color: peekedConversation.color,
|
|
|
|
firstName: peekedConversation.firstName,
|
|
|
|
isSelf: conversationId === state.user.ourConversationId,
|
|
|
|
name: peekedConversation.name,
|
|
|
|
profileName: peekedConversation.profileName,
|
|
|
|
title: peekedConversation.title,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-11-17 15:07:53 +00:00
|
|
|
call.remoteParticipants.forEach(
|
|
|
|
(remoteParticipant: GroupCallParticipantInfoType) => {
|
|
|
|
const remoteConversation = conversationSelector(
|
|
|
|
remoteParticipant.conversationId
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!remoteConversation) {
|
|
|
|
window.log.error(
|
|
|
|
'Remote participant has no corresponding conversation'
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
groupCallParticipants.push({
|
|
|
|
avatarPath: remoteConversation.avatarPath,
|
|
|
|
color: remoteConversation.color,
|
2020-11-19 18:13:36 +00:00
|
|
|
demuxId: remoteParticipant.demuxId,
|
2020-11-17 15:07:53 +00:00
|
|
|
firstName: remoteConversation.firstName,
|
|
|
|
hasRemoteAudio: remoteParticipant.hasRemoteAudio,
|
|
|
|
hasRemoteVideo: remoteParticipant.hasRemoteVideo,
|
2020-12-02 01:30:25 +00:00
|
|
|
isBlocked: Boolean(remoteConversation.isBlocked),
|
2020-11-17 15:07:53 +00:00
|
|
|
isSelf: remoteParticipant.isSelf,
|
2020-11-20 19:39:50 +00:00
|
|
|
name: remoteConversation.name,
|
2020-11-17 15:07:53 +00:00
|
|
|
profileName: remoteConversation.profileName,
|
2020-12-01 23:52:09 +00:00
|
|
|
speakerTime: remoteParticipant.speakerTime,
|
2020-11-17 15:07:53 +00:00
|
|
|
title: remoteConversation.title,
|
2020-11-19 18:13:36 +00:00
|
|
|
videoAspectRatio: remoteParticipant.videoAspectRatio,
|
2020-11-17 15:07:53 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
2020-12-02 01:30:25 +00:00
|
|
|
|
|
|
|
groupCallParticipants.sort((a, b) => a.title.localeCompare(b.title));
|
2020-11-17 15:07:53 +00:00
|
|
|
}
|
|
|
|
|
2020-06-04 18:16:19 +00:00
|
|
|
return {
|
2020-11-06 17:36:37 +00:00
|
|
|
activeCallState,
|
2020-11-17 15:07:53 +00:00
|
|
|
call,
|
2020-11-06 17:36:37 +00:00
|
|
|
conversation,
|
2020-11-20 20:14:07 +00:00
|
|
|
isCallFull,
|
|
|
|
groupCallPeekedParticipants,
|
2020-11-17 15:07:53 +00:00
|
|
|
groupCallParticipants,
|
2020-06-04 18:16:19 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-11-06 17:36:37 +00:00
|
|
|
const mapStateToIncomingCallProp = (state: StateType) => {
|
2020-12-02 18:11:54 +00:00
|
|
|
const call = getIncomingCall(state);
|
2020-11-06 17:36:37 +00:00
|
|
|
if (!call) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const conversation = getConversationSelector(state)(call.conversationId);
|
|
|
|
if (!conversation) {
|
|
|
|
window.log.error('The incoming call has no corresponding conversation');
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
call,
|
|
|
|
conversation,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const mapStateToProps = (state: StateType) => ({
|
|
|
|
activeCall: mapStateToActiveCallProp(state),
|
|
|
|
availableCameras: state.calling.availableCameras,
|
2020-11-13 19:57:55 +00:00
|
|
|
getGroupCallVideoFrameSource,
|
2020-11-06 17:36:37 +00:00
|
|
|
i18n: getIntl(state),
|
|
|
|
incomingCall: mapStateToIncomingCallProp(state),
|
|
|
|
me: getMe(state),
|
|
|
|
renderDeviceSelection,
|
|
|
|
});
|
|
|
|
|
2020-06-04 18:16:19 +00:00
|
|
|
const smart = connect(mapStateToProps, mapDispatchToProps);
|
|
|
|
|
|
|
|
export const SmartCallManager = smart(CallManager);
|