Add list of participants to the lobby, and add basic blocking for max participants

This commit is contained in:
Evan Hahn 2020-11-20 14:14:07 -06:00 committed by Josh Perez
parent f8b4862ed5
commit daef1feae8
11 changed files with 112 additions and 27 deletions

View file

@ -1166,6 +1166,10 @@
"message": "Join Call", "message": "Join Call",
"description": "Button label in the call lobby for joining a call" "description": "Button label in the call lobby for joining a call"
}, },
"calling__call-is-full": {
"message": "Call is full",
"description": "Button label in the call lobby when you can't join because the call is full"
},
"calling__button--video-disabled": { "calling__button--video-disabled": {
"message": "Camera disabled", "message": "Camera disabled",
"description": "Button tooltip label when the camera is disabled" "description": "Button tooltip label when the camera is disabled"

View file

@ -6454,6 +6454,10 @@ button.module-image__border-overlay:focus {
margin-left: 8px; margin-left: 8px;
margin-right: 8px; margin-right: 8px;
width: 160px; width: 160px;
&[disabled] {
opacity: 0.5;
}
} }
&__video { &__video {

View file

@ -101,6 +101,8 @@ story.add('Ongoing Direct Call', () => (
}, },
activeCallState: getCallState(), activeCallState: getCallState(),
conversation: getConversation(), conversation: getConversation(),
isCallFull: false,
groupCallPeekedParticipants: [],
groupCallParticipants: [], groupCallParticipants: [],
}, },
})} })}
@ -125,6 +127,8 @@ story.add('Ongoing Group Call', () => (
}, },
activeCallState: getCallState(), activeCallState: getCallState(),
conversation: getConversation(), conversation: getConversation(),
isCallFull: false,
groupCallPeekedParticipants: [],
groupCallParticipants: [], groupCallParticipants: [],
}, },
})} })}
@ -151,6 +155,8 @@ story.add('Call Request Needed', () => (
}), }),
activeCallState: getCallState(), activeCallState: getCallState(),
conversation: getConversation(), conversation: getConversation(),
isCallFull: false,
groupCallPeekedParticipants: [],
groupCallParticipants: [], groupCallParticipants: [],
}, },
})} })}

View file

