Cleanup of Me, Peer, PeerView, ScreenView and Democratic

master
Håvar Aambø Fosstveit 2019-04-02 13:37:13 +02:00
parent 978d49f045
commit a40b843848
5 changed files with 151 additions and 396 deletions

View File

@ -7,17 +7,27 @@ import PropTypes from 'prop-types';
import classnames from 'classnames'; import classnames from 'classnames';
import { getDeviceInfo } from 'mediasoup-client'; import { getDeviceInfo } from 'mediasoup-client';
import * as appPropTypes from '../appPropTypes'; import * as appPropTypes from '../appPropTypes';
import PeerView from '../VideoContainers/PeerView'; import VideoView from '../VideoContainers/VideoView';
import ScreenView from '../VideoContainers/ScreenView';
const styles = () => const styles = () =>
({ ({
root : root :
{ {
display : 'flex', display : 'flex',
flexDirection : 'row', flexDirection : 'row',
flex : '100 100 auto', margin : 6,
position : 'relative' flex : '0 0 auto',
boxShadow : 'var(--peer-shadow)',
border : 'var(--peer-border)',
backgroundColor : 'var(--peer-bg-color)',
backgroundImage : 'var(--peer-empty-avatar)',
backgroundPosition : 'bottom',
backgroundSize : 'auto 85%',
backgroundRepeat : 'no-repeat',
'&.active-speaker' :
{
borderColor : 'var(--active-speaker-border-color)'
}
}, },
viewContainer : viewContainer :
{ {
@ -74,6 +84,7 @@ class Me extends React.PureComponent
const { const {
roomClient, roomClient,
me, me,
activeSpeaker,
style, style,
advancedMode, advancedMode,
micProducer, micProducer,
@ -101,7 +112,12 @@ class Me extends React.PureComponent
return ( return (
<div <div
className={classes.root} className={
classnames(
classes.root,
activeSpeaker ? 'active-speaker' : null
)
}
ref={(node) => (this._rootNode = node)} ref={(node) => (this._rootNode = node)}
data-tip={tip} data-tip={tip}
data-tip-disable={!tip} data-tip-disable={!tip}
@ -110,10 +126,11 @@ class Me extends React.PureComponent
onMouseOut={this.handleMouseOut} onMouseOut={this.handleMouseOut}
> >
<div className={classnames(classes.viewContainer, 'webcam')} style={style}> <div className={classnames(classes.viewContainer, 'webcam')} style={style}>
<PeerView <VideoView
isMe isMe
advancedMode={advancedMode} advancedMode={advancedMode}
peer={me} peer={me}
showPeerInfo
audioTrack={micProducer ? micProducer.track : null} audioTrack={micProducer ? micProducer.track : null}
volume={micProducer ? micProducer.volume : null} volume={micProducer ? micProducer.volume : null}
videoTrack={webcamProducer ? webcamProducer.track : null} videoTrack={webcamProducer ? webcamProducer.track : null}
@ -129,12 +146,12 @@ class Me extends React.PureComponent
{ screenProducer ? { screenProducer ?
<div className={classnames(classes.viewContainer, 'screen')} style={style}> <div className={classnames(classes.viewContainer, 'screen')} style={style}>
<ScreenView <VideoView
isMe isMe
advancedMode={advancedMode} advancedMode={advancedMode}
screenTrack={screenProducer ? screenProducer.track : null} videoTrack={screenProducer ? screenProducer.track : null}
screenVisible={screenVisible} videoVisible={screenVisible}
screenCodec={screenProducer ? screenProducer.codec : null} videoCodec={screenProducer ? screenProducer.codec : null}
/> />
</div> </div>
:null :null
@ -180,6 +197,7 @@ Me.propTypes =
connected : PropTypes.bool.isRequired, connected : PropTypes.bool.isRequired,
advancedMode : PropTypes.bool, advancedMode : PropTypes.bool,
me : appPropTypes.Me.isRequired, me : appPropTypes.Me.isRequired,
activeSpeaker : PropTypes.bool,
micProducer : appPropTypes.Producer, micProducer : appPropTypes.Producer,
webcamProducer : appPropTypes.Producer, webcamProducer : appPropTypes.Producer,
screenProducer : appPropTypes.Producer, screenProducer : appPropTypes.Producer,
@ -202,7 +220,8 @@ const mapStateToProps = (state) =>
me : state.me, me : state.me,
micProducer : micProducer, micProducer : micProducer,
webcamProducer : webcamProducer, webcamProducer : webcamProducer,
screenProducer : screenProducer screenProducer : screenProducer,
activeSpeaker : state.me.name === state.room.activeSpeakerName
}; };
}; };

View File

@ -7,8 +7,7 @@ import { withRoomContext } from '../../RoomContext';
import { withStyles } from '@material-ui/core/styles'; import { withStyles } from '@material-ui/core/styles';
import { unstable_useMediaQuery as useMediaQuery } from '@material-ui/core/useMediaQuery'; import { unstable_useMediaQuery as useMediaQuery } from '@material-ui/core/useMediaQuery';
import * as stateActions from '../../actions/stateActions'; import * as stateActions from '../../actions/stateActions';
import PeerView from '../VideoContainers/PeerView'; import VideoView from '../VideoContainers/VideoView';
import ScreenView from '../VideoContainers/ScreenView';
import Fab from '@material-ui/core/Fab'; import Fab from '@material-ui/core/Fab';
import MicIcon from '@material-ui/icons/Mic'; import MicIcon from '@material-ui/icons/Mic';
import MicOffIcon from '@material-ui/icons/MicOff'; import MicOffIcon from '@material-ui/icons/MicOff';
@ -19,13 +18,17 @@ const styles = (theme) =>
({ ({
root : root :
{ {
overflow : 'hidden', flex : '0 0 auto',
flex : '0 0 auto', margin : 6,
margin : 6, boxShadow : 'var(--peer-shadow)',
boxShadow : 'var(--peer-shadow)', border : 'var(--peer-border)',
border : 'var(--peer-border)', touchAction : 'none',
touchAction : 'none', backgroundColor : 'var(--peer-bg-color)',
'&.webcam' : backgroundImage : 'var(--peer-empty-avatar)',
backgroundPosition : 'bottom',
backgroundSize : 'auto 85%',
backgroundRepeat : 'no-repeat',
'&.webcam' :
{ {
order : 2 order : 2
}, },
@ -36,18 +39,12 @@ const styles = (theme) =>
'&.hover' : '&.hover' :
{ {
boxShadow : '0px 1px 3px rgba(0, 0, 0, 0.05) inset, 0px 0px 8px rgba(82, 168, 236, 0.9)' boxShadow : '0px 1px 3px rgba(0, 0, 0, 0.05) inset, 0px 0px 8px rgba(82, 168, 236, 0.9)'
},
'&.active-speaker' :
{
borderColor : 'var(--active-speaker-border-color)'
} }
}, },
peerContainer :
{
width : '100%',
height : '100%',
display : 'flex',
flexDirection : 'row',
flex : '100 100 auto',
position : 'relative',
touchAction : 'none'
},
fab : fab :
{ {
margin : theme.spacing.unit margin : theme.spacing.unit
@ -86,32 +83,8 @@ const styles = (theme) =>
opacity : 1 opacity : 1
} }
}, },
pausedVideo : videoInfo :
{ {
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, top : 0,
bottom : 0, bottom : 0,
left : 0, left : 0,
@ -148,6 +121,7 @@ const Peer = (props) =>
roomClient, roomClient,
advancedMode, advancedMode,
peer, peer,
activeSpeaker,
micConsumer, micConsumer,
webcamConsumer, webcamConsumer,
screenConsumer, screenConsumer,
@ -192,7 +166,14 @@ const Peer = (props) =>
return ( return (
<React.Fragment> <React.Fragment>
<div <div
className={classnames(classes.root, 'webcam', hover ? 'hover' : null)} className={
classnames(
classes.root,
'webcam',
hover ? 'hover' : null,
activeSpeaker ? 'active-speaker' : null
)
}
onMouseOver={() => setHover(true)} onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)} onMouseOut={() => setHover(false)}
onTouchStart={() => onTouchStart={() =>
@ -213,21 +194,21 @@ const Peer = (props) =>
}, 2000); }, 2000);
}} }}
> >
<div className={classes.peerContainer}> { videoVisible && !webcamConsumer.supported ?
{ videoVisible && !webcamConsumer.supported ? <div className={classes.videoInfo} style={style}>
<div className={classes.incompatibleVideo}> <p>incompatible video</p>
<p>incompatible video</p> </div>
</div> :null
:null }
}
{ !videoVisible ? { !videoVisible ?
<div className={classes.pausedVideo}> <div className={classes.videoInfo} style={style}>
<p>this video is paused</p> <p>this video is paused</p>
</div> </div>
:null :null
} }
{ videoVisible && webcamConsumer.supported ?
<div className={classnames(classes.viewContainer)} style={style}> <div className={classnames(classes.viewContainer)} style={style}>
<div <div
className={classnames(classes.controls, webcamHover ? 'hover' : null)} className={classnames(classes.controls, webcamHover ? 'hover' : null)}
@ -300,9 +281,10 @@ const Peer = (props) =>
</Fab> </Fab>
</div> </div>
<PeerView <VideoView
advancedMode={advancedMode} advancedMode={advancedMode}
peer={peer} peer={peer}
showPeerInfo
volume={micConsumer ? micConsumer.volume : null} volume={micConsumer ? micConsumer.volume : null}
videoTrack={webcamConsumer ? webcamConsumer.track : null} videoTrack={webcamConsumer ? webcamConsumer.track : null}
videoVisible={videoVisible} videoVisible={videoVisible}
@ -311,7 +293,8 @@ const Peer = (props) =>
videoCodec={webcamConsumer ? webcamConsumer.codec : null} videoCodec={webcamConsumer ? webcamConsumer.codec : null}
/> />
</div> </div>
</div> :null
}
</div> </div>
{ screenConsumer ? { screenConsumer ?
@ -337,20 +320,21 @@ const Peer = (props) =>
}, 2000); }, 2000);
}} }}
> >
<div className={classes.peerContainer}> { screenVisible && !screenConsumer.supported ?
{ screenVisible && !screenConsumer.supported ? <div className={classes.videoInfo} style={style}>
<div className={classes.incompatibleVideo}> <p>incompatible video</p>
<p>incompatible video</p> </div>
</div> :null
:null }
}
{ !screenVisible ? { !screenVisible ?
<div className={classes.pausedVideo}> <div className={classes.videoInfo} style={style}>
<p>this video is paused</p> <p>this video is paused</p>
</div> </div>
:null :null
} }
{ screenVisible && screenConsumer.supported ?
<div className={classnames(classes.viewContainer)} style={style}> <div className={classnames(classes.viewContainer)} style={style}>
<div <div
className={classnames(classes.controls, screenHover ? 'hover' : null)} className={classnames(classes.controls, screenHover ? 'hover' : null)}
@ -405,15 +389,16 @@ const Peer = (props) =>
<FullScreenIcon /> <FullScreenIcon />
</Fab> </Fab>
</div> </div>
<ScreenView <VideoView
advancedMode={advancedMode} advancedMode={advancedMode}
screenTrack={screenConsumer ? screenConsumer.track : null} videoTrack={screenConsumer ? screenConsumer.track : null}
screenVisible={screenVisible} videoVisible={screenVisible}
screenProfile={screenProfile} videoProfile={screenProfile}
screenCodec={screenConsumer ? screenConsumer.codec : null} videoCodec={screenConsumer ? screenConsumer.codec : null}
/> />
</div> </div>
</div> :null
}
</div> </div>
:null :null
} }
@ -430,7 +415,7 @@ Peer.propTypes =
webcamConsumer : appPropTypes.Consumer, webcamConsumer : appPropTypes.Consumer,
screenConsumer : appPropTypes.Consumer, screenConsumer : appPropTypes.Consumer,
windowConsumer : PropTypes.number, windowConsumer : PropTypes.number,
streamDimensions : PropTypes.object, activeSpeaker : PropTypes.bool,
style : PropTypes.object, style : PropTypes.object,
toggleConsumerFullscreen : PropTypes.func.isRequired, toggleConsumerFullscreen : PropTypes.func.isRequired,
toggleConsumerWindow : PropTypes.func.isRequired, toggleConsumerWindow : PropTypes.func.isRequired,
@ -455,7 +440,8 @@ const mapStateToProps = (state, { name }) =>
micConsumer, micConsumer,
webcamConsumer, webcamConsumer,
screenConsumer, screenConsumer,
windowConsumer : state.room.windowConsumer windowConsumer : state.room.windowConsumer,
activeSpeaker : name === state.room.activeSpeakerName
}; };
}; };

