diff --git a/app/lib/components/Filmstrip.jsx b/app/lib/components/Filmstrip.jsx index 54a46e1..5a86b69 100644 --- a/app/lib/components/Filmstrip.jsx +++ b/app/lib/components/Filmstrip.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import ResizeObserver from 'resize-observer-polyfill'; import { connect } from 'react-redux'; import classnames from 'classnames'; +import * as stateActions from '../redux/stateActions'; import Peer from './Peer'; class Filmstrip extends Component @@ -15,15 +16,32 @@ class Filmstrip extends Component } state = { - selectedPeerName : null, - lastSpeaker : null, - width : 400 + 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. + // 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 = () => - this.state.selectedPeerName || this.state.lastSpeaker; + { + 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) => @@ -92,14 +110,6 @@ class Filmstrip extends Component } } - handleSelectPeer = (selectedPeerName) => - { - this.setState((oldState) => ({ - selectedPeerName : oldState.selectedPeerName === selectedPeerName ? - null : selectedPeerName - })); - }; - render() { const { peers, advancedMode } = this.props; @@ -130,9 +140,9 @@ class Filmstrip extends Component {Object.keys(peers).map((peerName) => (
this.handleSelectPeer(peerName)} + onClick={() => this.props.setSelectedPeer(peerName)} className={classnames('film', { - selected : this.state.selectedPeerName === peerName, + selected : this.props.selectedPeerName === peerName, active : this.state.lastSpeaker === peerName })} > @@ -156,17 +166,24 @@ Filmstrip.propTypes = { advancedMode : PropTypes.bool, peers : PropTypes.object.isRequired, consumers : PropTypes.object.isRequired, - myName : PropTypes.string.isRequired + myName : PropTypes.string.isRequired, + selectedPeerName : PropTypes.string, + setSelectedPeer : PropTypes.func.isRequired }; -const mapStateToProps = (state) => - ({ - activeSpeakerName : state.room.activeSpeakerName, - peers : state.peers, - consumers : state.consumers, - myName : state.me.name - }); +const mapStateToProps = (state) => ({ + activeSpeakerName : state.room.activeSpeakerName, + selectedPeerName : state.room.selectedPeerName, + peers : state.peers, + consumers : state.consumers, + myName : state.me.name +}); + +const mapDispatchToProps = { + setSelectedPeer : stateActions.setSelectedPeer +}; export default connect( - mapStateToProps + mapStateToProps, + mapDispatchToProps )(Filmstrip); diff --git a/app/lib/components/ParticipantList/ListMe.jsx b/app/lib/components/ParticipantList/ListMe.jsx index 0c2ffcb..6b86025 100644 --- a/app/lib/components/ParticipantList/ListMe.jsx +++ b/app/lib/components/ParticipantList/ListMe.jsx @@ -7,7 +7,7 @@ const ListMe = ({ me }) => const picture = me.picture || 'resources/images/avatar-empty.jpeg'; return ( -
  • +
  • diff --git a/app/lib/components/ParticipantList/ParticipantList.jsx b/app/lib/components/ParticipantList/ParticipantList.jsx index d2f727b..5f56983 100644 --- a/app/lib/components/ParticipantList/ParticipantList.jsx +++ b/app/lib/components/ParticipantList/ParticipantList.jsx @@ -1,51 +1,38 @@ import React from 'react'; import { connect } from 'react-redux'; +import classNames from 'classnames'; import * as appPropTypes from '../appPropTypes'; -import * as requestActions from '../../redux/requestActions'; import * as stateActions from '../../redux/stateActions'; import PropTypes from 'prop-types'; import ListPeer from './ListPeer'; import ListMe from './ListMe'; -class ParticipantList extends React.Component -{ - constructor(props) - { - super(props); - } +const ParticipantList = ({ advancedMode, peers, setSelectedPeer, selectedPeerName }) => ( +
    +
      + - render() - { - const { - advancedMode, - peers - } = this.props; - - return ( -
      -
        - - - { - peers.map((peer) => - { - return ( -
      • - -
      • - ); - }) - } -
      -
      - ); - } -} + {peers.map((peer) => ( +
    • setSelectedPeer(peer.name)} + > + +
    • + ))} +
    +
    +); ParticipantList.propTypes = { - advancedMode : PropTypes.bool, - peers : PropTypes.arrayOf(appPropTypes.Peer).isRequired + advancedMode : PropTypes.bool, + peers : PropTypes.arrayOf(appPropTypes.Peer).isRequired, + setSelectedPeer : PropTypes.func.isRequired, + selectedPeerName : PropTypes.string }; const mapStateToProps = (state) => @@ -53,26 +40,13 @@ const mapStateToProps = (state) => const peersArray = Object.values(state.peers); return { - peers : peersArray + peers : peersArray, + selectedPeerName : state.room.selectedPeerName }; }; -const mapDispatchToProps = (dispatch) => -{ - return { - handleChangeWebcam : (device) => - { - dispatch(requestActions.changeWebcam(device.value)); - }, - handleChangeAudioDevice : (device) => - { - dispatch(requestActions.changeAudioDevice(device.value)); - }, - onToggleAdvancedMode : () => - { - dispatch(stateActions.toggleAdvancedMode()); - } - }; +const mapDispatchToProps = { + setSelectedPeer : stateActions.setSelectedPeer }; const ParticipantListContainer = connect( diff --git a/app/lib/redux/reducers/room.js b/app/lib/redux/reducers/room.js index 4a787de..19872ec 100644 --- a/app/lib/redux/reducers/room.js +++ b/app/lib/redux/reducers/room.js @@ -7,7 +7,8 @@ const initialState = advancedMode : false, fullScreenConsumer : null, // ConsumerID toolbarsVisible : true, - mode : 'democratic' + mode : 'democratic', + selectedPeerName : null }; const room = (state = initialState, action) => @@ -70,6 +71,18 @@ const room = (state = initialState, action) => case 'SET_DISPLAY_MODE': return { ...state, mode: action.payload.mode }; + case 'SET_SELECTED_PEER': + { + const { selectedPeerName } = action.payload; + + return { + ...state, + + selectedPeerName : state.selectedPeerName === selectedPeerName ? + null : selectedPeerName + }; + } + default: return state; } diff --git a/app/lib/redux/stateActions.js b/app/lib/redux/stateActions.js index 008d308..1d8d09f 100644 --- a/app/lib/redux/stateActions.js +++ b/app/lib/redux/stateActions.js @@ -472,4 +472,9 @@ export const setPeerPicture = (peerName, picture) => export const loggedIn = () => ({ type : 'LOGGED_IN' - }); \ No newline at end of file + }); + +export const setSelectedPeer = (selectedPeerName) => ({ + type : 'SET_SELECTED_PEER', + payload : { selectedPeerName } +}); diff --git a/app/stylus/components/ParticipantList.styl b/app/stylus/components/ParticipantList.styl index 5b2d1f6..6dd24cc 100644 --- a/app/stylus/components/ParticipantList.styl +++ b/app/stylus/components/ParticipantList.styl @@ -7,15 +7,26 @@ > .list-item { padding: 0.5vmin; - border-bottom: 1px solid #ddd; + border-bottom: 1px solid #CBCBCB; width: 100%; overflow: hidden; + cursor: pointer; + + &.me { + cursor: auto; + } + + &.selected { + border-bottom-color: #377EFF; + } } } } [data-component='ListPeer'] { display: flex; + align-items: center; + > .indicators { left: 0; top: 0;