Group Calling: Improve mute state styling
This commit is contained in:
parent
c6eafbb8d5
commit
5cc7c9a66a
15 changed files with 203 additions and 126 deletions
|
@ -6460,6 +6460,10 @@ button.module-image__border-overlay:focus {
|
|||
justify-content: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.module-ongoing-call__group-call-remote-participant--audio-muted::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&--local {
|
||||
|
|
|
@ -8,6 +8,15 @@ import { getInitials } from '../util/getInitials';
|
|||
import { LocalizerType } from '../types/Util';
|
||||
import { ColorType } from '../types/Colors';
|
||||
|
||||
export enum AvatarSize {
|
||||
TWENTY_EIGHT = 28,
|
||||
THIRTY_TWO = 32,
|
||||
FIFTY_TWO = 52,
|
||||
EIGHTY = 80,
|
||||
NINETY_SIX = 96,
|
||||
ONE_HUNDRED_TWELVE = 112,
|
||||
}
|
||||
|
||||
export type Props = {
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
|
@ -18,7 +27,7 @@ export type Props = {
|
|||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
size: 28 | 32 | 52 | 80 | 96 | 112;
|
||||
size: AvatarSize;
|
||||
|
||||
onClick?: () => unknown;
|
||||
|
||||
|
|
|
@ -13,17 +13,15 @@ import {
|
|||
CallMode,
|
||||
CallState,
|
||||
GroupCallJoinState,
|
||||
GroupCallRemoteParticipantType,
|
||||
VideoFrameSource,
|
||||
} from '../types/Calling';
|
||||
import { ConversationType } from '../state/ducks/conversations';
|
||||
import {
|
||||
AcceptCallType,
|
||||
ActiveCallStateType,
|
||||
ActiveCallType,
|
||||
CancelCallType,
|
||||
DeclineCallType,
|
||||
DirectCallStateType,
|
||||
GroupCallStateType,
|
||||
HangUpType,
|
||||
SetLocalAudioType,
|
||||
SetLocalPreviewType,
|
||||
|
@ -35,13 +33,6 @@ import { LocalizerType } from '../types/Util';
|
|||
import { ColorType } from '../types/Colors';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
|
||||
interface ActiveCallType {
|
||||
activeCallState: ActiveCallStateType;
|
||||
call: DirectCallStateType | GroupCallStateType;
|
||||
conversation: ConversationType;
|
||||
groupCallParticipants: Array<GroupCallRemoteParticipantType>;
|
||||
}
|
||||
|
||||
export interface PropsType {
|
||||
activeCall?: ActiveCallType;
|
||||
availableCameras: Array<MediaDeviceInfo>;
|
||||
|
@ -205,8 +196,7 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
if (pip) {
|
||||
return (
|
||||
<CallingPip
|
||||
call={call}
|
||||
conversation={conversation}
|
||||
activeCall={activeCall}
|
||||
getGroupCallVideoFrameSource={getGroupCallVideoFrameSourceForActiveCall}
|
||||
hangUp={hangUp}
|
||||
hasLocalVideo={hasLocalVideo}
|
||||
|
@ -221,8 +211,7 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
|
|||
return (
|
||||
<>
|
||||
<CallScreen
|
||||
call={call}
|
||||
conversation={conversation}
|
||||
activeCall={activeCall}
|
||||
getGroupCallVideoFrameSource={getGroupCallVideoFrameSourceForActiveCall}
|
||||
hangUp={hangUp}
|
||||
hasLocalAudio={hasLocalAudio}
|
||||
|
|
|
@ -7,12 +7,15 @@ import { storiesOf } from '@storybook/react';
|
|||
import { boolean, select } from '@storybook/addon-knobs';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import { CallMode, CallState } from '../types/Calling';
|
||||
import {
|
||||
CallMode,
|
||||
CallState,
|
||||
GroupCallRemoteParticipantType,
|
||||
} from '../types/Calling';
|
||||
import { Colors } from '../types/Colors';
|
||||
import {
|
||||
DirectCallStateType,
|
||||
GroupCallStateType,
|
||||
GroupCallParticipantInfoType,
|
||||
} from '../state/ducks/calling';
|
||||
import { CallScreen, PropsType } from './CallScreen';
|
||||
import { setup as setupI18n } from '../../js/modules/i18n';
|
||||
|
@ -20,15 +23,13 @@ import enMessages from '../../_locales/en/messages.json';
|
|||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
function getGroupCallState(
|
||||
remoteParticipants: Array<GroupCallParticipantInfoType>
|
||||
): GroupCallStateType {
|
||||
function getGroupCallState(): GroupCallStateType {
|
||||
return {
|
||||
callMode: CallMode.Group,
|
||||
conversationId: '3051234567',
|
||||
connectionState: 2,
|
||||
joinState: 2,
|
||||
remoteParticipants,
|
||||
remoteParticipants: [],
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -59,24 +60,35 @@ const createProps = (
|
|||
overrideProps: {
|
||||
callState?: CallState;
|
||||
callTypeState?: DirectCallStateType | GroupCallStateType;
|
||||
groupCallParticipants?: Array<GroupCallRemoteParticipantType>;
|
||||
hasLocalAudio?: boolean;
|
||||
hasLocalVideo?: boolean;
|
||||
hasRemoteVideo?: boolean;
|
||||
remoteParticipants?: Array<GroupCallParticipantInfoType>;
|
||||
} = {}
|
||||
): PropsType => ({
|
||||
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(),
|
||||
activeCall: {
|
||||
activeCallState: {
|
||||
conversationId: '123',
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: true,
|
||||
pip: false,
|
||||
settingsDialogOpen: false,
|
||||
showParticipantsList: true,
|
||||
},
|
||||
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(),
|
||||
},
|
||||
groupCallParticipants: overrideProps.groupCallParticipants || [],
|
||||
},
|
||||
// We allow `any` here because this is fake and actually comes from RingRTC, which we
|
||||
// can't import.
|
||||
|
@ -164,16 +176,17 @@ story.add('hasRemoteVideo', () => {
|
|||
story.add('Group call - 1', () => (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callTypeState: getGroupCallState([
|
||||
callTypeState: getGroupCallState(),
|
||||
groupCallParticipants: [
|
||||
{
|
||||
conversationId: '123',
|
||||
demuxId: 0,
|
||||
hasRemoteAudio: true,
|
||||
hasRemoteVideo: true,
|
||||
isSelf: false,
|
||||
title: 'Tyler',
|
||||
videoAspectRatio: 1.3,
|
||||
},
|
||||
]),
|
||||
],
|
||||
})}
|
||||
/>
|
||||
));
|
||||
|
@ -181,32 +194,33 @@ story.add('Group call - 1', () => (
|
|||
story.add('Group call - Many', () => (
|
||||
<CallScreen
|
||||
{...createProps({
|
||||
callTypeState: getGroupCallState([
|
||||
callTypeState: getGroupCallState(),
|
||||
groupCallParticipants: [
|
||||
{
|
||||
conversationId: '123',
|
||||
demuxId: 0,
|
||||
hasRemoteAudio: true,
|
||||
hasRemoteVideo: true,
|
||||
isSelf: false,
|
||||
title: 'Amy',
|
||||
videoAspectRatio: 1.3,
|
||||
},
|
||||
{
|
||||
conversationId: '456',
|
||||
demuxId: 1,
|
||||
hasRemoteAudio: true,
|
||||
hasRemoteVideo: true,
|
||||
isSelf: true,
|
||||
title: 'Bob',
|
||||
videoAspectRatio: 1.3,
|
||||
},
|
||||
{
|
||||
conversationId: '789',
|
||||
demuxId: 2,
|
||||
hasRemoteAudio: true,
|
||||
hasRemoteVideo: true,
|
||||
isSelf: false,
|
||||
title: 'Alice',
|
||||
videoAspectRatio: 1.3,
|
||||
},
|
||||
]),
|
||||
],
|
||||
})}
|
||||
/>
|
||||
));
|
||||
|
|
|
@ -4,10 +4,8 @@
|
|||
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
||||
import { noop } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { ConversationType } from '../state/ducks/conversations';
|
||||
import {
|
||||
DirectCallStateType,
|
||||
GroupCallStateType,
|
||||
ActiveCallType,
|
||||
HangUpType,
|
||||
SetLocalAudioType,
|
||||
SetLocalPreviewType,
|
||||
|
@ -31,8 +29,7 @@ import { DirectCallRemoteParticipant } from './DirectCallRemoteParticipant';
|
|||
import { GroupCallRemoteParticipants } from './GroupCallRemoteParticipants';
|
||||
|
||||
export type PropsType = {
|
||||
call: DirectCallStateType | GroupCallStateType;
|
||||
conversation: ConversationType;
|
||||
activeCall: ActiveCallType;
|
||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||
hangUp: (_: HangUpType) => void;
|
||||
hasLocalAudio: boolean;
|
||||
|
@ -58,8 +55,7 @@ export type PropsType = {
|
|||
};
|
||||
|
||||
export const CallScreen: React.FC<PropsType> = ({
|
||||
call,
|
||||
conversation,
|
||||
activeCall,
|
||||
getGroupCallVideoFrameSource,
|
||||
hangUp,
|
||||
hasLocalAudio,
|
||||
|
@ -76,6 +72,8 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
togglePip,
|
||||
toggleSettings,
|
||||
}) => {
|
||||
const { call, conversation, groupCallParticipants } = activeCall;
|
||||
|
||||
const toggleAudio = useCallback(() => {
|
||||
setLocalAudio({
|
||||
enabled: !hasLocalAudio,
|
||||
|
@ -170,8 +168,9 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
isConnected = call.connectionState === GroupCallConnectionState.Connected;
|
||||
remoteParticipantsElement = (
|
||||
<GroupCallRemoteParticipants
|
||||
remoteParticipants={call.remoteParticipants}
|
||||
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
|
||||
i18n={i18n}
|
||||
remoteParticipants={groupCallParticipants}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
|
|
@ -20,11 +20,13 @@ function createParticipant(
|
|||
return {
|
||||
avatarPath: participantProps.avatarPath,
|
||||
color: Colors[randomColor],
|
||||
demuxId: 2,
|
||||
hasRemoteAudio: Boolean(participantProps.hasRemoteAudio),
|
||||
hasRemoteVideo: Boolean(participantProps.hasRemoteVideo),
|
||||
isSelf: Boolean(participantProps.isSelf),
|
||||
profileName: participantProps.title,
|
||||
title: String(participantProps.title),
|
||||
videoAspectRatio: 1.3,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ 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 {
|
||||
CallMode,
|
||||
|
@ -43,9 +44,23 @@ const defaultCall = {
|
|||
hasRemoteVideo: true,
|
||||
};
|
||||
|
||||
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||
call: overrideProps.call || defaultCall,
|
||||
conversation: overrideProps.conversation || conversation,
|
||||
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,
|
||||
groupCallParticipants: [],
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
getGroupCallVideoFrameSource: noop as any,
|
||||
hangUp: action('hang-up'),
|
||||
|
@ -59,39 +74,48 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
|||
const story = storiesOf('Components/CallingPip', module);
|
||||
|
||||
story.add('Default', () => {
|
||||
const props = createProps();
|
||||
const props = createProps({});
|
||||
return <CallingPip {...props} />;
|
||||
});
|
||||
|
||||
story.add('Contact (with avatar)', () => {
|
||||
const props = createProps({
|
||||
conversation: {
|
||||
...conversation,
|
||||
avatarPath: 'https://www.fillmurray.com/64/64',
|
||||
},
|
||||
});
|
||||
const props = createProps(
|
||||
{},
|
||||
{
|
||||
conversation: {
|
||||
...conversation,
|
||||
avatarPath: 'https://www.fillmurray.com/64/64',
|
||||
},
|
||||
}
|
||||
);
|
||||
return <CallingPip {...props} />;
|
||||
});
|
||||
|
||||
story.add('Contact (no color)', () => {
|
||||
const props = createProps({
|
||||
conversation: {
|
||||
...conversation,
|
||||
color: undefined,
|
||||
},
|
||||
});
|
||||
const props = createProps(
|
||||
{},
|
||||
{
|
||||
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,
|
||||
remoteParticipants: [],
|
||||
},
|
||||
});
|
||||
const props = createProps(
|
||||
{},
|
||||
{
|
||||
call: {
|
||||
callMode: CallMode.Group as CallMode.Group,
|
||||
conversationId: '3051234567',
|
||||
connectionState: GroupCallConnectionState.Connected,
|
||||
joinState: GroupCallJoinState.Joined,
|
||||
remoteParticipants: [],
|
||||
},
|
||||
}
|
||||
);
|
||||
return <CallingPip {...props} />;
|
||||
});
|
||||
|
|
|
@ -5,19 +5,16 @@ import React from 'react';
|
|||
import { Tooltip } from './Tooltip';
|
||||
import { CallingPipRemoteVideo } from './CallingPipRemoteVideo';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import { ConversationType } from '../state/ducks/conversations';
|
||||
import { VideoFrameSource } from '../types/Calling';
|
||||
import {
|
||||
DirectCallStateType,
|
||||
GroupCallStateType,
|
||||
ActiveCallType,
|
||||
HangUpType,
|
||||
SetLocalPreviewType,
|
||||
SetRendererCanvasType,
|
||||
} from '../state/ducks/calling';
|
||||
|
||||
export type PropsType = {
|
||||
call: DirectCallStateType | GroupCallStateType;
|
||||
conversation: ConversationType;
|
||||
activeCall: ActiveCallType;
|
||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||
hangUp: (_: HangUpType) => void;
|
||||
hasLocalVideo: boolean;
|
||||
|
@ -33,8 +30,7 @@ const PIP_DEFAULT_Y = 56;
|
|||
const PIP_PADDING = 8;
|
||||
|
||||
export const CallingPip = ({
|
||||
call,
|
||||
conversation,
|
||||
activeCall,
|
||||
getGroupCallVideoFrameSource,
|
||||
hangUp,
|
||||
hasLocalVideo,
|
||||
|
@ -167,8 +163,7 @@ export const CallingPip = ({
|
|||
}}
|
||||
>
|
||||
<CallingPipRemoteVideo
|
||||
call={call}
|
||||
conversation={conversation}
|
||||
activeCall={activeCall}
|
||||
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
|
||||
i18n={i18n}
|
||||
setRendererCanvas={setRendererCanvas}
|
||||
|
@ -185,7 +180,7 @@ export const CallingPip = ({
|
|||
aria-label={i18n('calling__hangup')}
|
||||
className="module-calling-pip__button--hangup"
|
||||
onClick={() => {
|
||||
hangUp({ conversationId: conversation.id });
|
||||
hangUp({ conversationId: activeCall.conversation.id });
|
||||
}}
|
||||
type="button"
|
||||
/>
|
||||
|
|
|
@ -7,29 +7,24 @@ import { CallBackgroundBlur } from './CallBackgroundBlur';
|
|||
import { DirectCallRemoteParticipant } from './DirectCallRemoteParticipant';
|
||||
import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import { ConversationType } from '../state/ducks/conversations';
|
||||
import { CallMode, VideoFrameSource } from '../types/Calling';
|
||||
import {
|
||||
DirectCallStateType,
|
||||
GroupCallStateType,
|
||||
SetRendererCanvasType,
|
||||
} from '../state/ducks/calling';
|
||||
import { ActiveCallType, SetRendererCanvasType } from '../state/ducks/calling';
|
||||
|
||||
export interface PropsType {
|
||||
call: DirectCallStateType | GroupCallStateType;
|
||||
conversation: ConversationType;
|
||||
activeCall: ActiveCallType;
|
||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||
i18n: LocalizerType;
|
||||
setRendererCanvas: (_: SetRendererCanvasType) => void;
|
||||
}
|
||||
|
||||
export const CallingPipRemoteVideo = ({
|
||||
call,
|
||||
conversation,
|
||||
activeCall,
|
||||
getGroupCallVideoFrameSource,
|
||||
i18n,
|
||||
setRendererCanvas,
|
||||
}: PropsType): JSX.Element => {
|
||||
const { call, conversation } = activeCall;
|
||||
|
||||
if (call.callMode === CallMode.Direct) {
|
||||
if (!call.hasRemoteVideo) {
|
||||
const {
|
||||
|
@ -76,17 +71,17 @@ export const CallingPipRemoteVideo = ({
|
|||
}
|
||||
|
||||
if (call.callMode === CallMode.Group) {
|
||||
const speaker = call.remoteParticipants[0];
|
||||
const { groupCallParticipants } = activeCall;
|
||||
const speaker = groupCallParticipants[0];
|
||||
|
||||
return (
|
||||
<div className="module-calling-pip__video--remote">
|
||||
<GroupCallRemoteParticipant
|
||||
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
|
||||
i18n={i18n}
|
||||
isInPip
|
||||
key={speaker.demuxId}
|
||||
demuxId={speaker.demuxId}
|
||||
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
|
||||
hasRemoteAudio={speaker.hasRemoteAudio}
|
||||
hasRemoteVideo={speaker.hasRemoteVideo}
|
||||
remoteParticipant={speaker}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -11,17 +11,21 @@ import React, {
|
|||
} from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { noop } from 'lodash';
|
||||
import { VideoFrameSource } from '../types/Calling';
|
||||
import {
|
||||
GroupCallRemoteParticipantType,
|
||||
VideoFrameSource,
|
||||
} from '../types/Calling';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import { CallBackgroundBlur } from './CallBackgroundBlur';
|
||||
import { Avatar, AvatarSize } from './Avatar';
|
||||
|
||||
// The max size video frame we'll support (in RGBA)
|
||||
const FRAME_BUFFER_SIZE = 1920 * 1080 * 4;
|
||||
|
||||
interface BasePropsType {
|
||||
demuxId: number;
|
||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||
hasRemoteAudio: boolean;
|
||||
hasRemoteVideo: boolean;
|
||||
i18n: LocalizerType;
|
||||
remoteParticipant: GroupCallRemoteParticipantType;
|
||||
}
|
||||
|
||||
interface InPipPropsType {
|
||||
|
@ -29,23 +33,28 @@ interface InPipPropsType {
|
|||
}
|
||||
|
||||
interface NotInPipPropsType {
|
||||
isInPip?: false;
|
||||
width: number;
|
||||
height: number;
|
||||
isInPip?: false;
|
||||
left: number;
|
||||
top: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
type PropsType = BasePropsType & (InPipPropsType | NotInPipPropsType);
|
||||
|
||||
export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
||||
props => {
|
||||
const { getGroupCallVideoFrameSource } = props;
|
||||
|
||||
const {
|
||||
avatarPath,
|
||||
color,
|
||||
profileName,
|
||||
title,
|
||||
demuxId,
|
||||
getGroupCallVideoFrameSource,
|
||||
hasRemoteAudio,
|
||||
hasRemoteVideo,
|
||||
} = props;
|
||||
} = props.remoteParticipant;
|
||||
|
||||
const [isWide, setIsWide] = useState(true);
|
||||
|
||||
|
@ -132,13 +141,25 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
canvasStyles = { height: '100%' };
|
||||
}
|
||||
|
||||
let avatarSize: number;
|
||||
|
||||
// TypeScript isn't smart enough to know that `isInPip` by itself disambiguates the
|
||||
// types, so we have to use `props.isInPip` instead.
|
||||
// eslint-disable-next-line react/destructuring-assignment
|
||||
if (props.isInPip) {
|
||||
containerStyles = canvasStyles;
|
||||
avatarSize = AvatarSize.FIFTY_TWO;
|
||||
} else {
|
||||
const { top, left, width, height } = props;
|
||||
const shorterDimension = Math.min(width, height);
|
||||
|
||||
if (shorterDimension >= 240) {
|
||||
avatarSize = AvatarSize.ONE_HUNDRED_TWELVE;
|
||||
} else if (shorterDimension >= 180) {
|
||||
avatarSize = AvatarSize.EIGHTY;
|
||||
} else {
|
||||
avatarSize = AvatarSize.FIFTY_TWO;
|
||||
}
|
||||
|
||||
containerStyles = {
|
||||
height,
|
||||
|
@ -177,9 +198,17 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
}}
|
||||
/>
|
||||
) : (
|
||||
<CallBackgroundBlur>
|
||||
{/* TODO: Improve the styling here. See DESKTOP-894. */}
|
||||
<span />
|
||||
<CallBackgroundBlur avatarPath={avatarPath} color={color}>
|
||||
<Avatar
|
||||
avatarPath={avatarPath}
|
||||
color={color || 'ultramarine'}
|
||||
noteToSelf={false}
|
||||
conversationType="direct"
|
||||
i18n={props.i18n}
|
||||
profileName={profileName}
|
||||
title={title}
|
||||
size={avatarSize}
|
||||
/>
|
||||
</CallBackgroundBlur>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
import React, { useState, useMemo } from 'react';
|
||||
import Measure from 'react-measure';
|
||||
import { takeWhile, chunk, maxBy, flatten } from 'lodash';
|
||||
import { VideoFrameSource } from '../types/Calling';
|
||||
import { GroupCallParticipantInfoType } from '../state/ducks/calling';
|
||||
import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant';
|
||||
import {
|
||||
GroupCallRemoteParticipantType,
|
||||
VideoFrameSource,
|
||||
} from '../types/Calling';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
|
||||
const MIN_RENDERED_HEIGHT = 10;
|
||||
const PARTICIPANT_MARGIN = 10;
|
||||
|
@ -17,13 +20,14 @@ interface Dimensions {
|
|||
}
|
||||
|
||||
interface GridArrangement {
|
||||
rows: Array<Array<GroupCallParticipantInfoType>>;
|
||||
rows: Array<Array<GroupCallRemoteParticipantType>>;
|
||||
scalar: number;
|
||||
}
|
||||
|
||||
interface PropsType {
|
||||
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
|
||||
remoteParticipants: ReadonlyArray<GroupCallParticipantInfoType>;
|
||||
i18n: LocalizerType;
|
||||
remoteParticipants: ReadonlyArray<GroupCallRemoteParticipantType>;
|
||||
}
|
||||
|
||||
// This component lays out group call remote participants. It uses a custom layout
|
||||
|
@ -52,6 +56,7 @@ interface PropsType {
|
|||
// 4. Lay out this arrangement on the screen.
|
||||
export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
|
||||
getGroupCallVideoFrameSource,
|
||||
i18n,
|
||||
remoteParticipants,
|
||||
}) => {
|
||||
const [containerDimensions, setContainerDimensions] = useState<Dimensions>({
|
||||
|
@ -82,7 +87,7 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
|
|||
//
|
||||
// This is primarily memoized for clarity, not performance. We only need the result,
|
||||
// not any of the "intermediate" values.
|
||||
const visibleParticipants: Array<GroupCallParticipantInfoType> = useMemo(() => {
|
||||
const visibleParticipants: Array<GroupCallRemoteParticipantType> = useMemo(() => {
|
||||
// Imagine that we laid out all of the rows end-to-end. That's the maximum total
|
||||
// width. So if there were 5 rows and the container was 100px wide, then we can't
|
||||
// possibly fit more than 500px of participants.
|
||||
|
@ -199,12 +204,11 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
|
|||
return (
|
||||
<GroupCallRemoteParticipant
|
||||
key={remoteParticipant.demuxId}
|
||||
demuxId={remoteParticipant.demuxId}
|
||||
getGroupCallVideoFrameSource={getGroupCallVideoFrameSource}
|
||||
hasRemoteAudio={remoteParticipant.hasRemoteAudio}
|
||||
hasRemoteVideo={remoteParticipant.hasRemoteVideo}
|
||||
height={gridParticipantHeight}
|
||||
i18n={i18n}
|
||||
left={left}
|
||||
remoteParticipant={remoteParticipant}
|
||||
top={top}
|
||||
width={renderedWidth}
|
||||
/>
|
||||
|
@ -235,7 +239,7 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
|
|||
};
|
||||
|
||||
function totalRemoteParticipantWidthAtMinHeight(
|
||||
remoteParticipants: ReadonlyArray<GroupCallParticipantInfoType>
|
||||
remoteParticipants: ReadonlyArray<GroupCallRemoteParticipantType>
|
||||
): number {
|
||||
return remoteParticipants.reduce(
|
||||
(result, { videoAspectRatio }) =>
|
||||
|
|
|
@ -9,6 +9,7 @@ import { missingCaseError } from '../../util/missingCaseError';
|
|||
import { notify } from '../../services/notify';
|
||||
import { calling } from '../../services/calling';
|
||||
import { StateType as RootStateType } from '../reducer';
|
||||
import { ConversationType } from './conversations';
|
||||
import {
|
||||
CallMode,
|
||||
CallState,
|
||||
|
@ -16,6 +17,7 @@ import {
|
|||
ChangeIODevicePayloadType,
|
||||
GroupCallConnectionState,
|
||||
GroupCallJoinState,
|
||||
GroupCallRemoteParticipantType,
|
||||
MediaDeviceSettings,
|
||||
} from '../../types/Calling';
|
||||
import { callingTones } from '../../util/callingTones';
|
||||
|
@ -54,6 +56,13 @@ export interface GroupCallStateType {
|
|||
remoteParticipants: Array<GroupCallParticipantInfoType>;
|
||||
}
|
||||
|
||||
export interface ActiveCallType {
|
||||
activeCallState: ActiveCallStateType;
|
||||
call: DirectCallStateType | GroupCallStateType;
|
||||
conversation: ConversationType;
|
||||
groupCallParticipants: Array<GroupCallRemoteParticipantType>;
|
||||
}
|
||||
|
||||
export interface ActiveCallStateType {
|
||||
conversationId: string;
|
||||
joinedAt?: number;
|
||||
|
|
|
@ -65,12 +65,14 @@ const mapStateToActiveCallProp = (state: StateType) => {
|
|||
groupCallParticipants.push({
|
||||
avatarPath: remoteConversation.avatarPath,
|
||||
color: remoteConversation.color,
|
||||
demuxId: remoteParticipant.demuxId,
|
||||
firstName: remoteConversation.firstName,
|
||||
hasRemoteAudio: remoteParticipant.hasRemoteAudio,
|
||||
hasRemoteVideo: remoteParticipant.hasRemoteVideo,
|
||||
isSelf: remoteParticipant.isSelf,
|
||||
profileName: remoteConversation.profileName,
|
||||
title: remoteConversation.title,
|
||||
videoAspectRatio: remoteParticipant.videoAspectRatio,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
@ -61,12 +61,14 @@ export enum GroupCallJoinState {
|
|||
export interface GroupCallRemoteParticipantType {
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
demuxId: number;
|
||||
firstName?: string;
|
||||
hasRemoteAudio: boolean;
|
||||
hasRemoteVideo: boolean;
|
||||
isSelf: boolean;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
videoAspectRatio: number;
|
||||
}
|
||||
|
||||
// Should match RingRTC's VideoFrameSource
|
||||
|
|
|
@ -14382,7 +14382,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/components/CallScreen.js",
|
||||
"line": " const localVideoRef = react_1.useRef(null);",
|
||||
"lineNumber": 39,
|
||||
"lineNumber": 40,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-10-26T21:35:52.858Z",
|
||||
"reasonDetail": "Used to get the local video element for rendering."
|
||||
|
@ -14427,7 +14427,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/components/CallingPip.tsx",
|
||||
"line": " const videoContainerRef = React.useRef(null);",
|
||||
"lineNumber": 46,
|
||||
"lineNumber": 42,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-10-26T19:12:24.410Z",
|
||||
"reasonDetail": "Element is measured. Its HTML is not used."
|
||||
|
@ -14436,7 +14436,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/components/CallingPip.tsx",
|
||||
"line": " const localVideoRef = React.useRef(null);",
|
||||
"lineNumber": 47,
|
||||
"lineNumber": 43,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-10-26T19:12:24.410Z",
|
||||
"reasonDetail": "Used to get the local video element for rendering."
|
||||
|
@ -14571,7 +14571,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/components/GroupCallRemoteParticipant.js",
|
||||
"line": " const remoteVideoRef = react_1.useRef(null);",
|
||||
"lineNumber": 24,
|
||||
"lineNumber": 26,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-11-11T21:56:04.179Z",
|
||||
"reasonDetail": "Needed to render the remote video element."
|
||||
|
@ -14580,7 +14580,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/components/GroupCallRemoteParticipant.js",
|
||||
"line": " const canvasContextRef = react_1.useRef(null);",
|
||||
"lineNumber": 25,
|
||||
"lineNumber": 27,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-11-17T23:29:38.698Z",
|
||||
"reasonDetail": "Doesn't touch the DOM."
|
||||
|
@ -14589,7 +14589,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/components/GroupCallRemoteParticipant.js",
|
||||
"line": " const frameBufferRef = react_1.useRef(new ArrayBuffer(FRAME_BUFFER_SIZE));",
|
||||
"lineNumber": 26,
|
||||
"lineNumber": 28,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-11-17T16:24:25.480Z",
|
||||
"reasonDetail": "Doesn't touch the DOM."
|
||||
|
|
Loading…
Add table
Reference in a new issue