From 2e166ca2b26b48d89896612cc101f38279e64696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Tue, 15 Oct 2019 08:03:18 +0200 Subject: [PATCH 01/74] Created reducer and stateactions for lobby. --- app/src/actions/stateActions.js | 24 +++++++++++++++ app/src/reducers/lobbyPeers.js | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 app/src/reducers/lobbyPeers.js diff --git a/app/src/actions/stateActions.js b/app/src/actions/stateActions.js index fda3ceb..13df082 100644 --- a/app/src/actions/stateActions.js +++ b/app/src/actions/stateActions.js @@ -391,6 +391,30 @@ export const setPeerVolume = (peerId, volume) => }; }; +export const addLobbyPeer = (lobbyPeer) => +{ + return { + type : 'ADD_LOBBY_PEER', + payload : { lobbyPeer } + }; +}; + +export const removeLobbyPeer = (peerId) => +{ + return { + type : 'REMOVE_LOBBY_PEER', + payload : { peerId } + }; +}; + +export const setLobbyPeerDisplayName = (displayName, peerId) => +{ + return { + type : 'SET_LOBBY_PEER_DISPLAY_NAME', + payload : { displayName, peerId } + }; +}; + export const addNotification = (notification) => { return { diff --git a/app/src/reducers/lobbyPeers.js b/app/src/reducers/lobbyPeers.js new file mode 100644 index 0000000..ad538f7 --- /dev/null +++ b/app/src/reducers/lobbyPeers.js @@ -0,0 +1,52 @@ +const lobbyPeer = (state = {}, action) => +{ + switch (action.type) + { + case 'ADD_LOBBY_PEER': + return action.payload.lobbyPeer; + + case 'SET_LOBBY_PEER_DISPLAY_NAME': + return { ...state, displayName: action.payload.displayName }; + + default: + return state; + } +}; + +const lobbyPeers = (state = {}, action) => +{ + switch (action.type) + { + case 'ADD_LOBBY_PEER': + { + return { ...state, [action.payload.lobbyPeer.id]: lobbyPeer(undefined, action) }; + } + + case 'REMOVE_LOBBY_PEER': + { + const { peerId } = action.payload; + const newState = { ...state }; + + delete newState[peerId]; + + return newState; + } + + case 'SET_LOBBY_PEER_DISPLAY_NAME': + { + const oldLobbyPeer = state[action.payload.peerId]; + + if (!oldLobbyPeer) + { + throw new Error('no Peer found'); + } + + return { ...state, [oldLobbyPeer.id]: lobbyPeer(oldLobbyPeer, action) }; + } + + default: + return state; + } +}; + +export default lobbyPeers; From 937f142c6e493a6da092fe9a423208ef5c111f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Tue, 15 Oct 2019 08:03:55 +0200 Subject: [PATCH 02/74] Added lobby to server, still missing some signaling. --- server/lib/Lobby.js | 80 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 server/lib/Lobby.js diff --git a/server/lib/Lobby.js b/server/lib/Lobby.js new file mode 100644 index 0000000..f606a91 --- /dev/null +++ b/server/lib/Lobby.js @@ -0,0 +1,80 @@ +'use strict'; + +const EventEmitter = require('events').EventEmitter; +const Logger = require('./Logger'); + +const logger = new Logger('Lobby'); + +class Lobby extends EventEmitter +{ + constructor() + { + logger.info('constructor()'); + + super(); + + this._peers = new Map(); + } + + close() + { + logger.info('close()'); + + // Close the peers + if (this._peers) + { + this._peers.forEach((peer) => + { + if (peer.socket) + peer.socket.disconnect(); + }); + } + + this._peers.clear(); + } + + peerList() + { + logger.info('peerList()'); + + return this._peers; + } + + promoteAllPeers() + { + logger.info('promoteAllPeers()'); + + if (this._peers) + { + this._peers.forEach((peer) => + { + if (peer.socket) + this.promotePeer(peer.peerId); + }); + } + } + + promotePeer(peerId) + { + logger.info('promotePeer() [peerId: %s]', peerId); + + const peer = this._peers.get(peerId); + + this._peers.delete(peerId); + + this.emit('promotePeer', peer); + } + + parkPeer({ peerId, consume, socket }) + { + logger.info('parkPeer()'); + + const peer = { peerId, socket, consume }; + + socket.emit('notification', { method: 'enteredLobby', data: {} }); + + this._peers.set(peerId, peer); + } +} + +module.exports = Lobby; \ No newline at end of file From 66a2becf63753a8e93ddd64898c365ff2e61d33e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Tue, 15 Oct 2019 10:52:14 +0200 Subject: [PATCH 03/74] Inital work on lobby. --- app/src/RoomClient.js | 40 +++ app/src/actions/stateActions.js | 4 +- .../ParticipantList/ListLobbyPeer.js | 231 ++++++++++++++++++ .../ParticipantList/ParticipantList.js | 28 ++- app/src/components/Selectors.js | 6 + app/src/reducers/lobbyPeers.js | 4 +- server/lib/Room.js | 48 +++- 7 files changed, 352 insertions(+), 9 deletions(-) create mode 100644 app/src/components/MeetingDrawer/ParticipantList/ListLobbyPeer.js diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index aea8f94..0f77197 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -1283,6 +1283,46 @@ export default class RoomClient break; } + case 'parkedPeer': + { + const { peerId } = notification.data; + + store.dispatch( + stateActions.addLobbyPeer(peerId)); + + store.dispatch(requestActions.notify( + { + text : 'New participant entered the lobby.' + })); + + break; + } + + case 'promotedPeer': + { + const { peerId } = notification.data; + + store.dispatch( + stateActions.removeLobbyPeer(peerId)); + + break; + } + + case 'parkedPeerDisplayName': + { + const { peerId, displayName } = notification.data; + + store.dispatch( + stateActions.setLobbyPeerDisplayName(displayName, peerId)); + + store.dispatch(requestActions.notify( + { + text : `Participant in lobby changed name to ${displayName}.` + })); + + break; + } + case 'activeSpeaker': { const { peerId } = notification.data; diff --git a/app/src/actions/stateActions.js b/app/src/actions/stateActions.js index 13df082..7908b99 100644 --- a/app/src/actions/stateActions.js +++ b/app/src/actions/stateActions.js @@ -391,11 +391,11 @@ export const setPeerVolume = (peerId, volume) => }; }; -export const addLobbyPeer = (lobbyPeer) => +export const addLobbyPeer = (peerId) => { return { type : 'ADD_LOBBY_PEER', - payload : { lobbyPeer } + payload : { peerId } }; }; diff --git a/app/src/components/MeetingDrawer/ParticipantList/ListLobbyPeer.js b/app/src/components/MeetingDrawer/ParticipantList/ListLobbyPeer.js new file mode 100644 index 0000000..7bc6178 --- /dev/null +++ b/app/src/components/MeetingDrawer/ParticipantList/ListLobbyPeer.js @@ -0,0 +1,231 @@ +import React from 'react'; +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'; + +const styles = (theme) => + ({ + root : + { + padding : theme.spacing(1), + width : '100%', + overflow : 'hidden', + cursor : 'auto', + display : 'flex' + }, + listPeer : + { + display : 'flex' + }, + avatar : + { + borderRadius : '50%', + height : '2rem' + }, + peerInfo : + { + fontSize : '1rem', + border : 'none', + display : 'flex', + paddingLeft : theme.spacing(1), + 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', + display : 'flex', + flexDirection : 'row', + justifyContent : 'flex-start', + alignItems : 'center' + }, + button : + { + flex : '0 0 auto', + margin : '0.3rem', + borderRadius : 2, + backgroundColor : 'rgba(0, 0, 0, 0.5)', + cursor : 'pointer', + 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 + }, + '&.unsupported' : + { + pointerEvents : 'none' + }, + '&.disabled' : + { + pointerEvents : 'none', + backgroundColor : 'var(--media-control-botton-disabled)' + }, + '&.on' : + { + backgroundColor : 'var(--media-control-botton-on)' + }, + '&.off' : + { + backgroundColor : 'var(--media-control-botton-off)' + } + } + }); + +const ListLobbyPeer = (props) => +{ + const { + // roomClient, + peer, + classes + } = props; + + const picture = peer.picture || EmptyAvatar; + + return ( +
+ Peer avatar + +
+ {peer.displayName} +
+
+ { /* peer.raiseHandState ? +
+ :null + */ } +
+
+ {/* { screenConsumer ? +
+ { + e.stopPropagation(); + screenVisible ? + roomClient.modifyPeerConsumer(peer.id, 'screen', true) : + roomClient.modifyPeerConsumer(peer.id, 'screen', false); + }} + > + { screenVisible ? + + : + + } +
+ :null + } +
+ { + e.stopPropagation(); + micEnabled ? + roomClient.modifyPeerConsumer(peer.id, 'mic', true) : + roomClient.modifyPeerConsumer(peer.id, 'mic', false); + }} + > + { micEnabled ? + + : + + } +
*/} +
+
+ ); +}; + +ListLobbyPeer.propTypes = +{ + roomClient : PropTypes.any.isRequired, + advancedMode : PropTypes.bool, + peer : appPropTypes.Peer.isRequired, + classes : PropTypes.object.isRequired +}; + +const mapStateToProps = (state, { id }) => +{ + return { + peer : state.lobbyPeers[id] + }; +}; + +export default withRoomContext(connect( + mapStateToProps, + null, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.lobbyPeers === next.lobbyPeers + ); + } + } +)(withStyles(styles)(ListLobbyPeer))); \ No newline at end of file diff --git a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js index a4f66ed..f8c98e4 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js @@ -2,13 +2,15 @@ import React from 'react'; import { connect } from 'react-redux'; import { passivePeersSelector, - spotlightPeersSelector + spotlightPeersSelector, + lobbyPeersKeySelector } 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'; @@ -78,6 +80,7 @@ class ParticipantList extends React.PureComponent passivePeers, selectedPeerId, spotlightPeers, + lobbyPeers, classes } = this.props; @@ -88,6 +91,22 @@ class ParticipantList extends React.PureComponent
+ + { lobbyPeers ? +
    +
  • Participants in Spotlight:
  • + { lobbyPeers.map((peerId) => ( +
  • + +
  • + ))} +
+ :null + } +
  • Participants in Spotlight:
  • { spotlightPeers.map((peer) => ( @@ -131,6 +150,7 @@ ParticipantList.propTypes = passivePeers : PropTypes.array, selectedPeerId : PropTypes.string, spotlightPeers : PropTypes.array, + lobbyPeers : PropTypes.array, classes : PropTypes.object.isRequired }; @@ -139,7 +159,8 @@ const mapStateToProps = (state) => return { passivePeers : passivePeersSelector(state), selectedPeerId : state.room.selectedPeerId, - spotlightPeers : spotlightPeersSelector(state) + spotlightPeers : spotlightPeersSelector(state), + lobbyPeers : lobbyPeersKeySelector(state) }; }; @@ -153,7 +174,8 @@ const ParticipantListContainer = withRoomContext(connect( return ( prev.peers === next.peers && prev.room.spotlights === next.room.spotlights && - prev.room.selectedPeerId === next.room.selectedPeerId + prev.room.selectedPeerId === next.room.selectedPeerId && + prev.lobbyPeers === next.lobbyPeers ); } } diff --git a/app/src/components/Selectors.js b/app/src/components/Selectors.js index 95291f4..d3e293d 100644 --- a/app/src/components/Selectors.js +++ b/app/src/components/Selectors.js @@ -4,6 +4,7 @@ const producersSelect = (state) => state.producers; const consumersSelect = (state) => state.consumers; const spotlightsSelector = (state) => state.room.spotlights; const peersSelector = (state) => state.peers; +const lobbyPeersSelector = (state) => state.lobbyPeers; const getPeerConsumers = (state, props) => (state.peers[props.id] ? state.peers[props.id].consumers : null); const getAllConsumers = (state) => state.consumers; @@ -12,6 +13,11 @@ const peersKeySelector = createSelector( (peers) => Object.keys(peers) ); +export const lobbyPeersKeySelector = createSelector( + lobbyPeersSelector, + (lobbyPeers) => Object.keys(lobbyPeers) +); + export const micProducersSelector = createSelector( producersSelect, (producers) => Object.values(producers).filter((producer) => producer.source === 'mic') diff --git a/app/src/reducers/lobbyPeers.js b/app/src/reducers/lobbyPeers.js index ad538f7..391d595 100644 --- a/app/src/reducers/lobbyPeers.js +++ b/app/src/reducers/lobbyPeers.js @@ -3,7 +3,7 @@ const lobbyPeer = (state = {}, action) => switch (action.type) { case 'ADD_LOBBY_PEER': - return action.payload.lobbyPeer; + return { peerId: action.payload.peerId }; case 'SET_LOBBY_PEER_DISPLAY_NAME': return { ...state, displayName: action.payload.displayName }; @@ -19,7 +19,7 @@ const lobbyPeers = (state = {}, action) => { case 'ADD_LOBBY_PEER': { - return { ...state, [action.payload.lobbyPeer.id]: lobbyPeer(undefined, action) }; + return { ...state, [action.payload.peerId]: lobbyPeer(undefined, action) }; } case 'REMOVE_LOBBY_PEER': diff --git a/server/lib/Room.js b/server/lib/Room.js index 8c7f8d6..e51da1f 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -2,6 +2,7 @@ const EventEmitter = require('events').EventEmitter; const Logger = require('./Logger'); +const Lobby = require('./Lobby'); const config = require('../config/config'); const logger = new Logger('Room'); @@ -54,6 +55,15 @@ class Room extends EventEmitter // Locked flag. this._locked = false; + this._lobby = new Lobby(); + + this._lobby.on('promotePeer', (peer) => + { + logger.info('promotePeer() [peer:"%o"]', peer); + + this._peerJoining({ ...peer }); + }); + this._chatHistory = []; this._fileHistory = []; @@ -118,6 +128,8 @@ class Room extends EventEmitter this._closed = true; + this._lobby.close(); + // Close the peers if (this._peers) { @@ -165,11 +177,21 @@ class Room extends EventEmitter } else if (this._locked) // Don't allow connections to a locked room { - this._notification(socket, 'roomLocked'); - socket.disconnect(true); + this._lobby.parkPeer({ peerId, consume, socket }); + + this._peers.forEach((peer) => + { + this._notification(peer.socket, 'parkedPeer', { peerId }); + }); + return; } + this._peerJoining({ peerId, consume, socket }); + } + + _peerJoining({ peerId, consume, socket }) + { socket.join(this._roomId); const peer = { id : peerId, socket : socket }; @@ -808,6 +830,28 @@ class Room extends EventEmitter break; } + case 'promotePeer': + { + const { peerId } = request.data; + + this._lobby.promotePeer(peerId); + + // Return no error + cb(); + + break; + } + + case 'promoteAllPeers': + { + this._lobby.promoteAllPeers(); + + // Return no error + cb(); + + break; + } + case 'sendFile': { const { magnetUri } = request.data; From 513f0efa0ba059c91540417f2d3a2bae438e1f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Wed, 16 Oct 2019 14:09:29 +0200 Subject: [PATCH 04/74] First working version of lobby. --- app/src/RoomClient.js | 45 ++++++- app/src/actions/stateActions.js | 8 ++ app/src/components/JoinDialog.js | 55 +++++++- .../ParticipantList/ListLobbyPeer.js | 117 ++---------------- .../ParticipantList/ParticipantList.js | 5 +- app/src/reducers/lobbyPeers.js | 9 +- app/src/reducers/rootReducer.js | 2 + server/lib/Lobby.js | 55 +++++++- server/lib/Room.js | 29 +++++ 9 files changed, 207 insertions(+), 118 deletions(-) diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 0f77197..1435dc1 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -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; diff --git a/app/src/actions/stateActions.js b/app/src/actions/stateActions.js index 7908b99..24f6870 100644 --- a/app/src/actions/stateActions.js +++ b/app/src/actions/stateActions.js @@ -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 { diff --git a/app/src/components/JoinDialog.js b/app/src/components/JoinDialog.js index ae11771..c5bca93 100644 --- a/app/src/components/JoinDialog.js +++ b/app/src/components/JoinDialog.js @@ -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 + + { + const { value } = event.target; + + changeDisplayName(value); + }} + margin='normal' + /> ); @@ -83,8 +101,39 @@ const JoinDialog = ({ JoinDialog.propTypes = { - roomClient : PropTypes.any.isRequired, - classes : PropTypes.object.isRequired + roomClient : PropTypes.any.isRequired, + displayName : PropTypes.string.isRequired, + changeDisplayName : PropTypes.func.isRequired, + classes : PropTypes.object.isRequired }; -export default withRoomContext(withStyles(styles)(JoinDialog)); \ No newline at end of file +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))); \ No newline at end of file diff --git a/app/src/components/MeetingDrawer/ParticipantList/ListLobbyPeer.js b/app/src/components/MeetingDrawer/ParticipantList/ListLobbyPeer.js index 7bc6178..85b0136 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ListLobbyPeer.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ListLobbyPeer.js @@ -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) =>
    {peer.displayName}
    -
    - { /* peer.raiseHandState ? -
    - :null - */ } -
    - {/* { screenConsumer ? -
    - { - e.stopPropagation(); - screenVisible ? - roomClient.modifyPeerConsumer(peer.id, 'screen', true) : - roomClient.modifyPeerConsumer(peer.id, 'screen', false); - }} - > - { screenVisible ? - - : - - } -
    - :null - }
    { e.stopPropagation(); - micEnabled ? - roomClient.modifyPeerConsumer(peer.id, 'mic', true) : - roomClient.modifyPeerConsumer(peer.id, 'mic', false); + roomClient.promoteLobbyPeer(peer.id); }} > - { micEnabled ? - - : - - } -
    */} + +
); @@ -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 }; diff --git a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js index f8c98e4..38f2aea 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js @@ -92,9 +92,9 @@ class ParticipantList extends React.PureComponent
- { lobbyPeers ? + { lobbyPeers.length > 0 ?
    -
  • Participants in Spotlight:
  • +
  • Participants in Lobby:
  • { lobbyPeers.map((peerId) => (
    • Participants in Spotlight:
    • { spotlightPeers.map((peer) => ( diff --git a/app/src/reducers/lobbyPeers.js b/app/src/reducers/lobbyPeers.js index 391d595..9b905ed 100644 --- a/app/src/reducers/lobbyPeers.js +++ b/app/src/reducers/lobbyPeers.js @@ -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) }; diff --git a/app/src/reducers/rootReducer.js b/app/src/reducers/rootReducer.js index ea78cdb..539c8d7 100644 --- a/app/src/reducers/rootReducer.js +++ b/app/src/reducers/rootReducer.js @@ -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, diff --git a/server/lib/Lobby.js b/server/lib/Lobby.js index f606a91..ddd7802 100644 --- a/server/lib/Lobby.js +++ b/server/lib/Lobby.js @@ -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; + } + } } } diff --git a/server/lib/Room.js b/server/lib/Room.js index e51da1f..f71a4a6 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -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 = []; From f51d07285de22aa2aa94d18cf318c493630a22f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Wed, 16 Oct 2019 21:41:55 +0200 Subject: [PATCH 05/74] Added pulsing effect to badge. --- .../ParticipantList/ParticipantList.js | 5 --- app/src/components/Room.js | 36 +++++++++++++++++-- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js index 38f2aea..85a71cb 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js @@ -90,8 +90,6 @@ class ParticipantList extends React.PureComponent
    • Me:
    -
    - { lobbyPeers.length > 0 ?
    • Participants in Lobby:
    • @@ -106,8 +104,6 @@ class ParticipantList extends React.PureComponent
    :null } - -
    • Participants in Spotlight:
    • { spotlightPeers.map((peer) => ( @@ -124,7 +120,6 @@ class ParticipantList extends React.PureComponent ))}
    -
    • Passive Participants:
    • { passivePeers.map((peerId) => ( diff --git a/app/src/components/Room.js b/app/src/components/Room.js index 040511e..3ebebeb 100644 --- a/app/src/components/Room.js +++ b/app/src/components/Room.js @@ -150,6 +150,38 @@ const styles = (theme) => } }); +const PulsingBadge = withStyles((theme) => + ({ + badge : + { + backgroundColor : theme.palette.secondary.main, + // boxShadow : `0 0 0 2px ${theme.palette.secondary.main}`, + '&::after' : + { + position : 'absolute', + width : '100%', + height : '100%', + borderRadius : '50%', + animation : '$ripple 1.2s infinite ease-in-out', + border : `3px solid ${theme.palette.secondary.main}`, + content : '""' + } + }, + '@keyframes ripple' : + { + '0%' : + { + transform : 'scale(.8)', + opacity : 1 + }, + '100%' : + { + transform : 'scale(2.4)', + opacity : 0 + } + } + }))(Badge); + class Room extends React.PureComponent { constructor(props) @@ -286,7 +318,7 @@ class Room extends React.PureComponent className={room.toolbarsVisible ? classes.show : classes.hide} > - @@ -298,7 +330,7 @@ class Room extends React.PureComponent > - + { window.config.logo ? Logo :null From 3495245e37bf18bd5024fccf17a288fa3bc8f8f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Thu, 17 Oct 2019 10:55:03 +0200 Subject: [PATCH 06/74] Updated serverhistory and fixed some bugs. --- app/src/RoomClient.js | 21 ++++++++++++++++++++- server/lib/Lobby.js | 37 +++++++++++++++---------------------- server/lib/Room.js | 39 ++++++++++++++++++++------------------- 3 files changed, 55 insertions(+), 42 deletions(-) diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 1435dc1..f9af263 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -607,7 +607,9 @@ export default class RoomClient const { chatHistory, fileHistory, - lastN + lastN, + locked, + lobbyPeers } = await this.sendRequest('serverHistory'); if (chatHistory.length > 0) @@ -635,6 +637,23 @@ export default class RoomClient this._spotlights.addSpeakerList(lastN); } + + locked ? + store.dispatch(stateActions.setRoomLocked()) : + store.dispatch(stateActions.setRoomUnLocked()); + + if (lobbyPeers.length > 0) + { + logger.debug('Got lobby peers'); + + lobbyPeers.forEach((peer) => + { + store.dispatch( + stateActions.addLobbyPeer(peer.peerId)); + store.dispatch( + stateActions.setLobbyPeerDisplayName(peer.displayName)); + }); + } } catch (error) { diff --git a/server/lib/Lobby.js b/server/lib/Lobby.js index ddd7802..be10725 100644 --- a/server/lib/Lobby.js +++ b/server/lib/Lobby.js @@ -16,7 +16,7 @@ class Lobby extends EventEmitter // Closed flag. this._closed = false; - this._peers = new Map(); + this._peers = {}; } close() @@ -25,49 +25,42 @@ class Lobby extends EventEmitter this._closed = true; - // Close the peers - if (this._peers) + Object.values(this._peers).forEach((peer) => { - this._peers.forEach((peer) => - { - if (peer.socket) - peer.socket.disconnect(); - }); - } + if (peer.socket) + peer.socket.disconnect(); + }); - this._peers.clear(); + this._peers = {}; } peerList() { logger.info('peerList()'); - return this._peers; + return Object.values(this._peers).map((peer) => ({ peerId: peer.peerId, displayName: peer.displayName })); } promoteAllPeers() { logger.info('promoteAllPeers()'); - if (this._peers) + Object.values(this._peers).forEach((peer) => { - this._peers.forEach((peer) => - { - if (peer.socket) - this.promotePeer(peer.peerId); - }); - } + if (peer.socket) + this.promotePeer(peer.peerId); + }); } promotePeer(peerId) { logger.info('promotePeer() [peerId: %s]', peerId); - const peer = this._peers.get(peerId); + const peer = this._peers[peerId]; this.emit('promotePeer', peer); - this._peers.delete(peerId); + delete this._peers[peerId]; } parkPeer({ peerId, consume, socket }) @@ -78,7 +71,7 @@ class Lobby extends EventEmitter socket.emit('notification', { method: 'enteredLobby', data: {} }); - this._peers.set(peerId, peer); + this._peers[peerId] = peer; socket.on('request', (request, cb) => { @@ -104,7 +97,7 @@ class Lobby extends EventEmitter this.emit('peerClosed', peer); - this._peers.delete(peer.peerId); + delete this._peers[peer.peerId]; }); } diff --git a/server/lib/Room.js b/server/lib/Room.js index f71a4a6..f1763f8 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -65,7 +65,7 @@ class Room extends EventEmitter this._peerJoining({ ...peer }); - this._peers.forEach((peer) => + Object.values(this._peers).forEach((peer) => { this._notification(peer.socket, 'promotedPeer', { peerId }); }); @@ -75,7 +75,7 @@ class Room extends EventEmitter { const { peerId, displayName } = peer; - this._peers.forEach((peer) => + Object.values(this._peers).forEach((peer) => { this._notification(peer.socket, 'lobbyPeerDisplayNameChanged', { peerId, displayName }); }); @@ -87,7 +87,7 @@ class Room extends EventEmitter const { peerId } = peer; - this._peers.forEach((peer) => + Object.values(this._peers).forEach((peer) => { this._notification(peer.socket, 'lobbyPeerClosed', { peerId }); }); @@ -99,9 +99,7 @@ class Room extends EventEmitter this._lastN = []; - // this._io = io; - - this._peers = new Map(); + this._peers = {}; // mediasoup Router instance. // @type {mediasoup.Router} @@ -121,7 +119,7 @@ class Room extends EventEmitter // producer.id, volume); // Notify all Peers. - this._peers.forEach((peer) => + Object.values(this._peers).forEach((peer) => { this._notification(peer.socket, 'activeSpeaker', { peerId : producer.appData.peerId, @@ -135,7 +133,7 @@ class Room extends EventEmitter // logger.debug('audioLevelObserver "silence" event'); // Notify all Peers. - this._peers.forEach((peer) => + Object.values(this._peers).forEach((peer) => { this._notification(peer.socket, 'activeSpeaker', { peerId : null }); }); @@ -162,7 +160,7 @@ class Room extends EventEmitter // Close the peers if (this._peers) { - this._peers.forEach((peer) => + Object.values(this._peers).forEach((peer) => { if (peer.socket) peer.socket.disconnect(); @@ -192,23 +190,23 @@ class Room extends EventEmitter logger.info('handleConnection() [peerId:"%s"]', peerId); // This will allow reconnects to join despite lock - if (this._peers.has(peerId)) + if (this._peers[peerId]) { logger.warn( 'handleConnection() | there is already a peer with same peerId, ' + 'closing the previous one [peerId:"%s"]', peerId); - const peer = this._peers.get(peerId); + const peer = this._peers[peerId]; peer.socket.disconnect(); - this._peers.delete(peerId); + delete this._peers[peerId]; } else if (this._locked) // Don't allow connections to a locked room { this._lobby.parkPeer({ peerId, consume, socket }); - this._peers.forEach((peer) => + Object.values(this._peers).forEach((peer) => { this._notification(peer.socket, 'parkedPeer', { peerId }); }); @@ -232,7 +230,7 @@ class Room extends EventEmitter this._lastN.push(peerId); } - this._peers.set(peerId, peer); + this._peers[peerId] = peer; this._handlePeer({ peer, consume }); this._notification(socket, 'roomReady'); @@ -253,7 +251,7 @@ class Room extends EventEmitter picture } = data; - const peer = this._peers.get(peerId); + const peer = this._peers[peerId]; if (peer) { @@ -326,7 +324,7 @@ class Room extends EventEmitter transport.close(); } - this._peers.delete(peer.id); + delete this._peers[peer.id]; // If this is the latest Peer in the room, close the room after a while. if (this._peers.size === 0) @@ -385,7 +383,7 @@ class Room extends EventEmitter const peerInfos = []; - this._peers.forEach((joinedPeer) => + Object.values(this._peers).forEach((joinedPeer) => { if (joinedPeer.data.joined) { @@ -548,7 +546,7 @@ class Room extends EventEmitter cb(null, { id: producer.id }); - this._peers.forEach((otherPeer) => + Object.values(this._peers).forEach((otherPeer) => { if (otherPeer.data.joined && otherPeer !== peer) { @@ -817,12 +815,15 @@ class Room extends EventEmitter case 'serverHistory': { // Return to sender + const lobbyPeers = this._lobby.peerList(); cb( null, { chatHistory : this._chatHistory, fileHistory : this._fileHistory, - lastN : this._lastN + lastN : this._lastN, + locked : this._locked, + lobbyPeers : lobbyPeers } ); From 4181afd1a3d6e8e741cc1dcf66cc43daa5516fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Fri, 18 Oct 2019 14:56:02 +0200 Subject: [PATCH 07/74] Further work on sessions and authentication --- app/src/RoomClient.js | 2 +- server/lib/Lobby.js | 52 ++++++++++++++++++++++++++++-- server/lib/Room.js | 4 +++ server/package.json | 4 +++ server/server.js | 75 +++++++++++++++++++++++++------------------ 5 files changed, 103 insertions(+), 34 deletions(-) diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index f9af263..56f13d8 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -305,7 +305,7 @@ export default class RoomClient login() { - const url = `/auth/login?roomId=${this._roomId}&peerId=${this._peerId}`; + const url = `/auth/login?id=${this._signalingSocket.io.engine.id}`; this._loginWindow = window.open(url, 'loginWindow'); } diff --git a/server/lib/Lobby.js b/server/lib/Lobby.js index be10725..1d77700 100644 --- a/server/lib/Lobby.js +++ b/server/lib/Lobby.js @@ -34,6 +34,32 @@ class Lobby extends EventEmitter this._peers = {}; } + authCallback(data, roomLocked) + { + logger.debug('authCallback() | [data:"%o", roomLocked:"%s"]', data, roomLocked); + + const { + peerId, + displayName, + picture + } = data; + + const peer = this._peers[peerId]; + + if (peer) + { + this._notification(peer.socket, 'auth', { + displayName : displayName, + picture : picture + }); + + if (!roomLocked) + { + this.promotePeer(peerId); + } + } + } + peerList() { logger.info('peerList()'); @@ -41,6 +67,11 @@ class Lobby extends EventEmitter return Object.values(this._peers).map((peer) => ({ peerId: peer.peerId, displayName: peer.displayName })); } + hasPeer(peerId) + { + return Boolean(this._peers[peerId]); + } + promoteAllPeers() { logger.info('promoteAllPeers()'); @@ -58,9 +89,12 @@ class Lobby extends EventEmitter const peer = this._peers[peerId]; - this.emit('promotePeer', peer); + if (peer) + { + this.emit('promotePeer', peer); - delete this._peers[peerId]; + delete this._peers[peerId]; + } } parkPeer({ peerId, consume, socket }) @@ -119,6 +153,20 @@ class Lobby extends EventEmitter } } } + + _notification(socket, method, data = {}, broadcast = false) + { + if (broadcast) + { + socket.broadcast.to(this._roomId).emit( + 'notification', { method, data } + ); + } + else + { + socket.emit('notification', { method, data }); + } + } } module.exports = Lobby; \ No newline at end of file diff --git a/server/lib/Room.js b/server/lib/Room.js index f1763f8..db10bb1 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -260,6 +260,10 @@ class Room extends EventEmitter picture : picture }); } + else if (this._lobby.hasPeer(peerId)) + { + this._lobby.authCallback(data, this._locked); + } } _handlePeer({ peer, consume }) diff --git a/server/package.json b/server/package.json index 1762e0c..0a98b10 100644 --- a/server/package.json +++ b/server/package.json @@ -11,12 +11,16 @@ "base-64": "^0.1.0", "colors": "^1.1.2", "compression": "^1.7.3", + "connect-redis": "^4.0.3", + "cookie-parser": "^1.4.4", "debug": "^4.1.0", "express": "^4.16.3", "express-session": "^1.16.1", "mediasoup": "^3.0.12", "openid-client": "^2.5.0", "passport": "^0.4.0", + "passport.socketio": "^3.7.0", + "redis": "^2.8.0", "socket.io": "^2.1.1", "spdy": "^4.0.0" }, diff --git a/server/server.js b/server/server.js index 366181e..3dffd3c 100755 --- a/server/server.js +++ b/server/server.js @@ -20,6 +20,12 @@ const base64 = require('base-64'); const passport = require('passport'); const { Issuer, Strategy } = require('openid-client'); const session = require('express-session'); +const passportSocketIo = require('passport.socketio'); +const cookieParser = require('cookie-parser'); +// Session storage +const redis = require('redis'); +const RedisStore = require('connect-redis')(session); +const redisClient = redis.createClient(); /* eslint-disable no-console */ console.log('- process.env.DEBUG:', process.env.DEBUG); @@ -49,6 +55,8 @@ const tls = key : fs.readFileSync(config.tls.key) }; +const sessionStore = new RedisStore({ client: redisClient }); + const app = express(); let httpsServer; let oidcClient; @@ -202,6 +210,7 @@ async function setupAuth(oidcIssuer) secret : config.cookieSecret, resave : true, saveUninitialized : true, + store : sessionStore, cookie : { secure: true } })); @@ -213,9 +222,8 @@ async function setupAuth(oidcIssuer) { passport.authenticate('oidc', { state : base64.encode(JSON.stringify({ - roomId : req.query.roomId, - peerId : req.query.peerId, - code : utils.random(10) + id : req.query.id, + code : utils.random(10) })) })(req, res, next); }); @@ -235,39 +243,37 @@ async function setupAuth(oidcIssuer) { const state = JSON.parse(base64.decode(req.query.state)); - if (rooms.has(state.roomId)) + let displayName; + let photo; + + if (req.user != null) { - let displayName; - let photo; + if (req.user.displayName != null) + displayName = req.user.displayName; + else + displayName = ''; - if (req.user != null) + if ( + req.user.Photos != null && + req.user.Photos[0] != null && + req.user.Photos[0].value != null + ) + photo = req.user.Photos[0].value; + else + photo = '/static/media/buddy.403cb9f6.svg'; + } + + // const room = rooms.get(state.roomId); + + io.sockets.socket(state.id).emit('notification', + { + method: 'auth', + data : { - if (req.user.displayName != null) - displayName = req.user.displayName; - else - displayName = ''; - - if ( - req.user.Photos != null && - req.user.Photos[0] != null && - req.user.Photos[0].value != null - ) - photo = req.user.Photos[0].value; - else - photo = '/static/media/buddy.403cb9f6.svg'; - } - - const data = - { - peerId : state.peerId, displayName : displayName, picture : photo - }; - - const room = rooms.get(state.roomId); - - room.authCallback(data); - } + } + }); res.send(''); } @@ -322,6 +328,13 @@ async function runWebSocketServer() { const io = require('socket.io')(httpsServer); + io.use(passportSocketIo.authorize({ + secret : config.cookieSecret, + passport : passport, + cookieParser : cookieParser, + store : sessionStore, + })); + // Handle connections from clients. io.on('connection', (socket) => { From 0299e5f661fc5d1b90076c61b56203f556f9cc7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Tue, 22 Oct 2019 15:04:28 +0200 Subject: [PATCH 08/74] Fixed some small bugs, now sharing session between Express and Socket.io. --- server/package.json | 5 +---- server/server.js | 54 +++++++++++++++++++-------------------------- 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/server/package.json b/server/package.json index 0a98b10..ab5c911 100644 --- a/server/package.json +++ b/server/package.json @@ -11,16 +11,13 @@ "base-64": "^0.1.0", "colors": "^1.1.2", "compression": "^1.7.3", - "connect-redis": "^4.0.3", - "cookie-parser": "^1.4.4", "debug": "^4.1.0", "express": "^4.16.3", "express-session": "^1.16.1", + "express-socket.io-session": "^1.3.5", "mediasoup": "^3.0.12", "openid-client": "^2.5.0", "passport": "^0.4.0", - "passport.socketio": "^3.7.0", - "redis": "^2.8.0", "socket.io": "^2.1.1", "spdy": "^4.0.0" }, diff --git a/server/server.js b/server/server.js index 3dffd3c..057b8b2 100755 --- a/server/server.js +++ b/server/server.js @@ -19,19 +19,19 @@ const base64 = require('base-64'); // auth const passport = require('passport'); const { Issuer, Strategy } = require('openid-client'); -const session = require('express-session'); -const passportSocketIo = require('passport.socketio'); -const cookieParser = require('cookie-parser'); -// Session storage -const redis = require('redis'); -const RedisStore = require('connect-redis')(session); -const redisClient = redis.createClient(); +const session = require('express-session')({ + secret : config.cookieSecret, + resave : true, + saveUninitialized : true, + cookie : { secure: true } +}); +const sharedSession = require('express-socket.io-session'); /* eslint-disable no-console */ console.log('- process.env.DEBUG:', process.env.DEBUG); console.log('- config.mediasoup.logLevel:', config.mediasoup.logLevel); console.log('- config.mediasoup.logTags:', config.mediasoup.logTags); -/* eslint-enable no-console */ +/* eslint-enable nopassportSocketIo-console */ const logger = new Logger(); @@ -55,9 +55,10 @@ const tls = key : fs.readFileSync(config.tls.key) }; -const sessionStore = new RedisStore({ client: redisClient }); - const app = express(); + +app.use(session); + let httpsServer; let oidcClient; let oidcStrategy; @@ -76,13 +77,13 @@ const auth = config.auth; async function run() { - if ( + if ( typeof(auth) !== 'undefined' && typeof(auth.issuerURL) !== 'undefined' && typeof(auth.clientOptions) !== 'undefined' ) { - Issuer.discover(auth.issuerURL).then( async (oidcIssuer) => + Issuer.discover(auth.issuerURL).then(async (oidcIssuer) => { // Setup authentication await setupAuth(oidcIssuer); @@ -96,10 +97,10 @@ async function run() // Run WebSocketServer. await runWebSocketServer(); }) - .catch((err) => - { - logger.error(err); - }); + .catch((err) => + { + logger.error(err); + }); } else { @@ -206,18 +207,10 @@ async function setupAuth(oidcIssuer) passport.use('oidc', oidcStrategy); - app.use(session({ - secret : config.cookieSecret, - resave : true, - saveUninitialized : true, - store : sessionStore, - cookie : { secure: true } - })); - app.use(passport.initialize()); app.use(passport.session()); - // login + // loginparams app.get('/auth/login', (req, res, next) => { passport.authenticate('oidc', { @@ -328,12 +321,11 @@ async function runWebSocketServer() { const io = require('socket.io')(httpsServer); - io.use(passportSocketIo.authorize({ - secret : config.cookieSecret, - passport : passport, - cookieParser : cookieParser, - store : sessionStore, - })); + io.use( + sharedSession(session, { + autoSave: true + }) + ); // Handle connections from clients. io.on('connection', (socket) => From 38cf88aa62e418a7ef4c5ac45efc11cee7d40993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Tue, 22 Oct 2019 15:30:04 +0200 Subject: [PATCH 09/74] Cleanup and handling of events. --- app/src/RoomClient.js | 2 +- server/lib/Lobby.js | 26 -------------------------- server/lib/Room.js | 30 +++++++++--------------------- server/server.js | 12 ++++++++---- 4 files changed, 18 insertions(+), 52 deletions(-) diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 56f13d8..243ce57 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -305,7 +305,7 @@ export default class RoomClient login() { - const url = `/auth/login?id=${this._signalingSocket.io.engine.id}`; + const url = `/auth/login?id=${this._signalingSocket.io.engine.id}&roomId=${this._roomId}&peerId=${this._peerId}`; this._loginWindow = window.open(url, 'loginWindow'); } diff --git a/server/lib/Lobby.js b/server/lib/Lobby.js index 1d77700..62da0e5 100644 --- a/server/lib/Lobby.js +++ b/server/lib/Lobby.js @@ -34,32 +34,6 @@ class Lobby extends EventEmitter this._peers = {}; } - authCallback(data, roomLocked) - { - logger.debug('authCallback() | [data:"%o", roomLocked:"%s"]', data, roomLocked); - - const { - peerId, - displayName, - picture - } = data; - - const peer = this._peers[peerId]; - - if (peer) - { - this._notification(peer.socket, 'auth', { - displayName : displayName, - picture : picture - }); - - if (!roomLocked) - { - this.promotePeer(peerId); - } - } - } - peerList() { logger.info('peerList()'); diff --git a/server/lib/Room.js b/server/lib/Room.js index db10bb1..ef9d941 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -179,9 +179,9 @@ class Room extends EventEmitter logStatus() { logger.info( - 'logStatus() [room id:"%s", peers:%s]', + 'logStatus() [room id:"%s", peers:%o]', this._roomId, - this._peers.size + this._peers ); } @@ -241,28 +241,16 @@ class Room extends EventEmitter return this._locked; } - authCallback(data) + peerAuthenticated(peerid) { - logger.debug('authCallback()'); + logger.debug('peerAuthenticated() | [peerId:"%s"]', peerId); - const { - peerId, - displayName, - picture - } = data; - - const peer = this._peers[peerId]; - - if (peer) + if (!this._locked) { - this._notification(peer.socket, 'auth', { - displayName : displayName, - picture : picture - }); - } - else if (this._lobby.hasPeer(peerId)) - { - this._lobby.authCallback(data, this._locked); + if (!Boolean(this._peers[peerid])) + { + this._lobby.promotePeer(peerId); + } } } diff --git a/server/server.js b/server/server.js index 057b8b2..2af5bdd 100755 --- a/server/server.js +++ b/server/server.js @@ -215,8 +215,10 @@ async function setupAuth(oidcIssuer) { passport.authenticate('oidc', { state : base64.encode(JSON.stringify({ - id : req.query.id, - code : utils.random(10) + id : req.query.id, + roomId : req.query.roomId, + peerId : req.query.peerId, + code : utils.random(10) })) })(req, res, next); }); @@ -256,7 +258,9 @@ async function setupAuth(oidcIssuer) photo = '/static/media/buddy.403cb9f6.svg'; } - // const room = rooms.get(state.roomId); + const room = rooms.get(state.roomId); + + room.peerAuthenticated(state.peerId); io.sockets.socket(state.id).emit('notification', { @@ -315,7 +319,7 @@ async function runHttpsServer() } /** - * Create a protoo WebSocketServer to allow WebSocket connections from browsers. + * Create a WebSocketServer to allow WebSocket connections from browsers. */ async function runWebSocketServer() { From 223642a44ff9727623794af54ec84f10795582e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Tue, 22 Oct 2019 15:35:46 +0200 Subject: [PATCH 10/74] Update dependencies. --- app/package.json | 36 ++++++++++++++++++------------------ server/package.json | 22 +++++++++++----------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/app/package.json b/app/package.json index b85d265..9a87fbb 100644 --- a/app/package.json +++ b/app/package.json @@ -6,32 +6,32 @@ "author": "Håvar Aambø Fosstveit ", "license": "MIT", "dependencies": { - "@material-ui/core": "^4.1.2", - "@material-ui/icons": "^4.2.1", - "bowser": "^2.4.0", - "create-torrent": "^3.33.0", + "@material-ui/core": "^4.5.1", + "@material-ui/icons": "^4.5.1", + "bowser": "^2.7.0", + "create-torrent": "^4.4.1", "domready": "^1.0.8", - "file-saver": "^2.0.1", + "file-saver": "^2.0.2", "hark": "^1.2.3", - "marked": "^0.6.1", - "mediasoup-client": "^3.0.6", - "notistack": "^0.5.1", + "marked": "^0.7.0", + "mediasoup-client": "^3.2.7", + "notistack": "^0.9.5", "prop-types": "^15.7.2", "random-string": "^0.2.0", - "react": "^16.8.5", - "react-cookie-consent": "^2.2.2", - "react-dom": "^16.8.5", - "react-redux": "^6.0.1", - "react-scripts": "2.1.8", - "redux": "^4.0.1", + "react": "^16.10.2", + "react-cookie-consent": "^2.5.0", + "react-dom": "^16.10.2", + "react-redux": "^7.1.1", + "react-scripts": "3.2.0", + "redux": "^4.0.4", "redux-logger": "^3.0.6", - "redux-persist": "^5.10.0", + "redux-persist": "^6.0.0", "redux-thunk": "^2.3.0", "reselect": "^4.0.0", "riek": "^1.1.0", - "socket.io-client": "^2.2.0", - "source-map-explorer": "^1.8.0", - "webtorrent": "^0.103.1" + "socket.io-client": "^2.3.0", + "source-map-explorer": "^2.1.0", + "webtorrent": "^0.107.16" }, "scripts": { "analyze-main": "source-map-explorer build/static/js/main.*", diff --git a/server/package.json b/server/package.json index ab5c911..76e6ab6 100644 --- a/server/package.json +++ b/server/package.json @@ -9,21 +9,21 @@ "dependencies": { "awaitqueue": "^1.0.0", "base-64": "^0.1.0", - "colors": "^1.1.2", - "compression": "^1.7.3", - "debug": "^4.1.0", - "express": "^4.16.3", - "express-session": "^1.16.1", + "colors": "^1.4.0", + "compression": "^1.7.4", + "debug": "^4.1.1", + "express": "^4.17.1", + "express-session": "^1.17.0", "express-socket.io-session": "^1.3.5", "mediasoup": "^3.0.12", - "openid-client": "^2.5.0", + "openid-client": "^3.7.3", "passport": "^0.4.0", - "socket.io": "^2.1.1", - "spdy": "^4.0.0" + "socket.io": "^2.3.0", + "spdy": "^4.0.1" }, "devDependencies": { - "gulp": "^4.0.0", - "gulp-eslint": "^5.0.0", - "gulp-plumber": "^1.2.0" + "gulp": "^4.0.2", + "gulp-eslint": "^6.0.0", + "gulp-plumber": "^1.2.1" } } From 12dd85a99d7af31860b36853191cf4c099d0562d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Wed, 23 Oct 2019 11:29:32 +0200 Subject: [PATCH 11/74] Lifted some logic up a level to clean up code. --- app/src/components/App.js | 56 +++++++ app/src/components/JoinDialog.js | 100 ++++++----- app/src/components/Lobby.js | 108 ++++++++++++ app/src/components/Room.js | 277 ++++++++++++++----------------- app/src/index.js | 4 +- server/server.js | 14 +- 6 files changed, 350 insertions(+), 209 deletions(-) create mode 100644 app/src/components/App.js create mode 100644 app/src/components/Lobby.js diff --git a/app/src/components/App.js b/app/src/components/App.js new file mode 100644 index 0000000..9cd566b --- /dev/null +++ b/app/src/components/App.js @@ -0,0 +1,56 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import Room from './Room'; +import JoinDialog from './JoinDialog'; +import Lobby from './Lobby'; + +const App = (props) => +{ + const { + room + } = props; + + if (room.lockedOut) + { + return ( + + ); + } + else if (!room.joined) + { + return ( + + ); + } + else + { + return ( + + ); + } +} + +App.propTypes = +{ + room : PropTypes.object.isRequired +}; + +const mapStateToProps = (state) => + ({ + room : state.room + }); + +export default connect( + mapStateToProps, + null, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.room === next.room + ); + } + } +)(App); \ No newline at end of file diff --git a/app/src/components/JoinDialog.js b/app/src/components/JoinDialog.js index c5bca93..ed7f290 100644 --- a/app/src/components/JoinDialog.js +++ b/app/src/components/JoinDialog.js @@ -14,6 +14,15 @@ const styles = (theme) => ({ root : { + display : 'flex', + width : '100%', + height : '100%', + backgroundColor : 'var(--background-color)', + backgroundImage : `url(${window.config.background})`, + backgroundAttachment : 'fixed', + backgroundPosition : 'center', + backgroundSize : 'cover', + backgroundRepeat : 'no-repeat' }, dialogPaper : { @@ -50,52 +59,53 @@ const JoinDialog = ({ }) => { return ( - - { window.config.logo ? - Logo - :null - } - You are about to join a meeting, how would you like to join? - - - - - { - const { value } = event.target; +
      + + { window.config.logo ? + Logo + :null + } + You are about to join a meeting, how would you like to join? + + + + + { + const { value } = event.target; - changeDisplayName(value); - }} - margin='normal' - /> - - + changeDisplayName(value); + }} + margin='normal' + /> + +
      + ); }; diff --git a/app/src/components/Lobby.js b/app/src/components/Lobby.js new file mode 100644 index 0000000..0d29762 --- /dev/null +++ b/app/src/components/Lobby.js @@ -0,0 +1,108 @@ +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 Paper from '@material-ui/core/Paper'; +import DialogActions from '@material-ui/core/DialogActions'; +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; + +const styles = (theme) => + ({ + root : + { + display : 'flex', + width : '100%', + height : '100%', + backgroundColor : 'var(--background-color)', + backgroundImage : `url(${window.config.background})`, + backgroundAttachment : 'fixed', + backgroundPosition : 'center', + backgroundSize : 'cover', + backgroundRepeat : 'no-repeat' + }, + dialogPaper : + { + width : '20vw', + padding : theme.spacing(2), + [theme.breakpoints.down('lg')] : + { + width : '30vw' + }, + [theme.breakpoints.down('md')] : + { + width : '40vw' + }, + [theme.breakpoints.down('sm')] : + { + width : '60vw' + }, + [theme.breakpoints.down('xs')] : + { + width : '80vw' + } + }, + logo : + { + display : 'block' + } + }); + +const Lobby = ({ + roomClient, + displayName, + changeDisplayName, + classes +}) => +{ + return ( +
      + + This room is locked at the moment, try again later. + +
      + ); +}; + +Lobby.propTypes = +{ + roomClient : PropTypes.any.isRequired, + displayName : PropTypes.string.isRequired, + changeDisplayName : PropTypes.func.isRequired, + classes : PropTypes.object.isRequired +}; + +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)(Lobby))); \ No newline at end of file diff --git a/app/src/components/Room.js b/app/src/components/Room.js index 3ebebeb..5385b21 100644 --- a/app/src/components/Room.js +++ b/app/src/components/Room.js @@ -13,7 +13,6 @@ import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import SwipeableDrawer from '@material-ui/core/SwipeableDrawer'; import Hidden from '@material-ui/core/Hidden'; -import Paper from '@material-ui/core/Paper'; import Typography from '@material-ui/core/Typography'; import IconButton from '@material-ui/core/IconButton'; import MenuIcon from '@material-ui/icons/Menu'; @@ -34,7 +33,6 @@ import LockIcon from '@material-ui/icons/Lock'; import LockOpenIcon from '@material-ui/icons/LockOpen'; import Button from '@material-ui/core/Button'; import Settings from './Settings/Settings'; -import JoinDialog from './JoinDialog'; const TIMEOUT = 10 * 1000; @@ -277,172 +275,139 @@ class Room extends React.PureComponent democratic : Democratic }[room.mode]; - if (room.lockedOut) - { - return ( -
      - - This room is locked at the moment, try again later. - -
      - ); - } - else if (!room.joined) - { - return ( -
      - -
      - ); - } - else - { - return ( -
      - - This website uses cookies to enhance the user experience. - + return ( +
      + + This website uses cookies to enhance the user experience. + - + - + - + - + - + - - - - toggleToolArea()} - className={classes.menuButton} - > - - - - { window.config.logo ? - Logo - :null - } - + + + toggleToolArea()} + className={classes.menuButton} > - { window.config.title } - -
      -
      - - { - if (room.locked) - { - roomClient.unlockRoom(); - } - else - { - roomClient.lockRoom(); - } - }} - > - { room.locked ? - - : - - } - - { this.fullscreen.fullscreenEnabled ? - - { this.state.fullscreen ? - - : - - } - - :null - } - setSettingsOpen(!room.settingsOpen)} - > - - - { loginEnabled ? - - { - loggedIn ? roomClient.logout() : roomClient.login(); - }} - > - { myPicture ? - - : - - } - - :null - } - -
      - - - + { room.locked ? + + : + + } + + { this.fullscreen.fullscreenEnabled && + + { this.state.fullscreen ? + + : + + } + + } + setSettingsOpen(!room.settingsOpen)} + > + + + { loginEnabled && + + { + loggedIn ? roomClient.logout() : roomClient.login(); + }} + > + { myPicture ? + + : + + } + + } + +
      +
      +
      + - + - -
      - ); - } + +
      + ); } } diff --git a/app/src/index.js b/app/src/index.js index 8e30c9d..0efc135 100644 --- a/app/src/index.js +++ b/app/src/index.js @@ -9,7 +9,7 @@ import RoomClient from './RoomClient'; import RoomContext from './RoomContext'; import deviceInfo from './deviceInfo'; import * as stateActions from './actions/stateActions'; -import Room from './components/Room'; +import App from './components/App'; import LoadingView from './components/LoadingView'; import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; import { PersistGate } from 'redux-persist/lib/integration/react'; @@ -94,7 +94,7 @@ function run() } persistor={persistor}> - + diff --git a/server/server.js b/server/server.js index 2af5bdd..4e42518 100755 --- a/server/server.js +++ b/server/server.js @@ -19,12 +19,7 @@ const base64 = require('base-64'); // auth const passport = require('passport'); const { Issuer, Strategy } = require('openid-client'); -const session = require('express-session')({ - secret : config.cookieSecret, - resave : true, - saveUninitialized : true, - cookie : { secure: true } -}); +const expressSession = require('express-session'); const sharedSession = require('express-socket.io-session'); /* eslint-disable no-console */ @@ -57,6 +52,13 @@ const tls = const app = express(); +const session = expressSession({ + secret : config.cookieSecret, + resave : true, + saveUninitialized : true, + cookie : { secure: true } +}) + app.use(session); let httpsServer; From e5ddaa458aebc128ee26d3809bd20b715176c168 Mon Sep 17 00:00:00 2001 From: Stefan Otto Date: Wed, 23 Oct 2019 11:47:41 +0200 Subject: [PATCH 12/74] lock Dialog --- app/src/RoomClient.js | 109 +++++++++- app/src/actions/stateActions.js | 30 +++ .../LockDialog}/ListLobbyPeer.js | 42 ++-- .../AccessControl/LockDialog/LockDialog.js | 191 ++++++++++++++++++ .../ParticipantList/ParticipantList.js | 26 +-- app/src/components/Room.js | 38 ++-- app/src/index.js | 3 +- app/src/reducers/room.js | 24 +++ server/lib/Room.js | 48 ++++- 9 files changed, 445 insertions(+), 66 deletions(-) rename app/src/components/{MeetingDrawer/ParticipantList => AccessControl/LockDialog}/ListLobbyPeer.js (76%) create mode 100644 app/src/components/AccessControl/LockDialog/LockDialog.js diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 243ce57..7c586ef 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -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) diff --git a/app/src/actions/stateActions.js b/app/src/actions/stateActions.js index 24f6870..1e72125 100644 --- a/app/src/actions/stateActions.js +++ b/app/src/actions/stateActions.js @@ -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 { diff --git a/app/src/components/MeetingDrawer/ParticipantList/ListLobbyPeer.js b/app/src/components/AccessControl/LockDialog/ListLobbyPeer.js similarity index 76% rename from app/src/components/MeetingDrawer/ParticipantList/ListLobbyPeer.js rename to app/src/components/AccessControl/LockDialog/ListLobbyPeer.js index 85b0136..665561d 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ListLobbyPeer.js +++ b/app/src/components/AccessControl/LockDialog/ListLobbyPeer.js @@ -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 ( -
      - Peer avatar - -
      - {peer.displayName} -
      -
      -
      + + + + + + { e.stopPropagation(); roomClient.promoteLobbyPeer(peer.id); }} - > - -
      -
      -
      + > + + + ); }; diff --git a/app/src/components/AccessControl/LockDialog/LockDialog.js b/app/src/components/AccessControl/LockDialog/LockDialog.js new file mode 100644 index 0000000..021cf20 --- /dev/null +++ b/app/src/components/AccessControl/LockDialog/LockDialog.js @@ -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 ( + handleCloseLockDialog({ lockDialogOpen: false })} + classes={{ + paper : classes.dialogPaper + }} + > + Room access control +
      + + Room lock + + + { + if (room.locked) + { + roomClient.unlockRoom(); + } + else + { + roomClient.lockRoom(); + } + }} + />} + label='Lock' + /> + {/* TODO: access code + roomClient.setJoinByAccessCode(event.target.checked) + } + />} + label='Join by Access code' + /> + + handleAccessCode(event.target.value)} + > + + + */} + + + + { lobbyPeers.length > 0 ? + + Participants in Lobby + + }> + { + lobbyPeers.map((peerId) => { return (); }) + } + + : null + } +
      + + + + +
      + ); +}; + +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))); \ No newline at end of file diff --git a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js index 85a71cb..484c7ba 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js @@ -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
    • Me:
    - { lobbyPeers.length > 0 ? -
      -
    • Participants in Lobby:
    • - { lobbyPeers.map((peerId) => ( -
    • - -
    • - ))} -
    - :null - }
    • Participants in Spotlight:
    • { 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 ); } } diff --git a/app/src/components/Room.js b/app/src/components/Room.js index 3ebebeb..c002a94 100644 --- a/app/src/components/Room.js +++ b/app/src/components/Room.js @@ -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
      - - { - if (room.locked) - { - roomClient.unlockRoom(); - } - else - { - roomClient.lockRoom(); - } - }} - > - { room.locked ? - - : - - } - { this.fullscreen.fullscreenEnabled ? + setLockDialogOpen(!room.lockDialogOpen)} + > + { room.locked ? : } + { loginEnabled ? + +
      ); @@ -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()); diff --git a/app/src/index.js b/app/src/index.js index 8e30c9d..c85fa18 100644 --- a/app/src/index.js +++ b/app/src/index.js @@ -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; diff --git a/app/src/reducers/room.js b/app/src/reducers/room.js index e1444c3..20a6026 100644 --- a/app/src/reducers/room.js +++ b/app/src/reducers/room.js @@ -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; diff --git a/server/lib/Room.js b/server/lib/Room.js index ef9d941..f382880 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -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; From 79ecaf74086b3e0461f802a7135e57755c9dc0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Wed, 23 Oct 2019 12:15:22 +0200 Subject: [PATCH 13/74] Small cleanups here and there. --- app/src/components/Containers/HiddenPeers.js | 2 +- app/src/components/Containers/Me.js | 23 +++++------ app/src/components/Containers/Peer.js | 38 ++++++++----------- app/src/components/Containers/SpeakerPeer.js | 12 ++---- app/src/components/JoinDialog.js | 35 +++++++++-------- .../MeetingDrawer/FileSharing/File.js | 12 ++---- .../MeetingDrawer/ParticipantList/ListMe.js | 3 +- .../MeetingDrawer/ParticipantList/ListPeer.js | 6 +-- app/src/components/MeetingViews/Democratic.js | 3 +- app/src/components/MeetingViews/Filmstrip.js | 6 +-- .../components/VideoContainers/VideoView.js | 19 +++------- app/src/components/VideoWindow/NewWindow.js | 6 +-- 12 files changed, 68 insertions(+), 97 deletions(-) diff --git a/app/src/components/Containers/HiddenPeers.js b/app/src/components/Containers/HiddenPeers.js index 7d155d0..4afb943 100644 --- a/app/src/components/Containers/HiddenPeers.js +++ b/app/src/components/Containers/HiddenPeers.js @@ -96,7 +96,7 @@ class HiddenPeers extends React.PureComponent onClick={() => openUsersTab()} >

      +{hiddenPeersCount}
      participant - {(hiddenPeersCount === 1) ? null : 's'} + {(hiddenPeersCount > 1) && 's'}

      ); diff --git a/app/src/components/Containers/Me.js b/app/src/components/Containers/Me.js index 8d3e465..2ff2adf 100644 --- a/app/src/components/Containers/Me.js +++ b/app/src/components/Containers/Me.js @@ -332,13 +332,11 @@ const Me = (props) => } }} > - { screenState === 'on' || screenState === 'unsupported' ? + { (screenState === 'on' || screenState === 'unsupported') && - :null } - { screenState === 'off' ? + { screenState === 'off' && - :null } @@ -351,10 +349,10 @@ const Me = (props) => peer={me} displayName={settings.displayName} showPeerInfo - videoTrack={webcamProducer ? webcamProducer.track : null} + videoTrack={webcamProducer && webcamProducer.track} videoVisible={videoVisible} - audioCodec={micProducer ? micProducer.codec : null} - videoCodec={webcamProducer ? webcamProducer.codec : null} + audioCodec={micProducer && micProducer.codec} + videoCodec={webcamProducer && webcamProducer.codec} onChangeDisplayName={(displayName) => { roomClient.changeDisplayName(displayName); @@ -364,9 +362,9 @@ const Me = (props) => - { screenProducer ? + { screenProducer &&
      setHover(true)} onMouseOut={() => setHover(false)} onTouchStart={() => @@ -390,7 +388,7 @@ const Me = (props) => >
      setHover(true)} onMouseOut={() => setHover(false)} onTouchStart={() => @@ -420,13 +418,12 @@ const Me = (props) => isScreen advancedMode={advancedMode} videoContain - videoTrack={screenProducer ? screenProducer.track : null} + videoTrack={screenProducer && screenProducer.track} videoVisible={screenVisible} - videoCodec={screenProducer ? screenProducer.codec : null} + videoCodec={screenProducer && screenProducer.codec} />
      - :null } ); diff --git a/app/src/components/Containers/Peer.js b/app/src/components/Containers/Peer.js index ee7fd2e..4abb38f 100644 --- a/app/src/components/Containers/Peer.js +++ b/app/src/components/Containers/Peer.js @@ -166,8 +166,8 @@ const Peer = (props) => classnames( classes.root, 'webcam', - hover ? 'hover' : null, - activeSpeaker ? 'active-speaker' : null + hover && 'hover', + activeSpeaker && 'active-speaker' ) } onMouseOver={() => setHover(true)} @@ -192,15 +192,14 @@ const Peer = (props) => style={rootStyle} >
      - { !videoVisible ? + { !videoVisible &&

      this video is paused

      - :null }
      setHover(true)} onMouseOut={() => setHover(false)} onTouchStart={() => @@ -241,7 +240,7 @@ const Peer = (props) => } - { !smallScreen ? + { !smallScreen && > - :null } peer={peer} displayName={peer.displayName} showPeerInfo - videoTrack={webcamConsumer ? webcamConsumer.track : null} + videoTrack={webcamConsumer && webcamConsumer.track} videoVisible={videoVisible} videoProfile={videoProfile} - audioCodec={micConsumer ? micConsumer.codec : null} - videoCodec={webcamConsumer ? webcamConsumer.codec : null} + audioCodec={micConsumer && micConsumer.codec} + videoCodec={webcamConsumer && webcamConsumer.codec} >
      - { screenConsumer ? + { screenConsumer &&
      setHover(true)} onMouseOut={() => setHover(false)} onTouchStart={() => @@ -314,17 +312,16 @@ const Peer = (props) => }} style={rootStyle} > - { !screenVisible ? + { !screenVisible &&

      this video is paused

      - :null } - { screenVisible ? + { screenVisible &&
      setHover(true)} onMouseOut={() => setHover(false)} onTouchStart={() => @@ -346,7 +343,7 @@ const Peer = (props) => }, 2000); }} > - { !smallScreen ? + { !smallScreen && > - :null }
      - :null }
      - :null } ); diff --git a/app/src/components/Containers/SpeakerPeer.js b/app/src/components/Containers/SpeakerPeer.js index 85aeb0b..c31baca 100644 --- a/app/src/components/Containers/SpeakerPeer.js +++ b/app/src/components/Containers/SpeakerPeer.js @@ -117,11 +117,10 @@ const SpeakerPeer = (props) => style={spacingStyle} >
      - { !videoVisible ? + { !videoVisible &&

      this video is paused

      - :null }
      - { screenConsumer ? + { screenConsumer &&
      - { !screenVisible ? + { !screenVisible &&

      this video is paused

      - :null } - { screenVisible ? + { screenVisible &&
      videoCodec={screenConsumer ? screenConsumer.codec : null} />
      - :null }
      - :null } ); diff --git a/app/src/components/JoinDialog.js b/app/src/components/JoinDialog.js index ed7f290..1864dbc 100644 --- a/app/src/components/JoinDialog.js +++ b/app/src/components/JoinDialog.js @@ -66,11 +66,27 @@ const JoinDialog = ({ paper : classes.dialogPaper }} > - { window.config.logo ? + { window.config.logo && Logo - :null } - You are about to join a meeting, how would you like to join? + + You are about to join a meeting. + Set your name that others will see, + and chose how you want to join? + + + { + const { value } = event.target; + + changeDisplayName(value); + }} + margin='normal' + /> - - { - const { value } = event.target; - - changeDisplayName(value); - }} - margin='normal' - />
      diff --git a/app/src/components/MeetingDrawer/FileSharing/File.js b/app/src/components/MeetingDrawer/FileSharing/File.js index cd3a985..dd89cd2 100644 --- a/app/src/components/MeetingDrawer/FileSharing/File.js +++ b/app/src/components/MeetingDrawer/FileSharing/File.js @@ -67,7 +67,7 @@ class File extends React.PureComponent Avatar
      - { file.files ? + { file.files && File finished downloading @@ -92,13 +92,12 @@ class File extends React.PureComponent
      ))} - :null } { `${displayName} shared a file` } - { !file.active && !file.files ? + { (!file.active && !file.files) &&
      { magnet.decode(magnetUri).dn } @@ -121,20 +120,17 @@ class File extends React.PureComponent }
      - :null } - { file.timeout ? + { file.timeout && If this process takes a long time, there might not be anyone seeding this torrent. Try asking someone to reupload the file that you want. - :null } - { file.active ? + { file.active && - :null } diff --git a/app/src/components/MeetingDrawer/ParticipantList/ListMe.js b/app/src/components/MeetingDrawer/ParticipantList/ListMe.js index f506f9c..04dcfc6 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ListMe.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ListMe.js @@ -91,9 +91,8 @@ const ListMe = (props) =>
      - { me.raisedHand ? + { me.raisedHand &&
      - :null }
      diff --git a/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js b/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js index 82f6840..cd19e9e 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js @@ -159,7 +159,7 @@ const ListPeer = (props) => {peer.displayName}
      - { peer.raiseHandState ? + { peer.raiseHandState &&
      ) } /> - :null }
      {children}
      - { screenConsumer ? + { screenConsumer &&
      }
      - :null }
      ); })} - { spotlightsLength < peersLength ? + { spotlightsLength < peersLength && - :null }
      ); diff --git a/app/src/components/MeetingViews/Filmstrip.js b/app/src/components/MeetingViews/Filmstrip.js index 934db64..fde6515 100644 --- a/app/src/components/MeetingViews/Filmstrip.js +++ b/app/src/components/MeetingViews/Filmstrip.js @@ -228,13 +228,12 @@ class Filmstrip extends React.PureComponent return (
      - { peers[activePeerId] ? + { peers[activePeerId] && - :null }
      @@ -286,11 +285,10 @@ class Filmstrip extends React.PureComponent
      - { spotlightsLength - :null }
      ); diff --git a/app/src/components/VideoContainers/VideoView.js b/app/src/components/VideoContainers/VideoView.js index 89f4c55..7947219 100644 --- a/app/src/components/VideoContainers/VideoView.js +++ b/app/src/components/VideoContainers/VideoView.js @@ -171,24 +171,17 @@ class VideoView extends React.PureComponent })} >
      - { audioCodec ? -

      {audioCodec}

      - :null - } + { audioCodec &&

      {audioCodec}

      } - { videoCodec ? -

      {videoCodec} {videoProfile}

      - :null - } + { videoCodec &&

      {videoCodec} {videoProfile}

      } - { (videoVisible && videoWidth !== null) ? + { (videoVisible && videoWidth !== null) &&

      {videoWidth}x{videoHeight}

      - :null }
      - { showPeerInfo ? + { showPeerInfo &&
      { isMe ? @@ -212,17 +205,15 @@ class VideoView extends React.PureComponent } - { advancedMode ? + { advancedMode &&
      {peer.device.name} {Math.floor(peer.device.version) || null}
      - :null }
      - :null } diff --git a/app/src/components/VideoWindow/NewWindow.js b/app/src/components/VideoWindow/NewWindow.js index b2255f4..1f58aa1 100644 --- a/app/src/components/VideoWindow/NewWindow.js +++ b/app/src/components/VideoWindow/NewWindow.js @@ -130,7 +130,7 @@ class NewWindow extends React.PureComponent return ReactDOM.createPortal([
      - {this.fullscreen.fullscreenEnabled && ( + { this.fullscreen.fullscreenEnabled &&
      }
      - )} + }
      - {this.props.children} + { this.props.children }
      ], this.container); } From 79609f3b7333641a46844ce0620f7594b5c1efd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Wed, 23 Oct 2019 13:08:24 +0200 Subject: [PATCH 14/74] Fixed some eslint stuff. --- app/.eslintrc.json | 333 ++++++++++++++++++++++++++++++++++++++++++++ app/package.json | 339 +-------------------------------------------- 2 files changed, 339 insertions(+), 333 deletions(-) create mode 100644 app/.eslintrc.json diff --git a/app/.eslintrc.json b/app/.eslintrc.json new file mode 100644 index 0000000..44a50ba --- /dev/null +++ b/app/.eslintrc.json @@ -0,0 +1,333 @@ +{ + "env": { + "browser": true, + "es6": true, + "node": true + }, + "plugins": [ + "import", + "react" + ], + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "react-app" + ], + "settings": { + "react": { + "pragma": "React", + "version": "16" + } + }, + "parser": "babel-eslint", + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module", + "ecmaFeatures": { + "impliedStrict": true, + "jsx": true + } + }, + "rules": { + "array-bracket-spacing": [ + 2, + "always", + { + "objectsInArrays": true, + "arraysInArrays": true + } + ], + "arrow-parens": [ + 2, + "always" + ], + "arrow-spacing": 2, + "block-spacing": [ + 2, + "always" + ], + "brace-style": [ + 2, + "allman", + { + "allowSingleLine": true + } + ], + "camelcase": 2, + "comma-dangle": 2, + "comma-spacing": [ + 2, + { + "before": false, + "after": true + } + ], + "comma-style": 2, + "computed-property-spacing": 2, + "constructor-super": 2, + "func-call-spacing": 2, + "generator-star-spacing": 2, + "guard-for-in": 2, + "indent": [ + 2, + "tab", + { + "SwitchCase": 1 + } + ], + "key-spacing": [ + 2, + { + "singleLine": { + "beforeColon": false, + "afterColon": true + }, + "multiLine": { + "beforeColon": true, + "afterColon": true, + "align": "colon" + } + } + ], + "keyword-spacing": 2, + "linebreak-style": [ + 2, + "unix" + ], + "lines-around-comment": [ + 2, + { + "allowBlockStart": true, + "allowObjectStart": true, + "beforeBlockComment": true, + "beforeLineComment": false + } + ], + "max-len": [ + 2, + 90, + { + "tabWidth": 2, + "comments": 110, + "ignoreUrls": true, + "ignoreStrings": true, + "ignoreTemplateLiterals": true, + "ignoreRegExpLiterals": true + } + ], + "newline-after-var": 2, + "newline-before-return": 2, + "newline-per-chained-call": 2, + "no-alert": 2, + "no-caller": 2, + "no-case-declarations": 2, + "no-catch-shadow": 2, + "no-class-assign": 2, + "no-confusing-arrow": [ + "error", + { + "allowParens": true + } + ], + "no-console": 2, + "no-const-assign": 2, + "no-debugger": 2, + "no-dupe-args": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-div-regex": 2, + "no-empty": [ + 2, + { + "allowEmptyCatch": true + } + ], + "no-empty-pattern": 2, + "no-else-return": 0, + "no-eval": 2, + "no-extend-native": 2, + "no-ex-assign": 2, + "no-extra-bind": 2, + "no-extra-boolean-cast": 2, + "no-extra-label": 2, + "no-extra-semi": 2, + "no-fallthrough": 2, + "no-func-assign": 2, + "no-global-assign": 2, + "no-implicit-coercion": 2, + "no-implicit-globals": 2, + "no-inner-declarations": 2, + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-lonely-if": 2, + "no-mixed-operators": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multi-spaces": 2, + "no-multi-str": 2, + "no-multiple-empty-lines": [ + 2, + { + "max": 1, + "maxEOF": 0, + "maxBOF": 0 + } + ], + "no-native-reassign": 2, + "no-negated-in-lhs": 2, + "no-new": 2, + "no-new-func": 2, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-proto": 2, + "no-prototype-builtins": 0, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-restricted-imports": 2, + "no-return-assign": 2, + "no-self-assign": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-shadow": 2, + "no-shadow-restricted-names": 2, + "no-spaced-func": 2, + "no-sparse-arrays": 2, + "no-this-before-super": 2, + "no-throw-literal": 2, + "no-undef": 2, + "no-unexpected-multiline": 2, + "no-unmodified-loop-condition": 2, + "no-unreachable": 2, + "no-unused-vars": [ + 1, + { + "vars": "all", + "args": "after-used" + } + ], + "no-use-before-define": [ + 2, + { + "functions": false + } + ], + "no-useless-call": 2, + "no-useless-computed-key": 2, + "no-useless-concat": 2, + "no-useless-rename": 2, + "no-var": 2, + "no-whitespace-before-property": 2, + "object-curly-newline": 0, + "object-curly-spacing": [ + 2, + "always" + ], + "object-property-newline": [ + 2, + { + "allowMultiplePropertiesPerLine": true + } + ], + "prefer-const": 2, + "prefer-rest-params": 2, + "prefer-spread": 2, + "prefer-template": 2, + "quotes": [ + 2, + "single", + { + "avoidEscape": true + } + ], + "semi": [ + 2, + "always" + ], + "semi-spacing": 2, + "space-before-blocks": 2, + "space-before-function-paren": [ + 2, + { + "anonymous": "never", + "named": "never", + "asyncArrow": "always" + } + ], + "space-in-parens": [ + 2, + "never" + ], + "spaced-comment": [ + 2, + "always" + ], + "strict": 2, + "valid-typeof": 2, + "eol-last": 0, + "yoda": 2, + "import/extensions": 2, + "import/no-duplicates": 2, + "jsx-quotes": [ + 2, + "prefer-single" + ], + "react/display-name": [ + 2, + { + "ignoreTranspilerName": false + } + ], + "react/forbid-prop-types": 0, + "react/jsx-boolean-value": 2, + "react/jsx-closing-bracket-location": 2, + "react/jsx-curly-spacing": 2, + "react/jsx-equals-spacing": 2, + "react/jsx-handler-names": 2, + "react/jsx-indent-props": [ + 2, + "tab" + ], + "react/jsx-indent": [ + 2, + "tab" + ], + "react/jsx-key": 2, + "react/jsx-max-props-per-line": 0, + "react/jsx-no-bind": 0, + "react/jsx-no-duplicate-props": 2, + "react/jsx-no-literals": 0, + "react/jsx-no-undef": 0, + "react/jsx-pascal-case": 2, + "react/jsx-sort-prop-types": 0, + "react/jsx-sort-props": 0, + "react/jsx-uses-react": 2, + "react/jsx-uses-vars": 2, + "react/no-danger": 2, + "react/no-deprecated": 2, + "react/no-did-mount-set-state": 2, + "react/no-did-update-set-state": 2, + "react/no-direct-mutation-state": 2, + "react/no-is-mounted": 2, + "react/no-multi-comp": 0, + "react/no-set-state": 0, + "react/no-string-refs": 0, + "react/no-unknown-property": 2, + "react/prefer-es6-class": 2, + "react/prop-types": [ + 2, + { + "skipUndeclared": true + } + ], + "react/react-in-jsx-scope": 2, + "react/self-closing-comp": 2, + "react/sort-comp": 0, + "react/jsx-wrap-multilines": [ + 2, + { + "declaration": false, + "assignment": false, + "return": true + } + ] + } +} \ No newline at end of file diff --git a/app/package.json b/app/package.json index 9a87fbb..191ff59 100644 --- a/app/package.json +++ b/app/package.json @@ -41,342 +41,15 @@ "test": "react-scripts test", "eject": "react-scripts eject" }, - "eslintConfig": { - "env": { - "browser": true, - "es6": true, - "node": true - }, - "plugins": [ - "import", - "react" - ], - "extends": [ - "eslint:recommended", - "plugin:react/recommended" - ], - "settings": { - "react": { - "pragma": "React", - "version": "16" - } - }, - "parser": "babel-eslint", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module", - "ecmaFeatures": { - "impliedStrict": true, - "jsx": true - } - }, - "rules": { - "array-bracket-spacing": [ - 2, - "always", - { - "objectsInArrays": true, - "arraysInArrays": true - } - ], - "arrow-parens": [ - 2, - "always" - ], - "arrow-spacing": 2, - "block-spacing": [ - 2, - "always" - ], - "brace-style": [ - 2, - "allman", - { - "allowSingleLine": true - } - ], - "camelcase": 2, - "comma-dangle": 2, - "comma-spacing": [ - 2, - { - "before": false, - "after": true - } - ], - "comma-style": 2, - "computed-property-spacing": 2, - "constructor-super": 2, - "func-call-spacing": 2, - "generator-star-spacing": 2, - "guard-for-in": 2, - "indent": [ - 2, - "tab", - { - "SwitchCase": 1 - } - ], - "key-spacing": [ - 2, - { - "singleLine": { - "beforeColon": false, - "afterColon": true - }, - "multiLine": { - "beforeColon": true, - "afterColon": true, - "align": "colon" - } - } - ], - "keyword-spacing": 2, - "linebreak-style": [ - 2, - "unix" - ], - "lines-around-comment": [ - 2, - { - "allowBlockStart": true, - "allowObjectStart": true, - "beforeBlockComment": true, - "beforeLineComment": false - } - ], - "max-len": [ - 2, - 90, - { - "tabWidth": 2, - "comments": 110, - "ignoreUrls": true, - "ignoreStrings": true, - "ignoreTemplateLiterals": true, - "ignoreRegExpLiterals": true - } - ], - "newline-after-var": 2, - "newline-before-return": 2, - "newline-per-chained-call": 2, - "no-alert": 2, - "no-caller": 2, - "no-case-declarations": 2, - "no-catch-shadow": 2, - "no-class-assign": 2, - "no-confusing-arrow": [ - "error", - { - "allowParens": true - } - ], - "no-console": 2, - "no-const-assign": 2, - "no-debugger": 2, - "no-dupe-args": 2, - "no-dupe-keys": 2, - "no-duplicate-case": 2, - "no-div-regex": 2, - "no-empty": [ - 2, - { - "allowEmptyCatch": true - } - ], - "no-empty-pattern": 2, - "no-else-return": 0, - "no-eval": 2, - "no-extend-native": 2, - "no-ex-assign": 2, - "no-extra-bind": 2, - "no-extra-boolean-cast": 2, - "no-extra-label": 2, - "no-extra-semi": 2, - "no-fallthrough": 2, - "no-func-assign": 2, - "no-global-assign": 2, - "no-implicit-coercion": 2, - "no-implicit-globals": 2, - "no-inner-declarations": 2, - "no-invalid-regexp": 2, - "no-irregular-whitespace": 2, - "no-lonely-if": 2, - "no-mixed-operators": 2, - "no-mixed-spaces-and-tabs": 2, - "no-multi-spaces": 2, - "no-multi-str": 2, - "no-multiple-empty-lines": [ - 2, - { - "max": 1, - "maxEOF": 0, - "maxBOF": 0 - } - ], - "no-native-reassign": 2, - "no-negated-in-lhs": 2, - "no-new": 2, - "no-new-func": 2, - "no-new-wrappers": 2, - "no-obj-calls": 2, - "no-proto": 2, - "no-prototype-builtins": 0, - "no-redeclare": 2, - "no-regex-spaces": 2, - "no-restricted-imports": 2, - "no-return-assign": 2, - "no-self-assign": 2, - "no-self-compare": 2, - "no-sequences": 2, - "no-shadow": 2, - "no-shadow-restricted-names": 2, - "no-spaced-func": 2, - "no-sparse-arrays": 2, - "no-this-before-super": 2, - "no-throw-literal": 2, - "no-undef": 2, - "no-unexpected-multiline": 2, - "no-unmodified-loop-condition": 2, - "no-unreachable": 2, - "no-unused-vars": [ - 1, - { - "vars": "all", - "args": "after-used" - } - ], - "no-use-before-define": [ - 2, - { - "functions": false - } - ], - "no-useless-call": 2, - "no-useless-computed-key": 2, - "no-useless-concat": 2, - "no-useless-rename": 2, - "no-var": 2, - "no-whitespace-before-property": 2, - "object-curly-newline": 0, - "object-curly-spacing": [ - 2, - "always" - ], - "object-property-newline": [ - 2, - { - "allowMultiplePropertiesPerLine": true - } - ], - "prefer-const": 2, - "prefer-rest-params": 2, - "prefer-spread": 2, - "prefer-template": 2, - "quotes": [ - 2, - "single", - { - "avoidEscape": true - } - ], - "semi": [ - 2, - "always" - ], - "semi-spacing": 2, - "space-before-blocks": 2, - "space-before-function-paren": [ - 2, - { - "anonymous": "never", - "named": "never", - "asyncArrow": "always" - } - ], - "space-in-parens": [ - 2, - "never" - ], - "spaced-comment": [ - 2, - "always" - ], - "strict": 2, - "valid-typeof": 2, - "eol-last": 0, - "yoda": 2, - "import/extensions": 2, - "import/no-duplicates": 2, - "jsx-quotes": [ - 2, - "prefer-single" - ], - "react/display-name": [ - 2, - { - "ignoreTranspilerName": false - } - ], - "react/forbid-prop-types": 0, - "react/jsx-boolean-value": 2, - "react/jsx-closing-bracket-location": 2, - "react/jsx-curly-spacing": 2, - "react/jsx-equals-spacing": 2, - "react/jsx-handler-names": 2, - "react/jsx-indent-props": [ - 2, - "tab" - ], - "react/jsx-indent": [ - 2, - "tab" - ], - "react/jsx-key": 2, - "react/jsx-max-props-per-line": 0, - "react/jsx-no-bind": 0, - "react/jsx-no-duplicate-props": 2, - "react/jsx-no-literals": 0, - "react/jsx-no-undef": 0, - "react/jsx-pascal-case": 2, - "react/jsx-sort-prop-types": 0, - "react/jsx-sort-props": 0, - "react/jsx-uses-react": 2, - "react/jsx-uses-vars": 2, - "react/no-danger": 2, - "react/no-deprecated": 2, - "react/no-did-mount-set-state": 2, - "react/no-did-update-set-state": 2, - "react/no-direct-mutation-state": 2, - "react/no-is-mounted": 2, - "react/no-multi-comp": 0, - "react/no-set-state": 0, - "react/no-string-refs": 0, - "react/no-unknown-property": 2, - "react/prefer-es6-class": 2, - "react/prop-types": [ - 2, - { - "skipUndeclared": true - } - ], - "react/react-in-jsx-scope": 2, - "react/self-closing-comp": 2, - "react/sort-comp": 0, - "react/jsx-wrap-multilines": [ - 2, - { - "declaration": false, - "assignment": false, - "return": true - } - ] - } - }, "browserslist": [ ">0.2%", "not dead", "not ie > 0", "not op_mini all" - ] + ], + "devDependencies": { + "eslint": "^6.5.1", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-react": "^7.16.0" + } } From 7b5601562f4ab70cd852e94bf51c97e9040329a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Wed, 23 Oct 2019 13:11:06 +0200 Subject: [PATCH 15/74] Small fixes. --- app/src/components/App.js | 2 +- app/src/components/Lobby.js | 12 +----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/app/src/components/App.js b/app/src/components/App.js index 9cd566b..ce29f80 100644 --- a/app/src/components/App.js +++ b/app/src/components/App.js @@ -29,7 +29,7 @@ const App = (props) => ); } -} +}; App.propTypes = { diff --git a/app/src/components/Lobby.js b/app/src/components/Lobby.js index 0d29762..915bc43 100644 --- a/app/src/components/Lobby.js +++ b/app/src/components/Lobby.js @@ -4,12 +4,8 @@ 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 Paper from '@material-ui/core/Paper'; -import DialogActions from '@material-ui/core/DialogActions'; -import Button from '@material-ui/core/Button'; -import TextField from '@material-ui/core/TextField'; const styles = (theme) => ({ @@ -53,9 +49,6 @@ const styles = (theme) => }); const Lobby = ({ - roomClient, - displayName, - changeDisplayName, classes }) => { @@ -70,10 +63,7 @@ const Lobby = ({ Lobby.propTypes = { - roomClient : PropTypes.any.isRequired, - displayName : PropTypes.string.isRequired, - changeDisplayName : PropTypes.func.isRequired, - classes : PropTypes.object.isRequired + classes : PropTypes.object.isRequired }; const mapStateToProps = (state) => From abca024c84a7b2ccc21e8e8e9bfc9fe6d225b077 Mon Sep 17 00:00:00 2001 From: Stefan Otto Date: Wed, 23 Oct 2019 13:29:55 +0200 Subject: [PATCH 16/74] eslint fixes;styling --- .../AccessControl/LockDialog/ListLobbyPeer.js | 17 +++++---- .../AccessControl/LockDialog/LockDialog.js | 35 ++++++++++--------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/app/src/components/AccessControl/LockDialog/ListLobbyPeer.js b/app/src/components/AccessControl/LockDialog/ListLobbyPeer.js index 665561d..814dbcd 100644 --- a/app/src/components/AccessControl/LockDialog/ListLobbyPeer.js +++ b/app/src/components/AccessControl/LockDialog/ListLobbyPeer.js @@ -69,6 +69,10 @@ const styles = (theme) => { backgroundColor : 'var(--media-control-botton-on)' } + }, + ListItem : + { + alignItems: 'center' } }); @@ -84,13 +88,14 @@ const ListLobbyPeer = (props) => return ( - + @@ -99,10 +104,10 @@ const ListLobbyPeer = (props) => disabled : peer.promotionInProgress })} onClick={(e) => - { - e.stopPropagation(); - roomClient.promoteLobbyPeer(peer.id); - }} + { + e.stopPropagation(); + roomClient.promoteLobbyPeer(peer.id); + }} > diff --git a/app/src/components/AccessControl/LockDialog/LockDialog.js b/app/src/components/AccessControl/LockDialog/LockDialog.js index 021cf20..f5b10ca 100644 --- a/app/src/components/AccessControl/LockDialog/LockDialog.js +++ b/app/src/components/AccessControl/LockDialog/LockDialog.js @@ -16,15 +16,14 @@ 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 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 : @@ -52,7 +51,7 @@ const styles = (theme) => }, lock : { - padding : theme.spacing.unit * 2 + padding : theme.spacing(2) } }); @@ -60,7 +59,7 @@ const LockDialog = ({ roomClient, room, handleCloseLockDialog, - handleAccessCode, + // handleAccessCode, lobbyPeers, classes }) => @@ -123,21 +122,25 @@ const LockDialog = ({ - { lobbyPeers.length > 0 ? - - Participants in Lobby - - }> - { - lobbyPeers.map((peerId) => { return (); }) + { (lobbyPeers.length > 0) && + + Participants in Lobby + } + > + { + lobbyPeers.map((peerId) => + { + return (); + }) + } - : null } - From dd9f95971db92958bdc963898dc0eb8f48a26c8e Mon Sep 17 00:00:00 2001 From: Stefan Otto Date: Wed, 23 Oct 2019 14:37:04 +0200 Subject: [PATCH 17/74] fix: rooms not closed on server after all peers left --- server/lib/Room.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/server/lib/Room.js b/server/lib/Room.js index f382880..e702367 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -163,17 +163,13 @@ class Room extends EventEmitter this._lobby.close(); - // Close the peers - if (this._peers) + Object.values(this._peers).forEach((peer) => { - Object.values(this._peers).forEach((peer) => - { - if (peer.socket) - peer.socket.disconnect(); - }); - } + if (peer.socket) + peer.socket.disconnect(); + }); - this._peers.clear(); + this._peers = {}; // Close the mediasoup Router. this._mediasoupRouter.close(); @@ -325,14 +321,14 @@ class Room extends EventEmitter delete this._peers[peer.id]; // If this is the latest Peer in the room, close the room after a while. - if (this._peers.size === 0) + if (Object.keys(this._peers).length == 0) { setTimeout(() => { if (this._closed) return; - if (this._peers.size === 0) + if (Object.keys(this._peers).length == 0) { logger.info( 'last Peer in the room left, closing the room [roomId:%s]', From 6596deda2a710b3fd82ee874defa30369233b509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Wed, 23 Oct 2019 19:46:44 +0200 Subject: [PATCH 18/74] Various small changes. --- app/src/RoomClient.js | 53 ++---- app/src/components/JoinDialog.js | 57 +++++- app/src/components/PeerAudio/PeerAudio.js | 3 +- .../components/VideoContainers/VideoView.js | 5 +- server/.eslintrc.js | 168 ----------------- server/.eslintrc.json | 171 ++++++++++++++++++ server/gulpfile.js | 44 ----- server/http-helpers.js | 43 ----- server/lib/Lobby.js | 8 +- server/lib/Logger.js | 2 - server/lib/Room.js | 52 +++--- server/package.json | 49 +++-- server/server.js | 49 +++-- server/util.js | 18 -- 14 files changed, 319 insertions(+), 403 deletions(-) delete mode 100644 server/.eslintrc.js create mode 100644 server/.eslintrc.json delete mode 100644 server/gulpfile.js delete mode 100644 server/http-helpers.js delete mode 100644 server/util.js diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 7c586ef..99bf103 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -610,61 +610,44 @@ export default class RoomClient const { chatHistory, fileHistory, - lastN, + lastNHistory, locked, lobbyPeers, accessCode } = await this.sendRequest('serverHistory'); - if (chatHistory.length > 0) - { - logger.debug('Got chat history'); - store.dispatch( - stateActions.addChatHistory(chatHistory)); - } + (chatHistory.length > 0) && store.dispatch( + stateActions.addChatHistory(chatHistory)); - if (fileHistory.length > 0) - { - logger.debug('Got files history'); + (fileHistory.length > 0) && store.dispatch( + stateActions.addFileHistory(fileHistory)); - store.dispatch(stateActions.addFileHistory(fileHistory)); - } - - if (lastN.length > 0) + if (lastNHistory.length > 0) { - logger.debug('Got lastN'); + logger.debug('Got lastNHistory'); // Remove our self from list - const index = lastN.indexOf(this._peerId); + const index = lastNHistory.indexOf(this._peerId); - lastN.splice(index, 1); + lastNHistory.splice(index, 1); - this._spotlights.addSpeakerList(lastN); + this._spotlights.addSpeakerList(lastNHistory); } locked ? store.dispatch(stateActions.setRoomLocked()) : store.dispatch(stateActions.setRoomUnLocked()); - if (lobbyPeers.length > 0) + (lobbyPeers.length > 0) && lobbyPeers.forEach((peer) => { - logger.debug('Got lobby peers'); + store.dispatch( + stateActions.addLobbyPeer(peer.peerId)); + store.dispatch( + stateActions.setLobbyPeerDisplayName(peer.displayName)); + }); - lobbyPeers.forEach((peer) => - { - store.dispatch( - stateActions.addLobbyPeer(peer.peerId)); - store.dispatch( - stateActions.setLobbyPeerDisplayName(peer.displayName)); - }); - } - - if (accessCode != null) - { - logger.debug('Got accessCode'); - - store.dispatch(stateActions.setAccessCode(accessCode)) - } + (accessCode != null) && store.dispatch( + stateActions.setAccessCode(accessCode)); } catch (error) { diff --git a/app/src/components/JoinDialog.js b/app/src/components/JoinDialog.js index 1864dbc..e27d78e 100644 --- a/app/src/components/JoinDialog.js +++ b/app/src/components/JoinDialog.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { connect } from 'react-redux'; import { withStyles } from '@material-ui/core/styles'; import { withRoomContext } from '../RoomContext'; @@ -58,6 +58,28 @@ const JoinDialog = ({ classes }) => { + const [ localDisplayName, setLocalDisplayName ] = useState(displayName); + + const handleKeyDown = (event) => + { + const { key } = event; + + switch (key) + { + case 'Enter': + case 'Escape': + { + if (localDisplayName !== '') // Don't allow empty displayName + changeDisplayName(localDisplayName); + else + setLocalDisplayName(displayName); + break; + } + default: + break; + } + }; + return (
      } - + + Welcome + + You are about to join a meeting. - Set your name that others will see, - and chose how you want to join? + Set the name that others will see, + and choose how you want to join? { const { value } = event.target; - changeDisplayName(value); + setLocalDisplayName(value); + }} + onKeyDown={handleKeyDown} + onBlur={() => + { + if (localDisplayName !== displayName) + changeDisplayName(localDisplayName); }} margin='normal' /> @@ -94,6 +125,17 @@ const JoinDialog = ({ roomClient.join({ joinVideo: false }); }} variant='contained' + color='secondary' + > + Sign in + + @@ -103,6 +145,7 @@ const JoinDialog = ({ roomClient.join({ joinVideo: true }); }} variant='contained' + color='secondary' > Audio and Video diff --git a/app/src/components/PeerAudio/PeerAudio.js b/app/src/components/PeerAudio/PeerAudio.js index 2e9c195..38d7faf 100644 --- a/app/src/components/PeerAudio/PeerAudio.js +++ b/app/src/components/PeerAudio/PeerAudio.js @@ -29,7 +29,8 @@ export default class PeerAudio extends React.PureComponent this._setTrack(audioTrack); } - componentWillReceiveProps(nextProps) + // eslint-disable-next-line camelcase + UNSAFE_componentWillReceiveProps(nextProps) { const { audioTrack } = nextProps; diff --git a/app/src/components/VideoContainers/VideoView.js b/app/src/components/VideoContainers/VideoView.js index 7947219..b90cc81 100644 --- a/app/src/components/VideoContainers/VideoView.js +++ b/app/src/components/VideoContainers/VideoView.js @@ -221,7 +221,7 @@ class VideoView extends React.PureComponent ref='video' className={classnames(classes.video, { hidden : !videoVisible, - 'isMe' : isMe && !isScreen, + 'isMe' : isMe && !isScreen, loading : videoProfile === 'none', contain : videoContain })} @@ -247,7 +247,8 @@ class VideoView extends React.PureComponent clearInterval(this._videoResolutionTimer); } - componentWillReceiveProps(nextProps) + // eslint-disable-next-line camelcase + UNSAFE_componentWillReceiveProps(nextProps) { const { videoTrack } = nextProps; diff --git a/server/.eslintrc.js b/server/.eslintrc.js deleted file mode 100644 index bcdd959..0000000 --- a/server/.eslintrc.js +++ /dev/null @@ -1,168 +0,0 @@ -module.exports = -{ - env: - { - browser: true, - es6: true, - node: true - }, - extends: - [ - 'eslint:recommended' - ], - settings: {}, - parserOptions: - { - ecmaVersion: 6, - sourceType: 'module', - ecmaFeatures: - { - impliedStrict: true - } - }, - rules: - { - 'array-bracket-spacing': [ 2, 'always', - { - objectsInArrays: true, - arraysInArrays: true - }], - 'arrow-parens': [ 2, 'always' ], - 'arrow-spacing': 2, - 'block-spacing': [ 2, 'always' ], - 'brace-style': [ 2, 'allman', { allowSingleLine: true } ], - 'camelcase': 2, - 'comma-dangle': 2, - 'comma-spacing': [ 2, { before: false, after: true } ], - 'comma-style': 2, - 'computed-property-spacing': 2, - 'constructor-super': 2, - 'func-call-spacing': 2, - 'generator-star-spacing': 2, - 'guard-for-in': 2, - 'indent': [ 2, 'tab', { 'SwitchCase': 1 } ], - 'key-spacing': [ 2, - { - singleLine: - { - beforeColon: false, - afterColon: true - }, - multiLine: - { - beforeColon: true, - afterColon: true, - align: 'colon' - } - }], - 'keyword-spacing': 2, - 'linebreak-style': [ 2, 'unix' ], - 'lines-around-comment': [ 2, - { - allowBlockStart: true, - allowObjectStart: true, - beforeBlockComment: true, - beforeLineComment: false - }], - 'max-len': [ 2, 90, - { - tabWidth: 2, - comments: 110, - ignoreUrls: true, - ignoreStrings: true, - ignoreTemplateLiterals: true, - ignoreRegExpLiterals: true - }], - 'newline-after-var': 2, - 'newline-before-return': 2, - 'newline-per-chained-call': 2, - 'no-alert': 2, - 'no-caller': 2, - 'no-case-declarations': 2, - 'no-catch-shadow': 2, - 'no-class-assign': 2, - 'no-confusing-arrow': 2, - 'no-console': 2, - 'no-const-assign': 2, - 'no-debugger': 2, - 'no-dupe-args': 2, - 'no-dupe-keys': 2, - 'no-duplicate-case': 2, - 'no-div-regex': 2, - 'no-empty': [ 2, { allowEmptyCatch: true } ], - 'no-empty-pattern': 2, - 'no-else-return': 0, - 'no-eval': 2, - 'no-extend-native': 2, - 'no-ex-assign': 2, - 'no-extra-bind': 2, - 'no-extra-boolean-cast': 2, - 'no-extra-label': 2, - 'no-extra-semi': 2, - 'no-fallthrough': 2, - 'no-func-assign': 2, - 'no-global-assign': 2, - 'no-implicit-coercion': 2, - 'no-implicit-globals': 2, - 'no-inner-declarations': 2, - 'no-invalid-regexp': 2, - 'no-invalid-this': 2, - 'no-irregular-whitespace': 2, - 'no-lonely-if': 2, - 'no-mixed-operators': 2, - 'no-mixed-spaces-and-tabs': 2, - 'no-multi-spaces': 2, - 'no-multi-str': 2, - 'no-multiple-empty-lines': [ 2, { max: 1, maxEOF: 0, maxBOF: 0 } ], - 'no-native-reassign': 2, - 'no-negated-in-lhs': 2, - 'no-new': 2, - 'no-new-func': 2, - 'no-new-wrappers': 2, - 'no-obj-calls': 2, - 'no-proto': 2, - 'no-prototype-builtins': 0, - 'no-redeclare': 2, - 'no-regex-spaces': 2, - 'no-restricted-imports': 2, - 'no-return-assign': 2, - 'no-self-assign': 2, - 'no-self-compare': 2, - 'no-sequences': 2, - 'no-shadow': 2, - 'no-shadow-restricted-names': 2, - 'no-spaced-func': 2, - 'no-sparse-arrays': 2, - 'no-this-before-super': 2, - 'no-throw-literal': 2, - 'no-undef': 2, - 'no-unexpected-multiline': 2, - 'no-unmodified-loop-condition': 2, - 'no-unreachable': 2, - 'no-unused-vars': [ 1, { vars: 'all', args: 'after-used' }], - 'no-use-before-define': [ 2, { functions: false } ], - 'no-useless-call': 2, - 'no-useless-computed-key': 2, - 'no-useless-concat': 2, - 'no-useless-rename': 2, - 'no-var': 2, - 'no-whitespace-before-property': 2, - 'object-curly-newline': 0, - 'object-curly-spacing': [ 2, 'always' ], - 'object-property-newline': [ 2, { allowMultiplePropertiesPerLine: true } ], - 'prefer-const': 2, - 'prefer-rest-params': 2, - 'prefer-spread': 2, - 'prefer-template': 2, - 'quotes': [ 2, 'single', { avoidEscape: true } ], - 'semi': [ 2, 'always' ], - 'semi-spacing': 2, - 'space-before-blocks': 2, - 'space-before-function-paren': [ 2, 'never' ], - 'space-in-parens': [ 2, 'never' ], - 'spaced-comment': [ 2, 'always' ], - 'strict': 0, - 'valid-typeof': 2, - 'yoda': 2 - } -}; diff --git a/server/.eslintrc.json b/server/.eslintrc.json new file mode 100644 index 0000000..d98852a --- /dev/null +++ b/server/.eslintrc.json @@ -0,0 +1,171 @@ +{ + "env": + { + "es6": true, + "node": true + }, + "extends": + [ + "eslint:recommended" + ], + "settings": {}, + "parserOptions": + { + "ecmaVersion": 2018, + "sourceType": "module", + "ecmaFeatures": + { + "impliedStrict": true + } + }, + "rules": + { + "array-bracket-spacing": [ 2, "always", + { + "objectsInArrays": true, + "arraysInArrays": true + }], + "arrow-parens": [ 2, "always" ], + "arrow-spacing": 2, + "block-spacing": [ 2, "always" ], + "brace-style": [ 2, "allman", { "allowSingleLine": true } ], + "camelcase": 2, + "comma-dangle": 2, + "comma-spacing": [ 2, { "before": false, "after": true } ], + "comma-style": 2, + "computed-property-spacing": 2, + "constructor-super": 2, + "func-call-spacing": 2, + "generator-star-spacing": 2, + "guard-for-in": 2, + "indent": [ 2, "tab", { "SwitchCase": 1 } ], + "key-spacing": [ 2, + { + "singleLine": + { + "beforeColon": false, + "afterColon": true + }, + "multiLine": + { + "beforeColon": true, + "afterColon": true, + "align": "colon" + } + }], + "keyword-spacing": 2, + "linebreak-style": [ 2, "unix" ], + "lines-around-comment": [ 2, + { + "allowBlockStart": true, + "allowObjectStart": true, + "beforeBlockComment": true, + "beforeLineComment": false + }], + "max-len": [ 2, 90, + { + "tabWidth": 2, + "comments": 90, + "ignoreUrls": true, + "ignoreStrings": true, + "ignoreTemplateLiterals": true, + "ignoreRegExpLiterals": true + }], + "newline-after-var": 2, + "newline-before-return": 2, + "newline-per-chained-call": 2, + "no-alert": 2, + "no-caller": 2, + "no-case-declarations": 2, + "no-catch-shadow": 2, + "no-class-assign": 2, + "no-confusing-arrow": 2, + "no-console": 2, + "no-const-assign": 2, + "no-debugger": 2, + "no-dupe-args": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-div-regex": 2, + "no-empty": [ 2, { "allowEmptyCatch": true } ], + "no-empty-pattern": 2, + "no-else-return": 0, + "no-eval": 2, + "no-extend-native": 2, + "no-ex-assign": 2, + "no-extra-bind": 2, + "no-extra-boolean-cast": 2, + "no-extra-label": 2, + "no-extra-semi": 2, + "no-fallthrough": 2, + "no-func-assign": 2, + "no-global-assign": 2, + "no-implicit-coercion": 2, + "no-implicit-globals": 2, + "no-inner-declarations": 2, + "no-invalid-regexp": 2, + "no-invalid-this": 2, + "no-irregular-whitespace": 2, + "no-lonely-if": 2, + "no-mixed-operators": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multi-spaces": 2, + "no-multi-str": 2, + "no-multiple-empty-lines": [ 1, { "max": 1, "maxEOF": 0, "maxBOF": 0 } ], + "no-native-reassign": 2, + "no-negated-in-lhs": 2, + "no-new": 2, + "no-new-func": 2, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-proto": 2, + "no-prototype-builtins": 0, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-restricted-imports": 2, + "no-return-assign": 2, + "no-self-assign": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-shadow": 2, + "no-shadow-restricted-names": 2, + "no-spaced-func": 2, + "no-sparse-arrays": 2, + "no-this-before-super": 2, + "no-throw-literal": 2, + "no-undef": 2, + "no-unexpected-multiline": 2, + "no-unmodified-loop-condition": 2, + "no-unreachable": 2, + "no-unused-vars": [ 1, { "vars": "all", "args": "after-used" }], + "no-use-before-define": [ 2, { "functions": false } ], + "no-useless-call": 2, + "no-useless-computed-key": 2, + "no-useless-concat": 2, + "no-useless-rename": 2, + "no-var": 2, + "no-whitespace-before-property": 2, + "object-curly-newline": 0, + "object-curly-spacing": [ 2, "always" ], + "object-property-newline": [ 2, { "allowMultiplePropertiesPerLine": true } ], + "prefer-const": 2, + "prefer-rest-params": 2, + "prefer-spread": 2, + "prefer-template": 2, + "quotes": [ 2, "single", { "avoidEscape": true } ], + "semi": [ 2, "always" ], + "semi-spacing": 2, + "space-before-blocks": 2, + "space-before-function-paren": [ 2, + { + "anonymous" : "never", + "named" : "never", + "asyncArrow" : "always" + }], + "space-in-parens": [ 2, "never" ], + "spaced-comment": [ 2, "always" ], + "strict": 2, + "valid-typeof": 2, + "yoda": 2 + } +} \ No newline at end of file diff --git a/server/gulpfile.js b/server/gulpfile.js deleted file mode 100644 index fa03823..0000000 --- a/server/gulpfile.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Tasks: - * - * gulp lint - * Checks source code - * - * gulp watch - * Observes changes in the code - * - * gulp - * Invokes both `lint` and `watch` tasks - */ - -const gulp = require('gulp'); -const plumber = require('gulp-plumber'); -const eslint = require('gulp-eslint'); - -const LINTING_FILES = -[ - 'gulpfile.js', - 'server.js', - 'config/config.example.js', - 'lib/**/*.js' -]; - -gulp.task('lint', () => -{ - - return gulp.src(LINTING_FILES) - .pipe(plumber()) - .pipe(eslint()) - .pipe(eslint.format()); -}); - -gulp.task('lint-fix', function() -{ - return gulp.src(LINTING_FILES) - .pipe(plumber()) - .pipe(eslint({ fix: true })) - .pipe(eslint.format()) - .pipe(gulp.dest((file) => file.base)); -}); - -gulp.task('default', gulp.series('lint')); diff --git a/server/http-helpers.js b/server/http-helpers.js deleted file mode 100644 index 9195b2f..0000000 --- a/server/http-helpers.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -const headers = { - 'access-control-allow-origin': '*', - 'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS', - 'access-control-allow-headers': 'content-type, accept', - 'access-control-max-age': 10, - 'Content-Type': 'application/json' -}; - -exports.prepareResponse = (req, cb) => -{ - let data = ''; - - req.on('data', (chunk) => - { - data += chunk; - }); - - req.on('end', () => - { - cb(data); - }); -}; - -exports.respond = (res, data, status) => -{ - status = status || 200; - res.writeHead(status, headers); - res.end(data); -}; - -exports.send404 = (res) => -{ - exports.respond(res, 'Not Found', 404); -}; - -exports.redirector = (res, loc, status) => -{ - status = status || 302; - res.writeHead(status, { Location: loc }); - res.end(); -}; diff --git a/server/lib/Lobby.js b/server/lib/Lobby.js index 62da0e5..e21be1d 100644 --- a/server/lib/Lobby.js +++ b/server/lib/Lobby.js @@ -1,5 +1,3 @@ -'use strict'; - const EventEmitter = require('events').EventEmitter; const Logger = require('./Logger'); @@ -38,7 +36,11 @@ class Lobby extends EventEmitter { logger.info('peerList()'); - return Object.values(this._peers).map((peer) => ({ peerId: peer.peerId, displayName: peer.displayName })); + return Object.values(this._peers).map((peer) => + ({ + peerId : peer.peerId, + displayName : peer.displayName + })); } hasPeer(peerId) diff --git a/server/lib/Logger.js b/server/lib/Logger.js index e1659dd..dc85948 100644 --- a/server/lib/Logger.js +++ b/server/lib/Logger.js @@ -1,5 +1,3 @@ -'use strict'; - const debug = require('debug'); const APP_NAME = 'multiparty-meeting-server'; diff --git a/server/lib/Room.js b/server/lib/Room.js index e702367..6335b56 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -1,5 +1,3 @@ -'use strict'; - const EventEmitter = require('events').EventEmitter; const Logger = require('./Logger'); const Lobby = require('./Lobby'); @@ -23,7 +21,7 @@ class Room extends EventEmitter logger.info('create() [roomId:%s, forceH264:%s]', roomId); // Router media codecs. - let mediaCodecs = config.mediasoup.router.mediaCodecs; + const mediaCodecs = config.mediasoup.router.mediaCodecs; // Create a mediasoup Router. const mediasoupRouter = await mediasoupWorker.createRouter({ mediaCodecs }); @@ -58,18 +56,19 @@ class Room extends EventEmitter // 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 ) + // access code to the room, + // applicable if ( _locked == true and _joinByAccessCode == true ) this._accessCode = ''; this._lobby = new Lobby(); - this._lobby.on('promotePeer', (peer) => + this._lobby.on('promotePeer', (promotedPeer) => { - logger.info('promotePeer() [peer:"%o"]', peer); + logger.info('promotePeer() [promotedPeer:"%o"]', promotedPeer); - const { peerId } = peer; + const { peerId } = promotedPeer; - this._peerJoining({ ...peer }); + this._peerJoining({ ...promotedPeer }); Object.values(this._peers).forEach((peer) => { @@ -77,9 +76,9 @@ class Room extends EventEmitter }); }); - this._lobby.on('lobbyPeerDisplayNameChanged', (peer) => + this._lobby.on('lobbyPeerDisplayNameChanged', (changedPeer) => { - const { peerId, displayName } = peer; + const { peerId, displayName } = changedPeer; Object.values(this._peers).forEach((peer) => { @@ -87,11 +86,11 @@ class Room extends EventEmitter }); }); - this._lobby.on('peerClosed', (peer) => + this._lobby.on('peerClosed', (closedPeer) => { - logger.info('peerClosed() [peer:"%o"]', peer); + logger.info('peerClosed() [closedPeer:"%o"]', closedPeer); - const { peerId } = peer; + const { peerId } = closedPeer; Object.values(this._peers).forEach((peer) => { @@ -141,7 +140,7 @@ class Room extends EventEmitter // Notify all Peers. Object.values(this._peers).forEach((peer) => { - this._notification(peer.socket, 'activeSpeaker', { peerId : null }); + this._notification(peer.socket, 'activeSpeaker', { peerId: null }); }); }); @@ -223,7 +222,7 @@ class Room extends EventEmitter { socket.join(this._roomId); - const peer = { id : peerId, socket : socket }; + const peer = { id: peerId, socket: socket }; const index = this._lastN.indexOf(peerId); @@ -243,13 +242,13 @@ class Room extends EventEmitter return this._locked; } - peerAuthenticated(peerid) + peerAuthenticated(peerId) { logger.debug('peerAuthenticated() | [peerId:"%s"]', peerId); if (!this._locked) { - if (!Boolean(this._peers[peerid])) + if (!this._peers[peerId]) { this._lobby.promotePeer(peerId); } @@ -810,15 +809,16 @@ class Room extends EventEmitter { // Return to sender const lobbyPeers = this._lobby.peerList(); + cb( null, { - chatHistory : this._chatHistory, - fileHistory : this._fileHistory, - lastN : this._lastN, - locked : this._locked, - lobbyPeers : lobbyPeers, - accessCode : this._accessCode + chatHistory : this._chatHistory, + fileHistory : this._fileHistory, + lastNHistory : this._lastN, + locked : this._locked, + lobbyPeers : lobbyPeers, + accessCode : this._accessCode } ); @@ -867,7 +867,7 @@ class Room extends EventEmitter peerId : peer.id, accessCode : accessCode }, true); - //} + // } // Return no error cb(); @@ -883,7 +883,7 @@ class Room extends EventEmitter // Spread to others this._notification(peer.socket, 'setJoinByAccessCode', { - peerId : peer.id, + peerId : peer.id, joinByAccessCode : joinByAccessCode }, true); @@ -892,7 +892,6 @@ class Room extends EventEmitter break; } - case 'promotePeer': { @@ -1080,7 +1079,6 @@ class Room extends EventEmitter kind : producer.kind, producerId : producer.id, id : consumer.id, - kind : consumer.kind, rtpParameters : consumer.rtpParameters, type : consumer.type, appData : producer.appData, diff --git a/server/package.json b/server/package.json index 76e6ab6..4a80107 100644 --- a/server/package.json +++ b/server/package.json @@ -1,29 +1,24 @@ { - "name": "multiparty-meeting-server", - "version": "3.0.0", - "private": true, - "description": "multiparty meeting server", - "author": "Håvar Aambø Fosstveit ", - "license": "MIT", - "main": "lib/index.js", - "dependencies": { - "awaitqueue": "^1.0.0", - "base-64": "^0.1.0", - "colors": "^1.4.0", - "compression": "^1.7.4", - "debug": "^4.1.1", - "express": "^4.17.1", - "express-session": "^1.17.0", - "express-socket.io-session": "^1.3.5", - "mediasoup": "^3.0.12", - "openid-client": "^3.7.3", - "passport": "^0.4.0", - "socket.io": "^2.3.0", - "spdy": "^4.0.1" - }, - "devDependencies": { - "gulp": "^4.0.2", - "gulp-eslint": "^6.0.0", - "gulp-plumber": "^1.2.1" - } + "name": "multiparty-meeting-server", + "version": "3.0.0", + "private": true, + "description": "multiparty meeting server", + "author": "Håvar Aambø Fosstveit ", + "license": "MIT", + "main": "lib/index.js", + "dependencies": { + "awaitqueue": "^1.0.0", + "base-64": "^0.1.0", + "colors": "^1.4.0", + "compression": "^1.7.4", + "debug": "^4.1.1", + "express": "^4.17.1", + "express-session": "^1.17.0", + "express-socket.io-session": "^1.3.5", + "mediasoup": "^3.0.12", + "openid-client": "^3.7.3", + "passport": "^0.4.0", + "socket.io": "^2.3.0", + "spdy": "^4.0.1" + } } diff --git a/server/server.js b/server/server.js index 4e42518..32d953d 100755 --- a/server/server.js +++ b/server/server.js @@ -1,7 +1,5 @@ #!/usr/bin/env node -'use strict'; - process.title = 'multiparty-meeting-server'; const config = require('./config/config'); @@ -14,7 +12,6 @@ const mediasoup = require('mediasoup'); const AwaitQueue = require('awaitqueue'); const Logger = require('./lib/Logger'); const Room = require('./lib/Room'); -const utils = require('./util'); const base64 = require('base-64'); // auth const passport = require('passport'); @@ -26,7 +23,7 @@ const sharedSession = require('express-socket.io-session'); console.log('- process.env.DEBUG:', process.env.DEBUG); console.log('- config.mediasoup.logLevel:', config.mediasoup.logLevel); console.log('- config.mediasoup.logTags:', config.mediasoup.logTags); -/* eslint-enable nopassportSocketIo-console */ +/* eslint-enable no-console */ const logger = new Logger(); @@ -57,11 +54,12 @@ const session = expressSession({ resave : true, saveUninitialized : true, cookie : { secure: true } -}) +}); app.use(session); let httpsServer; +let io; let oidcClient; let oidcStrategy; @@ -99,10 +97,10 @@ async function run() // Run WebSocketServer. await runWebSocketServer(); }) - .catch((err) => - { - logger.error(err); - }); + .catch((err) => + { + logger.error(err); + }); } else { @@ -219,8 +217,7 @@ async function setupAuth(oidcIssuer) state : base64.encode(JSON.stringify({ id : req.query.id, roomId : req.query.roomId, - peerId : req.query.peerId, - code : utils.random(10) + peerId : req.query.peerId })) })(req, res, next); }); @@ -265,14 +262,14 @@ async function setupAuth(oidcIssuer) room.peerAuthenticated(state.peerId); io.sockets.socket(state.id).emit('notification', - { - method: 'auth', - data : { - displayName : displayName, - picture : photo - } - }); + method : 'auth', + data : + { + displayName : displayName, + picture : photo + } + }); res.send(''); } @@ -325,11 +322,11 @@ async function runHttpsServer() */ async function runWebSocketServer() { - const io = require('socket.io')(httpsServer); + io = require('socket.io')(httpsServer); io.use( sharedSession(session, { - autoSave: true + autoSave : true }) ); @@ -356,14 +353,14 @@ async function runWebSocketServer() room.handleConnection({ peerId, socket }); }) - .catch((error) => - { - logger.error('room creation or room joining failed:%o', error); + .catch((error) => + { + logger.error('room creation or room joining failed:%o', error); - socket.disconnect(true); + socket.disconnect(true); - return; - }); + return; + }); }); } diff --git a/server/util.js b/server/util.js deleted file mode 100644 index cfa4548..0000000 --- a/server/util.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -var crypto = require('crypto'); - -exports.random = function (howMany, chars) { - chars = chars - || "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789"; - var rnd = crypto.randomBytes(howMany) - , value = new Array(howMany) - , len = len = Math.min(256, chars.length) - , d = 256 / len - - for (var i = 0; i < howMany; i++) { - value[i] = chars[Math.floor(rnd[i] / d)] - }; - - return value.join(''); -} From 1ee666125d1a1de07d0c404591b5330bbf94eaac Mon Sep 17 00:00:00 2001 From: Stefan Otto Date: Wed, 23 Oct 2019 23:56:20 +0200 Subject: [PATCH 19/74] Fix: closing room only if Lobby is empty --- server/lib/Lobby.js | 22 +++++++++++++---- server/lib/Room.js | 57 +++++++++++++++++++++++++++++++-------------- server/server.js | 9 +++++++ 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/server/lib/Lobby.js b/server/lib/Lobby.js index 62da0e5..fe88377 100644 --- a/server/lib/Lobby.js +++ b/server/lib/Lobby.js @@ -34,6 +34,14 @@ class Lobby extends EventEmitter this._peers = {}; } + checkEmpty() + { + logger.info('checkEmpty()'); + if (Object.keys(this._peers).length == 0) + return true + else return false; + } + peerList() { logger.info('peerList()'); @@ -75,6 +83,8 @@ class Lobby extends EventEmitter { logger.info('parkPeer()'); + if ( this._closed ) return; + const peer = { peerId, socket, consume }; socket.emit('notification', { method: 'enteredLobby', data: {} }); @@ -86,7 +96,8 @@ class Lobby extends EventEmitter logger.debug( 'Peer "request" event [method:%s, peerId:%s]', request.method, peer.peerId); - + + if (this._closed) return; this._handleSocketRequest(peer, request, cb) .catch((error) => { @@ -98,19 +109,22 @@ class Lobby extends EventEmitter socket.on('disconnect', () => { - if (this._closed) - return; - logger.debug('Peer "close" event [peerId:%s]', peer.peerId); + if (this._closed) return; + this.emit('peerClosed', peer); delete this._peers[peer.peerId]; + + if ( this.checkEmpty() ) this.emit('lobbyEmpty'); }); } async _handleSocketRequest(peer, request, cb) { + logger.debug('_handleSocketRequest [peer:%o], [request:%o]', peer, request); + if (this._closed) return; switch (request.method) { case 'changeDisplayName': diff --git a/server/lib/Room.js b/server/lib/Room.js index e702367..59c64df 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -99,6 +99,16 @@ class Room extends EventEmitter }); }); + // If nobody left in lobby we should check if room is empty too and initiating + // rooms selfdestruction sequence + this._lobby.on('lobbyEmpty', () => + { + if ( this.checkEmpty() ) + { + this.selfDestructCountdown(); + } + }) + this._chatHistory = []; this._fileHistory = []; @@ -155,6 +165,25 @@ class Room extends EventEmitter return this._roomId; } + selfDestructCountdown() + { + logger.debug('selfDestructCountdown() started') + setTimeout(() => + { + if (this._closed) + return; + + if (this.checkEmpty() && this._lobby.checkEmpty()) + { + logger.info( + 'Room deserted for some time, closing the room [roomId:%s]', + this._roomId); + this.close(); + } + else logger.debug('selfDestructCountdown() aborted; room is not empty!') + }, 10000); + } + close() { logger.debug('close()'); @@ -187,6 +216,13 @@ class Room extends EventEmitter ); } + // checks both room and lobby + checkEmpty() + { + if (( Object.keys(this._peers).length == 0) && (this._lobby.checkEmpty())) return true + else return false; + } + handleConnection({ peerId, consume, socket }) { logger.info('handleConnection() [peerId:"%s"]', peerId); @@ -296,7 +332,7 @@ class Room extends EventEmitter if (this._closed) return; - logger.debug('Peer "close" event [peerId:%s]', peer.id); + logger.debug('Peer "disconnect" event [peerId:%s]', peer.id); // If the Peer was joined, notify all Peers. if (peer.data.joined) @@ -320,23 +356,10 @@ class Room extends EventEmitter delete this._peers[peer.id]; - // If this is the latest Peer in the room, close the room after a while. - if (Object.keys(this._peers).length == 0) + // If this is the latest Peer in the room and lobby is empty, close the room after a while. + if (this.checkEmpty()) { - setTimeout(() => - { - if (this._closed) - return; - - if (Object.keys(this._peers).length == 0) - { - logger.info( - 'last Peer in the room left, closing the room [roomId:%s]', - this._roomId); - - this.close(); - } - }, 10000); + this.selfDestructCountdown(); } }); } diff --git a/server/server.js b/server/server.js index 4e42518..c1c85d9 100755 --- a/server/server.js +++ b/server/server.js @@ -126,6 +126,15 @@ async function run() room.logStatus(); } }, 120000); + + // check for deserted rooms + setInterval(() => + { + for (const room of rooms.values()) + { + room.checkEmpty(); + } + }, 10000); } async function setupAuth(oidcIssuer) From 1be82b45d8f4c6aa63eefc470627ec555ac16d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Thu, 24 Oct 2019 00:01:24 +0200 Subject: [PATCH 20/74] Fixed server security issues. --- server/package.json | 3 +++ server/server.js | 47 +++++++++++++++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/server/package.json b/server/package.json index 4a80107..67da924 100644 --- a/server/package.json +++ b/server/package.json @@ -9,12 +9,15 @@ "dependencies": { "awaitqueue": "^1.0.0", "base-64": "^0.1.0", + "body-parser": "^1.19.0", "colors": "^1.4.0", "compression": "^1.7.4", + "cookie-parser": "^1.4.4", "debug": "^4.1.1", "express": "^4.17.1", "express-session": "^1.17.0", "express-socket.io-session": "^1.3.5", + "helmet": "^3.21.2", "mediasoup": "^3.0.12", "openid-client": "^3.7.3", "passport": "^0.4.0", diff --git a/server/server.js b/server/server.js index 32d953d..8d6ccdb 100755 --- a/server/server.js +++ b/server/server.js @@ -7,12 +7,15 @@ const fs = require('fs'); const http = require('http'); const spdy = require('spdy'); const express = require('express'); +const bodyParser = require('body-parser'); +const cookieParser = require('cookie-parser'); const compression = require('compression'); const mediasoup = require('mediasoup'); const AwaitQueue = require('awaitqueue'); const Logger = require('./lib/Logger'); const Room = require('./lib/Room'); const base64 = require('base-64'); +const helmet = require('helmet'); // auth const passport = require('passport'); const { Issuer, Strategy } = require('openid-client'); @@ -49,20 +52,24 @@ const tls = const app = express(); +app.use(helmet.hsts()); + +app.use(cookieParser()); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); + const session = expressSession({ secret : config.cookieSecret, resave : true, saveUninitialized : true, - cookie : { secure: true } + cookie : { + secure : true, + httpOnly : true + } }); app.use(session); -let httpsServer; -let io; -let oidcClient; -let oidcStrategy; - passport.serializeUser((user, done) => { done(null, user); @@ -73,6 +80,11 @@ passport.deserializeUser((user, done) => done(null, user); }); +let httpsServer; +let io; +let oidcClient; +let oidcStrategy; + const auth = config.auth; async function run() @@ -261,15 +273,20 @@ async function setupAuth(oidcIssuer) room.peerAuthenticated(state.peerId); - io.sockets.socket(state.id).emit('notification', - { - method : 'auth', - data : + const socket = io.sockets.socket(state.id); + + if (socket) + { + socket.emit('notification', { - displayName : displayName, - picture : photo - } - }); + method : 'auth', + data : + { + displayName : displayName, + picture : photo + } + }); + } res.send(''); } @@ -335,6 +352,8 @@ async function runWebSocketServer() { const { roomId, peerId } = socket.handshake.query; + logger.info('socket.io "connection" | [session:"%o"]', socket.handshake.session); + if (!roomId || !peerId) { logger.warn('connection request without roomId and/or peerId'); From a8149d29d8d44c9ad813d6c2770b258648154616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Thu, 24 Oct 2019 00:08:49 +0200 Subject: [PATCH 21/74] Small fixes. --- server/lib/Room.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/server/lib/Room.js b/server/lib/Room.js index 7b0cf00..a59a340 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -102,11 +102,11 @@ class Room extends EventEmitter // rooms selfdestruction sequence this._lobby.on('lobbyEmpty', () => { - if ( this.checkEmpty() ) + if (this.checkEmpty()) { this.selfDestructCountdown(); } - }) + }); this._chatHistory = []; @@ -166,7 +166,8 @@ class Room extends EventEmitter selfDestructCountdown() { - logger.debug('selfDestructCountdown() started') + logger.debug('selfDestructCountdown() started'); + setTimeout(() => { if (this._closed) @@ -179,7 +180,8 @@ class Room extends EventEmitter this._roomId); this.close(); } - else logger.debug('selfDestructCountdown() aborted; room is not empty!') + else + logger.debug('selfDestructCountdown() aborted; room is not empty!'); }, 10000); } @@ -218,8 +220,10 @@ class Room extends EventEmitter // checks both room and lobby checkEmpty() { - if (( Object.keys(this._peers).length == 0) && (this._lobby.checkEmpty())) return true - else return false; + if ((Object.keys(this._peers).length == 0) && (this._lobby.checkEmpty())) + return true; + else + return false; } handleConnection({ peerId, consume, socket }) @@ -355,7 +359,8 @@ class Room extends EventEmitter delete this._peers[peer.id]; - // If this is the latest Peer in the room and lobby is empty, close the room after a while. + // If this is the last Peer in the room and + // lobby is empty, close the room after a while. if (this.checkEmpty()) { this.selfDestructCountdown(); From bf754d47976fdf00160468a6e70b8aa65744ccd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Thu, 24 Oct 2019 09:03:30 +0200 Subject: [PATCH 22/74] Some tests. --- app/public/authCallback.html | 14 ++++++++++++++ app/src/RoomClient.js | 5 +++++ app/src/components/JoinDialog.js | 27 ++++++++++++++++----------- server/server.js | 4 ++-- 4 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 app/public/authCallback.html diff --git a/app/public/authCallback.html b/app/public/authCallback.html new file mode 100644 index 0000000..51b7574 --- /dev/null +++ b/app/public/authCallback.html @@ -0,0 +1,14 @@ + + + + + Multiparty Meeting + + + + + diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 99bf103..6715b2d 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -323,6 +323,11 @@ export default class RoomClient this._loginWindow.close(); } + receiveFromChildWindow(data) + { + logger.debug('receiveFromChildWindow() | [data:"%o"]', data); + } + _soundNotification() { const alertPromise = this._soundAlert.play(); diff --git a/app/src/components/JoinDialog.js b/app/src/components/JoinDialog.js index e27d78e..70a2a94 100644 --- a/app/src/components/JoinDialog.js +++ b/app/src/components/JoinDialog.js @@ -54,6 +54,7 @@ const styles = (theme) => const JoinDialog = ({ roomClient, displayName, + loginEnabled, changeDisplayName, classes }) => @@ -119,16 +120,18 @@ const JoinDialog = ({ margin='normal' /> - + { loginEnabled && + + } - */} - - - - { (lobbyPeers.length > 0) && - - Participants in Lobby - - } - > - { - lobbyPeers.map((peerId) => + Lobby administration + {/* + + Room lock + + { - return (); - }) - } - - } - + if (room.locked) + { + roomClient.unlockRoom(); + } + else + { + roomClient.lockRoom(); + } + }} + />} + label='Lock' + /> + TODO: access code + roomClient.setJoinByAccessCode(event.target.checked) + } + />} + label='Join by Access code' + /> + + handleAccessCode(event.target.value)} + > + + + + + + */} + { lobbyPeers.length > 0 ? + + Participants in Lobby + + } + > + { + lobbyPeers.map((peerId) => + { + return (); + }) + } + + : + + + There are currently no one in the lobby. + + + }