Merge branch 'feature-mute-participants' into develop

master
Håvar Aambø Fosstveit 2018-06-15 09:31:58 +02:00
commit 668fa0f37f
9 changed files with 414 additions and 65 deletions

View File

@ -523,6 +523,150 @@ export default class RoomClient
});
}
mutePeerAudio(peerName)
{
logger.debug('mutePeerAudio() [peerName:"%s"]', peerName);
this._dispatch(
stateActions.setPeerAudioInProgress(peerName, true));
return Promise.resolve()
.then(() =>
{
for (const peer of this._room.peers)
{
if (peer.name === peerName)
{
for (const consumer of peer.consumers)
{
if (consumer.kind !== 'audio')
continue;
consumer.pause('mute-audio');
}
}
}
this._dispatch(
stateActions.setPeerAudioInProgress(peerName, false));
})
.catch((error) =>
{
logger.error('mutePeerAudio() failed: %o', error);
this._dispatch(
stateActions.setPeerAudioInProgress(peerName, false));
});
}
unmutePeerAudio(peerName)
{
logger.debug('unmutePeerAudio() [peerName:"%s"]', peerName);
this._dispatch(
stateActions.setPeerAudioInProgress(peerName, true));
return Promise.resolve()
.then(() =>
{
for (const peer of this._room.peers)
{
if (peer.name === peerName)
{
for (const consumer of peer.consumers)
{
if (consumer.kind !== 'audio' || !consumer.supported)
continue;
consumer.resume();
}
}
}
this._dispatch(
stateActions.setPeerAudioInProgress(peerName, false));
})
.catch((error) =>
{
logger.error('unmutePeerAudio() failed: %o', error);
this._dispatch(
stateActions.setPeerAudioInProgress(peerName, false));
});
}
pausePeerVideo(peerName)
{
logger.debug('pausePeerVideo() [peerName:"%s"]', peerName);
this._dispatch(
stateActions.setPeerVideoInProgress(peerName, true));
return Promise.resolve()
.then(() =>
{
for (const peer of this._room.peers)
{
if (peer.name === peerName)
{
for (const consumer of peer.consumers)
{
if (consumer.kind !== 'video')
continue;
consumer.pause('pause-video');
}
}
}
this._dispatch(
stateActions.setPeerVideoInProgress(peerName, false));
})
.catch((error) =>
{
logger.error('pausePeerVideo() failed: %o', error);
this._dispatch(
stateActions.setPeerVideoInProgress(peerName, false));
});
}
resumePeerVideo(peerName)
{
logger.debug('resumePeerVideo() [peerName:"%s"]', peerName);
this._dispatch(
stateActions.setPeerVideoInProgress(peerName, true));
return Promise.resolve()
.then(() =>
{
for (const peer of this._room.peers)
{
if (peer.name === peerName)
{
for (const consumer of peer.consumers)
{
if (consumer.kind !== 'video' || !consumer.supported)
continue;
consumer.resume();
}
}
}
this._dispatch(
stateActions.setPeerVideoInProgress(peerName, false));
})
.catch((error) =>
{
logger.error('resumePeerVideo() failed: %o', error);
this._dispatch(
stateActions.setPeerVideoInProgress(peerName, false));
});
}
enableAudioOnly()
{
logger.debug('enableAudioOnly()');

View File

@ -1,6 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import * as appPropTypes from './appPropTypes';
import * as requestActions from '../redux/requestActions';
import PeerView from './PeerView';
const Peer = (props) =>
@ -9,7 +12,11 @@ const Peer = (props) =>
peer,
micConsumer,
webcamConsumer,
screenConsumer
screenConsumer,
onMuteMic,
onUnmuteMic,
onDisableWebcam,
onEnableWebcam
} = props;
const micEnabled = (
@ -42,19 +49,33 @@ const Peer = (props) =>
return (
<div data-component='Peer'>
<div className='indicators'>
{peer.raiseHandState ?
<div className='icon raise-hand' />
:null
}
{!micEnabled ?
<div className='icon mic-off' />
:null
}
{!videoVisible ?
<div className='icon webcam-off' />
:null
}
<div className='controls'>
<div
className={classnames('button', 'mic', {
on : micEnabled,
off : !micEnabled,
disabled : peer.peerAudioInProgress
})}
onClick={(e) =>
{
e.stopPropagation();
micEnabled ? onMuteMic(peer.name) : onUnmuteMic(peer.name);
}}
/>
<div
className={classnames('button', 'webcam', {
on : videoVisible,
off : !videoVisible,
disabled : peer.peerVideoInProgress
})}
onClick={(e) =>
{
e.stopPropagation();
videoVisible ?
onDisableWebcam(peer.name) : onEnableWebcam(peer.name);
}}
/>
</div>
{videoVisible && !webcamConsumer.supported ?
@ -86,7 +107,11 @@ Peer.propTypes =
peer : appPropTypes.Peer.isRequired,
micConsumer : appPropTypes.Consumer,
webcamConsumer : appPropTypes.Consumer,
screenConsumer : appPropTypes.Consumer
screenConsumer : appPropTypes.Consumer,
onMuteMic : PropTypes.func.isRequired,
onUnmuteMic : PropTypes.func.isRequired,
onEnableWebcam : PropTypes.func.isRequired,
onDisableWebcam : PropTypes.func.isRequired
};
const mapStateToProps = (state, { name }) =>
@ -109,6 +134,32 @@ const mapStateToProps = (state, { name }) =>
};
};
const PeerContainer = connect(mapStateToProps)(Peer);
const mapDispatchToProps = (dispatch) =>
{
return {
onMuteMic : (peerName) =>
{
dispatch(requestActions.mutePeerAudio(peerName));
},
onUnmuteMic : (peerName) =>
{
dispatch(requestActions.unmutePeerAudio(peerName));
},
onEnableWebcam : (peerName) =>
{
dispatch(requestActions.resumePeerVideo(peerName));
},
onDisableWebcam : (peerName) =>
{
dispatch(requestActions.pausePeerVideo(peerName));
}
};
};
const PeerContainer = connect(
mapStateToProps,
mapDispatchToProps
)(Peer);
export default PeerContainer;

