First working version of lobby.

master
Håvar Aambø Fosstveit 2019-10-16 14:09:29 +02:00
parent 66a2becf63
commit 513f0efa0b
9 changed files with 207 additions and 118 deletions

View File

@ -934,6 +934,26 @@ export default class RoomClient
stateActions.setSelectedPeer(peerId));
}
async promoteLobbyPeer(peerId)
{
logger.debug('promoteLobbyPeer() [peerId:"%s"]', peerId);
store.dispatch(
stateActions.setLobbyPeerPromotionInProgress(peerId, true));
try
{
await this.sendRequest('promotePeer', { peerId });
}
catch (error)
{
logger.error('promoteLobbyPeer() failed: %o', error);
}
store.dispatch(
stateActions.setLobbyPeerPromotionInProgress(peerId, false));
}
// type: mic/webcam/screen
// mute: true/false
async modifyPeerConsumer(peerId, type, mute)
@ -1243,6 +1263,14 @@ export default class RoomClient
switch (notification.method)
{
case 'enteredLobby':
{
const { displayName } = store.getState().settings;
await this.sendRequest('changeDisplayName', { displayName });
break;
}
case 'roomReady':
{
await this._joinRoom({ joinVideo });
@ -1298,6 +1326,21 @@ export default class RoomClient
break;
}
case 'lobbyPeerClosed':
{
const { peerId } = notification.data;
store.dispatch(
stateActions.removeLobbyPeer(peerId));
store.dispatch(requestActions.notify(
{
text : 'Participant in lobby left.'
}));
break;
}
case 'promotedPeer':
{
const { peerId } = notification.data;
@ -1308,7 +1351,7 @@ export default class RoomClient
break;
}
case 'parkedPeerDisplayName':
case 'lobbyPeerDisplayNameChanged':
{
const { peerId, displayName } = notification.data;

View File

@ -415,6 +415,14 @@ export const setLobbyPeerDisplayName = (displayName, peerId) =>
};
};
export const setLobbyPeerPromotionInProgress = (peerId, flag) =>
{
return {
type : 'SET_LOBBY_PEER_PROMOTION_IN_PROGRESS',
payload : { peerId, flag }
};
};
export const addNotification = (notification) =>
{
return {

View File

@ -1,11 +1,14 @@
import React from 'react';
import { connect } from 'react-redux';
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 Typography from '@material-ui/core/Typography';
import DialogActions from '@material-ui/core/DialogActions';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
const styles = (theme) =>
({
@ -41,6 +44,8 @@ const styles = (theme) =>
const JoinDialog = ({
roomClient,
displayName,
changeDisplayName,
classes
}) =>
{
@ -76,6 +81,19 @@ const JoinDialog = ({
>
Audio and Video
</Button>
<TextField
id='displayname'
label='Name'
className={classes.textField}
value={displayName}
onChange={(event) =>
{
const { value } = event.target;
changeDisplayName(value);
}}
margin='normal'
/>
</DialogActions>
</Dialog>
);
@ -84,7 +102,38 @@ const JoinDialog = ({
JoinDialog.propTypes =
{
roomClient : PropTypes.any.isRequired,
displayName : PropTypes.string.isRequired,
changeDisplayName : PropTypes.func.isRequired,
classes : PropTypes.object.isRequired
};
export default withRoomContext(withStyles(styles)(JoinDialog));
const mapStateToProps = (state) =>
{
return {
displayName : state.settings.displayName
};
};
const mapDispatchToProps = (dispatch) =>
{
return {
changeDisplayName : (displayName) =>
{
dispatch(stateActions.setDisplayName(displayName));
}
};
};
export default withRoomContext(connect(
mapStateToProps,
mapDispatchToProps,
null,
{
areStatesEqual : (next, prev) =>
{
return (
prev.settings.displayName === next.settings.displayName
);
}
}
)(withStyles(styles)(JoinDialog)));

View File

@ -3,10 +3,9 @@ import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core/styles';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import * as appPropTypes from '../../appPropTypes';
import { withRoomContext } from '../../../RoomContext';
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
import HandIcon from '../../../images/icon-hand-white.svg';
import PromoteIcon from '@material-ui/icons/OpenInBrowser';
const styles = (theme) =>
({
@ -18,10 +17,6 @@ const styles = (theme) =>
cursor : 'auto',
display : 'flex'
},
listPeer :
{
display : 'flex'
},
avatar :
{
borderRadius : '50%',
@ -36,47 +31,6 @@ const styles = (theme) =>
flexGrow : 1,
alignItems : 'center'
},
indicators :
{
left : 0,
top : 0,
display : 'flex',
flexDirection : 'row',
justifyContent : 'flex-start',
alignItems : 'center',
transition : 'opacity 0.3s'
},
icon :
{
flex : '0 0 auto',
margin : '0.3rem',
borderRadius : 2,
backgroundPosition : 'center',
backgroundSize : '75%',
backgroundRepeat : 'no-repeat',
backgroundColor : 'rgba(0, 0, 0, 0.5)',
transitionProperty : 'opacity, background-color',
transitionDuration : '0.15s',
width : 'var(--media-control-button-size)',
height : 'var(--media-control-button-size)',
opacity : 0.85,
'&:hover' :
{
opacity : 1
},
'&.on' :
{
opacity : 1
},
'&.off' :
{
opacity : 0.2
},
'&.raise-hand' :
{
backgroundImage : `url(${HandIcon})`
}
},
controls :
{
float : 'right',
@ -101,22 +55,14 @@ const styles = (theme) =>
{
opacity : 1
},
'&.unsupported' :
{
pointerEvents : 'none'
},
'&.disabled' :
{
pointerEvents : 'none',
backgroundColor : 'var(--media-control-botton-disabled)'
},
'&.on' :
'&.promote' :
{
backgroundColor : 'var(--media-control-botton-on)'
},
'&.off' :
{
backgroundColor : 'var(--media-control-botton-off)'
}
}
});
@ -124,7 +70,7 @@ const styles = (theme) =>
const ListLobbyPeer = (props) =>
{
const {
// roomClient,
roomClient,
peer,
classes
} = props;
@ -138,64 +84,19 @@ const ListLobbyPeer = (props) =>
<div className={classes.peerInfo}>
{peer.displayName}
</div>
<div className={classes.indicators}>
{ /* peer.raiseHandState ?
<div className={
classnames(
classes.icon, 'raise-hand', {
on : peer.raiseHandState,
off : !peer.raiseHandState
}
)
}
/>
:null
*/ }
</div>
<div className={classes.controls}>
{/* { screenConsumer ?
<div
className={classnames(classes.button, 'screen', {
on : screenVisible,
off : !screenVisible,
disabled : peer.peerScreenInProgress
className={classnames(classes.button, 'promote', {
disabled : peer.promotionInProgress
})}
onClick={(e) =>
{
e.stopPropagation();
screenVisible ?
roomClient.modifyPeerConsumer(peer.id, 'screen', true) :
roomClient.modifyPeerConsumer(peer.id, 'screen', false);
roomClient.promoteLobbyPeer(peer.id);
}}
>
{ screenVisible ?
<ScreenIcon />
:
<ScreenOffIcon />
}
<PromoteIcon />
</div>
:null
}
<div
className={classnames(classes.button, 'mic', {
on : micEnabled,
off : !micEnabled,
disabled : peer.peerAudioInProgress
})}
onClick={(e) =>
{
e.stopPropagation();
micEnabled ?
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
}}
>
{ micEnabled ?
<MicIcon />
:
<MicOffIcon />
}
</div> */}
</div>
</div>
);
@ -205,7 +106,7 @@ ListLobbyPeer.propTypes =
{
roomClient : PropTypes.any.isRequired,
advancedMode : PropTypes.bool,
peer : appPropTypes.Peer.isRequired,
peer : PropTypes.object.isRequired,
classes : PropTypes.object.isRequired
};

View File

@ -92,9 +92,9 @@ class ParticipantList extends React.PureComponent
</ul>
<br />
{ lobbyPeers ?
{ lobbyPeers.length > 0 ?
<ul className={classes.list}>
<li className={classes.listheader}>Participants in Spotlight:</li>
<li className={classes.listheader}>Participants in Lobby:</li>
{ lobbyPeers.map((peerId) => (
<li
key={peerId}
@ -107,6 +107,7 @@ class ParticipantList extends React.PureComponent
:null
}
<br />
<ul className={classes.list}>
<li className={classes.listheader}>Participants in Spotlight:</li>
{ spotlightPeers.map((peer) => (

View File

@ -3,11 +3,14 @@ const lobbyPeer = (state = {}, action) =>
switch (action.type)
{
case 'ADD_LOBBY_PEER':
return { peerId: action.payload.peerId };
return { id: action.payload.peerId };
case 'SET_LOBBY_PEER_DISPLAY_NAME':
return { ...state, displayName: action.payload.displayName };
case 'SET_LOBBY_PEER_PROMOTION_IN_PROGRESS':
return { ...state, promotionInProgress: action.payload.flag };
default:
return state;
}
@ -33,12 +36,14 @@ const lobbyPeers = (state = {}, action) =>
}
case 'SET_LOBBY_PEER_DISPLAY_NAME':
case 'SET_LOBBY_PEER_PROMOTION_IN_PROGRESS':
{
const oldLobbyPeer = state[action.payload.peerId];
if (!oldLobbyPeer)
{
throw new Error('no Peer found');
// Tried to update non-existant lobbyPeer. Has probably been promoted, or left.
return state;
}
return { ...state, [oldLobbyPeer.id]: lobbyPeer(oldLobbyPeer, action) };

View File

@ -3,6 +3,7 @@ import room from './room';
import me from './me';
import producers from './producers';
import peers from './peers';
import lobbyPeers from './lobbyPeers';
import consumers from './consumers';
import peerVolumes from './peerVolumes';
import notifications from './notifications';
@ -16,6 +17,7 @@ export default combineReducers({
me,
producers,
peers,
lobbyPeers,
consumers,
peerVolumes,
notifications,

View File

@ -13,6 +13,9 @@ class Lobby extends EventEmitter
super();
// Closed flag.
this._closed = false;
this._peers = new Map();
}
@ -20,6 +23,8 @@ class Lobby extends EventEmitter
{
logger.info('close()');
this._closed = true;
// Close the peers
if (this._peers)
{
@ -60,9 +65,9 @@ class Lobby extends EventEmitter
const peer = this._peers.get(peerId);
this._peers.delete(peerId);
this.emit('promotePeer', peer);
this._peers.delete(peerId);
}
parkPeer({ peerId, consume, socket })
@ -74,6 +79,52 @@ class Lobby extends EventEmitter
socket.emit('notification', { method: 'enteredLobby', data: {} });
this._peers.set(peerId, peer);
socket.on('request', (request, cb) =>
{
logger.debug(
'Peer "request" event [method:%s, peerId:%s]',
request.method, peer.peerId);
this._handleSocketRequest(peer, request, cb)
.catch((error) =>
{
logger.error('request failed:%o', error);
cb(error);
});
});
socket.on('disconnect', () =>
{
if (this._closed)
return;
logger.debug('Peer "close" event [peerId:%s]', peer.peerId);
this.emit('peerClosed', peer);
this._peers.delete(peer.peerId);
});
}
async _handleSocketRequest(peer, request, cb)
{
switch (request.method)
{
case 'changeDisplayName':
{
const { displayName } = request.data;
peer.displayName = displayName;
this.emit('lobbyPeerDisplayNameChanged', peer);
cb();
break;
}
}
}
}

View File

@ -61,7 +61,36 @@ class Room extends EventEmitter
{
logger.info('promotePeer() [peer:"%o"]', peer);
const { peerId } = peer;
this._peerJoining({ ...peer });
this._peers.forEach((peer) =>
{
this._notification(peer.socket, 'promotedPeer', { peerId });
});
});
this._lobby.on('lobbyPeerDisplayNameChanged', (peer) =>
{
const { peerId, displayName } = peer;
this._peers.forEach((peer) =>
{
this._notification(peer.socket, 'lobbyPeerDisplayNameChanged', { peerId, displayName });
});
});
this._lobby.on('peerClosed', (peer) =>
{
logger.info('peerClosed() [peer:"%o"]', peer);
const { peerId } = peer;
this._peers.forEach((peer) =>
{
this._notification(peer.socket, 'lobbyPeerClosed', { peerId });
});
});
this._chatHistory = [];