@ -96,7 +96,9 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
call, call,
activeCallState, activeCallState,
conversation, conversation,
groupCallPeekedParticipants,
groupCallParticipants, groupCallParticipants,
isCallFull,
} = activeCall; } = activeCall;
const { const {
hasLocalAudio, hasLocalAudio,
@ -157,7 +159,7 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
} }
if (showCallLobby) { if (showCallLobby) {
const participantNames = groupCallParticipants.map(participant => const participantNames = groupCallPeekedParticipants.map(participant =>
participant.isSelf participant.isSelf
? i18n('you') ? i18n('you')
: participant.firstName || participant.title : participant.firstName || participant.title
@ -171,6 +173,7 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
hasLocalVideo={hasLocalVideo} hasLocalVideo={hasLocalVideo}
i18n={i18n} i18n={i18n}
isGroupCall={call.callMode === CallMode.Group} isGroupCall={call.callMode === CallMode.Group}
isCallFull={isCallFull}
me={me} me={me}
onCallCanceled={cancelActiveCall} onCallCanceled={cancelActiveCall}
onJoinCall={joinActiveCall} onJoinCall={joinActiveCall}

View file

@ -93,6 +93,8 @@ const createProps = (
type: 'direct', type: 'direct',
lastUpdated: Date.now(), lastUpdated: Date.now(),
}, },
isCallFull: false,
groupCallPeekedParticipants: [],
groupCallParticipants: overrideProps.groupCallParticipants || [], groupCallParticipants: overrideProps.groupCallParticipants || [],
}, },
// We allow `any` here because this is fake and actually comes from RingRTC, which we // We allow `any` here because this is fake and actually comes from RingRTC, which we

View file

@ -24,6 +24,7 @@ export type PropsType = {
hasLocalVideo: boolean; hasLocalVideo: boolean;
i18n: LocalizerType; i18n: LocalizerType;
isGroupCall: boolean; isGroupCall: boolean;
isCallFull?: boolean;
me: { me: {
avatarPath?: string; avatarPath?: string;
color?: ColorType; color?: ColorType;
@ -46,6 +47,7 @@ export const CallingLobby = ({
hasLocalVideo, hasLocalVideo,
i18n, i18n,
isGroupCall = false, isGroupCall = false,
isCallFull = false,
me, me,
onCallCanceled, onCallCanceled,
onJoinCall, onJoinCall,
@ -112,6 +114,45 @@ export const CallingLobby = ({
? CallingButtonType.AUDIO_ON ? CallingButtonType.AUDIO_ON
: CallingButtonType.AUDIO_OFF; : CallingButtonType.AUDIO_OFF;
let joinButton: JSX.Element;
if (isCallFull) {
joinButton = (
<button
className="module-button__green module-calling-lobby__button"
disabled
tabIndex={0}
type="button"
>
{i18n('calling__call-is-full')}
</button>
);
} else if (isCallConnecting) {
joinButton = (
<button
className="module-button__green module-calling-lobby__button"
disabled
tabIndex={0}
type="button"
>
<Spinner svgSize="small" />
</button>
);
} else {
joinButton = (
<button
className="module-button__green module-calling-lobby__button"
onClick={() => {
setIsCallConnecting(true);
onJoinCall();
}}
tabIndex={0}
type="button"
>
{isGroupCall ? i18n('calling__join') : i18n('calling__start')}
</button>
);
}
return ( return (
<div className="module-calling__container"> <div className="module-calling__container">
<CallingHeader <CallingHeader
@ -191,29 +232,7 @@ export const CallingLobby = ({
> >
{i18n('cancel')} {i18n('cancel')}
</button> </button>
{isCallConnecting && ( {joinButton}
<button
className="module-button__green module-calling-lobby__button"
disabled
tabIndex={0}
type="button"
>
<Spinner svgSize="small" />
</button>
)}
{!isCallConnecting && (
<button
className="module-button__green module-calling-lobby__button"
onClick={() => {
setIsCallConnecting(true);
onJoinCall();
}}
tabIndex={0}
type="button"
>
{isGroupCall ? i18n('calling__join') : i18n('calling__start')}
</button>
)}
</div> </div>
</div> </div>
); );

View file

@ -59,6 +59,8 @@ const createProps = (
}, },
call: activeCall.call || defaultCall, call: activeCall.call || defaultCall,
conversation: activeCall.conversation || conversation, conversation: activeCall.conversation || conversation,
isCallFull: false,
groupCallPeekedParticipants: [],
groupCallParticipants: [], groupCallParticipants: [],
}, },
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any

View file

@ -17,6 +17,7 @@ import {
ChangeIODevicePayloadType, ChangeIODevicePayloadType,
GroupCallConnectionState, GroupCallConnectionState,
GroupCallJoinState, GroupCallJoinState,
GroupCallPeekedParticipantType,
GroupCallRemoteParticipantType, GroupCallRemoteParticipantType,
MediaDeviceSettings, MediaDeviceSettings,
} from '../../types/Calling'; } from '../../types/Calling';
@ -71,6 +72,8 @@ export interface ActiveCallType {
activeCallState: ActiveCallStateType; activeCallState: ActiveCallStateType;
call: DirectCallStateType | GroupCallStateType; call: DirectCallStateType | GroupCallStateType;
conversation: ConversationType; conversation: ConversationType;
isCallFull: boolean;
groupCallPeekedParticipants: Array<GroupCallPeekedParticipantType>;
groupCallParticipants: Array<GroupCallRemoteParticipantType>; groupCallParticipants: Array<GroupCallRemoteParticipantType>;
} }

View file

@ -9,7 +9,11 @@ import { calling as callingService } from '../../services/calling';
import { getMe, getConversationSelector } from '../selectors/conversations'; import { getMe, getConversationSelector } from '../selectors/conversations';
import { getActiveCall, GroupCallParticipantInfoType } from '../ducks/calling'; import { getActiveCall, GroupCallParticipantInfoType } from '../ducks/calling';
import { getIncomingCall } from '../selectors/calling'; import { getIncomingCall } from '../selectors/calling';
import { CallMode, GroupCallRemoteParticipantType } from '../../types/Calling'; import {
CallMode,
GroupCallPeekedParticipantType,
GroupCallRemoteParticipantType,
} from '../../types/Calling';
import { StateType } from '../reducer'; import { StateType } from '../reducer';
import { getIntl } from '../selectors/user'; import { getIntl } from '../selectors/user';
@ -47,8 +51,34 @@ const mapStateToActiveCallProp = (state: StateType) => {
return undefined; return undefined;
} }
// TODO: The way we deal with remote participants isn't ideal. See DESKTOP-949.
let isCallFull = false;
const groupCallPeekedParticipants: Array<GroupCallPeekedParticipantType> = [];
const groupCallParticipants: Array<GroupCallRemoteParticipantType> = []; const groupCallParticipants: Array<GroupCallRemoteParticipantType> = [];
if (call && call.callMode === CallMode.Group) { 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,
});
});
call.remoteParticipants.forEach( call.remoteParticipants.forEach(
(remoteParticipant: GroupCallParticipantInfoType) => { (remoteParticipant: GroupCallParticipantInfoType) => {
const remoteConversation = conversationSelector( const remoteConversation = conversationSelector(
@ -83,6 +113,8 @@ const mapStateToActiveCallProp = (state: StateType) => {
activeCallState, activeCallState,
call, call,
conversation, conversation,
isCallFull,
groupCallPeekedParticipants,
groupCallParticipants, groupCallParticipants,
}; };
}; };

View file

@ -58,6 +58,16 @@ export enum GroupCallJoinState {
Joined = 2, Joined = 2,
} }
// TODO: The way we deal with remote participants isn't ideal. See DESKTOP-949.
export interface GroupCallPeekedParticipantType {
avatarPath?: string;
color?: ColorType;
firstName?: string;
isSelf: boolean;
name?: string;
profileName?: string;
title: string;
}
export interface GroupCallRemoteParticipantType { export interface GroupCallRemoteParticipantType {
avatarPath?: string; avatarPath?: string;
color?: ColorType; color?: ColorType;

View file

@ -14400,7 +14400,7 @@
"rule": "React-useRef", "rule": "React-useRef",
"path": "ts/components/CallingLobby.tsx", "path": "ts/components/CallingLobby.tsx",
"line": " const localVideoRef = React.useRef(null);", "line": " const localVideoRef = React.useRef(null);",
"lineNumber": 60, "lineNumber": 62,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-10-26T19:12:24.410Z", "updated": "2020-10-26T19:12:24.410Z",
"reasonDetail": "Used to get the local video element for rendering." "reasonDetail": "Used to get the local video element for rendering."