diff --git a/.gitignore b/.gitignore index 98d7543..252868b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/ +/app/build/ /app/public/config.js /server/config/ !/server/config/config.example.js diff --git a/app/src/components/Controls/Sidebar.js b/app/src/components/Controls/Sidebar.js index 1b70f40..bd55ff3 100644 --- a/app/src/components/Controls/Sidebar.js +++ b/app/src/components/Controls/Sidebar.js @@ -33,7 +33,7 @@ const styles = (theme) => }, fab : { - margin: theme.spacing.unit + margin : theme.spacing.unit } }); diff --git a/app/src/components/MeetingViews/Filmstrip.js b/app/src/components/MeetingViews/Filmstrip.js new file mode 100644 index 0000000..9eda0a9 --- /dev/null +++ b/app/src/components/MeetingViews/Filmstrip.js @@ -0,0 +1,303 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import ResizeObserver from 'resize-observer-polyfill'; +import { connect } from 'react-redux'; +import debounce from 'lodash/debounce'; +import { withStyles } from '@material-ui/core/styles'; +import classnames from 'classnames'; +import { withRoomContext } from '../../RoomContext'; +import Peer from '../Containers/Peer'; +import HiddenPeers from '../Containers/HiddenPeers'; + +const styles = (theme) => + ({ + root : + { + display : 'flex', + flexDirection : 'column', + alignItems : 'center', + height : '100%', + width : '100%' + }, + activePeerContainer : + { + width : '100%', + height : '80vh', + display : 'flex', + justifyContent : 'center', + alignItems : 'center' + }, + activePeer : + { + width : '100%', + border : '5px solid rgba(255, 255, 255, 0.15)', + boxShadow : '0px 5px 12px 2px rgba(17, 17, 17, 0.5)', + marginTop : 60 + }, + filmStrip : + { + display : 'flex', + background : 'rgba(0, 0, 0 , 0.5)', + width : '100%', + overflowX : 'auto', + height : '20vh', + alignItems : 'center' + }, + filmStripContent : + { + margin : '0 auto', + display : 'flex', + height : '100%', + alignItems : 'center' + }, + film : + { + height : '18vh', + flexShrink : 0, + paddingLeft : '1vh', + '& .active' : + { + borderColor : 'var(--active-speaker-border-color)' + }, + '&.selected' : + { + borderColor : 'var(--selected-peer-border-color)' + }, + '&:last-child' : + { + paddingRight : '1vh' + } + }, + filmContent : + { + height : '100%', + width : '100%', + border : '1px solid rgba(255,255,255,0.15)', + maxWidth : 'calc(18vh * (4 / 3))', + cursor : 'pointer', + '& .screen' : + { + maxWidth : 'calc(18vh * (2 * 4 / 3))', + border : 0 + } + }, + hiddenPeers : + { + + } + }); + +class Filmstrip extends Component +{ + constructor(props) + { + super(props); + + this.activePeerContainer = React.createRef(); + } + + state = { + lastSpeaker : null, + width : 400 + }; + + // 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. + getActivePeerName = () => + { + if (this.props.selectedPeerName) + { + return this.props.selectedPeerName; + } + + if (this.state.lastSpeaker) + { + return this.state.lastSpeaker; + } + + const peerNames = Object.keys(this.props.peers); + + if (peerNames.length > 0) + { + return peerNames[0]; + } + }; + + isSharingCamera = (peerName) => this.props.peers[peerName] && + this.props.peers[peerName].consumers.some((consumer) => + this.props.consumers[consumer].source === 'screen'); + + getRatio = () => + { + let ratio = 4 / 3; + + if (this.isSharingCamera(this.getActivePeerName())) + { + ratio *= 2; + } + + return ratio; + }; + + updateDimensions = debounce(() => + { + const container = this.activePeerContainer.current; + + if (container) + { + const ratio = this.getRatio(); + + let width = container.clientWidth; + + if (width / ratio > (container.clientHeight - 100)) + { + width = (container.clientHeight - 100) * ratio; + } + + this.setState({ + width + }); + } + }, 200); + + componentDidMount() + { + window.addEventListener('resize', this.updateDimensions); + const observer = new ResizeObserver(this.updateDimensions); + + observer.observe(this.activePeerContainer.current); + this.updateDimensions(); + } + + componentWillUnmount() + { + window.removeEventListener('resize', this.updateDimensions); + } + + componentDidUpdate(prevProps) + { + if (prevProps !== this.props) + { + this.updateDimensions(); + + if (this.props.activeSpeakerName !== this.props.myName) + { + // eslint-disable-next-line react/no-did-update-set-state + this.setState({ + lastSpeaker : this.props.activeSpeakerName + }); + } + } + } + + render() + { + const { + roomClient, + peers, + advancedMode, + spotlights, + spotlightsLength, + classes + } = this.props; + + const activePeerName = this.getActivePeerName(); + + return ( +