lock Dialog

master
Stefan Otto 2019-10-23 11:47:41 +02:00
parent 223642a44f
commit e5ddaa458a
9 changed files with 445 additions and 66 deletions

View File

@ -77,7 +77,7 @@ export default class RoomClient
}
constructor(
{ roomId, peerId, device, useSimulcast, produce, consume, forceTcp })
{ roomId, peerId, accessCode, device, useSimulcast, produce, consume, forceTcp })
{
logger.debug(
'constructor() [roomId: "%s", peerId: "%s", device: "%s", useSimulcast: "%s", produce: "%s", consume: "%s", forceTcp: "%s"]',
@ -112,6 +112,9 @@ export default class RoomClient
// My peer name.
this._peerId = peerId;
// Access code
this._accessCode = accessCode;
// Alert sound
this._soundAlert = new Audio('/sounds/notify.mp3');
@ -609,7 +612,8 @@ export default class RoomClient
fileHistory,
lastN,
locked,
lobbyPeers
lobbyPeers,
accessCode
} = await this.sendRequest('serverHistory');
if (chatHistory.length > 0)
@ -654,6 +658,13 @@ export default class RoomClient
stateActions.setLobbyPeerDisplayName(peer.displayName));
});
}
if (accessCode != null)
{
logger.debug('Got accessCode');
store.dispatch(stateActions.setAccessCode(accessCode))
}
}
catch (error)
{
@ -1385,6 +1396,46 @@ export default class RoomClient
break;
}
case 'setAccessCode':
{
const { accessCode } = notification.data;
store.dispatch(
stateActions.setAccessCode(accessCode));
store.dispatch(requestActions.notify(
{
text : 'Access code for room updated'
}));
break;
}
case 'setJoinByAccessCode':
{
const { joinByAccessCode } = notification.data;
store.dispatch(
stateActions.setJoinByAccessCode(joinByAccessCode));
if (joinByAccessCode)
{
store.dispatch(requestActions.notify(
{
text : 'Access code for room is now activated'
}));
}
else
{
store.dispatch(requestActions.notify(
{
text : 'Access code for room is now deactivated'
}));
}
break;
}
case 'activeSpeaker':
{
const { peerId } = notification.data;
@ -1843,6 +1894,60 @@ export default class RoomClient
}
}
async setAccessCode(code)
{
logger.debug('setAccessCode()');
try
{
await this.sendRequest('setAccessCode', { accessCode: code });
store.dispatch(
stateActions.setAccessCode(code));
store.dispatch(requestActions.notify(
{
text : 'Access code saved.'
}));
}
catch (error)
{
logger.error('setAccessCode() | failed: %o', error);
store.dispatch(requestActions.notify(
{
type : 'error',
text : 'Unable to set access code.'
}));
}
}
async setJoinByAccessCode(value)
{
logger.debug('setJoinByAccessCode()');
try
{
await this.sendRequest('setJoinByAccessCode', { joinByAccessCode: value });
store.dispatch(
stateActions.setJoinByAccessCode(value));
store.dispatch(requestActions.notify(
{
text : `You switched Join by access-code to ${value}`
}));
}
catch (error)
{
logger.error('setAccessCode() | failed: %o', error);
store.dispatch(requestActions.notify(
{
type : 'error',
text : 'Unable to set join by access code.'
}));
}
}
async enableMic()
{
if (this._micProducer)

View File

@ -43,12 +43,35 @@ export const setRoomLockedOut = () =>
};
};
export const setAccessCode = (accessCode) =>
{
return {
type : 'SET_ACCESS_CODE',
payload : { accessCode }
};
};
export const setJoinByAccessCode = (joinByAccessCode) =>
{
return {
type : 'SET_JOIN_BY_ACCESS_CODE',
payload : { joinByAccessCode }
};
};
export const setSettingsOpen = ({ settingsOpen }) =>
({
type : 'SET_SETTINGS_OPEN',
payload : { settingsOpen }
});
export const setLockDialogOpen = ({ lockDialogOpen }) =>
({
type : 'SET_LOCK_DIALOG_OPEN',
payload : { lockDialogOpen }
});
export const setMe = ({ peerId, device, loginEnabled }) =>
{
return {
@ -178,6 +201,13 @@ export const toggleSettings = () =>
};
};
export const toggleLockDialog = () =>
{
return {
type : 'TOGGLE_LOCK_DIALOG'
};
};
export const toggleToolArea = () =>
{
return {

View File

@ -4,6 +4,11 @@ import { withStyles } from '@material-ui/core/styles';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { withRoomContext } from '../../../RoomContext';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import Avatar from '@material-ui/core/Avatar';
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
import PromoteIcon from '@material-ui/icons/OpenInBrowser';
@ -78,27 +83,30 @@ const ListLobbyPeer = (props) =>
const picture = peer.picture || EmptyAvatar;
return (
<div className={classes.root}>
<img alt='Peer avatar' className={classes.avatar} src={picture} />
<div className={classes.peerInfo}>
{peer.displayName}
</div>
<div className={classes.controls}>
<div
className={classnames(classes.button, 'promote', {
disabled : peer.promotionInProgress
})}
onClick={(e) =>
<ListItem
key={peer.peerId}
button
alignItems="flex-start"
>
<ListItemAvatar>
<Avatar alt='Peer avatar' src={picture} />
</ListItemAvatar>
<ListItemText
primary={peer.displayName}
/>
<ListItemIcon
className={classnames(classes.button, 'promote', {
disabled : peer.promotionInProgress
})}
onClick={(e) =>
{
e.stopPropagation();
roomClient.promoteLobbyPeer(peer.id);
}}
>
<PromoteIcon />
</div>
</div>
</div>
>
<PromoteIcon />
</ListItemIcon>
</ListItem>
);
};

View File

@ -0,0 +1,191 @@
import React from 'react';
import { connect } from 'react-redux';
import {
lobbyPeersKeySelector
} from '../../Selectors';
import * as appPropTypes from '../../appPropTypes';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../../RoomContext';
import * as stateActions from '../../../actions/stateActions';
import PropTypes from 'prop-types';
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogActions from '@material-ui/core/DialogActions';
import Button from '@material-ui/core/Button';
import FormLabel from '@material-ui/core/FormLabel';
import FormControl from '@material-ui/core/FormControl';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import InputLabel from '@material-ui/core/InputLabel';
import OutlinedInput from '@material-ui/core/OutlinedInput';
import Switch from '@material-ui/core/Switch';
import List from '@material-ui/core/List';
import ListSubheader from '@material-ui/core/ListSubheader';
import ListLobbyPeer from './ListLobbyPeer';
const styles = (theme) =>
({
root :
{
},
dialogPaper :
{
width : '30vw',
[theme.breakpoints.down('lg')] :
{
width : '40vw'
},
[theme.breakpoints.down('md')] :
{
width : '50vw'
},
[theme.breakpoints.down('sm')] :
{
width : '70vw'
},
[theme.breakpoints.down('xs')] :
{
width : '90vw'
}
},
lock :
{
padding : theme.spacing.unit * 2
}
});
const LockDialog = ({
roomClient,
room,
handleCloseLockDialog,
handleAccessCode,
lobbyPeers,
classes
}) =>
{
return (
<Dialog
className={classes.root}
open={room.lockDialogOpen}
onClose={() => handleCloseLockDialog({ lockDialogOpen: false })}
classes={{
paper : classes.dialogPaper
}}
>
<DialogTitle id='form-dialog-title'>Room access control</DialogTitle>
<form className={classes.lock} autoComplete='off'>
<FormControl component='fieldset' className={classes.formControl}>
<FormLabel component='legend'>Room lock</FormLabel>
<FormGroup>
<FormControlLabel
control={
<Switch checked={room.locked} onChange={() =>
{
if (room.locked)
{
roomClient.unlockRoom();
}
else
{
roomClient.lockRoom();
}
}}
/>}
label='Lock'
/>
{/* TODO: access code
<FormControlLabel disabled={ room.locked ? false : true }
control={
<Checkbox checked={room.joinByAccessCode}
onChange={
(event) => roomClient.setJoinByAccessCode(event.target.checked)
}
/>}
label='Join by Access code'
/>
<InputLabel htmlFor='access-code-input' />
<OutlinedInput
disabled={ room.locked ? false : true }
id='acces-code-input'
label='Access code'
labelWidth={0}
variant='outlined'
value={room.accessCode}
onChange={(event) => handleAccessCode(event.target.value)}
>
</OutlinedInput>
<Button onClick={() => roomClient.setAccessCode(room.accessCode)} color='primary'>
save
</Button>
*/}
</FormGroup>
</FormControl>
{ lobbyPeers.length > 0 ?
<List subheader={
<ListSubheader component="div">
Participants in Lobby
</ListSubheader>
}>
{
lobbyPeers.map((peerId) => { return (<ListLobbyPeer id={peerId} />); })
}
</List>
: null
}
</form>
<DialogActions>
<Button onClick={() => handleCloseLockDialog({ lockDialogOpen: false })} color='primary'>
Close
</Button>
</DialogActions>
</Dialog>
);
};
LockDialog.propTypes =
{
roomClient : PropTypes.any.isRequired,
room : appPropTypes.Room.isRequired,
handleCloseLockDialog : PropTypes.func.isRequired,
handleAccessCode : PropTypes.func.isRequired,
lobbyPeers : PropTypes.array,
classes : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
{
return {
room : state.room,
lobbyPeers : lobbyPeersKeySelector(state)
};
};
const mapDispatchToProps = {
handleCloseLockDialog : stateActions.setLockDialogOpen,
handleAccessCode : stateActions.setAccessCode
};
export default withRoomContext(connect(
mapStateToProps,
mapDispatchToProps,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.room.locked === next.room.locked &&
prev.room.joinByAccessCode === next.room.joinByAccessCode &&
prev.room.accessCode === next.room.accessCode &&
prev.room.code === next.room.code &&
prev.room.lockDialogOpen === next.room.lockDialogOpen &&
prev.room.codeHidden === next.room.codeHidden &&
prev.lobbyPeers === next.lobbyPeers
);
}
}
)(withStyles(styles)(LockDialog)));

