diff --git a/app/src/components/Containers/Me.js b/app/src/components/Containers/Me.js index 553f2eb..64bf3ee 100644 --- a/app/src/components/Containers/Me.js +++ b/app/src/components/Containers/Me.js @@ -1,19 +1,27 @@ -import React from 'react'; +import React, { useState } from 'react'; import { connect } from 'react-redux'; import { meProducersSelector } from '../Selectors'; import { withRoomContext } from '../../RoomContext'; import { withStyles } from '@material-ui/core/styles'; +import { unstable_useMediaQuery as useMediaQuery } from '@material-ui/core/useMediaQuery'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import * as appPropTypes from '../appPropTypes'; import VideoView from '../VideoContainers/VideoView'; import Volume from './Volume'; +import Fab from '@material-ui/core/Fab'; +import Tooltip from '@material-ui/core/Tooltip'; +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'; -const styles = () => +const styles = (theme) => ({ root : { - flexDirection : 'row', flex : '0 0 auto', boxShadow : 'var(--peer-shadow)', border : 'var(--peer-border)', @@ -22,27 +30,78 @@ const styles = () => backgroundPosition : 'bottom', backgroundSize : 'auto 85%', backgroundRepeat : 'no-repeat', + '&.webcam' : + { + order : 1 + }, + '&.screen' : + { + order : 2 + }, + '&.hover' : + { + 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)' } }, + fab : + { + margin : theme.spacing.unit + }, viewContainer : { - position : 'relative', - '&.webcam' : + position : 'relative', + width : '100%', + height : '100%' + }, + controls : + { + position : 'absolute', + width : '100%', + height : '100%', + backgroundColor : 'rgba(0, 0, 0, 0.3)', + display : 'flex', + flexDirection : 'column', + justifyContent : 'center', + alignItems : 'flex-end', + padding : '0.4vmin', + zIndex : 21, + opacity : 0, + transition : 'opacity 0.3s', + touchAction : 'none', + '&.hover' : { - order : 2 + opacity : 1 }, - '&.screen' : + '& p' : { - order : 1 + position : 'absolute', + float : 'left', + top : '50%', + left : '50%', + transform : 'translate(-50%, -50%)', + color : 'rgba(255, 255, 255, 1)', + fontSize : '7em', + margin : 0 } } }); const Me = (props) => { + const [ hover, setHover ] = useState(false); + const [ webcamHover, setWebcamHover ] = useState(false); + const [ screenHover, setScreenHover ] = useState(false); + + let touchTimeout = null; + + let touchWebcamTimeout = null; + + let touchScreenTimeout = null; + const { roomClient, me, @@ -54,7 +113,8 @@ const Me = (props) => micProducer, webcamProducer, screenProducer, - classes + classes, + theme } = props; const videoVisible = ( @@ -69,23 +129,222 @@ const Me = (props) => !screenProducer.remotelyPaused ); + let micState; + + let micTip; + + if (!me.canSendMic) + { + micState = 'unsupported'; + micTip = 'Audio unsupported'; + } + else if (!micProducer) + { + micState = 'off'; + micTip = 'Activate audio'; + } + else if (!micProducer.locallyPaused && !micProducer.remotelyPaused) + { + micState = 'on'; + micTip = 'Mute audio'; + } + else + { + micState = 'muted'; + micTip = 'Unmute audio'; + } + + let webcamState; + + let webcamTip; + + if (!me.canSendWebcam) + { + webcamState = 'unsupported'; + webcamTip = 'Video unsupported'; + } + else if (webcamProducer) + { + webcamState = 'on'; + webcamTip = 'Stop video'; + } + else + { + webcamState = 'off'; + webcamTip = 'Start video'; + } + + let screenState; + + let screenTip; + + if (!me.canShareScreen) + { + screenState = 'unsupported'; + screenTip = 'Screen sharing not supported'; + } + else if (screenProducer) + { + screenState = 'on'; + screenTip = 'Stop screen sharing'; + } + else + { + screenState = 'off'; + screenTip = 'Start screen sharing'; + } + const spacingStyle = { 'margin' : spacing }; + const smallScreen = useMediaQuery(theme.breakpoints.down('sm')); + return (
setHover(true)} + onMouseOut={() => setHover(false)} + onTouchStart={() => + { + if (touchTimeout) + clearTimeout(touchTimeout); + + setHover(true); + }} + onTouchEnd={() => + { + if (touchTimeout) + clearTimeout(touchTimeout); + + touchTimeout = setTimeout(() => + { + setHover(false); + }, 2000); + }} style={spacingStyle} > -
+
+
setWebcamHover(true)} + onMouseOut={() => setWebcamHover(false)} + onTouchStart={() => + { + if (touchWebcamTimeout) + clearTimeout(touchWebcamTimeout); + + setWebcamHover(true); + }} + onTouchEnd={() => + { + if (touchWebcamTimeout) + clearTimeout(touchWebcamTimeout); + + touchWebcamTimeout = setTimeout(() => + { + setWebcamHover(false); + }, 2000); + }} + > +

