Participant list improvements

This commit is contained in:
Josh Perez 2020-11-20 14:39:50 -05:00 committed by Josh Perez
parent 7ca063a274
commit f8b4862ed5
13 changed files with 119 additions and 14 deletions

View file

@ -6110,8 +6110,30 @@ button.module-image__border-overlay:focus {
.module-calling-button {
&__participants {
@include color-svg('../images/icons/v2/group-solid-24.svg', $color-white);
display: inline-block;
height: 22px;
width: 22px;
&--container {
@include button-reset;
border: none;
color: $color-white;
}
&--shown {
background-color: $color-gray-75;
border-radius: 16px;
padding: 6px 8px;
padding-bottom: 2px;
margin-top: -6px;
margin-right: -8px;
}
&--count {
@include font-body-2-bold;
margin-left: 5px;
vertical-align: top;
}
}
&__settings {
@ -6592,6 +6614,10 @@ button.module-image__border-overlay:focus {
@include font-body-2-bold;
}
&__contact-icon {
background-color: $color-gray-25;
}
&__list {
height: 100%;
margin-bottom: 0;

View file

@ -178,6 +178,7 @@ const ActiveCallManager: React.FC<ActiveCallManagerPropsType> = ({
setLocalPreview={setLocalPreview}
setLocalAudio={setLocalAudio}
setLocalVideo={setLocalVideo}
showParticipantsList={showParticipantsList}
toggleParticipants={toggleParticipants}
toggleSettings={toggleSettings}
/>

View file

@ -195,7 +195,11 @@ export const CallScreen: React.FC<PropsType> = ({
});
const remoteParticipants =
call.callMode === CallMode.Group ? call.remoteParticipants.length : 0;
call.callMode === CallMode.Group
? activeCall.groupCallParticipants.length
: 0;
const { showParticipantsList } = activeCall.activeCallState;
return (
<div
@ -232,6 +236,7 @@ export const CallScreen: React.FC<PropsType> = ({
i18n={i18n}
isGroupCall={call.callMode === CallMode.Group}
remoteParticipants={remoteParticipants}
showParticipantsList={showParticipantsList}
toggleParticipants={toggleParticipants}
togglePip={togglePip}
toggleSettings={toggleSettings}

View file

@ -21,6 +21,10 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
'remoteParticipants',
overrideProps.remoteParticipants || 0
),
showParticipantsList: boolean(
'showParticipantsList',
Boolean(overrideProps.showParticipantsList)
),
toggleParticipants: () => action('toggle-participants'),
togglePip: () => action('toggle-pip'),
toggleSettings: () => action('toggle-settings'),
@ -44,6 +48,17 @@ story.add('With Participants', () => (
/>
));
story.add('With Participants (shown)', () => (
<CallingHeader
{...createProps({
canPip: true,
isGroupCall: true,
remoteParticipants: 10,
showParticipantsList: true,
})}
/>
));
story.add('Long Title', () => (
<CallingHeader
{...createProps({

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import classNames from 'classnames';
import { LocalizerType } from '../types/Util';
import { Tooltip, TooltipTheme } from './Tooltip';
@ -11,6 +12,7 @@ export type PropsType = {
i18n: LocalizerType;
isGroupCall?: boolean;
remoteParticipants?: number;
showParticipantsList: boolean;
toggleParticipants?: () => void;
togglePip?: () => void;
toggleSettings: () => void;
@ -22,6 +24,7 @@ export const CallingHeader = ({
i18n,
isGroupCall = false,
remoteParticipants,
showParticipantsList,
toggleParticipants,
togglePip,
toggleSettings,
@ -43,10 +46,20 @@ export const CallingHeader = ({
aria-label={i18n('calling__participants', [
String(remoteParticipants),
])}
className="module-calling-button__participants"
className={classNames(
'module-calling-button__participants--container',
{
'module-calling-button__participants--shown': showParticipantsList,
}
)}
onClick={toggleParticipants}
type="button"
/>
>
<i className="module-calling-button__participants" />
<span className="module-calling-button__participants--count">
{remoteParticipants}
</span>
</button>
</Tooltip>
</div>
) : null}

View file

@ -39,6 +39,10 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
setLocalAudio: action('set-local-audio'),
setLocalPreview: action('set-local-preview'),
setLocalVideo: action('set-local-video'),
showParticipantsList: boolean(
'showParticipantsList',
Boolean(overrideProps.showParticipantsList)
),
toggleParticipants: action('toggle-participants'),
toggleSettings: action('toggle-settings'),
});
@ -115,3 +119,12 @@ story.add('Group Call - 4', () => {
});
return <CallingLobby {...props} />;
});
story.add('Group Call - 4 (participants list)', () => {
const props = createProps({
isGroupCall: true,
participantNames: ['Sam', 'Cayce', 'April', 'Logan', 'Carl'],
showParticipantsList: true,
});
return <CallingLobby {...props} />;
});

View file

@ -34,6 +34,7 @@ export type PropsType = {
setLocalAudio: (_: SetLocalAudioType) => void;
setLocalVideo: (_: SetLocalVideoType) => void;
setLocalPreview: (_: SetLocalPreviewType) => void;
showParticipantsList: boolean;
toggleParticipants: () => void;
toggleSettings: () => void;
};
@ -52,6 +53,7 @@ export const CallingLobby = ({
setLocalAudio,
setLocalPreview,
setLocalVideo,
showParticipantsList,
toggleParticipants,
toggleSettings,
}: PropsType): JSX.Element => {
@ -117,6 +119,7 @@ export const CallingLobby = ({
i18n={i18n}
isGroupCall={isGroupCall}
remoteParticipants={participantNames.length}
showParticipantsList={showParticipantsList}
toggleParticipants={toggleParticipants}
toggleSettings={toggleSettings}
/>

View file

@ -24,6 +24,7 @@ function createParticipant(
hasRemoteAudio: Boolean(participantProps.hasRemoteAudio),
hasRemoteVideo: Boolean(participantProps.hasRemoteVideo),
isSelf: Boolean(participantProps.isSelf),
name: participantProps.name,
profileName: participantProps.title,
title: String(participantProps.title),
videoAspectRatio: 1.3,
@ -64,6 +65,7 @@ story.add('Many Participants', () => {
createParticipant({
hasRemoteAudio: true,
hasRemoteVideo: true,
name: 'Rage Trunks',
title: 'Rage Trunks',
}),
createParticipant({
@ -73,6 +75,7 @@ story.add('Many Participants', () => {
createParticipant({
hasRemoteAudio: true,
hasRemoteVideo: true,
name: 'Goku Black',
title: 'Goku Black',
}),
createParticipant({

View file

@ -7,6 +7,7 @@ import React from 'react';
import { createPortal } from 'react-dom';
import { Avatar } from './Avatar';
import { ContactName } from './conversation/ContactName';
import { InContactsIcon } from './InContactsIcon';
import { LocalizerType } from '../types/Util';
import { GroupCallRemoteParticipantType } from '../types/Calling';
@ -31,14 +32,24 @@ export const CallingParticipantsList = React.memo(
};
}, []);
const handleCancel = React.useCallback(
(e: React.MouseEvent) => {
if (e.target === e.currentTarget) {
onClose();
}
},
[onClose]
);
if (!root) {
return null;
}
return createPortal(
<div
role="presentation"
className="module-calling-participants-list__overlay"
onClick={handleCancel}
role="presentation"
>
<div className="module-calling-participants-list">
<div className="module-calling-participants-list__header">
@ -80,11 +91,22 @@ export const CallingParticipantsList = React.memo(
{i18n('you')}
</span>
) : (
<ContactName
i18n={i18n}
module="module-calling-participants-list__name"
title={participant.title}
/>
<>
<ContactName
i18n={i18n}
module="module-calling-participants-list__name"
title={participant.title}
/>
{participant.name ? (
<span>
{' '}
<InContactsIcon
className="module-calling-participants-list__contact-icon"
i18n={i18n}
/>
</span>
) : null}
</>
)}
</div>
<div>

View file

@ -2,26 +2,28 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import classNames from 'classnames';
import { Tooltip } from './Tooltip';
import { LocalizerType } from '../types/Util';
type PropsType = {
className?: string;
i18n: LocalizerType;
};
export const InContactsIcon = (props: PropsType): JSX.Element => {
const { i18n } = props;
const { className, i18n } = props;
/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
return (
<span className="module-in-contacts-icon__tooltip">
<Tooltip content={i18n('contactInAddressBook')}>
<span
tabIndex={0}
role="img"
aria-label={i18n('contactInAddressBook')}
className="module-in-contacts-icon__icon"
className={classNames('module-in-contacts-icon__icon', className)}
role="img"
tabIndex={0}
/>
</Tooltip>
</span>

View file

@ -70,6 +70,7 @@ const mapStateToActiveCallProp = (state: StateType) => {
hasRemoteAudio: remoteParticipant.hasRemoteAudio,
hasRemoteVideo: remoteParticipant.hasRemoteVideo,
isSelf: remoteParticipant.isSelf,
name: remoteConversation.name,
profileName: remoteConversation.profileName,
title: remoteConversation.title,
videoAspectRatio: remoteParticipant.videoAspectRatio,

View file

@ -66,6 +66,7 @@ export interface GroupCallRemoteParticipantType {
hasRemoteAudio: boolean;
hasRemoteVideo: boolean;
isSelf: boolean;
name?: string;
profileName?: string;
title: string;
videoAspectRatio: number;

View file

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