Move audio/video controls out to bottom of screen if on mobile
parent
ec8c347a23
commit
33ef7746a3
|
|
@ -200,7 +200,10 @@ export default class RoomClient
|
||||||
if (device.bowser.getPlatformType() === 'desktop')
|
if (device.bowser.getPlatformType() === 'desktop')
|
||||||
this._maxSpotlights = lastN;
|
this._maxSpotlights = lastN;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
this._maxSpotlights = mobileLastN;
|
this._maxSpotlights = mobileLastN;
|
||||||
|
store.dispatch(meActions.setIsMobile());
|
||||||
|
}
|
||||||
|
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
settingsActions.setLastN(this._maxSpotlights));
|
settingsActions.setLastN(this._maxSpotlights));
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,11 @@ export const setMe = ({ peerId, loginEnabled }) =>
|
||||||
payload : { peerId, loginEnabled }
|
payload : { peerId, loginEnabled }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setIsMobile = () =>
|
||||||
|
({
|
||||||
|
type : 'SET_IS_MOBILE'
|
||||||
|
});
|
||||||
|
|
||||||
export const loggedIn = (flag) =>
|
export const loggedIn = (flag) =>
|
||||||
({
|
({
|
||||||
type : 'LOGGED_IN',
|
type : 'LOGGED_IN',
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,8 @@ const Me = (props) =>
|
||||||
defaultMessage='ME'
|
defaultMessage='ME'
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
|
{ !me.isMobile &&
|
||||||
|
<React.Fragment>
|
||||||
<Tooltip title={micTip} placement={smallScreen ? 'top' : 'left'}>
|
<Tooltip title={micTip} placement={smallScreen ? 'top' : 'left'}>
|
||||||
<div>
|
<div>
|
||||||
<Fab
|
<Fab
|
||||||
|
|
@ -395,6 +397,8 @@ const Me = (props) =>
|
||||||
</Fab>
|
</Fab>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<VideoView
|
<VideoView
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ const Peer = (props) =>
|
||||||
advancedMode,
|
advancedMode,
|
||||||
peer,
|
peer,
|
||||||
activeSpeaker,
|
activeSpeaker,
|
||||||
|
isMobile,
|
||||||
micConsumer,
|
micConsumer,
|
||||||
webcamConsumer,
|
webcamConsumer,
|
||||||
screenConsumer,
|
screenConsumer,
|
||||||
|
|
@ -259,7 +260,7 @@ const Peer = (props) =>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
{ !smallScreen &&
|
{ !isMobile &&
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={intl.formatMessage({
|
title={intl.formatMessage({
|
||||||
id : 'label.newWindow',
|
id : 'label.newWindow',
|
||||||
|
|
@ -407,7 +408,7 @@ const Peer = (props) =>
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{ !smallScreen &&
|
{ !isMobile &&
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={intl.formatMessage({
|
title={intl.formatMessage({
|
||||||
id : 'label.newWindow',
|
id : 'label.newWindow',
|
||||||
|
|
@ -507,6 +508,7 @@ Peer.propTypes =
|
||||||
screenConsumer : appPropTypes.Consumer,
|
screenConsumer : appPropTypes.Consumer,
|
||||||
windowConsumer : PropTypes.string,
|
windowConsumer : PropTypes.string,
|
||||||
activeSpeaker : PropTypes.bool,
|
activeSpeaker : PropTypes.bool,
|
||||||
|
isMobile : PropTypes.bool,
|
||||||
spacing : PropTypes.number,
|
spacing : PropTypes.number,
|
||||||
style : PropTypes.object,
|
style : PropTypes.object,
|
||||||
smallButtons : PropTypes.bool,
|
smallButtons : PropTypes.bool,
|
||||||
|
|
@ -526,7 +528,8 @@ const makeMapStateToProps = (initialState, { id }) =>
|
||||||
peer : state.peers[id],
|
peer : state.peers[id],
|
||||||
...getPeerConsumers(state, id),
|
...getPeerConsumers(state, id),
|
||||||
windowConsumer : state.room.windowConsumer,
|
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.peers === next.peers &&
|
||||||
prev.consumers === next.consumers &&
|
prev.consumers === next.consumers &&
|
||||||
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
|
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
|
||||||
prev.room.windowConsumer === next.room.windowConsumer
|
prev.room.windowConsumer === next.room.windowConsumer &&
|
||||||
|
prev.me.isMobile === next.me.isMobile
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Tooltip title={micTip} placement='top'>
|
||||||
|
<div>
|
||||||
|
<Fab
|
||||||
|
aria-label='Mute mic'
|
||||||
|
className={classes.fab}
|
||||||
|
disabled={!me.canSendMic || me.audioInProgress}
|
||||||
|
color={micState === 'on' ? 'default' : 'secondary'}
|
||||||
|
size='large'
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
if (micState === 'off')
|
||||||
|
roomClient.enableMic();
|
||||||
|
else if (micState === 'on')
|
||||||
|
roomClient.muteMic();
|
||||||
|
else
|
||||||
|
roomClient.unmuteMic();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ micState === 'on' ?
|
||||||
|
<MicIcon />
|
||||||
|
:
|
||||||
|
<MicOffIcon />
|
||||||
|
}
|
||||||
|
</Fab>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title={webcamTip} placement='top'>
|
||||||
|
<div>
|
||||||
|
<Fab
|
||||||
|
aria-label='Mute video'
|
||||||
|
className={classes.fab}
|
||||||
|
disabled={!me.canSendWebcam || me.webcamInProgress}
|
||||||
|
color={webcamState === 'on' ? 'default' : 'secondary'}
|
||||||
|
size='large'
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
webcamState === 'on' ?
|
||||||
|
roomClient.disableWebcam() :
|
||||||
|
roomClient.enableWebcam();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ webcamState === 'on' ?
|
||||||
|
<VideoIcon />
|
||||||
|
:
|
||||||
|
<VideoOffIcon />
|
||||||
|
}
|
||||||
|
</Fab>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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)));
|
||||||
|
|
@ -23,6 +23,7 @@ import VideoWindow from './VideoWindow/VideoWindow';
|
||||||
import LockDialog from './AccessControl/LockDialog/LockDialog';
|
import LockDialog from './AccessControl/LockDialog/LockDialog';
|
||||||
import Settings from './Settings/Settings';
|
import Settings from './Settings/Settings';
|
||||||
import TopBar from './Controls/TopBar';
|
import TopBar from './Controls/TopBar';
|
||||||
|
import MobileControls from './Controls/MobileControls';
|
||||||
|
|
||||||
const TIMEOUT = 5 * 1000;
|
const TIMEOUT = 5 * 1000;
|
||||||
|
|
||||||
|
|
@ -140,6 +141,7 @@ class Room extends React.PureComponent
|
||||||
room,
|
room,
|
||||||
advancedMode,
|
advancedMode,
|
||||||
toolAreaOpen,
|
toolAreaOpen,
|
||||||
|
isMobile,
|
||||||
toggleToolArea,
|
toggleToolArea,
|
||||||
classes,
|
classes,
|
||||||
theme
|
theme
|
||||||
|
|
@ -204,6 +206,10 @@ class Room extends React.PureComponent
|
||||||
|
|
||||||
<View advancedMode={advancedMode} />
|
<View advancedMode={advancedMode} />
|
||||||
|
|
||||||
|
{ isMobile &&
|
||||||
|
<MobileControls />
|
||||||
|
}
|
||||||
|
|
||||||
{ room.lockDialogOpen &&
|
{ room.lockDialogOpen &&
|
||||||
<LockDialog />
|
<LockDialog />
|
||||||
}
|
}
|
||||||
|
|
@ -222,6 +228,7 @@ Room.propTypes =
|
||||||
advancedMode : PropTypes.bool.isRequired,
|
advancedMode : PropTypes.bool.isRequired,
|
||||||
toolAreaOpen : PropTypes.bool.isRequired,
|
toolAreaOpen : PropTypes.bool.isRequired,
|
||||||
setToolbarsVisible : PropTypes.func.isRequired,
|
setToolbarsVisible : PropTypes.func.isRequired,
|
||||||
|
isMobile : PropTypes.bool,
|
||||||
toggleToolArea : PropTypes.func.isRequired,
|
toggleToolArea : PropTypes.func.isRequired,
|
||||||
classes : PropTypes.object.isRequired,
|
classes : PropTypes.object.isRequired,
|
||||||
theme : PropTypes.object.isRequired
|
theme : PropTypes.object.isRequired
|
||||||
|
|
@ -231,7 +238,8 @@ const mapStateToProps = (state) =>
|
||||||
({
|
({
|
||||||
room : state.room,
|
room : state.room,
|
||||||
advancedMode : state.settings.advancedMode,
|
advancedMode : state.settings.advancedMode,
|
||||||
toolAreaOpen : state.toolarea.toolAreaOpen
|
toolAreaOpen : state.toolarea.toolAreaOpen,
|
||||||
|
isMobile : state.me.isMobile
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) =>
|
const mapDispatchToProps = (dispatch) =>
|
||||||
|
|
@ -256,7 +264,8 @@ export default connect(
|
||||||
return (
|
return (
|
||||||
prev.room === next.room &&
|
prev.room === next.room &&
|
||||||
prev.settings.advancedMode === next.settings.advancedMode &&
|
prev.settings.advancedMode === next.settings.advancedMode &&
|
||||||
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen
|
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen &&
|
||||||
|
prev.me.isMobile === next.me.isMobile
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ const initialState =
|
||||||
{
|
{
|
||||||
id : null,
|
id : null,
|
||||||
picture : null,
|
picture : null,
|
||||||
|
isMobile : false,
|
||||||
canSendMic : false,
|
canSendMic : false,
|
||||||
canSendWebcam : false,
|
canSendWebcam : false,
|
||||||
canShareScreen : false,
|
canShareScreen : false,
|
||||||
|
|
@ -36,6 +37,11 @@ const me = (state = initialState, action) =>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'SET_IS_MOBILE':
|
||||||
|
{
|
||||||
|
return { ...state, isMobile: true };
|
||||||
|
}
|
||||||
|
|
||||||
case 'LOGGED_IN':
|
case 'LOGGED_IN':
|
||||||
{
|
{
|
||||||
const { flag } = action.payload;
|
const { flag } = action.payload;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue