Added redux selectors to improve performance. Fixed drawer. Cleaned up code and removed some unused code.
parent
0478a44b74
commit
fd1e512a80
|
|
@ -29,6 +29,7 @@
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-persist": "^5.10.0",
|
"redux-persist": "^5.10.0",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
|
"reselect": "^4.0.0",
|
||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"riek": "^1.1.0",
|
"riek": "^1.1.0",
|
||||||
"socket.io-client": "^2.2.0",
|
"socket.io-client": "^2.2.0",
|
||||||
|
|
@ -168,7 +169,7 @@
|
||||||
"no-case-declarations": 2,
|
"no-case-declarations": 2,
|
||||||
"no-catch-shadow": 2,
|
"no-catch-shadow": 2,
|
||||||
"no-class-assign": 2,
|
"no-class-assign": 2,
|
||||||
"no-confusing-arrow": 2,
|
"no-confusing-arrow": ["error", {"allowParens": true}],
|
||||||
"no-console": 2,
|
"no-console": 2,
|
||||||
"no-const-assign": 2,
|
"no-const-assign": 2,
|
||||||
"no-debugger": 2,
|
"no-debugger": 2,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { meProducersSelector } from '../Selectors';
|
||||||
import { withRoomContext } from '../../RoomContext';
|
import { withRoomContext } from '../../RoomContext';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
@ -133,21 +134,11 @@ Me.propTypes =
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
const producersArray = Object.values(state.producers);
|
|
||||||
const micProducer =
|
|
||||||
producersArray.find((producer) => producer.source === 'mic');
|
|
||||||
const webcamProducer =
|
|
||||||
producersArray.find((producer) => producer.source === 'webcam');
|
|
||||||
const screenProducer =
|
|
||||||
producersArray.find((producer) => producer.source === 'screen');
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
connected : state.room.state === 'connected',
|
connected : state.room.state === 'connected',
|
||||||
me : state.me,
|
me : state.me,
|
||||||
micProducer : micProducer,
|
...meProducersSelector(state),
|
||||||
webcamProducer : webcamProducer,
|
activeSpeaker : state.me.name === state.room.activeSpeakerName
|
||||||
screenProducer : screenProducer,
|
|
||||||
activeSpeaker : state.me.name === state.room.activeSpeakerName
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { makePeerConsumerSelector } from '../Selectors';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import * as appPropTypes from '../appPropTypes';
|
import * as appPropTypes from '../appPropTypes';
|
||||||
|
|
@ -423,26 +424,21 @@ Peer.propTypes =
|
||||||
theme : PropTypes.object.isRequired
|
theme : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state, { name }) =>
|
const makeMapStateToProps = () =>
|
||||||
{
|
{
|
||||||
const peer = state.peers[name];
|
const getPeerConsumers = makePeerConsumerSelector();
|
||||||
const consumersArray = peer.consumers
|
|
||||||
.map((consumerId) => state.consumers[consumerId]);
|
|
||||||
const micConsumer =
|
|
||||||
consumersArray.find((consumer) => consumer.source === 'mic');
|
|
||||||
const webcamConsumer =
|
|
||||||
consumersArray.find((consumer) => consumer.source === 'webcam');
|
|
||||||
const screenConsumer =
|
|
||||||
consumersArray.find((consumer) => consumer.source === 'screen');
|
|
||||||
|
|
||||||
return {
|
const mapStateToProps = (state, props) =>
|
||||||
peer,
|
{
|
||||||
micConsumer,
|
return {
|
||||||
webcamConsumer,
|
peer : state.peers[props.name],
|
||||||
screenConsumer,
|
...getPeerConsumers(state, props),
|
||||||
windowConsumer : state.room.windowConsumer,
|
windowConsumer : state.room.windowConsumer,
|
||||||
activeSpeaker : name === state.room.activeSpeakerName
|
activeSpeaker : props.name === state.room.activeSpeakerName
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) =>
|
const mapDispatchToProps = (dispatch) =>
|
||||||
|
|
@ -462,6 +458,6 @@ const mapDispatchToProps = (dispatch) =>
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(withStyles(styles, { withTheme: true })(Peer)));
|
)(withStyles(styles, { withTheme: true })(Peer)));
|
||||||
|
|
|
||||||
|
|
@ -30,64 +30,71 @@ const styles = (theme) =>
|
||||||
width : '100%',
|
width : '100%',
|
||||||
height : '100%',
|
height : '100%',
|
||||||
backgroundColor : theme.palette.background.paper
|
backgroundColor : theme.palette.background.paper
|
||||||
|
},
|
||||||
|
appBar :
|
||||||
|
{
|
||||||
|
display : 'flex',
|
||||||
|
flexDirection : 'row'
|
||||||
|
},
|
||||||
|
tabsHeader :
|
||||||
|
{
|
||||||
|
flexGrow : 1
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
class MeetingDrawer extends React.PureComponent
|
const MeetingDrawer = (props) =>
|
||||||
{
|
{
|
||||||
handleChange = (event, value) =>
|
const {
|
||||||
{
|
currentToolTab,
|
||||||
this.props.setToolTab(tabs[value]);
|
unreadMessages,
|
||||||
};
|
unreadFiles,
|
||||||
|
closeDrawer,
|
||||||
|
setToolTab,
|
||||||
|
classes,
|
||||||
|
theme
|
||||||
|
} = props;
|
||||||
|
|
||||||
render()
|
return (
|
||||||
{
|
<div className={classes.root}>
|
||||||
const {
|
<AppBar
|
||||||
currentToolTab,
|
position='static'
|
||||||
unreadMessages,
|
color='default'
|
||||||
unreadFiles,
|
className={classes.appBar}
|
||||||
closeDrawer,
|
>
|
||||||
classes,
|
<Tabs
|
||||||
theme
|
className={classes.tabsHeader}
|
||||||
} = this.props;
|
value={tabs.indexOf(currentToolTab)}
|
||||||
|
onChange={(event, value) => setToolTab(tabs[value])}
|
||||||
return (
|
indicatorColor='primary'
|
||||||
<div className={classes.root}>
|
textColor='primary'
|
||||||
<AppBar position='static' color='default'>
|
variant='fullWidth'
|
||||||
<Tabs
|
>
|
||||||
value={tabs.indexOf(currentToolTab)}
|
<Tab
|
||||||
onChange={this.handleChange}
|
label={
|
||||||
indicatorColor='primary'
|
<Badge color='secondary' badgeContent={unreadMessages}>
|
||||||
textColor='primary'
|
Chat
|
||||||
variant='fullWidth'
|
</Badge>
|
||||||
>
|
}
|
||||||
<Tab
|
/>
|
||||||
label={
|
<Tab
|
||||||
<Badge color='secondary' badgeContent={unreadMessages}>
|
label={
|
||||||
Chat
|
<Badge color='secondary' badgeContent={unreadFiles}>
|
||||||
</Badge>
|
File sharing
|
||||||
}
|
</Badge>
|
||||||
/>
|
}
|
||||||
<Tab
|
/>
|
||||||
label={
|
<Tab label='Participants' />
|
||||||
<Badge color='secondary' badgeContent={unreadFiles}>
|
</Tabs>
|
||||||
File sharing
|
<IconButton onClick={closeDrawer}>
|
||||||
</Badge>
|
{theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
|
||||||
}
|
</IconButton>
|
||||||
/>
|
</AppBar>
|
||||||
<Tab label='Participants' />
|
{currentToolTab === 'chat' && <Chat />}
|
||||||
<IconButton onClick={closeDrawer}>
|
{currentToolTab === 'files' && <FileSharing />}
|
||||||
{theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
|
{currentToolTab === 'users' && <ParticipantList />}
|
||||||
</IconButton>
|
</div>
|
||||||
</Tabs>
|
);
|
||||||
</AppBar>
|
};
|
||||||
{currentToolTab === 'chat' && <Chat />}
|
|
||||||
{currentToolTab === 'files' && <FileSharing />}
|
|
||||||
{currentToolTab === 'users' && <ParticipantList />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MeetingDrawer.propTypes =
|
MeetingDrawer.propTypes =
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { makePeerConsumerSelector } from '../../Selectors';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
@ -269,26 +270,21 @@ ListPeer.propTypes =
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state, { name }) =>
|
const makeMapStateToProps = () =>
|
||||||
{
|
{
|
||||||
const peer = state.peers[name];
|
const getPeerConsumers = makePeerConsumerSelector();
|
||||||
const consumersArray = peer.consumers
|
|
||||||
.map((consumerId) => state.consumers[consumerId]);
|
|
||||||
const micConsumer =
|
|
||||||
consumersArray.find((consumer) => consumer.source === 'mic');
|
|
||||||
const webcamConsumer =
|
|
||||||
consumersArray.find((consumer) => consumer.source === 'webcam');
|
|
||||||
const screenConsumer =
|
|
||||||
consumersArray.find((consumer) => consumer.source === 'screen');
|
|
||||||
|
|
||||||
return {
|
const mapStateToProps = (state, props) =>
|
||||||
peer,
|
{
|
||||||
micConsumer,
|
return {
|
||||||
webcamConsumer,
|
peer : state.peers[props.name],
|
||||||
screenConsumer
|
...getPeerConsumers(state, props)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps
|
makeMapStateToProps
|
||||||
)(withStyles(styles)(ListPeer)));
|
)(withStyles(styles)(ListPeer)));
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
peersSelector,
|
||||||
|
videoBoxesSelector,
|
||||||
|
spotlightsSelector,
|
||||||
|
spotlightsLengthSelector
|
||||||
|
} from '../Selectors';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
|
@ -159,28 +165,22 @@ class Democratic extends React.PureComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
Democratic.propTypes =
|
Democratic.propTypes =
|
||||||
{
|
{
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
peers : PropTypes.object.isRequired,
|
peers : PropTypes.object.isRequired,
|
||||||
boxes : PropTypes.number,
|
boxes : PropTypes.number,
|
||||||
spotlightsLength : PropTypes.number,
|
spotlightsLength : PropTypes.number,
|
||||||
spotlights : PropTypes.array.isRequired,
|
spotlights : PropTypes.array.isRequired,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
const spotlights = state.room.spotlights;
|
|
||||||
const spotlightsLength = spotlights ? state.room.spotlights.length : 0;
|
|
||||||
const boxes = spotlightsLength + Object.values(state.consumers)
|
|
||||||
.filter((consumer) => consumer.source === 'screen').length + Object.values(state.producers)
|
|
||||||
.filter((producer) => producer.source === 'screen').length + 1;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
peers : state.peers,
|
peers : peersSelector(state),
|
||||||
boxes,
|
boxes : videoBoxesSelector(state),
|
||||||
spotlights,
|
spotlights : spotlightsSelector(state),
|
||||||
spotlightsLength
|
spotlightsLength : spotlightsLengthSelector(state)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import * as appPropTypes from '../appPropTypes';
|
|
||||||
import PeerAudio from './PeerAudio';
|
|
||||||
|
|
||||||
const AudioPeer = ({ micConsumer }) =>
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<PeerAudio
|
|
||||||
audioTrack={micConsumer ? micConsumer.track : null}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
AudioPeer.propTypes =
|
|
||||||
{
|
|
||||||
micConsumer : appPropTypes.Consumer,
|
|
||||||
name : PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapStateToProps = (state, { name }) =>
|
|
||||||
{
|
|
||||||
const peer = state.peers[name];
|
|
||||||
const consumersArray = peer.consumers
|
|
||||||
.map((consumerId) => state.consumers[consumerId]);
|
|
||||||
const micConsumer =
|
|
||||||
consumersArray.find((consumer) => consumer.source === 'mic');
|
|
||||||
|
|
||||||
return {
|
|
||||||
micConsumer
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const AudioPeerContainer = connect(
|
|
||||||
mapStateToProps
|
|
||||||
)(AudioPeer);
|
|
||||||
|
|
||||||
export default AudioPeerContainer;
|
|
||||||
|
|
@ -1,19 +1,24 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { micConsumerSelector } from '../Selectors';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import AudioPeer from './AudioPeer';
|
import PeerAudio from './PeerAudio';
|
||||||
|
|
||||||
const AudioPeers = ({ peers }) =>
|
const AudioPeers = (props) =>
|
||||||
{
|
{
|
||||||
|
const {
|
||||||
|
micConsumers
|
||||||
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-component='AudioPeers'>
|
<div data-component='AudioPeers'>
|
||||||
{
|
{
|
||||||
Object.values(peers).map((peer) =>
|
micConsumers.map((micConsumer) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
<AudioPeer
|
<PeerAudio
|
||||||
key={peer.name}
|
key={micConsumer.id}
|
||||||
name={peer.name}
|
audioTrack={micConsumer.track}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -24,12 +29,12 @@ const AudioPeers = ({ peers }) =>
|
||||||
|
|
||||||
AudioPeers.propTypes =
|
AudioPeers.propTypes =
|
||||||
{
|
{
|
||||||
peers : PropTypes.object
|
micConsumers : PropTypes.array
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
({
|
({
|
||||||
peers : state.peers
|
micConsumers : micConsumerSelector(state)
|
||||||
});
|
});
|
||||||
|
|
||||||
const AudioPeersContainer = connect(
|
const AudioPeersContainer = connect(
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import Typography from '@material-ui/core/Typography';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
import MenuIcon from '@material-ui/icons/Menu';
|
import MenuIcon from '@material-ui/icons/Menu';
|
||||||
|
import Avatar from '@material-ui/core/Avatar';
|
||||||
import Badge from '@material-ui/core/Badge';
|
import Badge from '@material-ui/core/Badge';
|
||||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||||
import Notifications from './Notifications/Notifications';
|
import Notifications from './Notifications/Notifications';
|
||||||
|
|
@ -152,7 +153,6 @@ class Room extends React.PureComponent
|
||||||
|
|
||||||
this.state =
|
this.state =
|
||||||
{
|
{
|
||||||
drawerOpen : false,
|
|
||||||
fullscreen : false
|
fullscreen : false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -220,7 +220,9 @@ class Room extends React.PureComponent
|
||||||
const {
|
const {
|
||||||
roomClient,
|
roomClient,
|
||||||
room,
|
room,
|
||||||
me,
|
myPicture,
|
||||||
|
loggedIn,
|
||||||
|
loginEnabled,
|
||||||
setSettingsOpen,
|
setSettingsOpen,
|
||||||
toolAreaOpen,
|
toolAreaOpen,
|
||||||
toggleToolArea,
|
toggleToolArea,
|
||||||
|
|
@ -338,16 +340,20 @@ class Room extends React.PureComponent
|
||||||
>
|
>
|
||||||
<SettingsIcon />
|
<SettingsIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
{ me.loginEnabled ?
|
{ loginEnabled ?
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label='Account'
|
aria-label='Account'
|
||||||
color='inherit'
|
color='inherit'
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
{
|
{
|
||||||
me.loggedIn ? roomClient.logout() : roomClient.login();
|
loggedIn ? roomClient.logout() : roomClient.login();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AccountCircle />
|
{ myPicture ?
|
||||||
|
<Avatar src={myPicture} />
|
||||||
|
:
|
||||||
|
<AccountCircle />
|
||||||
|
}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
:null
|
:null
|
||||||
}
|
}
|
||||||
|
|
@ -373,20 +379,6 @@ class Room extends React.PureComponent
|
||||||
|
|
||||||
<View advancedMode={room.advancedMode} />
|
<View advancedMode={room.advancedMode} />
|
||||||
|
|
||||||
{ /*
|
|
||||||
<Draggable handle='.me-handle' bounds='body' cancel='.display-name'>
|
|
||||||
<div
|
|
||||||
className={classnames(classes.meContainer, 'me-handle', {
|
|
||||||
'active-speaker' : amActiveSpeaker
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Me
|
|
||||||
advancedMode={room.advancedMode}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Draggable>
|
|
||||||
*/ }
|
|
||||||
|
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
|
|
||||||
<Settings />
|
<Settings />
|
||||||
|
|
@ -400,10 +392,10 @@ Room.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.object.isRequired,
|
roomClient : PropTypes.object.isRequired,
|
||||||
room : appPropTypes.Room.isRequired,
|
room : appPropTypes.Room.isRequired,
|
||||||
me : appPropTypes.Me.isRequired,
|
myPicture : PropTypes.string,
|
||||||
// amActiveSpeaker : PropTypes.bool.isRequired,
|
loggedIn : PropTypes.bool.isRequired,
|
||||||
|
loginEnabled : PropTypes.bool.isRequired,
|
||||||
toolAreaOpen : PropTypes.bool.isRequired,
|
toolAreaOpen : PropTypes.bool.isRequired,
|
||||||
screenProducer : appPropTypes.Producer,
|
|
||||||
setToolbarsVisible : PropTypes.func.isRequired,
|
setToolbarsVisible : PropTypes.func.isRequired,
|
||||||
setSettingsOpen : PropTypes.func.isRequired,
|
setSettingsOpen : PropTypes.func.isRequired,
|
||||||
toggleToolArea : PropTypes.func.isRequired,
|
toggleToolArea : PropTypes.func.isRequired,
|
||||||
|
|
@ -413,25 +405,18 @@ Room.propTypes =
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
({
|
||||||
const producersArray = Object.values(state.producers);
|
room : state.room,
|
||||||
const screenProducer =
|
loggedIn : state.me.loggedIn,
|
||||||
producersArray.find((producer) => producer.source === 'screen');
|
loginEnabled : state.me.loginEnabled,
|
||||||
|
myPicture : state.me.picture,
|
||||||
return {
|
toolAreaOpen : state.toolarea.toolAreaOpen,
|
||||||
room : state.room,
|
unread : state.toolarea.unreadMessages +
|
||||||
me : state.me,
|
|
||||||
screenProducer : screenProducer,
|
|
||||||
toolAreaOpen : state.toolarea.toolAreaOpen,
|
|
||||||
unread : state.toolarea.unreadMessages +
|
|
||||||
state.toolarea.unreadFiles
|
state.toolarea.unreadFiles
|
||||||
// amActiveSpeaker : state.me.name === state.room.activeSpeakerName,
|
});
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) =>
|
const mapDispatchToProps = (dispatch) =>
|
||||||
{
|
({
|
||||||
return {
|
|
||||||
setToolbarsVisible : (visible) =>
|
setToolbarsVisible : (visible) =>
|
||||||
{
|
{
|
||||||
dispatch(stateActions.setToolbarsVisible(visible));
|
dispatch(stateActions.setToolbarsVisible(visible));
|
||||||
|
|
@ -444,8 +429,7 @@ const mapDispatchToProps = (dispatch) =>
|
||||||
{
|
{
|
||||||
dispatch(stateActions.toggleToolArea());
|
dispatch(stateActions.toggleToolArea());
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
};
|
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const producersSelect = (state) => state.producers;
|
||||||
|
const consumersSelect = (state) => state.consumers;
|
||||||
|
|
||||||
|
export const spotlightsSelector = (state) => state.room.spotlights;
|
||||||
|
|
||||||
|
export const peersSelector = (state) => state.peers;
|
||||||
|
|
||||||
|
export const micProducersSelector = createSelector(
|
||||||
|
producersSelect,
|
||||||
|
(producers) => Object.values(producers).filter((producer) => producer.source === 'mic')
|
||||||
|
);
|
||||||
|
|
||||||
|
export const webcamProducersSelector = createSelector(
|
||||||
|
producersSelect,
|
||||||
|
(producers) => Object.values(producers).filter((producer) => producer.source === 'webcam')
|
||||||
|
);
|
||||||
|
|
||||||
|
export const screenProducersSelector = createSelector(
|
||||||
|
producersSelect,
|
||||||
|
(producers) => Object.values(producers).filter((producer) => producer.source === 'screen')
|
||||||
|
);
|
||||||
|
|
||||||
|
export const micProducerSelector = createSelector(
|
||||||
|
producersSelect,
|
||||||
|
(producers) => Object.values(producers).find((producer) => producer.source === 'mic')
|
||||||
|
);
|
||||||
|
|
||||||
|
export const webcamProducerSelector = createSelector(
|
||||||
|
producersSelect,
|
||||||
|
(producers) => Object.values(producers).find((producer) => producer.source === 'webcam')
|
||||||
|
);
|
||||||
|
|
||||||
|
export const screenProducerSelector = createSelector(
|
||||||
|
producersSelect,
|
||||||
|
(producers) => Object.values(producers).find((producer) => producer.source === 'screen')
|
||||||
|
);
|
||||||
|
|
||||||
|
export const micConsumerSelector = createSelector(
|
||||||
|
consumersSelect,
|
||||||
|
(consumers) => Object.values(consumers).filter((consumer) => consumer.source === 'mic')
|
||||||
|
);
|
||||||
|
|
||||||
|
export const webcamConsumerSelector = createSelector(
|
||||||
|
consumersSelect,
|
||||||
|
(consumers) => Object.values(consumers).filter((consumer) => consumer.source === 'webcam')
|
||||||
|
);
|
||||||
|
|
||||||
|
export const screenConsumerSelector = createSelector(
|
||||||
|
consumersSelect,
|
||||||
|
(consumers) => Object.values(consumers).filter((consumer) => consumer.source === 'screen')
|
||||||
|
);
|
||||||
|
|
||||||
|
export const spotlightsLengthSelector = createSelector(
|
||||||
|
spotlightsSelector,
|
||||||
|
(spotlights) => (spotlights ? spotlights.length : 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const videoBoxesSelector = createSelector(
|
||||||
|
spotlightsLengthSelector,
|
||||||
|
screenProducersSelector,
|
||||||
|
screenConsumerSelector,
|
||||||
|
(spotlightsLength, screenProducers, screenConsumers) =>
|
||||||
|
spotlightsLength + 1 + screenProducers.length + screenConsumers.length
|
||||||
|
);
|
||||||
|
|
||||||
|
export const meProducersSelector = createSelector(
|
||||||
|
micProducerSelector,
|
||||||
|
webcamProducerSelector,
|
||||||
|
screenProducerSelector,
|
||||||
|
(micProducer, webcamProducer, screenProducer) =>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
micProducer,
|
||||||
|
webcamProducer,
|
||||||
|
screenProducer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const getPeerConsumers = (state, props) => state.peers[props.name].consumers;
|
||||||
|
const getAllConsumers = (state) => state.consumers;
|
||||||
|
|
||||||
|
export const makePeerConsumerSelector = () =>
|
||||||
|
{
|
||||||
|
return createSelector(
|
||||||
|
getPeerConsumers,
|
||||||
|
getAllConsumers,
|
||||||
|
(consumers, allConsumers) =>
|
||||||
|
{
|
||||||
|
const consumersArray = consumers
|
||||||
|
.map((consumerId) => allConsumers[consumerId]);
|
||||||
|
const micConsumer =
|
||||||
|
consumersArray.find((consumer) => consumer.source === 'mic');
|
||||||
|
const webcamConsumer =
|
||||||
|
consumersArray.find((consumer) => consumer.source === 'webcam');
|
||||||
|
const screenConsumer =
|
||||||
|
consumersArray.find((consumer) => consumer.source === 'screen');
|
||||||
|
|
||||||
|
return { micConsumer, webcamConsumer, screenConsumer };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -5,8 +5,8 @@ import classnames from 'classnames';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import * as appPropTypes from '../appPropTypes';
|
import * as appPropTypes from '../appPropTypes';
|
||||||
import * as stateActions from '../../actions/stateActions';
|
import * as stateActions from '../../actions/stateActions';
|
||||||
import FullView from './FullView';
|
|
||||||
import FullScreenExitIcon from '@material-ui/icons/FullscreenExit';
|
import FullScreenExitIcon from '@material-ui/icons/FullscreenExit';
|
||||||
|
import VideoView from './VideoView';
|
||||||
|
|
||||||
const styles = () =>
|
const styles = () =>
|
||||||
({
|
({
|
||||||
|
|
@ -41,7 +41,12 @@ const styles = () =>
|
||||||
transitionProperty : 'opacity, background-color',
|
transitionProperty : 'opacity, background-color',
|
||||||
transitionDuration : '0.15s',
|
transitionDuration : '0.15s',
|
||||||
width : '5vmin',
|
width : '5vmin',
|
||||||
height : '5vmin'
|
height : '5vmin',
|
||||||
|
opacity : 0,
|
||||||
|
'&.visible' :
|
||||||
|
{
|
||||||
|
opacity : 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
icon :
|
icon :
|
||||||
{
|
{
|
||||||
|
|
@ -106,7 +111,7 @@ const FullScreenView = (props) =>
|
||||||
|
|
||||||
<div className={classes.controls}>
|
<div className={classes.controls}>
|
||||||
<div
|
<div
|
||||||
className={classnames(classes.button, 'room-controls', {
|
className={classnames(classes.button, {
|
||||||
visible : toolbarsVisible
|
visible : toolbarsVisible
|
||||||
})}
|
})}
|
||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
|
|
@ -119,8 +124,9 @@ const FullScreenView = (props) =>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FullView
|
<VideoView
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
|
videoContain
|
||||||
videoTrack={consumer ? consumer.track : null}
|
videoTrack={consumer ? consumer.track : null}
|
||||||
videoVisible={consumerVisible}
|
videoVisible={consumerVisible}
|
||||||
videoProfile={consumerProfile}
|
videoProfile={consumerProfile}
|
||||||
|
|
@ -139,23 +145,19 @@ FullScreenView.propTypes =
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
({
|
||||||
return {
|
|
||||||
consumer : state.consumers[state.room.fullScreenConsumer],
|
consumer : state.consumers[state.room.fullScreenConsumer],
|
||||||
toolbarsVisible : state.room.toolbarsVisible
|
toolbarsVisible : state.room.toolbarsVisible
|
||||||
};
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) =>
|
const mapDispatchToProps = (dispatch) =>
|
||||||
{
|
({
|
||||||
return {
|
|
||||||
toggleConsumerFullscreen : (consumer) =>
|
toggleConsumerFullscreen : (consumer) =>
|
||||||
{
|
{
|
||||||
if (consumer)
|
if (consumer)
|
||||||
dispatch(stateActions.toggleConsumerFullscreen(consumer.id));
|
dispatch(stateActions.toggleConsumerFullscreen(consumer.id));
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
|
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
|
||||||
|
|
||||||
const styles = () =>
|
|
||||||
({
|
|
||||||
root :
|
|
||||||
{
|
|
||||||
position : 'relative',
|
|
||||||
flex : '100 100 auto',
|
|
||||||
height : '100%',
|
|
||||||
width : '100%',
|
|
||||||
display : 'flex',
|
|
||||||
flexDirection : 'column',
|
|
||||||
overflow : 'hidden'
|
|
||||||
},
|
|
||||||
video :
|
|
||||||
{
|
|
||||||
flex : '100 100 auto',
|
|
||||||
height : '100%',
|
|
||||||
width : '100%',
|
|
||||||
objectFit : 'contain',
|
|
||||||
userSelect : 'none',
|
|
||||||
transitionProperty : 'opacity',
|
|
||||||
transitionDuration : '.15s',
|
|
||||||
backgroundColor : 'rgba(0, 0, 0, 1)',
|
|
||||||
'&.hidden' :
|
|
||||||
{
|
|
||||||
opacity : 0,
|
|
||||||
transitionDuration : '0s'
|
|
||||||
},
|
|
||||||
'&.loading' :
|
|
||||||
{
|
|
||||||
filter : 'blur(5px)'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
class FullView extends React.PureComponent
|
|
||||||
{
|
|
||||||
constructor(props)
|
|
||||||
{
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
// Latest received video track.
|
|
||||||
// @type {MediaStreamTrack}
|
|
||||||
this._videoTrack = null;
|
|
||||||
|
|
||||||
this.video = React.createRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
render()
|
|
||||||
{
|
|
||||||
const {
|
|
||||||
videoVisible,
|
|
||||||
videoProfile,
|
|
||||||
classes
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classes.root}>
|
|
||||||
<video
|
|
||||||
ref={this.video}
|
|
||||||
className={classnames(classes.video, {
|
|
||||||
hidden : !videoVisible,
|
|
||||||
loading : videoProfile === 'none'
|
|
||||||
})}
|
|
||||||
autoPlay
|
|
||||||
playsInline
|
|
||||||
muted={Boolean(true)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount()
|
|
||||||
{
|
|
||||||
const { videoTrack } = this.props;
|
|
||||||
|
|
||||||
this._setTracks(videoTrack);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate()
|
|
||||||
{
|
|
||||||
const { videoTrack } = this.props;
|
|
||||||
|
|
||||||
this._setTracks(videoTrack);
|
|
||||||
}
|
|
||||||
|
|
||||||
_setTracks(videoTrack)
|
|
||||||
{
|
|
||||||
if (this._videoTrack === videoTrack)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._videoTrack = videoTrack;
|
|
||||||
|
|
||||||
const video = this.video.current;
|
|
||||||
|
|
||||||
if (videoTrack)
|
|
||||||
{
|
|
||||||
const stream = new MediaStream();
|
|
||||||
|
|
||||||
stream.addTrack(videoTrack);
|
|
||||||
video.srcObject = stream;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
video.srcObject = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FullView.propTypes =
|
|
||||||
{
|
|
||||||
videoTrack : PropTypes.any,
|
|
||||||
videoVisible : PropTypes.bool,
|
|
||||||
videoProfile : PropTypes.string,
|
|
||||||
classes : PropTypes.object.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withStyles(styles)(FullView);
|
|
||||||
|
|
@ -42,7 +42,8 @@ const styles = () =>
|
||||||
},
|
},
|
||||||
'&.contain' :
|
'&.contain' :
|
||||||
{
|
{
|
||||||
objectFit : 'contain'
|
objectFit : 'contain',
|
||||||
|
backgroundColor : 'rgba(0, 0, 0, 1)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
info :
|
info :
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import NewWindow from './NewWindow';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as appPropTypes from '../appPropTypes';
|
import * as appPropTypes from '../appPropTypes';
|
||||||
import * as stateActions from '../../actions/stateActions';
|
import * as stateActions from '../../actions/stateActions';
|
||||||
import FullView from '../VideoContainers/FullView';
|
import VideoView from '../VideoContainers/VideoView';
|
||||||
|
|
||||||
const VideoWindow = (props) =>
|
const VideoWindow = (props) =>
|
||||||
{
|
{
|
||||||
|
|
@ -30,8 +30,9 @@ const VideoWindow = (props) =>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NewWindow onUnload={toggleConsumerWindow}>
|
<NewWindow onUnload={toggleConsumerWindow}>
|
||||||
<FullView
|
<VideoView
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
|
videoContain
|
||||||
videoTrack={consumer ? consumer.track : null}
|
videoTrack={consumer ? consumer.track : null}
|
||||||
videoVisible={consumerVisible}
|
videoVisible={consumerVisible}
|
||||||
videoProfile={consumerProfile}
|
videoProfile={consumerProfile}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue