diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index dba7c64..cffdd2a 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -1389,6 +1389,46 @@ export default class RoomClient peerActions.setPeerKickInProgress(peerId, false)); } + async mutePeer(peerId) + { + logger.debug('mutePeer() [peerId:"%s"]', peerId); + + store.dispatch( + peerActions.setMutePeerInProgress(peerId, true)); + + try + { + await this.sendRequest('moderator:mute', { peerId }); + } + catch (error) + { + logger.error('mutePeer() failed: %o', error); + } + + store.dispatch( + peerActions.setMutePeerInProgress(peerId, false)); + } + + async stopPeerVideo(peerId) + { + logger.debug('stopPeerVideo() [peerId:"%s"]', peerId); + + store.dispatch( + peerActions.setStopPeerVideoInProgress(peerId, true)); + + try + { + await this.sendRequest('moderator:stopVideo', { peerId }); + } + catch (error) + { + logger.error('stopPeerVideo() failed: %o', error); + } + + store.dispatch( + peerActions.setStopPeerVideoInProgress(peerId, false)); + } + async muteAllPeers() { logger.debug('muteAllPeers()'); diff --git a/app/src/actions/peerActions.js b/app/src/actions/peerActions.js index 414b744..738928c 100644 --- a/app/src/actions/peerActions.js +++ b/app/src/actions/peerActions.js @@ -69,3 +69,15 @@ export const setPeerKickInProgress = (peerId, flag) => type : 'SET_PEER_KICK_IN_PROGRESS', payload : { peerId, flag } }); + +export const setMutePeerInProgress = (peerId, flag) => + ({ + type : 'STOP_PEER_AUDIO_IN_PROGRESS', + payload : { peerId, flag } + }); + +export const setStopPeerVideoInProgress = (peerId, flag) => + ({ + type : 'STOP_PEER_VIDEO_IN_PROGRESS', + payload : { peerId, flag } + }); diff --git a/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js b/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js index 7e163f0..76f9b10 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ListPeer.js @@ -11,6 +11,8 @@ import IconButton from '@material-ui/core/IconButton'; import Tooltip from '@material-ui/core/Tooltip'; import VideocamIcon from '@material-ui/icons/Videocam'; import VideocamOffIcon from '@material-ui/icons/VideocamOff'; +import MicIcon from '@material-ui/icons/Mic'; +import MicOffIcon from '@material-ui/icons/MicOff'; import VolumeUpIcon from '@material-ui/icons/VolumeUp'; import VolumeOffIcon from '@material-ui/icons/VolumeOff'; import ScreenIcon from '@material-ui/icons/ScreenShare'; @@ -250,6 +252,60 @@ const ListPeer = (props) => } + { isModerator && micConsumer && + + + { + e.stopPropagation(); + + roomClient.mutePeer(peer.id); + }} + > + { !micConsumer.remotelyPaused ? + + : + + } + + + } + { isModerator && webcamConsumer && + + + { + e.stopPropagation(); + + roomClient.stopPeerVideo(peer.id); + }} + > + { !webcamConsumer.remotelyPaused ? + + : + + } + + + } {children} ); diff --git a/app/src/reducers/peers.js b/app/src/reducers/peers.js index 3e2c6a0..6c4fd1f 100644 --- a/app/src/reducers/peers.js +++ b/app/src/reducers/peers.js @@ -68,6 +68,18 @@ const peer = (state = {}, action) => return { ...state, roles }; } + case 'STOP_PEER_AUDIO_IN_PROGRESS': + return { + ...state, + stopPeerAudioInProgress : action.payload.flag + }; + + case 'STOP_PEER_VIDEO_IN_PROGRESS': + return { + ...state, + stopPeerVideoInProgress : action.payload.flag + }; + default: return state; } @@ -102,6 +114,8 @@ const peers = (state = {}, action) => case 'ADD_CONSUMER': case 'ADD_PEER_ROLE': case 'REMOVE_PEER_ROLE': + case 'STOP_PEER_AUDIO_IN_PROGRESS': + case 'STOP_PEER_VIDEO_IN_PROGRESS': { const oldPeer = state[action.payload.peerId]; diff --git a/server/lib/Room.js b/server/lib/Room.js index 02ba41c..77f00ee 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -1369,6 +1369,29 @@ class Room extends EventEmitter break; } + case 'moderator:mute': + { + if ( + !peer.roles.some( + (role) => permissionsFromRoles.MODERATE_ROOM.includes(role) + ) + ) + throw new Error('peer not authorized'); + + const { peerId } = request.data; + + const mutePeer = this._peers[peerId]; + + if (!mutePeer) + throw new Error(`peer with id "${peerId}" not found`); + + this._notification(mutePeer.socket, 'moderator:mute'); + + cb(); + + break; + } + case 'moderator:muteAll': { if ( @@ -1386,6 +1409,29 @@ class Room extends EventEmitter break; } + case 'moderator:stopVideo': + { + if ( + !peer.roles.some( + (role) => permissionsFromRoles.MODERATE_ROOM.includes(role) + ) + ) + throw new Error('peer not authorized'); + + const { peerId } = request.data; + + const stopVideoPeer = this._peers[peerId]; + + if (!stopVideoPeer) + throw new Error(`peer with id "${peerId}" not found`); + + this._notification(stopVideoPeer.socket, 'moderator:stopVideo'); + + cb(); + + break; + } + case 'moderator:stopAllVideo': { if (