Massive change. Changed to react-scripts (webpack) for building. Changed to material-ui where applicable. Changed to CSS-in-JS.

This commit is contained in:
Håvar Aambø Fosstveit
2019-03-29 14:12:13 +01:00
parent 36944a9ea7
commit 0449d6ff20
185 changed files with 4564 additions and 6442 deletions
@@ -0,0 +1,124 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { withStyles } from '@material-ui/core/styles';
import * as stateActions from '../../actions/stateActions';
import BuddyImage from '../../images/buddy.svg';
const styles = () =>
({
root :
{
width : '12vmin',
height : '9vmin',
position : 'absolute',
bottom : '3%',
right : '3%',
color : 'rgba(170, 170, 170, 1)',
cursor : 'pointer',
backgroundImage : `url(${BuddyImage})`,
backgroundColor : 'rgba(42, 75, 88, 1)',
backgroundPosition : 'bottom',
backgroundSize : 'auto 85%',
backgroundRepeat : 'no-repeat',
border : 'var(--peer-border)',
boxShadow : 'var(--peer-shadow)',
textAlign : 'center',
verticalAlign : 'middle',
lineHeight : '1.8vmin',
fontSize : '1.7vmin',
fontWeight : 'bolder',
animation : 'none',
'&.pulse' :
{
animation : 'pulse 0.5s'
}
},
'@keyframes pulse' :
{
'0%' :
{
transform : 'scale3d(1, 1, 1)'
},
'50%' :
{
transform : 'scale3d(1.2, 1.2, 1.2)'
},
'100%' :
{
transform : 'scale3d(1, 1, 1)'
}
}
});
class HiddenPeers extends Component
{
constructor(props)
{
super(props);
this.state = { className: '' };
}
componentDidUpdate(prevProps)
{
const { hiddenPeersCount } = this.props;
if (hiddenPeersCount !== prevProps.hiddenPeersCount)
{
// eslint-disable-next-line react/no-did-update-set-state
this.setState({ className: 'pulse' }, () =>
{
if (this.timeout)
{
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() =>
{
this.setState({ className: '' });
}, 500);
});
}
}
render()
{
const {
hiddenPeersCount,
openUsersTab,
classes
} = this.props;
return (
<div className={classnames(classes.root, this.state.className)} onClick={() => openUsersTab()}>
<p>+{hiddenPeersCount} <br /> participant
{(hiddenPeersCount === 1) ? null : 's'}
</p>
</div>
);
}
}
HiddenPeers.propTypes =
{
hiddenPeersCount : PropTypes.number,
openUsersTab : PropTypes.func.isRequired,
classes : PropTypes.object.isRequired
};
const mapDispatchToProps = (dispatch) =>
{
return {
openUsersTab : () =>
{
dispatch(stateActions.openToolArea());
dispatch(stateActions.setToolTab('users'));
}
};
};
export default connect(
null,
mapDispatchToProps
)(withStyles(styles)(HiddenPeers));
+332
View File
@@ -0,0 +1,332 @@
import React from 'react';
import { connect } from 'react-redux';
import { withRoomContext } from '../../RoomContext';
import { withStyles } from '@material-ui/core/styles';
import ReactTooltip from 'react-tooltip';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { getDeviceInfo } from 'mediasoup-client';
import * as appPropTypes from '../appPropTypes';
import PeerView from '../VideoContainers/PeerView';
import ScreenView from '../VideoContainers/ScreenView';
import MicIcon from '@material-ui/icons/Mic';
import MicOffIcon from '@material-ui/icons/MicOff';
import VideoIcon from '@material-ui/icons/Videocam';
import VideoOffIcon from '@material-ui/icons/VideocamOff';
const styles = () =>
({
root :
{
display : 'flex',
flexDirection : 'row',
flex : '100 100 auto',
position : 'relative'
},
viewContainer :
{
position : 'relative',
width : 'var(--me-width)',
height : 'var(--me-height)',
'&.webcam' :
{
order : 2
},
'&.screen' :
{
order : 1
}
},
controls :
{
position : 'absolute',
right : 0,
top : 0,
display : 'flex',
flexDirection : 'row',
padding : '0.4vmin',
zIndex : 20,
opacity : 0,
transition : 'opacity 0.3s',
'&.visible' :
{
opacity : 1
}
},
button :
{
flex : '0 0 auto',
margin : '0.2vmin',
borderRadius : 2,
opacity : 0.85,
width : 'var(--media-control-button-size)',
height : 'var(--media-control-button-size)',
backgroundColor : 'var(--media-control-button-color)',
'&:hover' :
{
opacity : 1
},
'&.unsupported' :
{
pointerEvents : 'none'
},
'&.disabled' :
{
pointerEvents : 'none',
backgroundColor : 'var(--media-control-botton-disabled)'
},
'&.on' :
{
backgroundColor : 'var(--media-control-botton-on)'
},
'&.off' :
{
backgroundColor : 'var(--media-control-botton-off)'
}
}
});
class Me extends React.Component
{
state = {
controlsVisible : false
};
handleMouseOver = () =>
{
this.setState({
controlsVisible : true
});
};
handleMouseOut = () =>
{
this.setState({
controlsVisible : false
});
};
constructor(props)
{
super(props);
this._mounted = false;
this._rootNode = null;
this._tooltip = true;
// TODO: Issue when using react-tooltip in Edge:
// https://github.com/wwayne/react-tooltip/issues/328
if (getDeviceInfo().flag === 'msedge')
this._tooltip = false;
}
render()
{
const {
roomClient,
connected,
me,
advancedMode,
micProducer,
webcamProducer,
screenProducer,
classes
} = this.props;
let micState;
if (!me.canSendMic)
micState = 'unsupported';
else if (!micProducer)
micState = 'unsupported';
else if (!micProducer.locallyPaused && !micProducer.remotelyPaused)
micState = 'on';
else
micState = 'off';
let webcamState;
if (!me.canSendWebcam)
webcamState = 'unsupported';
else if (webcamProducer)
webcamState = 'on';
else
webcamState = 'off';
const videoVisible = (
Boolean(webcamProducer) &&
!webcamProducer.locallyPaused &&
!webcamProducer.remotelyPaused
);
const screenVisible = (
Boolean(screenProducer) &&
!screenProducer.locallyPaused &&
!screenProducer.remotelyPaused
);
let tip;
if (!me.displayNameSet)
tip = 'Click on your name to change it';
return (
<div
className={classes.root}
ref={(node) => (this._rootNode = node)}
data-tip={tip}
data-tip-disable={!tip}
data-type='dark'
onMouseOver={this.handleMouseOver}
onMouseOut={this.handleMouseOut}
>
<div className={classnames(classes.viewContainer, 'webcam')}>
{ connected ?
<div className={classnames(classes.controls, 'visible')}>
<div
data-tip='keyboard shortcut: &lsquo;m&lsquo;'
data-type='dark'
data-place='bottom'
data-for='me'
className={classnames(classes.button, 'mic', micState, {
disabled : me.audioInProgress,
visible : micState === 'off' || this.state.controlsVisible
})}
onClick={() =>
{
micState === 'on' ?
roomClient.muteMic() :
roomClient.unmuteMic();
}}
>
{ micState === 'on' ?
<MicIcon />
:
<MicOffIcon />
}
</div>
<ReactTooltip
id='me'
effect='solid'
/>
<div
className={classnames(classes.button, 'webcam', webcamState, {
disabled : me.webcamInProgress,
visible : webcamState === 'off' || this.state.controlsVisible
})}
onClick={() =>
{
webcamState === 'on' ?
roomClient.disableWebcam() :
roomClient.enableWebcam();
}}
>
{ webcamState === 'on' ?
<VideoIcon />
:
<VideoOffIcon />
}
</div>
</div>
:null
}
<PeerView
isMe
advancedMode={advancedMode}
peer={me}
audioTrack={micProducer ? micProducer.track : null}
volume={micProducer ? micProducer.volume : null}
videoTrack={webcamProducer ? webcamProducer.track : null}
videoVisible={videoVisible}
audioCodec={micProducer ? micProducer.codec : null}
videoCodec={webcamProducer ? webcamProducer.codec : null}
onChangeDisplayName={(displayName) =>
{
roomClient.changeDisplayName(displayName);
}}
/>
</div>
{ screenProducer ?
<div className={classnames(classes.viewContainer, 'screen')}>
<ScreenView
isMe
advancedMode={advancedMode}
screenTrack={screenProducer ? screenProducer.track : null}
screenVisible={screenVisible}
screenCodec={screenProducer ? screenProducer.codec : null}
/>
</div>
:null
}
</div>
);
}
componentDidMount()
{
this._mounted = true;
if (this._tooltip)
{
setTimeout(() =>
{
if (!this._mounted || this.props.me.displayNameSet)
return;
ReactTooltip.show(this._rootNode);
}, 4000);
}
}
componentWillUnmount()
{
this._mounted = false;
}
componentWillReceiveProps(nextProps)
{
if (this._tooltip)
{
if (nextProps.me.displayNameSet)
ReactTooltip.hide(this._rootNode);
}
}
}
Me.propTypes =
{
roomClient : PropTypes.any.isRequired,
connected : PropTypes.bool.isRequired,
advancedMode : PropTypes.bool,
me : appPropTypes.Me.isRequired,
micProducer : appPropTypes.Producer,
webcamProducer : appPropTypes.Producer,
screenProducer : appPropTypes.Producer,
classes : PropTypes.object.isRequired
};
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 {
connected : state.room.state === 'connected',
me : state.me,
micProducer : micProducer,
webcamProducer : webcamProducer,
screenProducer : screenProducer
};
};
export default withRoomContext(connect(
mapStateToProps,
null
)(withStyles(styles)(Me)));
+397
View File
@@ -0,0 +1,397 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import * as appPropTypes from '../appPropTypes';
import { withRoomContext } from '../../RoomContext';
import { withStyles } from '@material-ui/core/styles';
import * as stateActions from '../../actions/stateActions';
import PeerView from '../VideoContainers/PeerView';
import ScreenView from '../VideoContainers/ScreenView';
import MicIcon from '@material-ui/icons/Mic';
import MicOffIcon from '@material-ui/icons/MicOff';
import NewWindowIcon from '@material-ui/icons/OpenInNew';
import FullScreenIcon from '@material-ui/icons/Fullscreen';
const styles = () =>
({
root :
{
width : '100%',
height : '100%',
display : 'flex',
flexDirection : 'row',
flex : '100 100 auto',
position : 'relative'
},
viewContainer :
{
position : 'relative',
flexGrow : 1,
'&.webcam' :
{
order : 2
},
'&.screen' :
{
order : 1,
maxWidth : '50%'
}
},
controls :
{
position : 'absolute',
right : 0,
top : 0,
display : 'flex',
flexDirection : 'row',
justifyContent : 'flex-start',
alignItems : 'center',
padding : '0.4vmin',
zIndex : 20,
opacity : 0,
transition : 'opacity 0.3s',
'&.visible' :
{
opacity : 1
}
},
button :
{
flex : '0 0 auto',
margin : '0.2vmin',
borderRadius : 2,
opacity : 0.85,
width : 'var(--media-control-button-size)',
height : 'var(--media-control-button-size)',
backgroundColor : 'var(--media-control-button-color)',
cursor : 'pointer',
transitionProperty : 'opacity, background-color',
transitionDuration : '0.15s',
'&:hover' :
{
opacity : 1
},
'&.unsupported' :
{
pointerEvents : 'none'
},
'&.disabled' :
{
pointerEvents : 'none',
backgroundColor : 'var(--media-control-botton-disabled)'
},
'&.on' :
{
backgroundColor : 'var(--media-control-botton-on)'
},
'&.off' :
{
backgroundColor : 'var(--media-control-botton-off)'
}
},
pausedVideo :
{
position : 'absolute',
zIndex : 11,
top : 0,
bottom : 0,
left : 0,
right : 0,
display : 'flex',
flexDirection : 'column',
justifyContent : 'center',
alignItems : 'center',
'& p' :
{
padding : '6px 12px',
borderRadius : 6,
userSelect : 'none',
pointerEvents : 'none',
fontSize : 20,
color : 'rgba(255, 255, 255, 0.55)'
}
},
incompatibleVideo :
{
position : 'absolute',
zIndex : 10,
top : 0,
bottom : 0,
left : 0,
right : 0,
display : 'flex',
flexDirection : 'column',
justifyContent : 'center',
alignItems : 'center',
'& p' :
{
padding : '6px 12px',
borderRadius : 6,
userSelect : 'none',
pointerEvents : 'none',
fontSize : 20,
color : 'rgba(255, 255, 255, 0.55)'
}
}
});
class Peer extends Component
{
state = {
controlsVisible : false
};
handleMouseOver = () =>
{
this.setState({
controlsVisible : true
});
};
handleMouseOut = () =>
{
this.setState({
controlsVisible : false
});
};
render()
{
const {
roomClient,
advancedMode,
peer,
micConsumer,
webcamConsumer,
screenConsumer,
toggleConsumerFullscreen,
toggleConsumerWindow,
style,
windowConsumer,
classes
} = this.props;
const micEnabled = (
Boolean(micConsumer) &&
!micConsumer.locallyPaused &&
!micConsumer.remotelyPaused
);
const videoVisible = (
Boolean(webcamConsumer) &&
!webcamConsumer.locallyPaused &&
!webcamConsumer.remotelyPaused
);
const screenVisible = (
Boolean(screenConsumer) &&
!screenConsumer.locallyPaused &&
!screenConsumer.remotelyPaused
);
let videoProfile;
if (webcamConsumer)
videoProfile = webcamConsumer.profile;
let screenProfile;
if (screenConsumer)
screenProfile = screenConsumer.profile;
return (
<div
className={classnames(classes.root, {
screen : screenConsumer
})}
onMouseOver={this.handleMouseOver}
onMouseOut={this.handleMouseOut}
>
{ videoVisible && !webcamConsumer.supported ?
<div className={classes.incompatibleVideo}>
<p>incompatible video</p>
</div>
:null
}
{ !videoVisible ?
<div className={classes.pausedVideo}>
<p>this video is paused</p>
</div>
:null
}
<div className={classnames(classes.viewContainer, 'webcam')} style={style}>
<div
className={classnames(classes.controls, {
visible : this.state.controlsVisible
})}
>
<div
className={classnames(classes.button, {
on : micEnabled,
off : !micEnabled,
disabled : peer.peerAudioInProgress
})}
onClick={(e) =>
{
e.stopPropagation();
micEnabled ?
roomClient.modifyPeerConsumer(peer.name, 'mic', true) :
roomClient.modifyPeerConsumer(peer.name, 'mic', false);
}}
>
{ micEnabled ?
<MicIcon />
:
<MicOffIcon />
}
</div>
<div
className={classnames(classes.button, {
disabled : !videoVisible ||
(windowConsumer === webcamConsumer.id)
})}
onClick={(e) =>
{
e.stopPropagation();
toggleConsumerWindow(webcamConsumer);
}}
>
<NewWindowIcon />
</div>
<div
className={classnames(classes.button, 'fullscreen', {
disabled : !videoVisible
})}
onClick={(e) =>
{
e.stopPropagation();
toggleConsumerFullscreen(webcamConsumer);
}}
>
<FullScreenIcon />
</div>
</div>
<PeerView
advancedMode={advancedMode}
peer={peer}
volume={micConsumer ? micConsumer.volume : null}
videoTrack={webcamConsumer ? webcamConsumer.track : null}
videoVisible={videoVisible}
videoProfile={videoProfile}
audioCodec={micConsumer ? micConsumer.codec : null}
videoCodec={webcamConsumer ? webcamConsumer.codec : null}
/>
</div>
{ screenConsumer ?
<div className={classnames(classes.viewContainer, 'screen')} style={style}>
<div
className={classnames(classes.controls, {
visible : this.state.controlsVisible
})}
>
<div
className={classnames(classes.button, 'newwindow', {
disabled : !screenVisible ||
(windowConsumer === screenConsumer.id)
})}
onClick={(e) =>
{
e.stopPropagation();
toggleConsumerWindow(screenConsumer);
}}
>
<NewWindowIcon />
</div>
<div
className={classnames(classes.button, 'fullscreen', {
disabled : !screenVisible
})}
onClick={(e) =>
{
e.stopPropagation();
toggleConsumerFullscreen(screenConsumer);
}}
>
<FullScreenIcon />
</div>
</div>
<ScreenView
advancedMode={advancedMode}
screenTrack={screenConsumer ? screenConsumer.track : null}
screenVisible={screenVisible}
screenProfile={screenProfile}
screenCodec={screenConsumer ? screenConsumer.codec : null}
/>
</div>
:null
}
</div>
);
}
}
Peer.propTypes =
{
roomClient : PropTypes.any.isRequired,
advancedMode : PropTypes.bool,
peer : appPropTypes.Peer.isRequired,
micConsumer : appPropTypes.Consumer,
webcamConsumer : appPropTypes.Consumer,
screenConsumer : appPropTypes.Consumer,
windowConsumer : PropTypes.number,
streamDimensions : PropTypes.object,
style : PropTypes.object,
toggleConsumerFullscreen : PropTypes.func.isRequired,
toggleConsumerWindow : PropTypes.func.isRequired,
classes : PropTypes.object
};
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');
const webcamConsumer =
consumersArray.find((consumer) => consumer.source === 'webcam');
const screenConsumer =
consumersArray.find((consumer) => consumer.source === 'screen');
return {
peer,
micConsumer,
webcamConsumer,
screenConsumer,
windowConsumer : state.room.windowConsumer
};
};
const mapDispatchToProps = (dispatch) =>
{
return {
toggleConsumerFullscreen : (consumer) =>
{
if (consumer)
dispatch(stateActions.toggleConsumerFullscreen(consumer.id));
},
toggleConsumerWindow : (consumer) =>
{
if (consumer)
dispatch(stateActions.toggleConsumerWindow(consumer.id));
}
};
};
export default withRoomContext(connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(Peer)));