View File

@ -2,15 +2,13 @@ import React from 'react';
import { connect } from 'react-redux';
import {
passivePeersSelector,
spotlightPeersSelector,
lobbyPeersKeySelector
spotlightPeersSelector
} from '../../Selectors';
import classNames from 'classnames';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../../RoomContext';
import PropTypes from 'prop-types';
import ListPeer from './ListPeer';
import ListLobbyPeer from './ListLobbyPeer';
import ListMe from './ListMe';
import Volume from '../../Containers/Volume';
@ -80,7 +78,6 @@ class ParticipantList extends React.PureComponent
passivePeers,
selectedPeerId,
spotlightPeers,
lobbyPeers,
classes
} = this.props;
@ -90,20 +87,6 @@ class ParticipantList extends React.PureComponent
<li className={classes.listheader}>Me:</li>
<ListMe />
</ul>
{ lobbyPeers.length > 0 ?
<ul className={classes.list}>
<li className={classes.listheader}>Participants in Lobby:</li>
{ lobbyPeers.map((peerId) => (
<li
key={peerId}
className={classes.listItem}
>
<ListLobbyPeer id={peerId} advancedMode={advancedMode} />
</li>
))}
</ul>
:null
}
<ul className={classes.list}>
<li className={classes.listheader}>Participants in Spotlight:</li>
{ spotlightPeers.map((peer) => (
@ -146,7 +129,6 @@ ParticipantList.propTypes =
passivePeers : PropTypes.array,
selectedPeerId : PropTypes.string,
spotlightPeers : PropTypes.array,
lobbyPeers : PropTypes.array,
classes : PropTypes.object.isRequired
};
@ -155,8 +137,7 @@ const mapStateToProps = (state) =>
return {
passivePeers : passivePeersSelector(state),
selectedPeerId : state.room.selectedPeerId,
spotlightPeers : spotlightPeersSelector(state),
lobbyPeers : lobbyPeersKeySelector(state)
spotlightPeers : spotlightPeersSelector(state)
};
};
@ -170,8 +151,7 @@ const ParticipantListContainer = withRoomContext(connect(
return (
prev.peers === next.peers &&
prev.room.spotlights === next.room.spotlights &&
prev.room.selectedPeerId === next.room.selectedPeerId &&
prev.lobbyPeers === next.lobbyPeers
prev.room.selectedPeerId === next.room.selectedPeerId
);
}
}

View File

@ -35,6 +35,7 @@ import LockOpenIcon from '@material-ui/icons/LockOpen';
import Button from '@material-ui/core/Button';
import Settings from './Settings/Settings';
import JoinDialog from './JoinDialog';
import LockDialog from './AccessControl/LockDialog/LockDialog';
const TIMEOUT = 10 * 1000;
@ -264,6 +265,7 @@ class Room extends React.PureComponent
loggedIn,
loginEnabled,
setSettingsOpen,
setLockDialogOpen,
toolAreaOpen,
toggleToolArea,
unread,
@ -345,28 +347,6 @@ class Room extends React.PureComponent
</Typography>
<div className={classes.grow} />
<div className={classes.actionButtons}>
<IconButton
aria-label='Lock room'
className={classes.actionButton}
color='inherit'
onClick={() =>
{
if (room.locked)
{
roomClient.unlockRoom();
}
else
{
roomClient.lockRoom();
}
}}
>
{ room.locked ?
<LockIcon />
:
<LockOpenIcon />
}
</IconButton>
{ this.fullscreen.fullscreenEnabled ?
<IconButton
aria-label='Fullscreen'
@ -390,6 +370,13 @@ class Room extends React.PureComponent
>
<SettingsIcon />
</IconButton>
<IconButton
aria-label='Lock'
color='inherit'
onClick={() => setLockDialogOpen(!room.lockDialogOpen)}
>
{ room.locked ? <LockIcon /> : <LockOpenIcon /> }
</IconButton>
{ loginEnabled ?
<IconButton
aria-label='Account'
@ -439,6 +426,8 @@ class Room extends React.PureComponent
<View advancedMode={advancedMode} />
<LockDialog />
<Settings />
</div>
);
@ -457,6 +446,7 @@ Room.propTypes =
toolAreaOpen : PropTypes.bool.isRequired,
setToolbarsVisible : PropTypes.func.isRequired,
setSettingsOpen : PropTypes.func.isRequired,
setLockDialogOpen : PropTypes.func.isRequired,
toggleToolArea : PropTypes.func.isRequired,
unread : PropTypes.number.isRequired,
classes : PropTypes.object.isRequired,
@ -485,6 +475,10 @@ const mapDispatchToProps = (dispatch) =>
{
dispatch(stateActions.setSettingsOpen({ settingsOpen }));
},
setLockDialogOpen : (lockDialogOpen) =>
{
dispatch(stateActions.setLockDialogOpen({ lockDialogOpen }));
},
toggleToolArea : () =>
{
dispatch(stateActions.toggleToolArea());

View File

@ -62,6 +62,7 @@ function run()
window.history.pushState('', '', urlParser.toString());
}
const accessCode = parameters.get('code');
const produce = parameters.get('produce') !== 'false';
const consume = parameters.get('consume') !== 'false';
const useSimulcast = parameters.get('simulcast') === 'true';
@ -84,7 +85,7 @@ function run()
);
roomClient = new RoomClient(
{ roomId, peerId, device, useSimulcast, produce, consume, forceTcp });
{ roomId, peerId, accessCode, device, useSimulcast, produce, consume, forceTcp });
global.CLIENT = roomClient;