View File

@ -315,7 +315,7 @@ PeerView.propTypes =
screenTrack : PropTypes.any,
videoVisible : PropTypes.bool.isRequired,
videoProfile : PropTypes.string,
screenVisible : PropTypes.bool.isRequired,
screenVisible : PropTypes.bool,
screenProfile : PropTypes.string,
audioCodec : PropTypes.string,
videoCodec : PropTypes.string,

View File

@ -16,9 +16,10 @@ class Peers extends React.Component
ratio : 1.334
};
}
updateDimensions()
updateDimensions(nextProps = null)
{
const n = this.props.peers.length;
const n = nextProps ? nextProps.peers.length : this.props.peers.length;
if (n == 0)
{
@ -62,6 +63,11 @@ class Peers extends React.Component
window.removeEventListener('resize', this.updateDimensions.bind(this));
}
componentWillReceiveProps(nextProps)
{
this.updateDimensions(nextProps);
}
render()
{
const {
@ -77,8 +83,6 @@ class Peers extends React.Component
'height' : peerHeight
};
this.updateDimensions();
return (
<div data-component='Peers' ref='peers'>
{

View File

@ -34,6 +34,32 @@ const peers = (state = initialState, action) =>
return { ...state, [newPeer.name]: newPeer };
}
case 'SET_PEER_VIDEO_IN_PROGRESS':
{
const { peerName, flag } = action.payload;
const peer = state[peerName];
if (!peer)
throw new Error('no Peer found');
const newPeer = { ...peer, peerVideoInProgress: flag };
return { ...state, [newPeer.name]: newPeer };
}
case 'SET_PEER_AUDIO_IN_PROGRESS':
{
const { peerName, flag } = action.payload;
const peer = state[peerName];
if (!peer)
throw new Error('no Peer found');
const newPeer = { ...peer, peerAudioInProgress: flag };
return { ...state, [newPeer.name]: newPeer };
}
case 'SET_PEER_RAISE_HAND_STATE':
{
const { peerName, raiseHandState } = action.payload;

View File

@ -78,6 +78,38 @@ export const disableAudioOnly = () =>
};
};
export const mutePeerAudio = (peerName) =>
{
return {
type : 'MUTE_PEER_AUDIO',
payload : { peerName }
};
};
export const unmutePeerAudio = (peerName) =>
{
return {
type : 'UNMUTE_PEER_AUDIO',
payload : { peerName }
};
};
export const pausePeerVideo = (peerName) =>
{
return {
type : 'PAUSE_PEER_VIDEO',
payload : { peerName }
};
};
export const resumePeerVideo = (peerName) =>
{
return {
type : 'RESUME_PEER_VIDEO',
payload : { peerName }
};
};
export const raiseHand = () =>
{
return {

View File

@ -102,6 +102,42 @@ export default ({ dispatch, getState }) => (next) =>
break;
}
case 'MUTE_PEER_AUDIO':
{
const { peerName } = action.payload;
client.mutePeerAudio(peerName);
break;
}
case 'UNMUTE_PEER_AUDIO':
{
const { peerName } = action.payload;
client.unmutePeerAudio(peerName);
break;
}
case 'PAUSE_PEER_VIDEO':
{
const { peerName } = action.payload;
client.pausePeerVideo(peerName);
break;
}
case 'RESUME_PEER_VIDEO':
{
const { peerName } = action.payload;
client.resumePeerVideo(peerName);
break;
}
case 'RAISE_HAND':
{
client.sendRaiseHandState(true);

View File

@ -86,6 +86,22 @@ export const setAudioOnlyInProgress = (flag) =>
};
};
export const setPeerVideoInProgress = (peerName, flag) =>
{
return {
type : 'SET_PEER_VIDEO_IN_PROGRESS',
payload : { peerName, flag }
};
};
export const setPeerAudioInProgress = (peerName, flag) =>
{
return {
type : 'SET_PEER_AUDIO_IN_PROGRESS',
payload : { peerName, flag }
};
};
export const setMyRaiseHandState = (flag) =>
{
return {

View File

@ -4,6 +4,87 @@
height: 100%;
width: 100%;
> .controls {
position: absolute;
z-index: 10;
right: 0;
top: 0;
display: flex;
flex-direction:; row;
justify-content: flex-start;
align-items: center;
> .button {
flex: 0 0 auto;
margin: 4px;
border-radius: 2px;
background-position: center;
background-size: 75%;
background-repeat: no-repeat;
background-color: rgba(#000, 0.5);
cursor: pointer;
transition-property: opacity, background-color;
transition-duration: 0.15s;
+desktop() {
width: 24px;
height: 24px;
opacity: 0.85;
&:hover {
opacity: 1;
}
}
+mobile() {
width: 22px;
height: 22px;
}
&.unsupported {
pointer-events: none;
}
&.disabled {
pointer-events: none;
opacity: 0.5;
}
&.on {
background-color: rgba(#fff, 0.7);
}
&.mic {
&.on {
background-image: url('/resources/images/icon_mic_black_on.svg');
}
&.off {
background-image: url('/resources/images/icon_mic_white_off.svg');
background-color: rgba(#d42241, 0.7);
}
&.unsupported {
background-image: url('/resources/images/icon_mic_white_unsupported.svg');
}
}
&.webcam {
&.on {
background-image: url('/resources/images/icon_webcam_black_on.svg');
}
&.off {
background-image: url('/resources/images/icon_webcam_white_on.svg');
}
&.unsupported {
background-image: url('/resources/images/icon_webcam_white_unsupported.svg');
}
}
}
}
+mobile() {
display: flex;
flex-direction: column;
@ -11,47 +92,6 @@
align-items: center;
}
> .indicators {
position: absolute;
z-index: 10
top: 0;
left: 0;
right: 0;
display: flex;
flex-direction:; row;
justify-content: flex-end;
align-items: center;
> .icon {
flex: 0 0 auto;
margin: 4px;
margin-left: 0;
width: 32px;
height: 32px;
background-position: center;
background-size: 75%;
background-repeat: no-repeat;
transition-property: opacity;
transition-duration: 0.15s;
+desktop() {
opacity: 0.85;
}
&.raise-hand {
background-image: url('/resources/images/icon_remote_raise_hand.svg');
}
&.mic-off {
background-image: url('/resources/images/icon_remote_mic_white_off.svg');
}
&.webcam-off {
background-image: url('/resources/images/icon_remote_webcam_white_off.svg');
}
}
}
.incompatible-video {
position: absolute;
z-index: 2