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;