View File

@ -4,6 +4,8 @@ const initialState =
state : 'new', // new/connecting/connected/disconnected/closed,
locked : false,
lockedOut : false,
accessCode : '', // access code to the room if locked and joinByAccessCode == true
joinByAccessCode : true, // if true: accessCode is a possibility to open the room
activeSpeakerId : null,
torrentSupport : false,
showSettings : false,
@ -14,6 +16,7 @@ const initialState =
selectedPeerId : null,
spotlights : [],
settingsOpen : false,
lockDialogOpen : false,
joined : false
};
@ -53,6 +56,27 @@ const room = (state = initialState, action) =>
return { ...state, lockedOut: true };
}
case 'SET_ACCESS_CODE':
{
const { accessCode } = action.payload;
return { ...state, accessCode };
}
case 'SET_JOIN_BY_ACCESS_CODE':
{
const { joinByAccessCode } = action.payload;
return { ...state, joinByAccessCode };
}
case 'SET_LOCK_DIALOG_OPEN':
{
const { lockDialogOpen } = action.payload;
return { ...state, lockDialogOpen };
}
case 'SET_SETTINGS_OPEN':
{
const { settingsOpen } = action.payload;

View File

@ -55,6 +55,12 @@ class Room extends EventEmitter
// Locked flag.
this._locked = false;
// if true: accessCode is a possibility to open the room
this._joinByAccesCode = true;
// access code to the room, applicable if ( _locked == true and _joinByAccessCode == true )
this._accessCode = '';
this._lobby = new Lobby();
this._lobby.on('promotePeer', (peer) =>
@ -815,7 +821,8 @@ class Room extends EventEmitter
fileHistory : this._fileHistory,
lastN : this._lastN,
locked : this._locked,
lobbyPeers : lobbyPeers
lobbyPeers : lobbyPeers,
accessCode : this._accessCode
}
);
@ -852,6 +859,45 @@ class Room extends EventEmitter
break;
}
case 'setAccessCode':
{
const { accessCode } = request.data;
this._accessCode = accessCode;
// Spread to others
// if (request.public) {
this._notification(peer.socket, 'setAccessCode', {
peerId : peer.id,
accessCode : accessCode
}, true);
//}
// Return no error
cb();
break;
}
case 'setJoinByAccessCode':
{
const { joinByAccessCode } = request.data;
this._joinByAccessCode = joinByAccessCode;
// Spread to others
this._notification(peer.socket, 'setJoinByAccessCode', {
peerId : peer.id,
joinByAccessCode : joinByAccessCode
}, true);
// Return no error
cb();
break;
}
case 'promotePeer':
{
const { peerId } = request.data;