diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 0189103..11d72b2 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -799,7 +799,7 @@ export default class RoomClient if (volume !== this._micProducer.volume) { this._micProducer.volume = volume; - store.dispatch(stateActions.setProducerVolume(this._micProducer.id, volume)); + store.dispatch(stateActions.setPeerVolume(this._peerName, volume)); } }); @@ -1504,7 +1504,7 @@ export default class RoomClient if (volume !== producer.volume) { producer.volume = volume; - store.dispatch(stateActions.setProducerVolume(producer.id, volume)); + store.dispatch(stateActions.setPeerVolume(this._peerName, volume)); } }); @@ -1937,7 +1937,7 @@ export default class RoomClient if (volume !== consumer.volume) { consumer.volume = volume; - store.dispatch(stateActions.setConsumerVolume(consumer.id, volume)); + store.dispatch(stateActions.setPeerVolume(consumer.peer.name, volume)); } }); } diff --git a/app/src/actions/stateActions.js b/app/src/actions/stateActions.js index bb57e28..968e949 100644 --- a/app/src/actions/stateActions.js +++ b/app/src/actions/stateActions.js @@ -378,19 +378,11 @@ export const setConsumerTrack = (consumerId, track) => }; }; -export const setConsumerVolume = (consumerId, volume) => +export const setPeerVolume = (peerName, volume) => { return { - type : 'SET_CONSUMER_VOLUME', - payload : { consumerId, volume } - }; -}; - -export const setProducerVolume = (producerId, volume) => -{ - return { - type : 'SET_PRODUCER_VOLUME', - payload : { producerId, volume } + type : 'SET_PEER_VOLUME', + payload : { peerName, volume } }; }; diff --git a/app/src/components/Containers/Me.js b/app/src/components/Containers/Me.js index d63448d..2934dee 100644 --- a/app/src/components/Containers/Me.js +++ b/app/src/components/Containers/Me.js @@ -55,6 +55,7 @@ const Me = (props) => micProducer, webcamProducer, screenProducer, + volume, classes } = props; @@ -87,7 +88,7 @@ const Me = (props) => peer={me} showPeerInfo audioTrack={micProducer ? micProducer.track : null} - volume={micProducer ? micProducer.volume : null} + volume={volume} videoTrack={webcamProducer ? webcamProducer.track : null} videoVisible={videoVisible} audioCodec={micProducer ? micProducer.codec : null} @@ -121,13 +122,13 @@ const Me = (props) => Me.propTypes = { roomClient : PropTypes.any.isRequired, - connected : PropTypes.bool.isRequired, advancedMode : PropTypes.bool, me : appPropTypes.Me.isRequired, activeSpeaker : PropTypes.bool, micProducer : appPropTypes.Producer, webcamProducer : appPropTypes.Producer, screenProducer : appPropTypes.Producer, + volume : PropTypes.number, style : PropTypes.object, classes : PropTypes.object.isRequired }; @@ -135,9 +136,9 @@ Me.propTypes = const mapStateToProps = (state) => { return { - connected : state.room.state === 'connected', me : state.me, ...meProducersSelector(state), + volume : state.peerVolumes[state.me.name], activeSpeaker : state.me.name === state.room.activeSpeakerName }; }; diff --git a/app/src/components/Containers/Peer.js b/app/src/components/Containers/Peer.js index e89bbaa..91d0db3 100644 --- a/app/src/components/Containers/Peer.js +++ b/app/src/components/Containers/Peer.js @@ -125,6 +125,7 @@ const Peer = (props) => micConsumer, webcamConsumer, screenConsumer, + volume, toggleConsumerFullscreen, toggleConsumerWindow, style, @@ -133,6 +134,9 @@ const Peer = (props) => theme } = props; + if (!peer) + return; + const micEnabled = ( Boolean(micConsumer) && !micConsumer.locallyPaused && @@ -285,7 +289,7 @@ const Peer = (props) => advancedMode={advancedMode} peer={peer} showPeerInfo - volume={micConsumer ? micConsumer.volume : null} + volume={volume} videoTrack={webcamConsumer ? webcamConsumer.track : null} videoVisible={videoVisible} videoProfile={videoProfile} @@ -411,10 +415,11 @@ Peer.propTypes = { roomClient : PropTypes.any.isRequired, advancedMode : PropTypes.bool, - peer : appPropTypes.Peer.isRequired, + peer : appPropTypes.Peer, micConsumer : appPropTypes.Consumer, webcamConsumer : appPropTypes.Consumer, screenConsumer : appPropTypes.Consumer, + volume : PropTypes.number, windowConsumer : PropTypes.number, activeSpeaker : PropTypes.bool, style : PropTypes.object, @@ -424,15 +429,16 @@ Peer.propTypes = theme : PropTypes.object.isRequired }; -const makeMapStateToProps = () => +const makeMapStateToProps = (initialState, props) => { const getPeerConsumers = makePeerConsumerSelector(); - const mapStateToProps = (state, props) => + const mapStateToProps = (state) => { return { peer : state.peers[props.name], ...getPeerConsumers(state, props), + volume : state.peerVolumes[props.name], windowConsumer : state.room.windowConsumer, activeSpeaker : props.name === state.room.activeSpeakerName }; diff --git a/app/src/components/Controls/Sidebar.js b/app/src/components/Controls/Sidebar.js index 7319f35..a35a0af 100644 --- a/app/src/components/Controls/Sidebar.js +++ b/app/src/components/Controls/Sidebar.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; +import { meProducersSelector } from '../Selectors'; import { withStyles } from '@material-ui/core/styles'; import { unstable_useMediaQuery as useMediaQuery } from '@material-ui/core/useMediaQuery'; import classnames from 'classnames'; @@ -8,7 +9,6 @@ import * as appPropTypes from '../appPropTypes'; import { withRoomContext } from '../../RoomContext'; import Fab from '@material-ui/core/Fab'; import Tooltip from '@material-ui/core/Tooltip'; -// import Avatar from '@material-ui/core/Avatar'; import MicIcon from '@material-ui/icons/Mic'; import MicOffIcon from '@material-ui/icons/MicOff'; import VideoIcon from '@material-ui/icons/Videocam'; @@ -18,8 +18,6 @@ import ScreenOffIcon from '@material-ui/icons/StopScreenShare'; import ExtensionIcon from '@material-ui/icons/Extension'; import LockIcon from '@material-ui/icons/Lock'; import LockOpenIcon from '@material-ui/icons/LockOpen'; -// import HandOff from '../../images/icon-hand-black.svg'; -// import HandOn from '../../images/icon-hand-white.svg'; import LeaveIcon from '@material-ui/icons/Cancel'; const styles = (theme) => @@ -309,16 +307,24 @@ Sidebar.propTypes = const mapStateToProps = (state) => ({ toolbarsVisible : state.room.toolbarsVisible, - micProducer : Object.values(state.producers) - .find((producer) => producer.source === 'mic'), - webcamProducer : Object.values(state.producers) - .find((producer) => producer.source === 'webcam'), - screenProducer : Object.values(state.producers) - .find((producer) => producer.source === 'screen'), - me : state.me, - locked : state.room.locked + ...meProducersSelector(state), + me : state.me, + locked : state.room.locked }); export default withRoomContext(connect( - mapStateToProps + mapStateToProps, + null, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.room.toolbarsVisible === next.room.toolbarsVisible && + prev.room.locked === next.room.locked && + prev.producers === next.producers && + prev.me === next.me + ); + } + } )(withStyles(styles, { withTheme: true })(Sidebar))); diff --git a/app/src/components/MeetingDrawer/Chat/MessageList.js b/app/src/components/MeetingDrawer/Chat/MessageList.js index 9aec4ba..3e8b2d8 100644 --- a/app/src/components/MeetingDrawer/Chat/MessageList.js +++ b/app/src/components/MeetingDrawer/Chat/MessageList.js @@ -100,4 +100,17 @@ const mapStateToProps = (state) => myPicture : state.me.picture }); -export default connect(mapStateToProps, null)(withStyles(styles)(MessageList)); \ No newline at end of file +export default connect( + mapStateToProps, + null, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.chatmessages === next.chatmessages && + prev.me.picture === next.me.picture + ); + } + } +)(withStyles(styles)(MessageList)); \ No newline at end of file diff --git a/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js b/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js index e6574b8..e98d09c 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js @@ -169,9 +169,13 @@ const ListPeer = (props) => peer, micConsumer, screenConsumer, + volume, classes } = props; + if (!peer) + return; + const micEnabled = ( Boolean(micConsumer) && !micConsumer.locallyPaused && @@ -208,7 +212,7 @@ const ListPeer = (props) => }
-
+
{ screenConsumer ? @@ -267,18 +271,20 @@ ListPeer.propTypes = micConsumer : appPropTypes.Consumer, webcamConsumer : appPropTypes.Consumer, screenConsumer : appPropTypes.Consumer, + volume : PropTypes.number, classes : PropTypes.object.isRequired }; -const makeMapStateToProps = () => +const makeMapStateToProps = (initialState, props) => { const getPeerConsumers = makePeerConsumerSelector(); - const mapStateToProps = (state, props) => + const mapStateToProps = (state) => { return { - peer : state.peers[props.name], - ...getPeerConsumers(state, props) + peer : state.peers[props.name], + ...getPeerConsumers(state, props), + volume : state.peerVolumes[props.name] }; }; diff --git a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js index c03bfa5..1b6655f 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js @@ -1,8 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; +import { + passivePeersSelector +} from '../../Selectors'; import classNames from 'classnames'; import { withStyles } from '@material-ui/core/styles'; -import * as appPropTypes from '../../appPropTypes'; import { withRoomContext } from '../../../RoomContext'; import PropTypes from 'prop-types'; import ListPeer from './ListPeer'; @@ -71,7 +73,7 @@ class ParticipantList extends React.PureComponent const { roomClient, advancedMode, - peers, + passivePeers, selectedPeerName, spotlights, classes @@ -86,38 +88,30 @@ class ParticipantList extends React.PureComponent
  • Participants in Spotlight:
  • - {peers.filter((peer) => - { - return (spotlights.find((spotlight) => - { return (spotlight === peer.name); })); - }).map((peer) => ( + { spotlights.map((peerName) => (
  • roomClient.setSelectedPeer(peer.name)} + onClick={() => roomClient.setSelectedPeer(peerName)} > - +
  • ))}

  • Passive Participants:
  • - {peers.filter((peer) => - { - return !(spotlights.find((spotlight) => - { return (spotlight === peer.name); })); - }).map((peer) => ( + { passivePeers.map((peerName) => (
  • roomClient.setSelectedPeer(peer.name)} + onClick={() => roomClient.setSelectedPeer(peerName)} > - +
  • ))}
@@ -130,25 +124,33 @@ ParticipantList.propTypes = { roomClient : PropTypes.any.isRequired, advancedMode : PropTypes.bool, - peers : PropTypes.arrayOf(appPropTypes.Peer).isRequired, + passivePeers : PropTypes.array, selectedPeerName : PropTypes.string, spotlights : PropTypes.array.isRequired, classes : PropTypes.object.isRequired }; const mapStateToProps = (state) => -{ - const peersArray = Object.values(state.peers); - - return { - peers : peersArray, + ({ + passivePeers : passivePeersSelector(state), selectedPeerName : state.room.selectedPeerName, spotlights : state.room.spotlights - }; -}; + }); const ParticipantListContainer = withRoomContext(connect( - mapStateToProps + mapStateToProps, + null, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.peers === next.peers && + prev.spotlights === next.spotlights && + prev.room.selectedPeerName === next.room.selectedPeerName + ); + } + } )(withStyles(styles)(ParticipantList))); export default ParticipantListContainer; diff --git a/app/src/components/MeetingViews/Democratic.js b/app/src/components/MeetingViews/Democratic.js index a5e6772..14211d6 100644 --- a/app/src/components/MeetingViews/Democratic.js +++ b/app/src/components/MeetingViews/Democratic.js @@ -1,9 +1,8 @@ import React from 'react'; import { connect } from 'react-redux'; import { - peersSelector, + peersLengthSelector, videoBoxesSelector, - spotlightsSelector, spotlightsLengthSelector } from '../Selectors'; import PropTypes from 'prop-types'; @@ -117,7 +116,7 @@ class Democratic extends React.PureComponent { const { advancedMode, - peers, + peersLength, spotlights, spotlightsLength, classes @@ -135,27 +134,20 @@ class Democratic extends React.PureComponent advancedMode={advancedMode} style={style} /> - { Object.keys(peers).map((peerName) => + { spotlights.map((peerName) => { - if (spotlights.find((spotlightsElement) => spotlightsElement === peerName)) - { - return ( - - ); - } - else - { - return (''); - } + return ( + + ); })} - { spotlightsLength < Object.keys(peers).length ? + { spotlightsLength < peersLength ? :null } @@ -167,7 +159,7 @@ class Democratic extends React.PureComponent Democratic.propTypes = { advancedMode : PropTypes.bool, - peers : PropTypes.object.isRequired, + peersLength : PropTypes.number, boxes : PropTypes.number, spotlightsLength : PropTypes.number, spotlights : PropTypes.array.isRequired, @@ -177,13 +169,25 @@ Democratic.propTypes = const mapStateToProps = (state) => { return { - peers : peersSelector(state), + peersLength : peersLengthSelector(state), boxes : videoBoxesSelector(state), - spotlights : spotlightsSelector(state), + spotlights : state.room.spotlights, spotlightsLength : spotlightsLengthSelector(state) }; }; export default connect( - mapStateToProps + mapStateToProps, + null, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.producers === next.producers && + prev.consumers === next.consumers && + prev.spotlights === next.spotlights + ); + } + } )(withStyles(styles)(Democratic)); diff --git a/app/src/components/Room.js b/app/src/components/Room.js index 1f64460..aa4bf0f 100644 --- a/app/src/components/Room.js +++ b/app/src/components/Room.js @@ -433,5 +433,20 @@ const mapDispatchToProps = (dispatch) => export default withRoomContext(connect( mapStateToProps, - mapDispatchToProps + mapDispatchToProps, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.room === next.room && + prev.me.loggedIn === next.me.loggedIn && + prev.me.loginEnabled === next.me.loginEnabled && + prev.me.picture === next.me.picture && + prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen && + prev.toolarea.unreadMessages === next.toolarea.unreadMessages && + prev.toolarea.unreadFiles === next.toolarea.unreadFiles + ); + } + } )(withStyles(styles, { withTheme: true })(Room))); \ No newline at end of file diff --git a/app/src/components/Selectors.js b/app/src/components/Selectors.js index 836d158..0497119 100644 --- a/app/src/components/Selectors.js +++ b/app/src/components/Selectors.js @@ -5,7 +5,7 @@ const consumersSelect = (state) => state.consumers; export const spotlightsSelector = (state) => state.room.spotlights; -export const peersSelector = (state) => state.peers; +const peersSelector = (state) => state.peers; export const micProducersSelector = createSelector( producersSelect, @@ -57,6 +57,28 @@ export const spotlightsLengthSelector = createSelector( (spotlights) => (spotlights ? spotlights.length : 0) ); +export const spotlightPeersSelector = createSelector( + spotlightsSelector, + peersSelector, + (spotlights, peers) => spotlights.map((peerName) => peers[peerName]) +); + +export const peersLengthSelector = createSelector( + peersSelector, + (peers) => Object.values(peers).length +); + +const peersKeySelector = createSelector( + peersSelector, + (peers) => Object.keys(peers) +); + +export const passivePeersSelector = createSelector( + peersKeySelector, + spotlightsSelector, + (peers, spotlights) => peers.filter((peerName) => !spotlights.includes(peerName)) +); + export const videoBoxesSelector = createSelector( spotlightsLengthSelector, screenProducersSelector, @@ -79,7 +101,8 @@ export const meProducersSelector = createSelector( } ); -const getPeerConsumers = (state, props) => state.peers[props.name].consumers; +const getPeerConsumers = (state, props) => + (state.peers[props.name] ? state.peers[props.name].consumers : null); const getAllConsumers = (state) => state.consumers; export const makePeerConsumerSelector = () => @@ -89,6 +112,9 @@ export const makePeerConsumerSelector = () => getAllConsumers, (consumers, allConsumers) => { + if (!consumers) + return null; + const consumersArray = consumers .map((consumerId) => allConsumers[consumerId]); const micConsumer = @@ -101,4 +127,4 @@ export const makePeerConsumerSelector = () => return { micConsumer, webcamConsumer, screenConsumer }; } ); -}; \ No newline at end of file +}; diff --git a/app/src/reducers/consumers.js b/app/src/reducers/consumers.js index 117cdc5..758efd2 100644 --- a/app/src/reducers/consumers.js +++ b/app/src/reducers/consumers.js @@ -25,6 +25,7 @@ const consumers = (state = initialState, action) => { const { consumerId, originator } = action.payload; const consumer = state[consumerId]; + let newConsumer; if (originator === 'local') @@ -35,19 +36,11 @@ const consumers = (state = initialState, action) => return { ...state, [consumerId]: newConsumer }; } - case 'SET_CONSUMER_VOLUME': - { - const { consumerId, volume } = action.payload; - const consumer = state[consumerId]; - const newConsumer = { ...consumer, volume }; - - return { ...state, [consumerId]: newConsumer }; - } - case 'SET_CONSUMER_RESUMED': { const { consumerId, originator } = action.payload; const consumer = state[consumerId]; + let newConsumer; if (originator === 'local') diff --git a/app/src/reducers/peerVolumes.js b/app/src/reducers/peerVolumes.js new file mode 100644 index 0000000..6b89db8 --- /dev/null +++ b/app/src/reducers/peerVolumes.js @@ -0,0 +1,44 @@ +const initialState = {}; + +const peerVolumes = (state = initialState, action) => +{ + switch (action.type) + { + case 'SET_ME': + { + const { + peerName + } = action.payload; + + return { ...state, [peerName]: 0 }; + } + case 'ADD_PEER': + { + const { peer } = action.payload; + + return { ...state, [peer.name]: 0 }; + } + + case 'REMOVE_PEER': + { + const { peerName } = action.payload; + const newState = { ...state }; + + delete newState[peerName]; + + return newState; + } + + case 'SET_PEER_VOLUME': + { + const { peerName, volume } = action.payload; + + return { ...state, [peerName]: volume }; + } + + default: + return state; + } +}; + +export default peerVolumes; diff --git a/app/src/reducers/producers.js b/app/src/reducers/producers.js index 736f70b..64aee90 100644 --- a/app/src/reducers/producers.js +++ b/app/src/reducers/producers.js @@ -25,6 +25,7 @@ const producers = (state = initialState, action) => { const { producerId, originator } = action.payload; const producer = state[producerId]; + let newProducer; if (originator === 'local') @@ -35,19 +36,11 @@ const producers = (state = initialState, action) => return { ...state, [producerId]: newProducer }; } - case 'SET_PRODUCER_VOLUME': - { - const { producerId, volume } = action.payload; - const producer = state[producerId]; - const newProducer = { ...producer, volume }; - - return { ...state, [producerId]: newProducer }; - } - case 'SET_PRODUCER_RESUMED': { const { producerId, originator } = action.payload; const producer = state[producerId]; + let newProducer; if (originator === 'local') diff --git a/app/src/reducers/rootReducer.js b/app/src/reducers/rootReducer.js index 8b3fdce..b7e3b75 100644 --- a/app/src/reducers/rootReducer.js +++ b/app/src/reducers/rootReducer.js @@ -4,6 +4,7 @@ import me from './me'; import producers from './producers'; import peers from './peers'; import consumers from './consumers'; +import peerVolumes from './peerVolumes'; import notifications from './notifications'; import chatmessages from './chatmessages'; import toolarea from './toolarea'; @@ -15,6 +16,7 @@ export default combineReducers({ producers, peers, consumers, + peerVolumes, notifications, chatmessages, toolarea, diff --git a/app/src/store.js b/app/src/store.js index 4b247db..14c8460 100644 --- a/app/src/store.js +++ b/app/src/store.js @@ -28,8 +28,7 @@ if (process.env.NODE_ENV !== 'production') const reduxLogger = createLogger( { // filter VOLUME level actions from log - predicate : (getState, action) => ! (action.type === 'SET_PRODUCER_VOLUME' - || action.type === 'SET_CONSUMER_VOLUME'), + predicate : (getState, action) => !(action.type === 'SET_PEER_VOLUME'), duration : true, timestamp : false, level : 'log',