feat(DominantSpeakerName): Implement
This commit is contained in:
parent
b7ab3ea052
commit
7263829763
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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';
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue