import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { withStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; import { videoBoxesSelector } from '../Selectors'; import { withRoomContext } from '../../RoomContext'; import Me from '../Containers/Me'; import Peer from '../Containers/Peer'; import SpeakerPeer from '../Containers/SpeakerPeer'; import Grid from '@material-ui/core/Grid'; const RATIO = 1.334; const PADDING_V = 40; const PADDING_H = 0; const FILMSTRING_PADDING_V = 10; const FILMSTRING_PADDING_H = 0; const styles = () => ({ root : { height : '100%', width : '100%', display : 'grid', overflow : 'hidden', gridTemplateColumns : '1fr', gridTemplateRows : '1fr 0.25fr' }, speaker : { gridArea : '1 / 1 / 1 / 1', display : 'flex', justifyContent : 'center', alignItems : 'center' }, filmStrip : { gridArea : '2 / 1 / 2 / 1' }, filmItem : { display : 'flex', border : 'var(--peer-border)', '&.selected' : { borderColor : 'var(--selected-peer-border-color)' }, '&.active' : { borderColor : 'var(--selected-peer-border-color)' } }, hiddenToolBar : { paddingTop : 0, transition : 'padding .5s' }, showingToolBar : { paddingTop : 60, transition : 'padding .5s' } }); class Filmstrip extends React.PureComponent { constructor(props) { super(props); this.resizeTimeout = null; this.rootContainer = React.createRef(); this.activePeerContainer = React.createRef(); this.filmStripContainer = React.createRef(); } state = { lastSpeaker : null }; // Find the name of the peer which is currently speaking. This is either // the latest active speaker, or the manually selected peer, or, if no // person has spoken yet, the first peer in the list of peers. getActivePeerId = () => { const { selectedPeerId, peers } = this.props; const { lastSpeaker } = this.state; if (selectedPeerId && peers[selectedPeerId]) { return this.props.selectedPeerId; } if (lastSpeaker && peers[lastSpeaker]) { return this.state.lastSpeaker; } const peerIds = Object.keys(peers); if (peerIds.length > 0) { return peerIds[0]; } }; isSharingCamera = (peerId) => this.props.peers[peerId] && this.props.peers[peerId].consumers.some((consumer) => this.props.consumers[consumer].source === 'screen'); updateDimensions = () => { const newState = {}; const root = this.rootContainer.current; if (!root) return; const availableWidth = root.clientWidth; // Grid is: // 4/5 speaker // 1/5 filmstrip const availableSpeakerHeight = (root.clientHeight * 0.8) - (this.props.toolbarsVisible || this.props.permanentTopBar ? PADDING_V : PADDING_H); const availableFilmstripHeight = root.clientHeight * 0.2; const speaker = this.activePeerContainer.current; if (speaker) { let speakerWidth = (availableWidth - PADDING_H); let speakerHeight = speakerWidth / RATIO; if (this.isSharingCamera(this.getActivePeerId())) { speakerWidth /= 2; speakerHeight = speakerWidth / RATIO; } if (speakerHeight > (availableSpeakerHeight - PADDING_V)) { speakerHeight = (availableSpeakerHeight - PADDING_V); speakerWidth = speakerHeight * RATIO; } newState.speakerWidth = speakerWidth; newState.speakerHeight = speakerHeight; } const filmStrip = this.filmStripContainer.current; if (filmStrip) { let filmStripHeight = availableFilmstripHeight - FILMSTRING_PADDING_V; let filmStripWidth = filmStripHeight * RATIO; if ( (filmStripWidth * this.props.boxes) > (availableWidth - FILMSTRING_PADDING_H) ) { filmStripWidth = (availableWidth - FILMSTRING_PADDING_H) / this.props.boxes; filmStripHeight = filmStripWidth / RATIO; } newState.filmStripWidth = filmStripWidth; newState.filmStripHeight = filmStripHeight; } this.setState({ ...newState }); }; componentDidMount() { // window.resize event listener window.addEventListener('resize', () => { // clear the timeout clearTimeout(this.resizeTimeout); // start timing for event "completion" this.resizeTimeout = setTimeout(() => this.updateDimensions(), 250); }); this.updateDimensions(); } componentWillUnmount() { window.removeEventListener('resize', this.updateDimensions); } componentDidUpdate(prevProps) { if (prevProps !== this.props) { if ( this.props.activeSpeakerId != null && this.props.activeSpeakerId !== this.props.myId ) { // eslint-disable-next-line react/no-did-update-set-state this.setState({ lastSpeaker : this.props.activeSpeakerId }); } this.updateDimensions(); } } render() { const { roomClient, peers, myId, advancedMode, spotlights, toolbarsVisible, permanentTopBar, classes } = this.props; const activePeerId = this.getActivePeerId(); const speakerStyle = { width : this.state.speakerWidth, height : this.state.speakerHeight }; const peerStyle = { width : this.state.filmStripWidth, height : this.state.filmStripHeight }; return (
{ peers[activePeerId] && }
{ Object.keys(peers).map((peerId) => { if (spotlights.find((spotlightsElement) => spotlightsElement === peerId)) { return (
roomClient.setSelectedPeer(peerId)} className={classnames(classes.filmItem, { selected : this.props.selectedPeerId === peerId, active : peerId === activePeerId })} >
); } else { return (''); } })}
); } } Filmstrip.propTypes = { roomClient : PropTypes.any.isRequired, activeSpeakerId : PropTypes.string, advancedMode : PropTypes.bool, peers : PropTypes.object.isRequired, consumers : PropTypes.object.isRequired, myId : PropTypes.string.isRequired, selectedPeerId : PropTypes.string, spotlights : PropTypes.array.isRequired, boxes : PropTypes.number, toolbarsVisible : PropTypes.bool.isRequired, toolAreaOpen : PropTypes.bool.isRequired, permanentTopBar : PropTypes.bool, classes : PropTypes.object.isRequired }; const mapStateToProps = (state) => { return { activeSpeakerId : state.room.activeSpeakerId, selectedPeerId : state.room.selectedPeerId, peers : state.peers, consumers : state.consumers, myId : state.me.id, spotlights : state.room.spotlights, boxes : videoBoxesSelector(state), toolbarsVisible : state.room.toolbarsVisible, toolAreaOpen : state.toolarea.toolAreaOpen, permanentTopBar : state.settings.permanentTopBar }; }; export default withRoomContext(connect( mapStateToProps, null, null, { areStatesEqual : (next, prev) => { return ( prev.room.activeSpeakerId === next.room.activeSpeakerId && prev.room.selectedPeerId === next.room.selectedPeerId && prev.room.toolbarsVisible === next.room.toolbarsVisible && prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen && prev.settings.permanentTopBar === next.settings.permanentTopBar && prev.peers === next.peers && prev.consumers === next.consumers && prev.room.spotlights === next.room.spotlights && prev.me.id === next.me.id ); } } )(withStyles(styles)(Filmstrip)));