ME

+ +
+ + { + if (micState === 'off') + roomClient.enableMic(); + else if (micState === 'on') + roomClient.muteMic(); + else + roomClient.unmuteMic(); + }} + > + { micState === 'on' ? + + : + + } + +
+
+ +
+ + { + webcamState === 'on' ? + roomClient.disableWebcam() : + roomClient.enableWebcam(); + }} + > + { webcamState === 'on' ? + + : + + } + +
+
+ +
+ + { + switch (screenState) + { + case 'on': + { + roomClient.disableScreenSharing(); + break; + } + case 'off': + { + roomClient.enableScreenSharing(); + break; + } + default: + { + break; + } + } + }} + > + { screenState === 'on' || screenState === 'unsupported' ? + + :null + } + { screenState === 'off' ? + + :null + } + +
+
+
+
{ screenProducer ? -
-
+
setHover(true)} + onMouseOut={() => setHover(false)} + onTouchStart={() => + { + if (touchTimeout) + clearTimeout(touchTimeout); + + setHover(true); + }} + onTouchEnd={() => + { + if (touchTimeout) + clearTimeout(touchTimeout); + + touchTimeout = setTimeout(() => + { + setHover(false); + }, 2000); + }} + style={spacingStyle} + > +
+
setScreenHover(true)} + onMouseOut={() => setScreenHover(false)} + onTouchStart={() => + { + if (touchScreenTimeout) + clearTimeout(touchScreenTimeout); + + setScreenHover(true); + }} + onTouchEnd={() => + { + + if (touchScreenTimeout) + clearTimeout(touchScreenTimeout); + + touchScreenTimeout = setTimeout(() => + { + setScreenHover(false); + }, 2000); + }} + > +

ME

+
+ @@ -164,4 +472,4 @@ export default withRoomContext(connect( ); } } -)(withStyles(styles)(Me))); +)(withStyles(styles, { withTheme: true })(Me))); diff --git a/app/src/components/Containers/Peer.js b/app/src/components/Containers/Peer.js index 0e4875d..2d3a338 100644 --- a/app/src/components/Containers/Peer.js +++ b/app/src/components/Containers/Peer.js @@ -31,11 +31,11 @@ const styles = (theme) => backgroundRepeat : 'no-repeat', '&.webcam' : { - order : 2 + order : 4 }, '&.screen' : { - order : 1 + order : 3 }, '&.hover' : { @@ -52,17 +52,9 @@ const styles = (theme) => }, viewContainer : { - position : 'relative', - width : '100%', - height : '100%', - '&.webcam' : - { - order : 2 - }, - '&.screen' : - { - order : 1 - } + position : 'relative', + width : '100%', + height : '100%' }, controls : { diff --git a/app/src/components/Controls/Sidebar.js b/app/src/components/Controls/Sidebar.js deleted file mode 100644 index 9ef0583..0000000 --- a/app/src/components/Controls/Sidebar.js +++ /dev/null @@ -1,331 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { meProducersSelector } from '../Selectors'; -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 Tooltip from '@material-ui/core/Tooltip'; -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 LockIcon from '@material-ui/icons/Lock'; -import LockOpenIcon from '@material-ui/icons/LockOpen'; -import LeaveIcon from '@material-ui/icons/Cancel'; - -const styles = (theme) => - ({ - root : - { - position : 'fixed', - zIndex : 500, - display : 'flex', - [theme.breakpoints.up('md')] : - { - top : '50%', - transform : 'translate(0%, -50%)', - 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 - }, - show : - { - opacity : 1, - transition : 'opacity .5s' - }, - hide : - { - opacity : 0, - transition : 'opacity .5s' - } - }); - -const Sidebar = (props) => -{ - const { - roomClient, - toolbarsVisible, - me, - micProducer, - webcamProducer, - screenProducer, - locked, - classes, - theme - } = props; - - let micState; - - let micTip; - - if (!me.canSendMic) - { - micState = 'unsupported'; - micTip = 'Audio unsupported'; - } - else if (!micProducer) - { - micState = 'off'; - micTip = 'Activate audio'; - } - else if (!micProducer.locallyPaused && !micProducer.remotelyPaused) - { - micState = 'on'; - micTip = 'Mute audio'; - } - else - { - micState = 'muted'; - micTip = 'Unmute audio'; - } - - let webcamState; - - let webcamTip; - - if (!me.canSendWebcam) - { - webcamState = 'unsupported'; - webcamTip = 'Video unsupported'; - } - else if (webcamProducer) - { - webcamState = 'on'; - webcamTip = 'Stop video'; - } - else - { - webcamState = 'off'; - webcamTip = 'Start video'; - } - - let screenState; - - let screenTip; - - if (!me.canShareScreen) - { - screenState = 'unsupported'; - screenTip = 'Screen sharing not supported'; - } - else if (screenProducer) - { - screenState = 'on'; - screenTip = 'Stop screen sharing'; - } - else - { - screenState = 'off'; - screenTip = 'Start screen sharing'; - } - - const smallScreen = useMediaQuery(theme.breakpoints.down('sm')); - - return ( -
- -
- - { - if (micState === 'off') - roomClient.enableMic(); - else if (micState === 'on') - roomClient.muteMic(); - else - roomClient.unmuteMic(); - }} - > - { micState === 'on' ? - - : - - } - -
-
- -
- - { - webcamState === 'on' ? - roomClient.disableWebcam() : - roomClient.enableWebcam(); - }} - > - { webcamState === 'on' ? - - : - - } - -
-
- -
- - { - switch (screenState) - { - case 'on': - { - roomClient.disableScreenSharing(); - break; - } - case 'off': - { - roomClient.enableScreenSharing(); - break; - } - default: - { - break; - } - } - }} - > - { screenState === 'on' || screenState === 'unsupported' ? - - :null - } - { screenState === 'off' ? - - :null - } - -
-
- - - - { - if (locked) - { - roomClient.unlockRoom(); - } - else - { - roomClient.lockRoom(); - } - }} - > - { locked ? - - : - - } - - - - { /* roomClient.sendRaiseHandState(!me.raiseHand)} - > - - */ } - - - roomClient.close()} - > - - - -
- ); -}; - -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, - theme : PropTypes.object.isRequired -}; - -const mapStateToProps = (state) => - ({ - toolbarsVisible : state.room.toolbarsVisible, - ...meProducersSelector(state), - me : state.me, - locked : state.room.locked - }); - -export default withRoomContext(connect( - mapStateToProps, - null, - null, - { - areStatesEqual : (next, prev) => - { - return ( - prev.room.toolbarsVisible === next.room.toolbarsVisible && - prev.room.locked === next.room.locked && - prev.producers === next.producers && - prev.me === next.me - ); - } - } -)(withStyles(styles, { withTheme: true })(Sidebar))); diff --git a/app/src/components/Room.js b/app/src/components/Room.js index afcc75d..c71897e 100644 --- a/app/src/components/Room.js +++ b/app/src/components/Room.js @@ -27,10 +27,12 @@ import Filmstrip from './MeetingViews/Filmstrip'; import AudioPeers from './PeerAudio/AudioPeers'; import FullScreenView from './VideoContainers/FullScreenView'; import VideoWindow from './VideoWindow/VideoWindow'; -import Sidebar from './Controls/Sidebar'; import FullScreenIcon from '@material-ui/icons/Fullscreen'; import FullScreenExitIcon from '@material-ui/icons/FullscreenExit'; import SettingsIcon from '@material-ui/icons/Settings'; +import LockIcon from '@material-ui/icons/Lock'; +import LockOpenIcon from '@material-ui/icons/LockOpen'; +import LeaveIcon from '@material-ui/icons/Cancel'; import Settings from './Settings/Settings'; import JoinDialog from './JoinDialog'; @@ -306,6 +308,34 @@ class Room extends React.PureComponent
+ roomClient.close()} + > + + + + { + if (room.locked) + { + roomClient.unlockRoom(); + } + else + { + roomClient.lockRoom(); + } + }} + > + { room.locked ? + + : + + } + { this.fullscreen.fullscreenEnabled ? - -
);