Move audio/video controls out to bottom of screen if on mobile

auto_join_3.3
Håvar Aambø Fosstveit 2020-03-23 15:30:43 +01:00
parent ec8c347a23
commit 33ef7746a3
7 changed files with 299 additions and 96 deletions

View File

@ -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));

View File

@ -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',

View File

@ -299,102 +299,106 @@ const Me = (props) =>
defaultMessage='ME'
/>
</p>
<Tooltip title={micTip} placement={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'device.muteAudio',
defaultMessage : 'Mute audio'
})}
className={classes.fab}
disabled={!me.canSendMic || me.audioInProgress}
color={micState === 'on' ? 'default' : 'secondary'}
size={smallButtons ? 'small' : '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={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'device.startVideo',
defaultMessage : 'Start video'
})}
className={classes.fab}
disabled={!me.canSendWebcam || me.webcamInProgress}
color={webcamState === 'on' ? 'default' : 'secondary'}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
webcamState === 'on' ?
roomClient.disableWebcam() :
roomClient.enableWebcam();
}}
>
{ webcamState === 'on' ?
<VideoIcon />
:
<VideoOffIcon />
}
</Fab>
</div>
</Tooltip>
<Tooltip title={screenTip} placement={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'device.startScreenSharing',
defaultMessage : 'Start screen sharing'
})}
className={classes.fab}
disabled={!me.canShareScreen || me.screenShareInProgress}
color={screenState === 'on' ? 'primary' : 'default'}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
switch (screenState)
{
case 'on':
{ !me.isMobile &&
<React.Fragment>
<Tooltip title={micTip} placement={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'device.muteAudio',
defaultMessage : 'Mute audio'
})}
className={classes.fab}
disabled={!me.canSendMic || me.audioInProgress}
color={micState === 'on' ? 'default' : 'secondary'}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
roomClient.disableScreenSharing();
break;
if (micState === 'off')
roomClient.enableMic();
else if (micState === 'on')
roomClient.muteMic();
else
roomClient.unmuteMic();
}}
>
{ micState === 'on' ?
<MicIcon />
:
<MicOffIcon />
}
case 'off':
</Fab>
</div>
</Tooltip>
<Tooltip title={webcamTip} placement={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'device.startVideo',
defaultMessage : 'Start video'
})}
className={classes.fab}
disabled={!me.canSendWebcam || me.webcamInProgress}
color={webcamState === 'on' ? 'default' : 'secondary'}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
roomClient.enableScreenSharing();
break;
webcamState === 'on' ?
roomClient.disableWebcam() :
roomClient.enableWebcam();
}}
>
{ webcamState === 'on' ?
<VideoIcon />
:
<VideoOffIcon />
}
default:
</Fab>
</div>
</Tooltip>
<Tooltip title={screenTip} placement={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'device.startScreenSharing',
defaultMessage : 'Start screen sharing'
})}
className={classes.fab}
disabled={!me.canShareScreen || me.screenShareInProgress}
color={screenState === 'on' ? 'primary' : 'default'}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
break;
switch (screenState)
{
case 'on':
{
roomClient.disableScreenSharing();
break;
}
case 'off':
{
roomClient.enableScreenSharing();
break;
}
default:
{
break;
}
}
}}
>
{ (screenState === 'on' || screenState === 'unsupported') &&
<ScreenOffIcon/>
}
}
}}
>
{ (screenState === 'on' || screenState === 'unsupported') &&
<ScreenOffIcon/>
}
{ screenState === 'off' &&
<ScreenIcon/>
}
</Fab>
</div>
</Tooltip>
{ screenState === 'off' &&
<ScreenIcon/>
}
</Fab>
</div>
</Tooltip>
</React.Fragment>
}
</div>
<VideoView

View File

@ -121,6 +121,7 @@ const Peer = (props) =>
advancedMode,
peer,
activeSpeaker,
isMobile,
micConsumer,
webcamConsumer,
screenConsumer,
@ -259,7 +260,7 @@ const Peer = (props) =>
</div>
</Tooltip>
{ !smallScreen &&
{ !isMobile &&
<Tooltip
title={intl.formatMessage({
id : 'label.newWindow',
@ -407,7 +408,7 @@ const Peer = (props) =>
}, 2000);
}}
>
{ !smallScreen &&
{ !isMobile &&
<Tooltip
title={intl.formatMessage({
id : 'label.newWindow',
@ -507,6 +508,7 @@ Peer.propTypes =
screenConsumer : appPropTypes.Consumer,
windowConsumer : PropTypes.string,
activeSpeaker : PropTypes.bool,
isMobile : PropTypes.bool,
spacing : PropTypes.number,
style : PropTypes.object,
smallButtons : PropTypes.bool,
@ -526,7 +528,8 @@ const makeMapStateToProps = (initialState, { id }) =>
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
);
}
}

View File

@ -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)));

View File

@ -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
<View advancedMode={advancedMode} />
{ isMobile &&
<MobileControls />
}
{ room.lockDialogOpen &&
<LockDialog />
}
@ -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
);
}
}

View File

@ -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;