feat(DominantSpeakerName): Implement

This commit is contained in:
Mihai-Andrei Uscat 2021-07-20 11:37:52 +03:00 committed by Mihai-Andrei Uscat
parent b7ab3ea052
commit 7263829763
9 changed files with 150 additions and 7 deletions

View File

@ -105,12 +105,15 @@
margin: 0 auto; margin: 0 auto;
max-width: 100%; max-width: 100%;
pointer-events: all; pointer-events: all;
background-color: #131519;
padding-bottom: env(safe-area-inset-bottom, 0);
box-shadow: 0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15);
border-radius: 6px; border-radius: 6px;
} }
.toolbox-content-wrapper::after {
content: '';
background: $newToolbarBackgroundColor;
padding-bottom: env(safe-area-inset-bottom, 0);
}
.toolbox-content-items { .toolbox-content-items {
background: $newToolbarBackgroundColor; background: $newToolbarBackgroundColor;
border-radius: 6px; border-radius: 6px;
@ -118,6 +121,7 @@
padding: 6px; padding: 6px;
text-align: center; text-align: center;
pointer-events: all; pointer-events: all;
box-shadow: 0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15);
>div { >div {
margin-left: 8px; margin-left: 8px;

View File

@ -16,8 +16,8 @@
z-index: $subtitlesZ; z-index: $subtitlesZ;
&.lifted { &.lifted {
// Lift subtitle above toolbar+invite box. // Lift subtitle above toolbar+dominant speaker box.
bottom: $newToolbarSize + 112px + 40px; bottom: $newToolbarSize + 36px + 40px;
} }
span { span {

View File

@ -0,0 +1,45 @@
// @flow
import { makeStyles } from '@material-ui/core/styles';
import React from 'react';
type Props = {
/**
* The name to be displayed within the badge.
*/
name: string
}
const useStyles = makeStyles(theme => {
return {
badge: {
background: 'rgba(0, 0, 0, 0.6)',
borderRadius: '3px',
color: theme.palette.text01,
maxWidth: '50%',
overflow: 'hidden',
padding: '2px 16px',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}
};
});
/**
* Component that displays a name badge.
*
* @param {Props} props - The props of the component.
* @returns {ReactElement}
*/
const DisplayNameBadge = ({ name }: Props) => {
const classes = useStyles();
return (
<div className = { classes.badge }>
{name}
</div>
);
};
export default DisplayNameBadge;

View File

@ -0,0 +1,60 @@
// @flow
import { makeStyles } from '@material-ui/core/styles';
import React from 'react';
import { useSelector } from 'react-redux';
import { getLocalParticipant } from '../../../base/participants';
import { withPixelLineHeight } from '../../../base/styles/functions.web';
import { getLargeVideoParticipant } from '../../../large-video/functions';
import { isToolboxVisible } from '../../../toolbox/functions.web';
import { isLayoutTileView } from '../../../video-layout';
import DisplayNameBadge from './DisplayNameBadge';
const useStyles = makeStyles(theme => {
return {
badgeContainer: {
...withPixelLineHeight(theme.typography.bodyShortRegularLarge),
alignItems: 'center',
display: 'flex',
justifyContent: 'center',
marginBottom: theme.spacing(2),
transition: 'margin-bottom 0.3s'
},
containerElevated: {
marginBottom: theme.spacing(7)
}
};
});
/**
* Component that renders the dominant speaker's name as a badge above the toolbar in stage view.
*
* @returns {ReactElement|null}
*/
const DominantSpeakerName = () => {
const classes = useStyles();
const largeVideoParticipant = useSelector(getLargeVideoParticipant);
const nameToDisplay = largeVideoParticipant?.name;
const selectedId = largeVideoParticipant?.id;
const localParticipant = useSelector(getLocalParticipant);
const localId = localParticipant?.id;
const isTileView = useSelector(isLayoutTileView);
const toolboxVisible = useSelector(isToolboxVisible);
if (nameToDisplay && selectedId !== localId && !isTileView) {
return (
<div
className = { `${classes.badgeContainer}${toolboxVisible ? '' : ` ${classes.containerElevated}`}` }>
<DisplayNameBadge name = { nameToDisplay } />
</div>
);
}
return null;
};
export default DominantSpeakerName;

View File

@ -3,3 +3,4 @@
export { default as DisplayName } from './DisplayName'; export { default as DisplayName } from './DisplayName';
export { default as DisplayNameLabel } from './DisplayNameLabel'; export { default as DisplayNameLabel } from './DisplayNameLabel';
export { default as DisplayNamePrompt } from './DisplayNamePrompt'; export { default as DisplayNamePrompt } from './DisplayNamePrompt';
export { default as DominantSpeakerName } from './DominantSpeakerName';

View File

@ -0,0 +1,15 @@
// @flow
import { getParticipantById } from '../base/participants';
/**
* Selector for the participant currently displaying on the large video.
*
* @param {Object} state - The redux state.
* @returns {Object}
*/
export function getLargeVideoParticipant(state: Object) {
const { participantId } = state['features/large-video'];
return getParticipantById(state, participantId);
}

View File

@ -2,8 +2,10 @@
import React from 'react'; import React from 'react';
import { getParticipantCountWithFake } from '../../base/participants'; import { getLocalParticipant } from '../../base/participants';
import { connect } from '../../base/redux'; import { connect } from '../../base/redux';
import { getLargeVideoParticipant } from '../../large-video/functions';
import { isLayoutTileView } from '../../video-layout';
import { import {
_abstractMapStateToProps, _abstractMapStateToProps,
@ -74,9 +76,13 @@ class Captions
* @returns {Object} * @returns {Object}
*/ */
function mapStateToProps(state) { function mapStateToProps(state) {
const isTileView = isLayoutTileView(state);
const largeVideoParticipant = getLargeVideoParticipant(state);
const localParticipant = getLocalParticipant(state);
return { return {
..._abstractMapStateToProps(state), ..._abstractMapStateToProps(state),
_isLifted: getParticipantCountWithFake(state) < 2 _isLifted: largeVideoParticipant && largeVideoParticipant?.id !== localParticipant?.id && !isTileView
}; };
} }

View File

@ -24,6 +24,7 @@ import { connect } from '../../../base/redux';
import { getLocalVideoTrack } from '../../../base/tracks'; import { getLocalVideoTrack } from '../../../base/tracks';
import { toggleChat } from '../../../chat'; import { toggleChat } from '../../../chat';
import { ChatButton } from '../../../chat/components'; import { ChatButton } from '../../../chat/components';
import { DominantSpeakerName } from '../../../display-name';
import { EmbedMeetingButton } from '../../../embed-meeting'; import { EmbedMeetingButton } from '../../../embed-meeting';
import { SharedDocumentButton } from '../../../etherpad'; import { SharedDocumentButton } from '../../../etherpad';
import { FeedbackButton } from '../../../feedback'; import { FeedbackButton } from '../../../feedback';
@ -1145,6 +1146,7 @@ class Toolbox extends Component<Props> {
onFocus = { this._onTabIn } onFocus = { this._onTabIn }
onMouseOut = { this._onMouseOut } onMouseOut = { this._onMouseOut }
onMouseOver = { this._onMouseOver }> onMouseOver = { this._onMouseOver }>
<DominantSpeakerName />
<div className = 'toolbox-content-items'> <div className = 'toolbox-content-items'>
{mainMenuButtons.map(({ Content, key, ...rest }) => Content !== Separator && ( {mainMenuButtons.map(({ Content, key, ...rest }) => Content !== Separator && (
<Content <Content

View File

@ -208,3 +208,13 @@ export function updateAutoPinnedParticipant(
dispatch(pinParticipant(latestScreenShareParticipantId)); dispatch(pinParticipant(latestScreenShareParticipantId));
} }
} }
/**
* Selector for whether we are currently in tile view.
*
* @param {Object} state - The redux state.
* @returns {boolean}
*/
export function isLayoutTileView(state: Object) {
return getCurrentLayout(state) === LAYOUTS.TILE_VIEW;
}