Group calling participants refactor
This commit is contained in:
parent
be99bbe87a
commit
c85ea814b1
18 changed files with 750 additions and 436 deletions
|
@ -35,8 +35,8 @@ const getConversation = () => ({
|
|||
lastUpdated: Date.now(),
|
||||
});
|
||||
|
||||
const getCallState = () => ({
|
||||
conversationId: '3051234567',
|
||||
const getCommonActiveCallData = () => ({
|
||||
conversation: getConversation(),
|
||||
joinedAt: Date.now(),
|
||||
hasLocalAudio: boolean('hasLocalAudio', true),
|
||||
hasLocalVideo: boolean('hasLocalVideo', false),
|
||||
|
@ -69,6 +69,7 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
|
|||
hangUp: action('hang-up'),
|
||||
i18n,
|
||||
me: {
|
||||
uuid: 'cb0dd0c8-7393-41e9-a0aa-d631c4109541',
|
||||
color: select('Caller color', Colors, 'ultramarine' as ColorType),
|
||||
title: text('Caller Title', 'Morty Smith'),
|
||||
},
|
||||
|
@ -92,19 +93,11 @@ story.add('Ongoing Direct Call', () => (
|
|||
<CallManager
|
||||
{...createProps({
|
||||
activeCall: {
|
||||
call: {
|
||||
callMode: CallMode.Direct as CallMode.Direct,
|
||||
conversationId: '3051234567',
|
||||
callState: CallState.Accepted,
|
||||
isIncoming: false,
|
||||
isVideoCall: true,
|
||||
hasRemoteVideo: true,
|
||||
},
|
||||
activeCallState: getCallState(),
|
||||
conversation: getConversation(),
|
||||
isCallFull: false,
|
||||
groupCallPeekedParticipants: [],
|
||||
groupCallParticipants: [],
|
||||
...getCommonActiveCallData(),
|
||||
callMode: CallMode.Direct,
|
||||
callState: CallState.Accepted,
|
||||
peekedParticipants: [],
|
||||
remoteParticipants: [{ hasRemoteVideo: true }],
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
@ -114,23 +107,14 @@ story.add('Ongoing Group Call', () => (
|
|||
<CallManager
|
||||
{...createProps({
|
||||
activeCall: {
|
||||
call: {
|
||||
callMode: CallMode.Group as CallMode.Group,
|
||||
conversationId: '3051234567',
|
||||
connectionState: GroupCallConnectionState.Connected,
|
||||
joinState: GroupCallJoinState.Joined,
|
||||
peekInfo: {
|
||||
conversationIds: [],
|
||||
maxDevices: 16,
|
||||
deviceCount: 0,
|
||||
},
|
||||
remoteParticipants: [],
|
||||
},
|
||||
activeCallState: getCallState(),
|
||||
conversation: getConversation(),
|
||||
isCallFull: false,
|
||||
groupCallPeekedParticipants: [],
|
||||
groupCallParticipants: [],
|
||||
...getCommonActiveCallData(),
|
||||
callMode: CallMode.Group,
|
||||
connectionState: GroupCallConnectionState.Connected,
|
||||
deviceCount: 0,
|
||||
joinState: GroupCallJoinState.Joined,
|
||||
maxDevices: 5,
|
||||
peekedParticipants: [],
|
||||
remoteParticipants: [],
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
@ -151,14 +135,12 @@ story.add('Call Request Needed', () => (
|
|||
<CallManager
|
||||
{...createProps({
|
||||
activeCall: {
|
||||
call: getIncomingCallState({
|
||||
callEndedReason: CallEndedReason.RemoteHangupNeedPermission,
|
||||
}),
|
||||
activeCallState: getCallState(),
|
||||
conversation: getConversation(),
|
||||
isCallFull: false,
|
||||
groupCallPeekedParticipants: [],
|
||||
groupCallParticipants: [],
|
||||
...getCommonActiveCallData(),
|
||||
callEndedReason: CallEndedReason.RemoteHangupNeedPermission,
|
||||
callMode: CallMode.Direct,
|
||||
callState: CallState.Accepted,
|
||||
peekedParticipants: [],
|
||||
remoteParticipants: [{ hasRemoteVideo: true }],
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
|
|
@ -9,6 +9,7 @@ import { CallingParticipantsList } from './CallingParticipantsList';
|
|||
import { CallingPip } from './CallingPip';
|
||||
import { IncomingCallBar } from './IncomingCallBar';
|
||||
import {
|
||||
ActiveCallType,
|
||||
CallEndedReason,
|
||||
CallMode,
|
||||
CallState,
|
||||
|
@ -19,7 +20,6 @@ import {
|
|||
import { ConversationType } from '../state/ducks/conversations';
|
||||
import {
|
||||
AcceptCallType,
|
||||
ActiveCallType,
|
||||
CancelCallType,
|
||||
DeclineCallType,
|
||||
DirectCallStateType,
|
||||
|
@ -61,6 +61,7 @@ export interface PropsType {
|
|||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
uuid: string;
|
||||
};
|
||||
setGroupCallVideoRequest: (_: SetGroupCallVideoRequestType) => void;
|
||||
setLocalAudio: (_: SetLocalAudioType) => void;
|
||||
|
@ -97,21 +98,15 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
toggleSettings,
|
||||
}) => {
|
||||
const {
|
||||
call,
|
||||
activeCallState,
|
||||
conversation,
|
||||
groupCallPeekedParticipants,
|
||||
groupCallParticipants,
|
||||
isCallFull,
|
||||
} = activeCall;
|
||||
const {
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
joinedAt,
|
||||
peekedParticipants,
|
||||
pip,
|
||||
settingsDialogOpen,
|
||||
showParticipantsList,
|
||||
} = activeCallState;
|
||||
} = activeCall;
|
||||
|
||||
const cancelActiveCall = useCallback(() => {
|
||||
cancelCall({ conversationId: conversation.id });
|
||||
|
@ -119,12 +114,18 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
|
||||
const joinActiveCall = useCallback(() => {
|
||||
startCall({
|
||||
callMode: call.callMode,
|
||||
callMode: activeCall.callMode,
|
||||
conversationId: conversation.id,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
});
|
||||
}, [startCall, call.callMode, conversation.id, hasLocalAudio, hasLocalVideo]);
|
||||
}, [
|
||||
startCall,
|
||||
activeCall.callMode,
|
||||
conversation.id,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
]);
|
||||
|
||||
const getGroupCallVideoFrameSourceForActiveCall = useCallback(
|
||||
(demuxId: number) => {
|
||||
|
@ -143,11 +144,12 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
[setGroupCallVideoRequest, conversation.id]
|
||||
);
|
||||
|
||||
let isCallFull: boolean;
|
||||
let showCallLobby: boolean;
|
||||
|
||||
switch (call.callMode) {
|
||||
switch (activeCall.callMode) {
|
||||
case CallMode.Direct: {
|
||||
const { callState, callEndedReason } = call;
|
||||
const { callState, callEndedReason } = activeCall;
|
||||
const ended = callState === CallState.Ended;
|
||||
if (
|
||||
ended &&
|
||||
|
@ -162,22 +164,19 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
);
|
||||
}
|
||||
showCallLobby = !callState;
|
||||
isCallFull = false;
|
||||
break;
|
||||
}
|
||||
case CallMode.Group: {
|
||||
showCallLobby = call.joinState === GroupCallJoinState.NotJoined;
|
||||
showCallLobby = activeCall.joinState === GroupCallJoinState.NotJoined;
|
||||
isCallFull = activeCall.deviceCount >= activeCall.maxDevices;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw missingCaseError(call);
|
||||
throw missingCaseError(activeCall);
|
||||
}
|
||||
|
||||
if (showCallLobby) {
|
||||
const participantNames = groupCallPeekedParticipants.map(participant =>
|
||||
participant.isSelf
|
||||
? i18n('you')
|
||||
: participant.firstName || participant.title
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<CallingLobby
|
||||
|
@ -186,12 +185,12 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
hasLocalAudio={hasLocalAudio}
|
||||
hasLocalVideo={hasLocalVideo}
|
||||
i18n={i18n}
|
||||
isGroupCall={call.callMode === CallMode.Group}
|
||||
isGroupCall={activeCall.callMode === CallMode.Group}
|
||||
isCallFull={isCallFull}
|
||||
me={me}
|
||||
onCallCanceled={cancelActiveCall}
|
||||
onJoinCall={joinActiveCall}
|
||||
participantNames={participantNames}
|
||||
peekedParticipants={peekedParticipants}
|
||||
setLocalPreview={setLocalPreview}
|
||||
setLocalAudio={setLocalAudio}
|
||||
setLocalVideo={setLocalVideo}
|
||||
|
@ -200,11 +199,11 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
toggleSettings={toggleSettings}
|
||||
/>
|
||||
{settingsDialogOpen && renderDeviceSelection()}
|
||||
{showParticipantsList && call.callMode === CallMode.Group ? (
|
||||
{showParticipantsList && activeCall.callMode === CallMode.Group ? (
|
||||
<CallingParticipantsList
|
||||
i18n={i18n}
|
||||
onClose={toggleParticipants}
|
||||
participants={groupCallParticipants}
|
||||
participants={peekedParticipants}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
|
@ -227,14 +226,30 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
);
|
||||
}
|
||||
|
||||
const groupCallParticipantsForParticipantsList =
|
||||
activeCall.callMode === CallMode.Group
|
||||
? [
|
||||
...activeCall.remoteParticipants.map(participant => ({
|
||||
...participant,
|
||||
hasAudio: participant.hasRemoteAudio,
|
||||
hasVideo: participant.hasRemoteVideo,
|
||||
isSelf: false,
|
||||
})),
|
||||
{
|
||||
...me,
|
||||
hasAudio: hasLocalAudio,
|
||||
hasVideo: hasLocalVideo,
|
||||
isSelf: true,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<CallScreen
|
||||
activeCall={activeCall}
|
||||
getGroupCallVideoFrameSource={getGroupCallVideoFrameSourceForActiveCall}
|
||||
hangUp={hangUp}
|
||||
hasLocalAudio={hasLocalAudio}
|
||||
hasLocalVideo={hasLocalVideo}
|
||||
i18n={i18n}
|
||||
joinedAt={joinedAt}
|
||||
me={me}
|
||||
|
@ -249,11 +264,11 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
toggleSettings={toggleSettings}
|
||||
/>
|
||||
{settingsDialogOpen && renderDeviceSelection()}
|
||||
{showParticipantsList && call.callMode === CallMode.Group ? (
|
||||
{showParticipantsList && activeCall.callMode === CallMode.Group ? (
|
||||
<CallingParticipantsList
|
||||
i18n={i18n}
|
||||
onClose={toggleParticipants}
|
||||
participants={groupCallParticipants}
|
||||
participants={groupCallParticipantsForParticipantsList}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
|
|
|
@ -12,102 +12,128 @@ import {
|
|||
CallState,
|
||||
GroupCallConnectionState,
|
||||
GroupCallJoinState,
|
||||
GroupCallPeekedParticipantType,
|
||||
GroupCallRemoteParticipantType,
|
||||
} from '../types/Calling';
|
||||
import { Colors } from '../types/Colors';
|
||||
import {
|
||||
DirectCallStateType,
|
||||
GroupCallStateType,
|
||||
} from '../state/ducks/calling';
|
||||
import { CallScreen, PropsType } from './CallScreen';
|
||||
import { setup as setupI18n } from '../../js/modules/i18n';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
function getGroupCallState(): GroupCallStateType {
|
||||
return {
|
||||
callMode: CallMode.Group,
|
||||
conversationId: '3051234567',
|
||||
connectionState: GroupCallConnectionState.Connected,
|
||||
joinState: GroupCallJoinState.Joined,
|
||||
peekInfo: {
|
||||
conversationIds: [],
|
||||
maxDevices: 16,
|
||||
deviceCount: 0,
|
||||
},
|
||||
remoteParticipants: [],
|
||||
};
|
||||
const conversation = {
|
||||
id: '3051234567',
|
||||
avatarPath: undefined,
|
||||
color: Colors[0],
|
||||
title: 'Rick Sanchez',
|
||||
name: 'Rick Sanchez',
|
||||
phoneNumber: '3051234567',
|
||||
profileName: 'Rick Sanchez',
|
||||
markedUnread: false,
|
||||
type: 'direct' as const,
|
||||
lastUpdated: Date.now(),
|
||||
};
|
||||
|
||||
interface OverridePropsBase {
|
||||
hasLocalAudio?: boolean;
|
||||
hasLocalVideo?: boolean;
|
||||
}
|
||||
|
||||
function getDirectCallState(
|
||||
overrideProps: {
|
||||
callState?: CallState;
|
||||
hasRemoteVideo?: boolean;
|
||||
} = {}
|
||||
): DirectCallStateType {
|
||||
return {
|
||||
callMode: CallMode.Direct,
|
||||
conversationId: '3051234567',
|
||||
callState: select(
|
||||
'callState',
|
||||
CallState,
|
||||
overrideProps.callState || CallState.Accepted
|
||||
),
|
||||
hasRemoteVideo: boolean(
|
||||
'hasRemoteVideo',
|
||||
Boolean(overrideProps.hasRemoteVideo)
|
||||
),
|
||||
isIncoming: false,
|
||||
isVideoCall: true,
|
||||
};
|
||||
interface DirectCallOverrideProps extends OverridePropsBase {
|
||||
callMode: CallMode.Direct;
|
||||
callState?: CallState;
|
||||
hasRemoteVideo?: boolean;
|
||||
}
|
||||
|
||||
interface GroupCallOverrideProps extends OverridePropsBase {
|
||||
callMode: CallMode.Group;
|
||||
connectionState?: GroupCallConnectionState;
|
||||
peekedParticipants?: Array<GroupCallPeekedParticipantType>;
|
||||
remoteParticipants?: Array<GroupCallRemoteParticipantType>;
|
||||
}
|
||||
|
||||
const createActiveDirectCallProp = (
|
||||
overrideProps: DirectCallOverrideProps
|
||||
) => ({
|
||||
callMode: CallMode.Direct as CallMode.Direct,
|
||||
conversation,
|
||||
callState: select(
|
||||
'callState',
|
||||
CallState,
|
||||
overrideProps.callState || CallState.Accepted
|
||||
),
|
||||
peekedParticipants: [] as [],
|
||||
remoteParticipants: [
|
||||
{
|
||||
hasRemoteVideo: boolean(
|
||||
'hasRemoteVideo',
|
||||
Boolean(overrideProps.hasRemoteVideo)
|
||||
),
|
||||
},
|
||||
] as [
|
||||
{
|
||||
hasRemoteVideo: boolean;
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
const createActiveGroupCallProp = (overrideProps: GroupCallOverrideProps) => ({
|
||||
callMode: CallMode.Group as CallMode.Group,
|
||||
connectionState:
|
||||
overrideProps.connectionState || GroupCallConnectionState.Connected,
|
||||
joinState: GroupCallJoinState.Joined,
|
||||
maxDevices: 5,
|
||||
deviceCount: (overrideProps.remoteParticipants || []).length,
|
||||
// Because remote participants are a superset, we can use them in place of peeked
|
||||
// participants.
|
||||
peekedParticipants:
|
||||
overrideProps.peekedParticipants || overrideProps.remoteParticipants || [],
|
||||
remoteParticipants: overrideProps.remoteParticipants || [],
|
||||
});
|
||||
|
||||
const createActiveCallProp = (
|
||||
overrideProps: DirectCallOverrideProps | GroupCallOverrideProps
|
||||
) => {
|
||||
const baseResult = {
|
||||
joinedAt: Date.now(),
|
||||
conversation,
|
||||
hasLocalAudio: boolean(
|
||||
'hasLocalAudio',
|
||||
overrideProps.hasLocalAudio || false
|
||||
),
|
||||
hasLocalVideo: boolean(
|
||||
'hasLocalVideo',
|
||||
overrideProps.hasLocalVideo || false
|
||||
),
|
||||
pip: false,
|
||||
settingsDialogOpen: false,
|
||||
showParticipantsList: false,
|
||||
};
|
||||
|
||||
switch (overrideProps.callMode) {
|
||||
case CallMode.Direct:
|
||||
return { ...baseResult, ...createActiveDirectCallProp(overrideProps) };
|
||||
case CallMode.Group:
|
||||
return { ...baseResult, ...createActiveGroupCallProp(overrideProps) };
|
||||
default:
|
||||
throw missingCaseError(overrideProps);
|
||||
}
|
||||
};
|
||||
|
||||
const createProps = (
|
||||
overrideProps: {
|
||||
callState?: CallState;
|
||||
callTypeState?: DirectCallStateType | GroupCallStateType;
|
||||
groupCallParticipants?: Array<GroupCallRemoteParticipantType>;
|
||||
hasLocalAudio?: boolean;
|
||||
hasLocalVideo?: boolean;
|
||||
hasRemoteVideo?: boolean;
|
||||
} = {}
|
||||
overrideProps: DirectCallOverrideProps | GroupCallOverrideProps = {
|
||||
callMode: CallMode.Direct as CallMode.Direct,
|
||||
}
|
||||
): PropsType => ({
|
||||
activeCall: {
|
||||
activeCallState: {
|
||||
conversationId: '123',
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: true,
|
||||
pip: false,
|
||||
settingsDialogOpen: false,
|
||||
showParticipantsList: false,
|
||||
},
|
||||
call: overrideProps.callTypeState || getDirectCallState(overrideProps),
|
||||
conversation: {
|
||||
id: '3051234567',
|
||||
avatarPath: undefined,
|
||||
color: Colors[0],
|
||||
title: 'Rick Sanchez',
|
||||
name: 'Rick Sanchez',
|
||||
phoneNumber: '3051234567',
|
||||
profileName: 'Rick Sanchez',
|
||||
markedUnread: false,
|
||||
type: 'direct',
|
||||
lastUpdated: Date.now(),
|
||||
},
|
||||
isCallFull: false,
|
||||
groupCallPeekedParticipants: [],
|
||||
groupCallParticipants: overrideProps.groupCallParticipants || [],
|
||||
},
|
||||
activeCall: createActiveCallProp(overrideProps),
|
||||
// We allow `any` here because this is fake and actually comes from RingRTC, which we
|
||||
// can't import.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
getGroupCallVideoFrameSource: noop as any,
|
||||
hangUp: action('hang-up'),
|
||||
hasLocalAudio: boolean('hasLocalAudio', overrideProps.hasLocalAudio || false),
|
||||
hasLocalVideo: boolean('hasLocalVideo', overrideProps.hasLocalVideo || false),
|
||||
i18n,
|
||||
joinedAt: Date.now(),
|
||||
me: {
|
||||
color: Colors[1],
|
||||
name: 'Morty Smith',
|
||||
|
@ -135,6 +161,7 @@ story.add('Pre-Ring', () => {
|
|||
return (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callMode: CallMode.Direct,
|
||||
callState: CallState.Prering,
|
||||
})}
|
||||
/>
|
||||
|
@ -145,6 +172,7 @@ story.add('Ringing', () => {
|
|||
return (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callMode: CallMode.Direct,
|
||||
callState: CallState.Ringing,
|
||||
})}
|
||||
/>
|
||||
|
@ -155,6 +183,7 @@ story.add('Reconnecting', () => {
|
|||
return (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callMode: CallMode.Direct,
|
||||
callState: CallState.Reconnecting,
|
||||
})}
|
||||
/>
|
||||
|
@ -165,6 +194,7 @@ story.add('Ended', () => {
|
|||
return (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callMode: CallMode.Direct,
|
||||
callState: CallState.Ended,
|
||||
})}
|
||||
/>
|
||||
|
@ -172,23 +202,45 @@ story.add('Ended', () => {
|
|||
});
|
||||
|
||||
story.add('hasLocalAudio', () => {
|
||||
return <CallScreen {...createProps({ hasLocalAudio: true })} />;
|
||||
return (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callMode: CallMode.Direct,
|
||||
hasLocalAudio: true,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
story.add('hasLocalVideo', () => {
|
||||
return <CallScreen {...createProps({ hasLocalVideo: true })} />;
|
||||
return (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callMode: CallMode.Direct,
|
||||
hasLocalVideo: true,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
story.add('hasRemoteVideo', () => {
|
||||
return <CallScreen {...createProps({ hasRemoteVideo: true })} />;
|
||||
return (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callMode: CallMode.Direct,
|
||||
hasRemoteVideo: true,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
story.add('Group call - 1', () => (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callTypeState: getGroupCallState(),
|
||||
groupCallParticipants: [
|
||||
callMode: CallMode.Group,
|
||||
remoteParticipants: [
|
||||
{
|
||||
uuid: '72fa60e5-25fb-472d-8a56-e56867c57dda',
|
||||
demuxId: 0,
|
||||
hasRemoteAudio: true,
|
||||
hasRemoteVideo: true,
|
||||
|
@ -205,9 +257,10 @@ story.add('Group call - 1', () => (
|
|||
story.add('Group call - Many', () => (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callTypeState: getGroupCallState(),
|
||||
groupCallParticipants: [
|
||||
callMode: CallMode.Group,
|
||||
remoteParticipants: [
|
||||
{
|
||||
uuid: '094586f5-8fc2-4ce2-a152-2dfcc99f4630',
|
||||
demuxId: 0,
|
||||
hasRemoteAudio: true,
|
||||
hasRemoteVideo: true,
|
||||
|
@ -217,6 +270,7 @@ story.add('Group call - Many', () => (
|
|||
videoAspectRatio: 1.3,
|
||||
},
|
||||
{
|
||||
uuid: 'cb5bdb24-4cbb-4650-8a7a-1a2807051e74',
|
||||
demuxId: 1,
|
||||
hasRemoteAudio: true,
|
||||
hasRemoteVideo: true,
|
||||
|
@ -226,6 +280,7 @@ story.add('Group call - Many', () => (
|
|||
videoAspectRatio: 1.3,
|
||||
},
|
||||
{
|
||||
uuid: '2d7d13ae-53dc-4a51-8dc7-976cd85e0b57',
|
||||
demuxId: 2,
|
||||
hasRemoteAudio: true,
|
||||
hasRemoteVideo: true,
|
||||
|
@ -242,12 +297,11 @@ story.add('Group call - Many', () => (
|
|||
story.add('Group call - reconnecting', () => (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callTypeState: {
|
||||
...getGroupCallState(),
|
||||
connectionState: GroupCallConnectionState.Reconnecting,
|
||||
},
|
||||
groupCallParticipants: [
|
||||
callMode: CallMode.Group,
|
||||
connectionState: GroupCallConnectionState.Reconnecting,
|
||||
remoteParticipants: [
|
||||
{
|
||||
uuid: '33871c64-0c22-45ce-8aa4-0ec237ac4a31',
|
||||
demuxId: 0,
|
||||
hasRemoteAudio: true,
|
||||
hasRemoteVideo: true,
|
||||
|
|
|
@ -5,7 +5,6 @@ import React, { useState, useRef, useEffect, useCallback } from 'react';
|
|||
import { noop } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
ActiveCallType,
|
||||
HangUpType,
|
||||
SetLocalAudioType,
|
||||
SetLocalPreviewType,
|
||||
|
@ -17,6 +16,7 @@ import { CallingHeader } from './CallingHeader';
|
|||
import { CallingButton, CallingButtonType } from './CallingButton';
|
||||
import { CallBackgroundBlur } from './CallBackgroundBlur';
|
||||
import {
|
||||
ActiveCallType,
|
||||
CallMode,
|
||||
CallState,
|
||||
GroupCallConnectionState,
|
||||
|
@ -34,8 +34,6 @@ export type PropsType = {
|
|||
activeCall: ActiveCallType;
|
||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||
hangUp: (_: HangUpType) => void;
|
||||
hasLocalAudio: boolean;
|
||||
hasLocalVideo: boolean;
|
||||
i18n: LocalizerType;
|
||||
joinedAt?: number;
|
||||
me: {
|
||||
|
@ -61,8 +59,6 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
activeCall,
|
||||
getGroupCallVideoFrameSource,
|
||||
hangUp,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
i18n,
|
||||
joinedAt,
|
||||
me,
|
||||
|
@ -76,7 +72,12 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
togglePip,
|
||||
toggleSettings,
|
||||
}) => {
|
||||
const { call, conversation, groupCallParticipants } = activeCall;
|
||||
const {
|
||||
conversation,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
showParticipantsList,
|
||||
} = activeCall;
|
||||
|
||||
const toggleAudio = useCallback(() => {
|
||||
setLocalAudio({
|
||||
|
@ -148,23 +149,25 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
};
|
||||
}, [toggleAudio, toggleVideo]);
|
||||
|
||||
let hasRemoteVideo: boolean;
|
||||
const hasRemoteVideo = activeCall.remoteParticipants.some(
|
||||
remoteParticipant => remoteParticipant.hasRemoteVideo
|
||||
);
|
||||
|
||||
let headerMessage: string | undefined;
|
||||
let headerTitle: string | undefined;
|
||||
let isConnected: boolean;
|
||||
let participantCount: number;
|
||||
let remoteParticipantsElement: JSX.Element;
|
||||
|
||||
switch (call.callMode) {
|
||||
switch (activeCall.callMode) {
|
||||
case CallMode.Direct:
|
||||
hasRemoteVideo = Boolean(call.hasRemoteVideo);
|
||||
headerMessage = renderHeaderMessage(
|
||||
i18n,
|
||||
call.callState || CallState.Prering,
|
||||
activeCall.callState || CallState.Prering,
|
||||
acceptedDuration
|
||||
);
|
||||
headerTitle = conversation.title;
|
||||
isConnected = call.callState === CallState.Accepted;
|
||||
isConnected = activeCall.callState === CallState.Accepted;
|
||||
participantCount = isConnected ? 2 : 0;
|
||||
remoteParticipantsElement = (
|
||||
<DirectCallRemoteParticipant
|
||||
|
@ -176,26 +179,24 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
);
|
||||
break;
|
||||
case CallMode.Group:
|
||||
hasRemoteVideo = call.remoteParticipants.some(
|
||||
remoteParticipant => remoteParticipant.hasRemoteVideo
|
||||
);
|
||||
participantCount = activeCall.groupCallParticipants.length;
|
||||
participantCount = activeCall.remoteParticipants.length + 1;
|
||||
headerMessage = undefined;
|
||||
headerTitle = activeCall.groupCallParticipants.length
|
||||
headerTitle = activeCall.remoteParticipants.length
|
||||
? undefined
|
||||
: i18n('calling__in-this-call--zero');
|
||||
isConnected = call.connectionState === GroupCallConnectionState.Connected;
|
||||
isConnected =
|
||||
activeCall.connectionState === GroupCallConnectionState.Connected;
|
||||
remoteParticipantsElement = (
|
||||
<GroupCallRemoteParticipants
|
||||
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
|
||||
i18n={i18n}
|
||||
remoteParticipants={groupCallParticipants}
|
||||
remoteParticipants={activeCall.remoteParticipants}
|
||||
setGroupCallVideoRequest={setGroupCallVideoRequest}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw missingCaseError(call);
|
||||
throw missingCaseError(activeCall);
|
||||
}
|
||||
|
||||
const videoButtonType = hasLocalVideo
|
||||
|
@ -214,14 +215,12 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
!showControls && !isAudioOnly && isConnected,
|
||||
});
|
||||
|
||||
const { showParticipantsList } = activeCall.activeCallState;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'module-calling__container',
|
||||
`module-ongoing-call__container--${getCallModeClassSuffix(
|
||||
call.callMode
|
||||
activeCall.callMode
|
||||
)}`
|
||||
)}
|
||||
onMouseMove={() => {
|
||||
|
@ -229,9 +228,9 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
}}
|
||||
role="group"
|
||||
>
|
||||
{call.callMode === CallMode.Group ? (
|
||||
{activeCall.callMode === CallMode.Group ? (
|
||||
<GroupCallToastManager
|
||||
connectionState={call.connectionState}
|
||||
connectionState={activeCall.connectionState}
|
||||
i18n={i18n}
|
||||
/>
|
||||
) : null}
|
||||
|
@ -241,7 +240,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
<CallingHeader
|
||||
canPip
|
||||
i18n={i18n}
|
||||
isGroupCall={call.callMode === CallMode.Group}
|
||||
isGroupCall={activeCall.callMode === CallMode.Group}
|
||||
message={headerMessage}
|
||||
participantCount={participantCount}
|
||||
showParticipantsList={showParticipantsList}
|
||||
|
|
|
@ -5,6 +5,7 @@ import * as React from 'react';
|
|||
import { storiesOf } from '@storybook/react';
|
||||
import { boolean } from '@storybook/addon-knobs';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { v4 as generateUuid } from 'uuid';
|
||||
|
||||
import { ColorType } from '../types/Colors';
|
||||
import { CallingLobby, PropsType } from './CallingLobby';
|
||||
|
@ -35,7 +36,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
|||
me: overrideProps.me || { color: 'ultramarine' as ColorType },
|
||||
onCallCanceled: action('on-call-canceled'),
|
||||
onJoinCall: action('on-join-call'),
|
||||
participantNames: overrideProps.participantNames || [],
|
||||
peekedParticipants: overrideProps.peekedParticipants || [],
|
||||
setLocalAudio: action('set-local-audio'),
|
||||
setLocalPreview: action('set-local-preview'),
|
||||
setLocalVideo: action('set-local-video'),
|
||||
|
@ -47,6 +48,12 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
|||
toggleSettings: action('toggle-settings'),
|
||||
});
|
||||
|
||||
const fakePeekedParticipant = (title: string) => ({
|
||||
isSelf: false,
|
||||
title,
|
||||
uuid: generateUuid(),
|
||||
});
|
||||
|
||||
const story = storiesOf('Components/CallingLobby', module);
|
||||
|
||||
story.add('Default', () => {
|
||||
|
@ -86,44 +93,51 @@ story.add('Local Video', () => {
|
|||
return <CallingLobby {...props} />;
|
||||
});
|
||||
|
||||
story.add('Group Call - 0', () => {
|
||||
const props = createProps({ isGroupCall: true, participantNames: [] });
|
||||
story.add('Group Call - 0 peeked participants', () => {
|
||||
const props = createProps({ isGroupCall: true, peekedParticipants: [] });
|
||||
return <CallingLobby {...props} />;
|
||||
});
|
||||
|
||||
story.add('Group Call - 1', () => {
|
||||
const props = createProps({ isGroupCall: true, participantNames: ['Sam'] });
|
||||
return <CallingLobby {...props} />;
|
||||
});
|
||||
|
||||
story.add('Group Call - 2', () => {
|
||||
story.add('Group Call - 1 peeked participant', () => {
|
||||
const props = createProps({
|
||||
isGroupCall: true,
|
||||
participantNames: ['Sam', 'Cayce'],
|
||||
peekedParticipants: ['Sam'].map(fakePeekedParticipant),
|
||||
});
|
||||
return <CallingLobby {...props} />;
|
||||
});
|
||||
|
||||
story.add('Group Call - 3', () => {
|
||||
story.add('Group Call - 2 peeked participants', () => {
|
||||
const props = createProps({
|
||||
isGroupCall: true,
|
||||
participantNames: ['Sam', 'Cayce', 'April'],
|
||||
peekedParticipants: ['Sam', 'Cayce'].map(fakePeekedParticipant),
|
||||
});
|
||||
return <CallingLobby {...props} />;
|
||||
});
|
||||
|
||||
story.add('Group Call - 4', () => {
|
||||
story.add('Group Call - 3 peeked participants', () => {
|
||||
const props = createProps({
|
||||
isGroupCall: true,
|
||||
participantNames: ['Sam', 'Cayce', 'April', 'Logan', 'Carl'],
|
||||
peekedParticipants: ['Sam', 'Cayce', 'April'].map(fakePeekedParticipant),
|
||||
});
|
||||
return <CallingLobby {...props} />;
|
||||
});
|
||||
|
||||
story.add('Group Call - 4 (participants list)', () => {
|
||||
story.add('Group Call - 4 peeked participants', () => {
|
||||
const props = createProps({
|
||||
isGroupCall: true,
|
||||
participantNames: ['Sam', 'Cayce', 'April', 'Logan', 'Carl'],
|
||||
peekedParticipants: ['Sam', 'Cayce', 'April', 'Logan', 'Carl'].map(
|
||||
fakePeekedParticipant
|
||||
),
|
||||
});
|
||||
return <CallingLobby {...props} />;
|
||||
});
|
||||
|
||||
story.add('Group Call - 4 peeked participants (participants list)', () => {
|
||||
const props = createProps({
|
||||
isGroupCall: true,
|
||||
peekedParticipants: ['Sam', 'Cayce', 'April', 'Logan', 'Carl'].map(
|
||||
fakePeekedParticipant
|
||||
),
|
||||
showParticipantsList: true,
|
||||
});
|
||||
return <CallingLobby {...props} />;
|
||||
|
|
|
@ -31,7 +31,12 @@ export type PropsType = {
|
|||
};
|
||||
onCallCanceled: () => void;
|
||||
onJoinCall: () => void;
|
||||
participantNames: Array<string>;
|
||||
peekedParticipants: Array<{
|
||||
firstName?: string;
|
||||
isSelf: boolean;
|
||||
title: string;
|
||||
uuid: string;
|
||||
}>;
|
||||
setLocalAudio: (_: SetLocalAudioType) => void;
|
||||
setLocalVideo: (_: SetLocalVideoType) => void;
|
||||
setLocalPreview: (_: SetLocalPreviewType) => void;
|
||||
|
@ -51,7 +56,7 @@ export const CallingLobby = ({
|
|||
me,
|
||||
onCallCanceled,
|
||||
onJoinCall,
|
||||
participantNames,
|
||||
peekedParticipants,
|
||||
setLocalAudio,
|
||||
setLocalPreview,
|
||||
setLocalVideo,
|
||||
|
@ -114,6 +119,16 @@ export const CallingLobby = ({
|
|||
? CallingButtonType.AUDIO_ON
|
||||
: CallingButtonType.AUDIO_OFF;
|
||||
|
||||
// It should be rare to see yourself in this list, but it's possible if (1) you rejoin
|
||||
// quickly, causing the server to return stale state (2) you have joined on another
|
||||
// device.
|
||||
// TODO: Improve the "it's you" case; see DESKTOP-926.
|
||||
const participantNames = peekedParticipants.map(participant =>
|
||||
participant.isSelf
|
||||
? i18n('you')
|
||||
: participant.firstName || participant.title
|
||||
);
|
||||
|
||||
let joinButton: JSX.Element;
|
||||
if (isCallFull) {
|
||||
joinButton = (
|
||||
|
@ -159,7 +174,7 @@ export const CallingLobby = ({
|
|||
title={conversation.title}
|
||||
i18n={i18n}
|
||||
isGroupCall={isGroupCall}
|
||||
participantCount={participantNames.length}
|
||||
participantCount={peekedParticipants.length}
|
||||
showParticipantsList={showParticipantsList}
|
||||
toggleParticipants={toggleParticipants}
|
||||
toggleSettings={toggleSettings}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import * as React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { v4 as generateUuid } from 'uuid';
|
||||
|
||||
import { CallingParticipantsList, PropsType } from './CallingParticipantsList';
|
||||
import { Colors } from '../types/Colors';
|
||||
|
@ -29,6 +30,7 @@ function createParticipant(
|
|||
profileName: participantProps.title,
|
||||
title: String(participantProps.title),
|
||||
videoAspectRatio: 1.3,
|
||||
uuid: generateUuid(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -9,18 +9,28 @@ import { Avatar } from './Avatar';
|
|||
import { ContactName } from './conversation/ContactName';
|
||||
import { InContactsIcon } from './InContactsIcon';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import { GroupCallRemoteParticipantType } from '../types/Calling';
|
||||
import { GroupCallPeekedParticipantType } from '../types/Calling';
|
||||
|
||||
interface ParticipantType extends GroupCallPeekedParticipantType {
|
||||
hasAudio?: boolean;
|
||||
hasVideo?: boolean;
|
||||
}
|
||||
|
||||
export type PropsType = {
|
||||
readonly i18n: LocalizerType;
|
||||
readonly onClose: () => void;
|
||||
readonly participants: Array<GroupCallRemoteParticipantType>;
|
||||
readonly participants: Array<ParticipantType>;
|
||||
};
|
||||
|
||||
export const CallingParticipantsList = React.memo(
|
||||
({ i18n, onClose, participants }: PropsType) => {
|
||||
const [root, setRoot] = React.useState<HTMLElement | null>(null);
|
||||
|
||||
const sortedParticipants = React.useMemo<Array<ParticipantType>>(
|
||||
() => participants.sort((a, b) => a.title.localeCompare(b.title)),
|
||||
[participants]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
|
@ -70,10 +80,13 @@ export const CallingParticipantsList = React.memo(
|
|||
/>
|
||||
</div>
|
||||
<ul className="module-calling-participants-list__list">
|
||||
{participants.map(
|
||||
(participant: GroupCallRemoteParticipantType, index: number) => (
|
||||
{sortedParticipants.map(
|
||||
(participant: ParticipantType, index: number) => (
|
||||
<li
|
||||
className="module-calling-participants-list__contact"
|
||||
// It's tempting to use `participant.uuid` as the `key` here, but that
|
||||
// can result in duplicate keys for participants who have joined on
|
||||
// multiple devices.
|
||||
key={index}
|
||||
>
|
||||
<div>
|
||||
|
@ -110,10 +123,10 @@ export const CallingParticipantsList = React.memo(
|
|||
)}
|
||||
</div>
|
||||
<div>
|
||||
{!participant.hasRemoteAudio ? (
|
||||
{participant.hasAudio === false ? (
|
||||
<span className="module-calling-participants-list__muted--audio" />
|
||||
) : null}
|
||||
{!participant.hasRemoteVideo ? (
|
||||
{participant.hasVideo === false ? (
|
||||
<span className="module-calling-participants-list__muted--video" />
|
||||
) : null}
|
||||
</div>
|
||||
|
|
|
@ -9,9 +9,9 @@ import { action } from '@storybook/addon-actions';
|
|||
|
||||
import { ColorType } from '../types/Colors';
|
||||
import { ConversationTypeType } from '../state/ducks/conversations';
|
||||
import { ActiveCallType } from '../state/ducks/calling';
|
||||
import { CallingPip, PropsType } from './CallingPip';
|
||||
import {
|
||||
ActiveCallType,
|
||||
CallMode,
|
||||
CallState,
|
||||
GroupCallConnectionState,
|
||||
|
@ -35,34 +35,26 @@ const conversation = {
|
|||
lastUpdated: Date.now(),
|
||||
};
|
||||
|
||||
const defaultCall = {
|
||||
const getCommonActiveCallData = () => ({
|
||||
conversation,
|
||||
hasLocalAudio: boolean('hasLocalAudio', true),
|
||||
hasLocalVideo: boolean('hasLocalVideo', false),
|
||||
joinedAt: Date.now(),
|
||||
pip: true,
|
||||
settingsDialogOpen: false,
|
||||
showParticipantsList: false,
|
||||
});
|
||||
|
||||
const defaultCall: ActiveCallType = {
|
||||
...getCommonActiveCallData(),
|
||||
callMode: CallMode.Direct as CallMode.Direct,
|
||||
conversationId: '3051234567',
|
||||
callState: CallState.Accepted,
|
||||
isIncoming: false,
|
||||
isVideoCall: true,
|
||||
hasRemoteVideo: true,
|
||||
peekedParticipants: [],
|
||||
remoteParticipants: [{ hasRemoteVideo: true }],
|
||||
};
|
||||
|
||||
const createProps = (
|
||||
overrideProps: Partial<PropsType> = {},
|
||||
activeCall: Partial<ActiveCallType> = {}
|
||||
): PropsType => ({
|
||||
activeCall: {
|
||||
activeCallState: {
|
||||
conversationId: '123',
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: true,
|
||||
pip: false,
|
||||
settingsDialogOpen: false,
|
||||
showParticipantsList: true,
|
||||
},
|
||||
call: activeCall.call || defaultCall,
|
||||
conversation: activeCall.conversation || conversation,
|
||||
isCallFull: false,
|
||||
groupCallPeekedParticipants: [],
|
||||
groupCallParticipants: [],
|
||||
},
|
||||
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||
activeCall: overrideProps.activeCall || defaultCall,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
getGroupCallVideoFrameSource: noop as any,
|
||||
hangUp: action('hang-up'),
|
||||
|
@ -82,48 +74,43 @@ story.add('Default', () => {
|
|||
});
|
||||
|
||||
story.add('Contact (with avatar)', () => {
|
||||
const props = createProps(
|
||||
{},
|
||||
{
|
||||
const props = createProps({
|
||||
activeCall: {
|
||||
...defaultCall,
|
||||
conversation: {
|
||||
...conversation,
|
||||
avatarPath: 'https://www.fillmurray.com/64/64',
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
return <CallingPip {...props} />;
|
||||
});
|
||||
|
||||
story.add('Contact (no color)', () => {
|
||||
const props = createProps(
|
||||
{},
|
||||
{
|
||||
const props = createProps({
|
||||
activeCall: {
|
||||
...defaultCall,
|
||||
conversation: {
|
||||
...conversation,
|
||||
color: undefined,
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
return <CallingPip {...props} />;
|
||||
});
|
||||
|
||||
story.add('Group Call', () => {
|
||||
const props = createProps(
|
||||
{},
|
||||
{
|
||||
call: {
|
||||
callMode: CallMode.Group as CallMode.Group,
|
||||
conversationId: '3051234567',
|
||||
connectionState: GroupCallConnectionState.Connected,
|
||||
joinState: GroupCallJoinState.Joined,
|
||||
peekInfo: {
|
||||
conversationIds: [],
|
||||
maxDevices: 16,
|
||||
deviceCount: 0,
|
||||
},
|
||||
remoteParticipants: [],
|
||||
},
|
||||
}
|
||||
);
|
||||
const props = createProps({
|
||||
activeCall: {
|
||||
...getCommonActiveCallData(),
|
||||
callMode: CallMode.Group as CallMode.Group,
|
||||
connectionState: GroupCallConnectionState.Connected,
|
||||
joinState: GroupCallJoinState.Joined,
|
||||
maxDevices: 5,
|
||||
deviceCount: 0,
|
||||
peekedParticipants: [],
|
||||
remoteParticipants: [],
|
||||
},
|
||||
});
|
||||
return <CallingPip {...props} />;
|
||||
});
|
||||
|
|
|
@ -5,9 +5,12 @@ import React from 'react';
|
|||
import { minBy, debounce, noop } from 'lodash';
|
||||
import { CallingPipRemoteVideo } from './CallingPipRemoteVideo';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import { GroupCallVideoRequest, VideoFrameSource } from '../types/Calling';
|
||||
import {
|
||||
ActiveCallType,
|
||||
GroupCallVideoRequest,
|
||||
VideoFrameSource,
|
||||
} from '../types/Calling';
|
||||
import {
|
||||
HangUpType,
|
||||
SetLocalPreviewType,
|
||||
SetRendererCanvasType,
|
||||
|
|
|
@ -9,12 +9,13 @@ import { DirectCallRemoteParticipant } from './DirectCallRemoteParticipant';
|
|||
import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import {
|
||||
ActiveCallType,
|
||||
CallMode,
|
||||
GroupCallRemoteParticipantType,
|
||||
GroupCallVideoRequest,
|
||||
VideoFrameSource,
|
||||
} from '../types/Calling';
|
||||
import { ActiveCallType, SetRendererCanvasType } from '../state/ducks/calling';
|
||||
import { SetRendererCanvasType } from '../state/ducks/calling';
|
||||
import { usePageVisibility } from '../util/hooks';
|
||||
import { nonRenderedRemoteParticipant } from '../util/ringrtc/nonRenderedRemoteParticipant';
|
||||
|
||||
|
@ -74,31 +75,31 @@ export const CallingPipRemoteVideo = ({
|
|||
setGroupCallVideoRequest,
|
||||
setRendererCanvas,
|
||||
}: PropsType): JSX.Element => {
|
||||
const { call, conversation, groupCallParticipants } = activeCall;
|
||||
const { conversation } = activeCall;
|
||||
|
||||
const isPageVisible = usePageVisibility();
|
||||
|
||||
const activeGroupCallSpeaker:
|
||||
| undefined
|
||||
| GroupCallRemoteParticipantType = useMemo(() => {
|
||||
if (call.callMode !== CallMode.Group) {
|
||||
if (activeCall.callMode !== CallMode.Group) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return maxBy(
|
||||
groupCallParticipants,
|
||||
activeCall.remoteParticipants,
|
||||
participant => participant.speakerTime || -Infinity
|
||||
);
|
||||
}, [call.callMode, groupCallParticipants]);
|
||||
}, [activeCall.callMode, activeCall.remoteParticipants]);
|
||||
|
||||
useEffect(() => {
|
||||
if (call.callMode !== CallMode.Group) {
|
||||
if (activeCall.callMode !== CallMode.Group) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPageVisible) {
|
||||
setGroupCallVideoRequest(
|
||||
groupCallParticipants.map(participant => {
|
||||
activeCall.remoteParticipants.map(participant => {
|
||||
const isVisible =
|
||||
participant === activeGroupCallSpeaker &&
|
||||
participant.hasRemoteVideo;
|
||||
|
@ -116,19 +117,21 @@ export const CallingPipRemoteVideo = ({
|
|||
);
|
||||
} else {
|
||||
setGroupCallVideoRequest(
|
||||
groupCallParticipants.map(nonRenderedRemoteParticipant)
|
||||
activeCall.remoteParticipants.map(nonRenderedRemoteParticipant)
|
||||
);
|
||||
}
|
||||
}, [
|
||||
call.callMode,
|
||||
groupCallParticipants,
|
||||
activeCall.callMode,
|
||||
activeCall.remoteParticipants,
|
||||
activeGroupCallSpeaker,
|
||||
isPageVisible,
|
||||
setGroupCallVideoRequest,
|
||||
]);
|
||||
|
||||
if (call.callMode === CallMode.Direct) {
|
||||
if (!call.hasRemoteVideo) {
|
||||
if (activeCall.callMode === CallMode.Direct) {
|
||||
const { hasRemoteVideo } = activeCall.remoteParticipants[0];
|
||||
|
||||
if (!hasRemoteVideo) {
|
||||
return <NoVideo activeCall={activeCall} i18n={i18n} />;
|
||||
}
|
||||
|
||||
|
@ -136,7 +139,7 @@ export const CallingPipRemoteVideo = ({
|
|||
<div className="module-calling-pip__video--remote">
|
||||
<DirectCallRemoteParticipant
|
||||
conversation={conversation}
|
||||
hasRemoteVideo={call.hasRemoteVideo}
|
||||
hasRemoteVideo={hasRemoteVideo}
|
||||
i18n={i18n}
|
||||
setRendererCanvas={setRendererCanvas}
|
||||
/>
|
||||
|
@ -144,7 +147,7 @@ export const CallingPipRemoteVideo = ({
|
|||
);
|
||||
}
|
||||
|
||||
if (call.callMode === CallMode.Group) {
|
||||
if (activeCall.callMode === CallMode.Group) {
|
||||
if (!activeGroupCallSpeaker) {
|
||||
return <NoVideo activeCall={activeCall} i18n={i18n} />;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue