From 33ef7746a32ab6a6f4b3661d4ab6f8feaad426fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Mon, 23 Mar 2020 15:30:43 +0100 Subject: [PATCH] Move audio/video controls out to bottom of screen if on mobile --- app/src/RoomClient.js | 3 + app/src/actions/meActions.js | 5 + app/src/components/Containers/Me.js | 184 +++++++++--------- app/src/components/Containers/Peer.js | 12 +- app/src/components/Controls/MobileControls.js | 172 ++++++++++++++++ app/src/components/Room.js | 13 +- app/src/reducers/me.js | 6 + 7 files changed, 299 insertions(+), 96 deletions(-) create mode 100644 app/src/components/Controls/MobileControls.js diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 9b63866..6baff6d 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -200,7 +200,10 @@ export default class RoomClient if (device.bowser.getPlatformType() === 'desktop') this._maxSpotlights = lastN; else + { this._maxSpotlights = mobileLastN; + store.dispatch(meActions.setIsMobile()); + } store.dispatch( settingsActions.setLastN(this._maxSpotlights)); diff --git a/app/src/actions/meActions.js b/app/src/actions/meActions.js index b704880..0162ee8 100644 --- a/app/src/actions/meActions.js +++ b/app/src/actions/meActions.js @@ -4,6 +4,11 @@ export const setMe = ({ peerId, loginEnabled }) => payload : { peerId, loginEnabled } }); +export const setIsMobile = () => + ({ + type : 'SET_IS_MOBILE' + }); + export const loggedIn = (flag) => ({ type : 'LOGGED_IN', diff --git a/app/src/components/Containers/Me.js b/app/src/components/Containers/Me.js index 9a2d21e..3107b27 100644 --- a/app/src/components/Containers/Me.js +++ b/app/src/components/Containers/Me.js @@ -299,102 +299,106 @@ const Me = (props) => defaultMessage='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': + { !me.isMobile && + + +
+ { - roomClient.disableScreenSharing(); - break; + if (micState === 'off') + roomClient.enableMic(); + else if (micState === 'on') + roomClient.muteMic(); + else + roomClient.unmuteMic(); + }} + > + { micState === 'on' ? + + : + } - case 'off': + +
+
+ +
+ { - roomClient.enableScreenSharing(); - break; + webcamState === 'on' ? + roomClient.disableWebcam() : + roomClient.enableWebcam(); + }} + > + { webcamState === 'on' ? + + : + } - default: + +
+
+ +
+ { - break; + switch (screenState) + { + case 'on': + { + roomClient.disableScreenSharing(); + break; + } + case 'off': + { + roomClient.enableScreenSharing(); + break; + } + default: + { + break; + } + } + }} + > + { (screenState === 'on' || screenState === 'unsupported') && + } - } - }} - > - { (screenState === 'on' || screenState === 'unsupported') && - - } - { screenState === 'off' && - - } - -
-
+ { screenState === 'off' && + + } +
+
+
+ + } advancedMode, peer, activeSpeaker, + isMobile, micConsumer, webcamConsumer, screenConsumer, @@ -259,7 +260,7 @@ const Peer = (props) => - { !smallScreen && + { !isMobile && }, 2000); }} > - { !smallScreen && + { !isMobile && peer : state.peers[id], ...getPeerConsumers(state, id), windowConsumer : state.room.windowConsumer, - activeSpeaker : id === state.room.activeSpeakerId + activeSpeaker : id === state.room.activeSpeakerId, + isMobile : state.me.isMobile }; }; @@ -560,7 +563,8 @@ export default withRoomContext(connect( prev.peers === next.peers && prev.consumers === next.consumers && prev.room.activeSpeakerId === next.room.activeSpeakerId && - prev.room.windowConsumer === next.room.windowConsumer + prev.room.windowConsumer === next.room.windowConsumer && + prev.me.isMobile === next.me.isMobile ); } } diff --git a/app/src/components/Controls/MobileControls.js b/app/src/components/Controls/MobileControls.js new file mode 100644 index 0000000..48ab33e --- /dev/null +++ b/app/src/components/Controls/MobileControls.js @@ -0,0 +1,172 @@ +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 * 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'; + +const styles = (theme) => + ({ + root : + { + position : 'fixed', + zIndex : 500, + display : 'flex', + flexDirection : 'row', + bottom : '0.5em', + left : '50%', + transform : 'translate(-50%, -0%)' + }, + fab : + { + margin : theme.spacing(1) + } + }); + +const MobileControls = (props) => +{ + const { + roomClient, + me, + micProducer, + webcamProducer, + classes + } = 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'; + } + + 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' ? + + : + + } + +
+
+
+ ); +}; + +MobileControls.propTypes = +{ + roomClient : PropTypes.any.isRequired, + me : appPropTypes.Me.isRequired, + micProducer : appPropTypes.Producer, + webcamProducer : appPropTypes.Producer, + classes : PropTypes.object.isRequired, + theme : PropTypes.object.isRequired +}; + +const mapStateToProps = (state) => + ({ + ...meProducersSelector(state), + me : state.me + }); + +export default withRoomContext(connect( + mapStateToProps, + null, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.producers === next.producers && + prev.me === next.me + ); + } + } +)(withStyles(styles, { withTheme: true })(MobileControls))); \ No newline at end of file diff --git a/app/src/components/Room.js b/app/src/components/Room.js index 59b0665..1d6a180 100644 --- a/app/src/components/Room.js +++ b/app/src/components/Room.js @@ -23,6 +23,7 @@ import VideoWindow from './VideoWindow/VideoWindow'; import LockDialog from './AccessControl/LockDialog/LockDialog'; import Settings from './Settings/Settings'; import TopBar from './Controls/TopBar'; +import MobileControls from './Controls/MobileControls'; const TIMEOUT = 5 * 1000; @@ -140,6 +141,7 @@ class Room extends React.PureComponent room, advancedMode, toolAreaOpen, + isMobile, toggleToolArea, classes, theme @@ -204,6 +206,10 @@ class Room extends React.PureComponent + { isMobile && + + } + { room.lockDialogOpen && } @@ -222,6 +228,7 @@ Room.propTypes = advancedMode : PropTypes.bool.isRequired, toolAreaOpen : PropTypes.bool.isRequired, setToolbarsVisible : PropTypes.func.isRequired, + isMobile : PropTypes.bool, toggleToolArea : PropTypes.func.isRequired, classes : PropTypes.object.isRequired, theme : PropTypes.object.isRequired @@ -231,7 +238,8 @@ const mapStateToProps = (state) => ({ room : state.room, advancedMode : state.settings.advancedMode, - toolAreaOpen : state.toolarea.toolAreaOpen + toolAreaOpen : state.toolarea.toolAreaOpen, + isMobile : state.me.isMobile }); const mapDispatchToProps = (dispatch) => @@ -256,7 +264,8 @@ export default connect( return ( prev.room === next.room && prev.settings.advancedMode === next.settings.advancedMode && - prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen + prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen && + prev.me.isMobile === next.me.isMobile ); } } diff --git a/app/src/reducers/me.js b/app/src/reducers/me.js index e80375a..ca97e32 100644 --- a/app/src/reducers/me.js +++ b/app/src/reducers/me.js @@ -2,6 +2,7 @@ const initialState = { id : null, picture : null, + isMobile : false, canSendMic : false, canSendWebcam : false, canShareScreen : false, @@ -36,6 +37,11 @@ const me = (state = initialState, action) => }; } + case 'SET_IS_MOBILE': + { + return { ...state, isMobile: true }; + } + case 'LOGGED_IN': { const { flag } = action.payload;