View File

@ -130,6 +130,8 @@ class Democratic extends React.PureComponent
const { const {
advancedMode, advancedMode,
amActiveSpeaker, amActiveSpeaker,
activeSpeakerName,
selectedPeerName,
peers, peers,
spotlights, spotlights,
spotlightsLength, spotlightsLength,
@ -144,16 +146,10 @@ class Democratic extends React.PureComponent
return ( return (
<div className={classes.root} ref={this.peersRef}> <div className={classes.root} ref={this.peersRef}>
<div <Me
className={classnames(classes.peerContainer, { advancedMode={advancedMode}
'active-speaker' : amActiveSpeaker style={style}
})} />
>
<Me
advancedMode={advancedMode}
style={style}
/>
</div>
{ Object.keys(peers).map((peerName) => { Object.keys(peers).map((peerName) =>
{ {
if (spotlights.find((spotlightsElement) => spotlightsElement === peerName)) if (spotlights.find((spotlightsElement) => spotlightsElement === peerName))

View File

@ -1,246 +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',
backgroundColor : 'var(--peer-bg-color)',
backgroundImage : 'var(--peer-empty-avatar)',
backgroundPosition : 'bottom',
backgroundSize : 'auto 85%',
backgroundRepeat : 'no-repeat'
},
video :
{
flex : '100 100 auto',
height : '100%',
width : '100%',
objectFit : 'cover',
userSelect : 'none',
transitionProperty : 'opacity',
transitionDuration : '.15s',
backgroundColor : 'var(--peer-video-bg-color)',
'&.is-me' :
{
transform : 'scaleX(-1)'
},
'&.hidden' :
{
opacity : 0,
transitionDuration : '0s'
},
'&.loading' :
{
filter : 'blur(5px)'
}
},
info :
{
position : 'absolute',
zIndex : 10,
top : '0.6vmin',
left : '0.6vmin',
bottom : 0,
right : 0,
display : 'flex',
flexDirection : 'column',
justifyContent : 'space-between'
},
media :
{
flex : '0 0 auto',
display : 'flex',
flexDirection : 'row'
},
box :
{
padding : '0.4vmin',
borderRadius : 2,
backgroundColor : 'rgba(0, 0, 0, 0.25)',
'& p' :
{
userSelect : 'none',
pointerEvents : 'none',
margin : 0,
color : 'rgba(255, 255, 255, 0.7)',
fontSize : 10,
'&:last-child' :
{
marginBottom : 0
}
}
}
});
class ScreenView extends React.PureComponent
{
constructor(props)
{
super(props);
this.state =
{
screenWidth : null,
screenHeight : null
};
// Latest received screen track.
// @type {MediaStreamTrack}
this._screenTrack = null;
// Periodic timer for showing video resolution.
this._screenResolutionTimer = null;
}
render()
{
const {
isMe,
advancedMode,
screenVisible,
screenProfile,
screenCodec,
classes
} = this.props;
const {
screenWidth,
screenHeight
} = this.state;
return (
<div className={classes.root}>
<div className={classes.info}>
{ advancedMode ?
<div className={classnames(classes.media, { 'is-me': isMe })}>
{ screenVisible ?
<div className={classes.box}>
{ screenCodec ?
<p>{screenCodec} {screenProfile}</p>
:null
}
{ (screenVisible && screenWidth !== null) ?
<p>{screenWidth}x{screenHeight}</p>
:null
}
</div>
:null
}
</div>
:null
}
</div>
<video
ref='video'
className={classnames(classes.video, {
hidden : !screenVisible,
'is-me' : isMe,
loading : screenProfile === 'none'
})}
autoPlay
playsInline
muted={Boolean(true)}
/>
</div>
);
}
componentDidMount()
{
const { screenTrack } = this.props;
this._setTracks(screenTrack);
}
componentWillUnmount()
{
clearInterval(this._screenResolutionTimer);
}
componentWillReceiveProps(nextProps)
{
const { screenTrack } = nextProps;
this._setTracks(screenTrack);
}
_setTracks(screenTrack)
{
if (this._screenTrack === screenTrack)
return;
this._screenTrack = screenTrack;
clearInterval(this._screenResolutionTimer);
this._hideScreenResolution();
const { video } = this.refs;
if (screenTrack)
{
const stream = new MediaStream();
if (screenTrack)
stream.addTrack(screenTrack);
video.srcObject = stream;
if (screenTrack)
this._showScreenResolution();
}
else
{
video.srcObject = null;
}
}
_showScreenResolution()
{
this._screenResolutionTimer = setInterval(() =>
{
const { screenWidth, screenHeight } = this.state;
const { video } = this.refs;
// Don't re-render if nothing changed.
if (video.videoWidth === screenWidth && video.videoHeight === screenHeight)
return;
this.setState(
{
screenWidth : video.videoWidth,
screenHeight : video.videoHeight
});
}, 1000);
}
_hideScreenResolution()
{
this.setState({ screenWidth: null, screenHeight: null });
}
}
ScreenView.propTypes =
{
isMe : PropTypes.bool,
advancedMode : PropTypes.bool,
screenTrack : PropTypes.any,
screenVisible : PropTypes.bool,
screenProfile : PropTypes.string,
screenCodec : PropTypes.string,
classes : PropTypes.object.isRequired
};
export default withStyles(styles)(ScreenView);

View File

@ -9,18 +9,13 @@ const styles = () =>
({ ({
root : root :
{ {
position : 'relative', position : 'relative',
flex : '100 100 auto', flex : '100 100 auto',
height : '100%', height : '100%',
width : '100%', width : '100%',
display : 'flex', display : 'flex',
flexDirection : 'column', flexDirection : 'column',
overflow : 'hidden', overflow : 'hidden'
backgroundColor : 'var(--peer-bg-color)',
backgroundImage : 'var(--peer-empty-avatar)',
backgroundPosition : 'bottom',
backgroundSize : 'auto 85%',
backgroundRepeat : 'no-repeat'
}, },
video : video :
{ {
@ -210,7 +205,7 @@ const styles = () =>
} }
}); });
class PeerView extends React.PureComponent class VideoView extends React.PureComponent
{ {
constructor(props) constructor(props)
{ {
@ -241,6 +236,7 @@ class PeerView extends React.PureComponent
isMe, isMe,
peer, peer,
volume, volume,
showPeerInfo,
advancedMode, advancedMode,
videoVisible, videoVisible,
videoProfile, videoProfile,
@ -280,37 +276,40 @@ class PeerView extends React.PureComponent
:null :null
} }
<div className={classes.peer}> { showPeerInfo ?
{ isMe ? <div className={classes.peer}>
<EditableInput { isMe ?
value={peer.displayName} <EditableInput
propName='displayName' value={peer.displayName}
className={classnames(classes.displayNameEdit, 'display-name')} propName='displayName'
classLoading='loading' className={classnames(classes.displayNameEdit, 'display-name')}
classInvalid='invalid' classLoading='loading'
shouldBlockWhileLoading classInvalid='invalid'
editProps={{ shouldBlockWhileLoading
maxLength : 30, editProps={{
autoCorrect : false, maxLength : 30,
spellCheck : false autoCorrect : false,
}} spellCheck : false
onChange={({ displayName }) => onChangeDisplayName(displayName)} }}
/> onChange={({ displayName }) => onChangeDisplayName(displayName)}
: />
<span className={classes.displayNameStatic}> :
{peer.displayName} <span className={classes.displayNameStatic}>
</span> {peer.displayName}
}
{ advancedMode ?
<div className={classes.deviceInfo}>
<span>
{peer.device.name} {Math.floor(peer.device.version) || null}
</span> </span>
</div> }
:null
} { advancedMode ?
</div> <div className={classes.deviceInfo}>
<span>
{peer.device.name} {Math.floor(peer.device.version) || null}
</span>
</div>
:null
}
</div>
:null
}
</div> </div>
<video <video
@ -411,11 +410,12 @@ class PeerView extends React.PureComponent
} }
} }
PeerView.propTypes = VideoView.propTypes =
{ {
isMe : PropTypes.bool, isMe : PropTypes.bool,
peer : PropTypes.oneOfType( peer : PropTypes.oneOfType(
[ appPropTypes.Me, appPropTypes.Peer ]).isRequired, [ appPropTypes.Me, appPropTypes.Peer ]),
showPeerInfo : PropTypes.bool,
advancedMode : PropTypes.bool, advancedMode : PropTypes.bool,
audioTrack : PropTypes.any, audioTrack : PropTypes.any,
volume : PropTypes.number, volume : PropTypes.number,
@ -428,4 +428,4 @@ PeerView.propTypes =
classes : PropTypes.object.isRequired classes : PropTypes.object.isRequired
}; };
export default withStyles(styles)(PeerView); export default withStyles(styles)(VideoView);