Moved Me window into view with other Peer containers.

master
Håvar Aambø Fosstveit 2019-04-01 15:08:25 +02:00
parent 9b78a81ef0
commit 49ebf5330b
6 changed files with 444 additions and 516 deletions

View File

@ -9,10 +9,6 @@ import { getDeviceInfo } from 'mediasoup-client';
import * as appPropTypes from '../appPropTypes'; import * as appPropTypes from '../appPropTypes';
import PeerView from '../VideoContainers/PeerView'; import PeerView from '../VideoContainers/PeerView';
import ScreenView from '../VideoContainers/ScreenView'; 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 = () => const styles = () =>
({ ({
@ -36,53 +32,6 @@ const styles = () =>
{ {
order : 1 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)'
}
} }
}); });
@ -124,8 +73,8 @@ class Me extends React.PureComponent
{ {
const { const {
roomClient, roomClient,
connected,
me, me,
style,
advancedMode, advancedMode,
micProducer, micProducer,
webcamProducer, webcamProducer,
@ -133,26 +82,6 @@ class Me extends React.PureComponent
classes classes
} = this.props; } = 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 = ( const videoVisible = (
Boolean(webcamProducer) && Boolean(webcamProducer) &&
!webcamProducer.locallyPaused && !webcamProducer.locallyPaused &&
@ -180,57 +109,7 @@ class Me extends React.PureComponent
onMouseOver={this.handleMouseOver} onMouseOver={this.handleMouseOver}
onMouseOut={this.handleMouseOut} onMouseOut={this.handleMouseOut}
> >
<div className={classnames(classes.viewContainer, 'webcam')}> <div className={classnames(classes.viewContainer, 'webcam')} style={style}>
{ 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 <PeerView
isMe isMe
advancedMode={advancedMode} advancedMode={advancedMode}
@ -249,7 +128,7 @@ class Me extends React.PureComponent
</div> </div>
{ screenProducer ? { screenProducer ?
<div className={classnames(classes.viewContainer, 'screen')}> <div className={classnames(classes.viewContainer, 'screen')} style={style}>
<ScreenView <ScreenView
isMe isMe
advancedMode={advancedMode} advancedMode={advancedMode}
@ -304,6 +183,7 @@ Me.propTypes =
micProducer : appPropTypes.Producer, micProducer : appPropTypes.Producer,
webcamProducer : appPropTypes.Producer, webcamProducer : appPropTypes.Producer,
screenProducer : appPropTypes.Producer, screenProducer : appPropTypes.Producer,
style : PropTypes.object,
classes : PropTypes.object.isRequired classes : PropTypes.object.isRequired
}; };

View File

@ -5,15 +5,17 @@ import classnames from 'classnames';
import * as appPropTypes from '../appPropTypes'; import * as appPropTypes from '../appPropTypes';
import { withRoomContext } from '../../RoomContext'; 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 * as stateActions from '../../actions/stateActions'; import * as stateActions from '../../actions/stateActions';
import PeerView from '../VideoContainers/PeerView'; import PeerView from '../VideoContainers/PeerView';
import ScreenView from '../VideoContainers/ScreenView'; import ScreenView from '../VideoContainers/ScreenView';
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';
import NewWindowIcon from '@material-ui/icons/OpenInNew'; import NewWindowIcon from '@material-ui/icons/OpenInNew';
import FullScreenIcon from '@material-ui/icons/Fullscreen'; import FullScreenIcon from '@material-ui/icons/Fullscreen';
const styles = () => const styles = (theme) =>
({ ({
root : root :
{ {
@ -24,6 +26,10 @@ const styles = () =>
flex : '100 100 auto', flex : '100 100 auto',
position : 'relative' position : 'relative'
}, },
fab :
{
margin : theme.spacing.unit
},
viewContainer : viewContainer :
{ {
position : 'relative', position : 'relative',
@ -40,56 +46,23 @@ const styles = () =>
}, },
controls : controls :
{ {
position : 'absolute', position : 'absolute',
right : 0, width : '100%',
top : 0, height : '100%',
display : 'flex', backgroundColor : 'rgba(0, 0, 0, 0.3)',
flexDirection : 'row', display : 'flex',
justifyContent : 'flex-start', flexDirection : 'row',
alignItems : 'center', justifyContent : 'center',
padding : '0.4vmin', alignItems : 'center',
zIndex : 20, padding : '0.4vmin',
opacity : 0, zIndex : 20,
transition : 'opacity 0.3s', opacity : 0,
'&.visible' : transition : 'opacity 0.3s',
'&:hover' :
{ {
opacity : 1 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 : pausedVideo :
{ {
position : 'absolute', position : 'absolute',
@ -136,207 +109,186 @@ const styles = () =>
} }
}); });
class Peer extends React.PureComponent const Peer = (props) =>
{ {
state = { const {
controlsVisible : false roomClient,
}; advancedMode,
peer,
micConsumer,
webcamConsumer,
screenConsumer,
toggleConsumerFullscreen,
toggleConsumerWindow,
style,
windowConsumer,
classes,
theme
} = props;
handleMouseOver = () => const micEnabled = (
{ Boolean(micConsumer) &&
this.setState({ !micConsumer.locallyPaused &&
controlsVisible : true !micConsumer.remotelyPaused
}); );
};
handleMouseOut = () => const videoVisible = (
{ Boolean(webcamConsumer) &&
this.setState({ !webcamConsumer.locallyPaused &&
controlsVisible : false !webcamConsumer.remotelyPaused
}); );
};
render() const screenVisible = (
{ Boolean(screenConsumer) &&
const { !screenConsumer.locallyPaused &&
roomClient, !screenConsumer.remotelyPaused
advancedMode, );
peer,
micConsumer,
webcamConsumer,
screenConsumer,
toggleConsumerFullscreen,
toggleConsumerWindow,
style,
windowConsumer,
classes
} = this.props;
const micEnabled = ( let videoProfile;
Boolean(micConsumer) &&
!micConsumer.locallyPaused &&
!micConsumer.remotelyPaused
);
const videoVisible = ( if (webcamConsumer)
Boolean(webcamConsumer) && videoProfile = webcamConsumer.profile;
!webcamConsumer.locallyPaused &&
!webcamConsumer.remotelyPaused
);
const screenVisible = ( let screenProfile;
Boolean(screenConsumer) &&
!screenConsumer.locallyPaused &&
!screenConsumer.remotelyPaused
);
let videoProfile; if (screenConsumer)
screenProfile = screenConsumer.profile;
if (webcamConsumer) const smallScreen = useMediaQuery(theme.breakpoints.down('sm'));
videoProfile = webcamConsumer.profile;
let screenProfile; return (
<div
className={classnames(classes.root, {
screen : screenConsumer
})}
>
{ videoVisible && !webcamConsumer.supported ?
<div className={classes.incompatibleVideo}>
<p>incompatible video</p>
</div>
:null
}
if (screenConsumer) { !videoVisible ?
screenProfile = screenConsumer.profile; <div className={classes.pausedVideo}>
<p>this video is paused</p>
</div>
:null
}
return ( <div className={classnames(classes.viewContainer, 'webcam')} style={style}>
<div <div
className={classnames(classes.root, { className={classes.controls}
screen : screenConsumer >
})} <Fab
onMouseOver={this.handleMouseOver} aria-label='Mute mic'
onMouseOut={this.handleMouseOut} className={classes.fab}
> color={micEnabled ? 'default' : 'secondary'}
{ videoVisible && !webcamConsumer.supported ? onClick={() =>
<div className={classes.incompatibleVideo}> {
<p>incompatible video</p> micEnabled ?
</div> roomClient.modifyPeerConsumer(peer.name, 'mic', true) :
:null roomClient.modifyPeerConsumer(peer.name, 'mic', false);
} }}
{ !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 { micEnabled ?
className={classnames(classes.button, { <MicIcon />
on : micEnabled, :
off : !micEnabled, <MicOffIcon />
disabled : peer.peerAudioInProgress }
})} </Fab>
onClick={(e) =>
{
e.stopPropagation();
micEnabled ?
roomClient.modifyPeerConsumer(peer.name, 'mic', true) :
roomClient.modifyPeerConsumer(peer.name, 'mic', false);
}}
>
{ micEnabled ?
<MicIcon />
:
<MicOffIcon />
}
</div>
<div { !smallScreen ?
className={classnames(classes.button, { <Fab
disabled : !videoVisible || aria-label='New window'
(windowConsumer === webcamConsumer.id) className={classes.fab}
})} disabled={
onClick={(e) => !videoVisible ||
(windowConsumer === webcamConsumer.id)
}
onClick={() =>
{ {
e.stopPropagation();
toggleConsumerWindow(webcamConsumer); toggleConsumerWindow(webcamConsumer);
}} }}
> >
<NewWindowIcon /> <NewWindowIcon />
</div> </Fab>
:null
}
<div <Fab
className={classnames(classes.button, 'fullscreen', { aria-label='Fullscreen'
disabled : !videoVisible className={classes.fab}
})} disabled={!videoVisible}
onClick={(e) => onClick={() =>
{ {
e.stopPropagation(); toggleConsumerFullscreen(webcamConsumer);
toggleConsumerFullscreen(webcamConsumer); }}
}} >
> <FullScreenIcon />
<FullScreenIcon /> </Fab>
</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> </div>
{ screenConsumer ? <PeerView
<div className={classnames(classes.viewContainer, 'screen')} style={style}> advancedMode={advancedMode}
<div peer={peer}
className={classnames(classes.controls, { volume={micConsumer ? micConsumer.volume : null}
visible : this.state.controlsVisible videoTrack={webcamConsumer ? webcamConsumer.track : null}
})} videoVisible={videoVisible}
> videoProfile={videoProfile}
<div audioCodec={micConsumer ? micConsumer.codec : null}
className={classnames(classes.button, 'newwindow', { videoCodec={webcamConsumer ? webcamConsumer.codec : null}
disabled : !screenVisible || />
(windowConsumer === screenConsumer.id) </div>
})}
onClick={(e) => { screenConsumer ?
<div className={classnames(classes.viewContainer, 'screen')} style={style}>
<div
className={classes.controls}
>
{ !smallScreen ?
<Fab
aria-label='New window'
className={classes.fab}
disabled={
!screenVisible ||
(windowConsumer === screenConsumer.id)
}
onClick={() =>
{ {
e.stopPropagation();
toggleConsumerWindow(screenConsumer); toggleConsumerWindow(screenConsumer);
}} }}
> >
<NewWindowIcon /> <NewWindowIcon />
</div> </Fab>
:null
}
<div <Fab
className={classnames(classes.button, 'fullscreen', { aria-label='Fullscreen'
disabled : !screenVisible className={classes.fab}
})} disabled={!screenVisible}
onClick={(e) => onClick={() =>
{ {
e.stopPropagation(); toggleConsumerFullscreen(screenConsumer);
toggleConsumerFullscreen(screenConsumer); }}
}} >
> <FullScreenIcon />
<FullScreenIcon /> </Fab>
</div>
</div>
<ScreenView
advancedMode={advancedMode}
screenTrack={screenConsumer ? screenConsumer.track : null}
screenVisible={screenVisible}
screenProfile={screenProfile}
screenCodec={screenConsumer ? screenConsumer.codec : null}
/>
</div> </div>
:null <ScreenView
} advancedMode={advancedMode}
</div> screenTrack={screenConsumer ? screenConsumer.track : null}
); screenVisible={screenVisible}
} screenProfile={screenProfile}
} screenCodec={screenConsumer ? screenConsumer.codec : null}
/>
</div>
:null
}
</div>
);
};
Peer.propTypes = Peer.propTypes =
{ {
@ -351,7 +303,8 @@ Peer.propTypes =
style : PropTypes.object, style : PropTypes.object,
toggleConsumerFullscreen : PropTypes.func.isRequired, toggleConsumerFullscreen : PropTypes.func.isRequired,
toggleConsumerWindow : PropTypes.func.isRequired, toggleConsumerWindow : PropTypes.func.isRequired,
classes : PropTypes.object classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired
}; };
const mapStateToProps = (state, { name }) => const mapStateToProps = (state, { name }) =>
@ -394,4 +347,4 @@ const mapDispatchToProps = (dispatch) =>
export default withRoomContext(connect( export default withRoomContext(connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(withStyles(styles)(Peer))); )(withStyles(styles, { withTheme: true })(Peer)));

View File

@ -2,34 +2,49 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
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 classnames from 'classnames'; import classnames from 'classnames';
import * as appPropTypes from '../appPropTypes'; import * as appPropTypes from '../appPropTypes';
import { withRoomContext } from '../../RoomContext'; import { withRoomContext } from '../../RoomContext';
import Fab from '@material-ui/core/Fab'; import Fab from '@material-ui/core/Fab';
import Avatar from '@material-ui/core/Avatar'; // 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';
import VideoOffIcon from '@material-ui/icons/VideocamOff';
import ScreenIcon from '@material-ui/icons/ScreenShare'; import ScreenIcon from '@material-ui/icons/ScreenShare';
import ScreenOffIcon from '@material-ui/icons/StopScreenShare'; import ScreenOffIcon from '@material-ui/icons/StopScreenShare';
import ExtensionIcon from '@material-ui/icons/Extension'; import ExtensionIcon from '@material-ui/icons/Extension';
import LockIcon from '@material-ui/icons/Lock'; import LockIcon from '@material-ui/icons/Lock';
import LockOpenIcon from '@material-ui/icons/LockOpen'; import LockOpenIcon from '@material-ui/icons/LockOpen';
import HandOff from '../../images/icon-hand-black.svg'; // import HandOff from '../../images/icon-hand-black.svg';
import HandOn from '../../images/icon-hand-white.svg'; // import HandOn from '../../images/icon-hand-white.svg';
import LeaveIcon from '@material-ui/icons/Cancel'; import LeaveIcon from '@material-ui/icons/Cancel';
const styles = (theme) => const styles = (theme) =>
({ ({
root : root :
{ {
position : 'fixed', position : 'fixed',
zIndex : 500, zIndex : 500,
top : '50%', display : 'flex',
transform : 'translate(0%, -50%)', [theme.breakpoints.up('md')] :
display : 'flex', {
flexDirection : 'column', top : '50%',
justifyContent : 'center', transform : 'translate(0%, -50%)',
alignItems : 'center', flexDirection : 'column',
left : '1.0em', justifyContent : 'center',
width : '2.6em' alignItems : 'center',
left : '1.0em',
width : '2.6em'
},
[theme.breakpoints.down('sm')] :
{
flexDirection : 'row',
bottom : '0.5em',
left : '50%',
transform : 'translate(-50%, -0%)'
}
}, },
fab : fab :
{ {
@ -47,150 +62,218 @@ const styles = (theme) =>
} }
}); });
class Sidebar extends React.PureComponent const Sidebar = (props) =>
{ {
render() const {
roomClient,
toolbarsVisible,
me,
micProducer,
webcamProducer,
screenProducer,
locked,
classes,
theme
} = 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';
let screenState;
if (me.needExtension)
{ {
const { screenState = 'need-extension';
roomClient,
toolbarsVisible,
me,
screenProducer,
locked,
classes
} = this.props;
let screenState;
if (me.needExtension)
{
screenState = 'need-extension';
}
else if (!me.canShareScreen)
{
screenState = 'unsupported';
}
else if (screenProducer)
{
screenState = 'on';
}
else
{
screenState = 'off';
}
return (
<div
className={
classnames(classes.root, toolbarsVisible ? classes.show : classes.hide)
}
>
<Fab
aria-label='Share screen'
className={classes.fab}
disabled={!me.canShareScreen || me.screenShareInProgress}
color={screenState === 'on' ? 'primary' : 'default'}
onClick={() =>
{
switch (screenState)
{
case 'on':
{
roomClient.disableScreenSharing();
break;
}
case 'off':
{
roomClient.enableScreenSharing();
break;
}
case 'need-extension':
{
roomClient.installExtension();
break;
}
default:
{
break;
}
}
}}
>
{ screenState === 'on' || screenState === 'unsupported' ?
<ScreenOffIcon/>
:null
}
{ screenState === 'off' ?
<ScreenIcon/>
:null
}
{ screenState === 'need-extension' ?
<ExtensionIcon/>
:null
}
</Fab>
<Fab
aria-label='Room lock'
className={classes.fab}
color={locked ? 'primary' : 'default'}
onClick={() =>
{
if (locked)
{
roomClient.unlockRoom();
}
else
{
roomClient.lockRoom();
}
}}
>
{ locked ?
<LockIcon />
:
<LockOpenIcon />
}
</Fab>
<Fab
aria-label='Raise hand'
className={classes.fab}
disabled={me.raiseHandInProgress}
color={me.raiseHand ? 'primary' : 'default'}
onClick={() => roomClient.sendRaiseHandState(!me.raiseHand)}
>
<Avatar alt='Hand' src={me.raiseHand ? HandOn : HandOff} />
</Fab>
<Fab
aria-label='Leave meeting'
className={classes.fab}
color='secondary'
onClick={() => roomClient.close()}
>
<LeaveIcon />
</Fab>
</div>
);
} }
} else if (!me.canShareScreen)
{
screenState = 'unsupported';
}
else if (screenProducer)
{
screenState = 'on';
}
else
{
screenState = 'off';
}
const smallScreen = useMediaQuery(theme.breakpoints.down('sm'));
return (
<div
className={
classnames(classes.root, toolbarsVisible ? classes.show : classes.hide)
}
>
<Fab
aria-label='Mute mic'
className={classes.fab}
color={micState === 'on' ? 'default' : 'secondary'}
size={smallScreen ? 'large' : 'medium'}
onClick={() =>
{
micState === 'on' ?
roomClient.muteMic() :
roomClient.unmuteMic();
}}
>
{ micState === 'on' ?
<MicIcon />
:
<MicOffIcon />
}
</Fab>
<Fab
aria-label='Mute video'
className={classes.fab}
color={webcamState === 'on' ? 'default' : 'secondary'}
size={smallScreen ? 'large' : 'medium'}
onClick={() =>
{
webcamState === 'on' ?
roomClient.disableWebcam() :
roomClient.enableWebcam();
}}
>
{ webcamState === 'on' ?
<VideoIcon />
:
<VideoOffIcon />
}
</Fab>
<Fab
aria-label='Share screen'
className={classes.fab}
disabled={!me.canShareScreen || me.screenShareInProgress}
color={screenState === 'on' ? 'primary' : 'default'}
size={smallScreen ? 'large' : 'medium'}
onClick={() =>
{
switch (screenState)
{
case 'on':
{
roomClient.disableScreenSharing();
break;
}
case 'off':
{
roomClient.enableScreenSharing();
break;
}
case 'need-extension':
{
roomClient.installExtension();
break;
}
default:
{
break;
}
}
}}
>
{ screenState === 'on' || screenState === 'unsupported' ?
<ScreenOffIcon/>
:null
}
{ screenState === 'off' ?
<ScreenIcon/>
:null
}
{ screenState === 'need-extension' ?
<ExtensionIcon/>
:null
}
</Fab>
<Fab
aria-label='Room lock'
className={classes.fab}
color={locked ? 'primary' : 'default'}
size={smallScreen ? 'large' : 'medium'}
onClick={() =>
{
if (locked)
{
roomClient.unlockRoom();
}
else
{
roomClient.lockRoom();
}
}}
>
{ locked ?
<LockIcon />
:
<LockOpenIcon />
}
</Fab>
{ /* <Fab
aria-label='Raise hand'
className={classes.fab}
disabled={me.raiseHandInProgress}
color={me.raiseHand ? 'primary' : 'default'}
size='large'
onClick={() => roomClient.sendRaiseHandState(!me.raiseHand)}
>
<Avatar alt='Hand' src={me.raiseHand ? HandOn : HandOff} />
</Fab> */ }
<Fab
aria-label='Leave meeting'
className={classes.fab}
color='secondary'
size={smallScreen ? 'large' : 'medium'}
onClick={() => roomClient.close()}
>
<LeaveIcon />
</Fab>
</div>
);
};
Sidebar.propTypes = Sidebar.propTypes =
{ {
roomClient : PropTypes.any.isRequired, roomClient : PropTypes.any.isRequired,
toolbarsVisible : PropTypes.bool.isRequired, toolbarsVisible : PropTypes.bool.isRequired,
me : appPropTypes.Me.isRequired, me : appPropTypes.Me.isRequired,
micProducer : appPropTypes.Producer,
webcamProducer : appPropTypes.Producer,
screenProducer : appPropTypes.Producer, screenProducer : appPropTypes.Producer,
locked : PropTypes.bool.isRequired, locked : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired
}; };
const mapStateToProps = (state) => const mapStateToProps = (state) =>
({ ({
toolbarsVisible : state.room.toolbarsVisible, toolbarsVisible : state.room.toolbarsVisible,
screenProducer : Object.values(state.producers) 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'), .find((producer) => producer.source === 'screen'),
me : state.me, me : state.me,
locked : state.room.locked locked : state.room.locked
@ -198,4 +281,4 @@ const mapStateToProps = (state) =>
export default withRoomContext(connect( export default withRoomContext(connect(
mapStateToProps mapStateToProps
)(withStyles(styles)(Sidebar))); )(withStyles(styles, { withTheme: true })(Sidebar)));

View File

@ -5,11 +5,12 @@ import classnames from 'classnames';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { withStyles } from '@material-ui/core/styles'; import { withStyles } from '@material-ui/core/styles';
import Peer from '../Containers/Peer'; import Peer from '../Containers/Peer';
import Me from '../Containers/Me';
import HiddenPeers from '../Containers/HiddenPeers'; import HiddenPeers from '../Containers/HiddenPeers';
import ResizeObserver from 'resize-observer-polyfill'; import ResizeObserver from 'resize-observer-polyfill';
const RATIO = 1.334; const RATIO = 1.334;
const PADDING = 100; const PADDING = 60;
const styles = () => const styles = () =>
({ ({
@ -23,7 +24,7 @@ const styles = () =>
justifyContent : 'center', justifyContent : 'center',
alignItems : 'center', alignItems : 'center',
alignContent : 'center', alignContent : 'center',
paddingTop : 70, paddingTop : 30,
paddingBottom : 30 paddingBottom : 30
}, },
peerContainer : peerContainer :
@ -126,6 +127,7 @@ class Democratic extends React.PureComponent
const { const {
advancedMode, advancedMode,
activeSpeakerName, activeSpeakerName,
amActiveSpeaker,
peers, peers,
spotlights, spotlights,
spotlightsLength, spotlightsLength,
@ -140,6 +142,16 @@ class Democratic extends React.PureComponent
return ( return (
<div className={classes.root} ref={this.peersRef}> <div className={classes.root} ref={this.peersRef}>
<div
className={classnames(classes.peerContainer, 'me-handle', {
'active-speaker' : amActiveSpeaker
})}
>
<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))
@ -181,6 +193,7 @@ Democratic.propTypes =
advancedMode : PropTypes.bool, advancedMode : PropTypes.bool,
peers : PropTypes.object.isRequired, peers : PropTypes.object.isRequired,
boxes : PropTypes.number, boxes : PropTypes.number,
amActiveSpeaker : PropTypes.bool.isRequired,
activeSpeakerName : PropTypes.string, activeSpeakerName : PropTypes.string,
selectedPeerName : PropTypes.string, selectedPeerName : PropTypes.string,
spotlightsLength : PropTypes.number, spotlightsLength : PropTypes.number,
@ -193,13 +206,15 @@ const mapStateToProps = (state) =>
const spotlights = state.room.spotlights; const spotlights = state.room.spotlights;
const spotlightsLength = spotlights ? state.room.spotlights.length : 0; const spotlightsLength = spotlights ? state.room.spotlights.length : 0;
const boxes = spotlightsLength + Object.values(state.consumers) const boxes = spotlightsLength + Object.values(state.consumers)
.filter((consumer) => consumer.source === 'screen').length; .filter((consumer) => consumer.source === 'screen').length + Object.values(state.producers)
.filter((producer) => producer.source === 'screen').length + 1;
return { return {
peers : state.peers, peers : state.peers,
boxes, boxes,
activeSpeakerName : state.room.activeSpeakerName, activeSpeakerName : state.room.activeSpeakerName,
selectedPeerName : state.room.selectedPeerName, selectedPeerName : state.room.selectedPeerName,
amActiveSpeaker : state.me.name === state.room.activeSpeakerName,
spotlights, spotlights,
spotlightsLength spotlightsLength
}; };

View File

@ -2,11 +2,11 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import * as appPropTypes from './appPropTypes'; import * as appPropTypes from './appPropTypes';
import classnames from 'classnames'; // import classnames from 'classnames';
import { withRoomContext } from '../RoomContext'; import { withRoomContext } from '../RoomContext';
import { withStyles } from '@material-ui/core/styles'; import { withStyles } from '@material-ui/core/styles';
import * as stateActions from '../actions/stateActions'; import * as stateActions from '../actions/stateActions';
import Draggable from 'react-draggable'; // import Draggable from 'react-draggable';
import { idle } from '../utils'; import { idle } from '../utils';
import FullScreen from './FullScreen'; import FullScreen from './FullScreen';
import CookieConsent from 'react-cookie-consent'; import CookieConsent from 'react-cookie-consent';
@ -26,7 +26,7 @@ import Notifications from './Notifications/Notifications';
import MeetingDrawer from './MeetingDrawer/MeetingDrawer'; import MeetingDrawer from './MeetingDrawer/MeetingDrawer';
import Democratic from './MeetingViews/Democratic'; import Democratic from './MeetingViews/Democratic';
import Filmstrip from './MeetingViews/Filmstrip'; import Filmstrip from './MeetingViews/Filmstrip';
import Me from './Containers/Me'; // import Me from './Containers/Me';
import AudioPeers from './PeerAudio/AudioPeers'; import AudioPeers from './PeerAudio/AudioPeers';
import FullScreenView from './VideoContainers/FullScreenView'; import FullScreenView from './VideoContainers/FullScreenView';
import VideoWindow from './VideoWindow/VideoWindow'; import VideoWindow from './VideoWindow/VideoWindow';
@ -68,7 +68,12 @@ const styles = (theme) =>
}, },
logo : logo :
{ {
marginLeft : 20 display : 'none',
marginLeft : 20,
[theme.breakpoints.up('sm')] :
{
display : 'block'
}
}, },
show : show :
{ {
@ -126,8 +131,8 @@ const styles = (theme) =>
boxShadow : 'var(--me-shadow)', boxShadow : 'var(--me-shadow)',
transitionProperty : 'border-color', transitionProperty : 'border-color',
transitionDuration : '0.15s', transitionDuration : '0.15s',
top : '8%', top : '5em',
left : '1%', left : '1em',
border : 'var(--me-border)', border : 'var(--me-border)',
'&.active-speaker' : '&.active-speaker' :
{ {
@ -215,7 +220,7 @@ class Room extends React.PureComponent
roomClient, roomClient,
room, room,
me, me,
amActiveSpeaker, // amActiveSpeaker,
setSettingsOpen, setSettingsOpen,
toolAreaOpen, toolAreaOpen,
toggleToolArea, toggleToolArea,
@ -337,7 +342,8 @@ class Room extends React.PureComponent
<IconButton <IconButton
aria-label='Account' aria-label='Account'
color='inherit' color='inherit'
onClick={() => { onClick={() =>
{
me.loggedIn ? roomClient.logout() : roomClient.login(); me.loggedIn ? roomClient.logout() : roomClient.login();
}} }}
> >
@ -367,6 +373,7 @@ class Room extends React.PureComponent
<View advancedMode={room.advancedMode} /> <View advancedMode={room.advancedMode} />
{ /*
<Draggable handle='.me-handle' bounds='body' cancel='.display-name'> <Draggable handle='.me-handle' bounds='body' cancel='.display-name'>
<div <div
className={classnames(classes.meContainer, 'me-handle', { className={classnames(classes.meContainer, 'me-handle', {
@ -378,6 +385,7 @@ class Room extends React.PureComponent
/> />
</div> </div>
</Draggable> </Draggable>
*/ }
<Sidebar /> <Sidebar />
@ -393,7 +401,7 @@ Room.propTypes =
roomClient : PropTypes.object.isRequired, roomClient : PropTypes.object.isRequired,
room : appPropTypes.Room.isRequired, room : appPropTypes.Room.isRequired,
me : appPropTypes.Me.isRequired, me : appPropTypes.Me.isRequired,
amActiveSpeaker : PropTypes.bool.isRequired, // amActiveSpeaker : PropTypes.bool.isRequired,
toolAreaOpen : PropTypes.bool.isRequired, toolAreaOpen : PropTypes.bool.isRequired,
screenProducer : appPropTypes.Producer, screenProducer : appPropTypes.Producer,
setToolbarsVisible : PropTypes.func.isRequired, setToolbarsVisible : PropTypes.func.isRequired,
@ -411,13 +419,13 @@ const mapStateToProps = (state) =>
producersArray.find((producer) => producer.source === 'screen'); producersArray.find((producer) => producer.source === 'screen');
return { return {
room : state.room, room : state.room,
me : state.me, me : state.me,
amActiveSpeaker : state.me.name === state.room.activeSpeakerName, screenProducer : screenProducer,
screenProducer : screenProducer, toolAreaOpen : state.toolarea.toolAreaOpen,
toolAreaOpen : state.toolarea.toolAreaOpen, unread : state.toolarea.unreadMessages +
unread : state.toolarea.unreadMessages +
state.toolarea.unreadFiles state.toolarea.unreadFiles
// amActiveSpeaker : state.me.name === state.room.activeSpeakerName,
}; };
}; };

View File

@ -1,17 +1,6 @@
:root { :root {
--background-color: rgba(114, 119, 143, 1.0); --background-color: rgba(114, 119, 143, 1.0);
--media-control-button-color: rgba(255, 255, 255, 0.85);
--media-control-botton-on: rgba(255, 255, 255, 0.7);
--media-control-botton-off: rgba(212, 34, 65, 0.7);
--media-control-botton-disabled: rgba(255, 255, 255, 0.5);
--media-control-button-size: 1.5em;
--me-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12);
--me-border: 1px solid rgba(255, 255, 255, 0.15);
--me-width: 20vmin;
--me-height: 15vmin;
--peer-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12); --peer-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12);
--peer-border: 1px solid rgba(255, 255, 255, 0.15); --peer-border: 1px solid rgba(255, 255, 255, 0.15);
--peer-empty-avatar: url('./images/buddy.svg'); --peer-empty-avatar: url('./images/buddy.svg');