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 (