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 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 = () =>
({
@ -36,53 +32,6 @@ const styles = () =>
{
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 {
roomClient,
connected,
me,
style,
advancedMode,
micProducer,
webcamProducer,
@ -133,26 +82,6 @@ class Me extends React.PureComponent
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 &&
@ -180,57 +109,7 @@ class Me extends React.PureComponent
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
}
<div className={classnames(classes.viewContainer, 'webcam')} style={style}>
<PeerView
isMe
advancedMode={advancedMode}
@ -249,7 +128,7 @@ class Me extends React.PureComponent
</div>
{ screenProducer ?
<div className={classnames(classes.viewContainer, 'screen')}>
<div className={classnames(classes.viewContainer, 'screen')} style={style}>
<ScreenView
isMe
advancedMode={advancedMode}
@ -304,6 +183,7 @@ Me.propTypes =
micProducer : appPropTypes.Producer,
webcamProducer : appPropTypes.Producer,
screenProducer : appPropTypes.Producer,
style : PropTypes.object,
classes : PropTypes.object.isRequired
};

View File

@ -5,15 +5,17 @@ import classnames from 'classnames';
import * as appPropTypes from '../appPropTypes';
import { withRoomContext } from '../../RoomContext';
import { withStyles } from '@material-ui/core/styles';
import { unstable_useMediaQuery as useMediaQuery } from '@material-ui/core/useMediaQuery';
import * as stateActions from '../../actions/stateActions';
import PeerView from '../VideoContainers/PeerView';
import ScreenView from '../VideoContainers/ScreenView';
import Fab from '@material-ui/core/Fab';
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 = () =>
const styles = (theme) =>
({
root :
{
@ -24,6 +26,10 @@ const styles = () =>
flex : '100 100 auto',
position : 'relative'
},
fab :
{
margin : theme.spacing.unit
},
viewContainer :
{
position : 'relative',
@ -41,53 +47,20 @@ const styles = () =>
controls :
{
position : 'absolute',
right : 0,
top : 0,
width : '100%',
height : '100%',
backgroundColor : 'rgba(0, 0, 0, 0.3)',
display : 'flex',
flexDirection : 'row',
justifyContent : 'flex-start',
justifyContent : 'center',
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 :
@ -136,27 +109,7 @@ const styles = () =>
}
});
class Peer extends React.PureComponent
{
state = {
controlsVisible : false
};
handleMouseOver = () =>
{
this.setState({
controlsVisible : true
});
};
handleMouseOut = () =>
{
this.setState({
controlsVisible : false
});
};
render()
const Peer = (props) =>
{
const {
roomClient,
@ -169,8 +122,9 @@ class Peer extends React.PureComponent
toggleConsumerWindow,
style,
windowConsumer,
classes
} = this.props;
classes,
theme
} = props;
const micEnabled = (
Boolean(micConsumer) &&
@ -200,13 +154,13 @@ class Peer extends React.PureComponent
if (screenConsumer)
screenProfile = screenConsumer.profile;
const smallScreen = useMediaQuery(theme.breakpoints.down('sm'));
return (
<div
className={classnames(classes.root, {
screen : screenConsumer
})}
onMouseOver={this.handleMouseOver}
onMouseOut={this.handleMouseOut}
>
{ videoVisible && !webcamConsumer.supported ?
<div className={classes.incompatibleVideo}>
@ -224,19 +178,14 @@ class Peer extends React.PureComponent
<div className={classnames(classes.viewContainer, 'webcam')} style={style}>
<div
className={classnames(classes.controls, {
visible : this.state.controlsVisible
})}
className={classes.controls}
>
<div
className={classnames(classes.button, {
on : micEnabled,
off : !micEnabled,
disabled : peer.peerAudioInProgress
})}
onClick={(e) =>
<Fab
aria-label='Mute mic'
className={classes.fab}
color={micEnabled ? 'default' : 'secondary'}
onClick={() =>
{
e.stopPropagation();
micEnabled ?
roomClient.modifyPeerConsumer(peer.name, 'mic', true) :
roomClient.modifyPeerConsumer(peer.name, 'mic', false);
@ -247,34 +196,37 @@ class Peer extends React.PureComponent
:
<MicOffIcon />
}
</div>
</Fab>
<div
className={classnames(classes.button, {
disabled : !videoVisible ||
{ !smallScreen ?
<Fab
aria-label='New window'
className={classes.fab}
disabled={
!videoVisible ||
(windowConsumer === webcamConsumer.id)
})}
onClick={(e) =>
}
onClick={() =>
{
e.stopPropagation();
toggleConsumerWindow(webcamConsumer);
}}
>
<NewWindowIcon />
</div>
</Fab>
:null
}
<div
className={classnames(classes.button, 'fullscreen', {
disabled : !videoVisible
})}
onClick={(e) =>
<Fab
aria-label='Fullscreen'
className={classes.fab}
disabled={!videoVisible}
onClick={() =>
{
e.stopPropagation();
toggleConsumerFullscreen(webcamConsumer);
}}
>
<FullScreenIcon />
</div>
</Fab>
</div>
<PeerView
@ -292,36 +244,37 @@ class Peer extends React.PureComponent
{ screenConsumer ?
<div className={classnames(classes.viewContainer, 'screen')} style={style}>
<div
className={classnames(classes.controls, {
visible : this.state.controlsVisible
})}
className={classes.controls}
>
<div
className={classnames(classes.button, 'newwindow', {
disabled : !screenVisible ||
{ !smallScreen ?
<Fab
aria-label='New window'
className={classes.fab}
disabled={
!screenVisible ||
(windowConsumer === screenConsumer.id)
})}
onClick={(e) =>
}
onClick={() =>
{
e.stopPropagation();
toggleConsumerWindow(screenConsumer);
}}
>
<NewWindowIcon />
</div>
</Fab>
:null
}
<div
className={classnames(classes.button, 'fullscreen', {
disabled : !screenVisible
})}
onClick={(e) =>
<Fab
aria-label='Fullscreen'
className={classes.fab}
disabled={!screenVisible}
onClick={() =>
{
e.stopPropagation();
toggleConsumerFullscreen(screenConsumer);
}}
>
<FullScreenIcon />
</div>
</Fab>
</div>
<ScreenView
advancedMode={advancedMode}
@ -335,8 +288,7 @@ class Peer extends React.PureComponent
}
</div>
);
}
}
};
Peer.propTypes =
{
@ -351,7 +303,8 @@ Peer.propTypes =
style : PropTypes.object,
toggleConsumerFullscreen : PropTypes.func.isRequired,
toggleConsumerWindow : PropTypes.func.isRequired,
classes : PropTypes.object
classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired
};
const mapStateToProps = (state, { name }) =>
@ -394,4 +347,4 @@ const mapDispatchToProps = (dispatch) =>
export default withRoomContext(connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(Peer)));
)(withStyles(styles, { withTheme: true })(Peer)));

View File

@ -2,18 +2,23 @@ import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core/styles';
import { unstable_useMediaQuery as useMediaQuery } from '@material-ui/core/useMediaQuery';
import classnames from 'classnames';
import * as appPropTypes from '../appPropTypes';
import { withRoomContext } from '../../RoomContext';
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 ScreenOffIcon from '@material-ui/icons/StopScreenShare';
import ExtensionIcon from '@material-ui/icons/Extension';
import LockIcon from '@material-ui/icons/Lock';
import LockOpenIcon from '@material-ui/icons/LockOpen';
import HandOff from '../../images/icon-hand-black.svg';
import HandOn from '../../images/icon-hand-white.svg';
// import HandOff from '../../images/icon-hand-black.svg';
// import HandOn from '../../images/icon-hand-white.svg';
import LeaveIcon from '@material-ui/icons/Cancel';
const styles = (theme) =>
@ -22,15 +27,25 @@ const styles = (theme) =>
{
position : 'fixed',
zIndex : 500,
display : 'flex',
[theme.breakpoints.up('md')] :
{
top : '50%',
transform : 'translate(0%, -50%)',
display : 'flex',
flexDirection : 'column',
justifyContent : 'center',
alignItems : 'center',
left : '1.0em',
width : '2.6em'
},
[theme.breakpoints.down('sm')] :
{
flexDirection : 'row',
bottom : '0.5em',
left : '50%',
transform : 'translate(-50%, -0%)'
}
},
fab :
{
margin : theme.spacing.unit
@ -47,18 +62,39 @@ const styles = (theme) =>
}
});
class Sidebar extends React.PureComponent
{
render()
const Sidebar = (props) =>
{
const {
roomClient,
toolbarsVisible,
me,
micProducer,
webcamProducer,
screenProducer,
locked,
classes
} = this.props;
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;
@ -79,18 +115,56 @@ class Sidebar extends React.PureComponent
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)
@ -135,6 +209,7 @@ class Sidebar extends React.PureComponent
aria-label='Room lock'
className={classes.fab}
color={locked ? 'primary' : 'default'}
size={smallScreen ? 'large' : 'medium'}
onClick={() =>
{
if (locked)
@ -154,42 +229,50 @@ class Sidebar extends React.PureComponent
}
</Fab>
<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> */ }
<Fab
aria-label='Leave meeting'
className={classes.fab}
color='secondary'
size={smallScreen ? 'large' : 'medium'}
onClick={() => roomClient.close()}
>
<LeaveIcon />
</Fab>
</div>
);
}
}
};
Sidebar.propTypes =
{
roomClient : PropTypes.any.isRequired,
toolbarsVisible : PropTypes.bool.isRequired,
me : appPropTypes.Me.isRequired,
micProducer : appPropTypes.Producer,
webcamProducer : appPropTypes.Producer,
screenProducer : appPropTypes.Producer,
locked : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired
classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
({
toolbarsVisible : state.room.toolbarsVisible,
micProducer : Object.values(state.producers)
.find((producer) => producer.source === 'mic'),
webcamProducer : Object.values(state.producers)
.find((producer) => producer.source === 'webcam'),
screenProducer : Object.values(state.producers)
.find((producer) => producer.source === 'screen'),
me : state.me,
@ -198,4 +281,4 @@ const mapStateToProps = (state) =>
export default withRoomContext(connect(
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 { withStyles } from '@material-ui/core/styles';
import Peer from '../Containers/Peer';
import Me from '../Containers/Me';
import HiddenPeers from '../Containers/HiddenPeers';
import ResizeObserver from 'resize-observer-polyfill';
const RATIO = 1.334;
const PADDING = 100;
const PADDING = 60;
const styles = () =>
({
@ -23,7 +24,7 @@ const styles = () =>
justifyContent : 'center',
alignItems : 'center',
alignContent : 'center',
paddingTop : 70,
paddingTop : 30,
paddingBottom : 30
},
peerContainer :
@ -126,6 +127,7 @@ class Democratic extends React.PureComponent
const {
advancedMode,
activeSpeakerName,
amActiveSpeaker,
peers,
spotlights,
spotlightsLength,
@ -140,6 +142,16 @@ class Democratic extends React.PureComponent
return (
<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) =>
{
if (spotlights.find((spotlightsElement) => spotlightsElement === peerName))
@ -181,6 +193,7 @@ Democratic.propTypes =
advancedMode : PropTypes.bool,
peers : PropTypes.object.isRequired,
boxes : PropTypes.number,
amActiveSpeaker : PropTypes.bool.isRequired,
activeSpeakerName : PropTypes.string,
selectedPeerName : PropTypes.string,
spotlightsLength : PropTypes.number,
@ -193,13 +206,15 @@ 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;
.filter((consumer) => consumer.source === 'screen').length + Object.values(state.producers)
.filter((producer) => producer.source === 'screen').length + 1;
return {
peers : state.peers,
boxes,
activeSpeakerName : state.room.activeSpeakerName,
selectedPeerName : state.room.selectedPeerName,
amActiveSpeaker : state.me.name === state.room.activeSpeakerName,
spotlights,
spotlightsLength
};

View File

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

View File

@ -1,17 +1,6 @@
:root {
--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-border: 1px solid rgba(255, 255, 255, 0.15);
--peer-empty-avatar: url('./images/buddy.svg');