Merge branch 'develop' into feat-audio-settings
commit
59d4bd2ce7
|
|
@ -27,6 +27,7 @@
|
||||||
"react": "^16.10.2",
|
"react": "^16.10.2",
|
||||||
"react-cookie-consent": "^2.5.0",
|
"react-cookie-consent": "^2.5.0",
|
||||||
"react-dom": "^16.10.2",
|
"react-dom": "^16.10.2",
|
||||||
|
"react-flip-toolkit": "^7.0.9",
|
||||||
"react-intl": "^3.4.0",
|
"react-intl": "^3.4.0",
|
||||||
"react-redux": "^7.1.1",
|
"react-redux": "^7.1.1",
|
||||||
"react-router-dom": "^5.1.2",
|
"react-router-dom": "^5.1.2",
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,7 @@ let Spotlights;
|
||||||
let requestTimeout,
|
let requestTimeout,
|
||||||
transportOptions,
|
transportOptions,
|
||||||
lastN,
|
lastN,
|
||||||
mobileLastN,
|
mobileLastN;
|
||||||
defaultResolution;
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'test')
|
if (process.env.NODE_ENV !== 'test')
|
||||||
{
|
{
|
||||||
|
|
@ -40,8 +39,7 @@ if (process.env.NODE_ENV !== 'test')
|
||||||
requestTimeout,
|
requestTimeout,
|
||||||
transportOptions,
|
transportOptions,
|
||||||
lastN,
|
lastN,
|
||||||
mobileLastN,
|
mobileLastN
|
||||||
defaultResolution
|
|
||||||
} = window.config);
|
} = window.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,9 +203,6 @@ export default class RoomClient
|
||||||
// Our WebTorrent client
|
// Our WebTorrent client
|
||||||
this._webTorrent = null;
|
this._webTorrent = null;
|
||||||
|
|
||||||
if (defaultResolution)
|
|
||||||
store.dispatch(settingsActions.setVideoResolution(defaultResolution));
|
|
||||||
|
|
||||||
// Max spotlights
|
// Max spotlights
|
||||||
if (device.platform === 'desktop')
|
if (device.platform === 'desktop')
|
||||||
this._maxSpotlights = lastN;
|
this._maxSpotlights = lastN;
|
||||||
|
|
@ -1111,6 +1106,37 @@ export default class RoomClient
|
||||||
meActions.setAudioOutputInProgress(false));
|
meActions.setAudioOutputInProgress(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async changeAudioOutputDevice(deviceId)
|
||||||
|
{
|
||||||
|
logger.debug('changeAudioOutputDevice() [deviceId: %s]', deviceId);
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
meActions.setAudioOutputInProgress(true));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const device = this._audioOutputDevices[deviceId];
|
||||||
|
|
||||||
|
if (!device)
|
||||||
|
throw new Error('Selected audio output device no longer avaibale');
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
'changeAudioOutputDevice() | new selected [audio output device:%o]',
|
||||||
|
device);
|
||||||
|
|
||||||
|
store.dispatch(settingsActions.setSelectedAudioOutputDevice(deviceId));
|
||||||
|
|
||||||
|
await this._updateAudioOutputDevices();
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
logger.error('changeAudioOutputDevice() failed: %o', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
meActions.setAudioOutputInProgress(false));
|
||||||
|
}
|
||||||
|
|
||||||
async changeVideoResolution(resolution)
|
async changeVideoResolution(resolution)
|
||||||
{
|
{
|
||||||
logger.debug('changeVideoResolution() [resolution: %s]', resolution);
|
logger.debug('changeVideoResolution() [resolution: %s]', resolution);
|
||||||
|
|
@ -1538,6 +1564,26 @@ export default class RoomClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async lowerPeerHand(peerId)
|
||||||
|
{
|
||||||
|
logger.debug('lowerPeerHand() [peerId:"%s"]', peerId);
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
peerActions.setPeerRaisedHandInProgress(peerId, true));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await this.sendRequest('moderator:lowerHand', { peerId });
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
logger.error('lowerPeerHand() | [error:"%o"]', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
peerActions.setPeerRaisedHandInProgress(peerId, false));
|
||||||
|
}
|
||||||
|
|
||||||
async setRaisedHand(raisedHand)
|
async setRaisedHand(raisedHand)
|
||||||
{
|
{
|
||||||
logger.debug('setRaisedHand: ', raisedHand);
|
logger.debug('setRaisedHand: ', raisedHand);
|
||||||
|
|
@ -1780,6 +1826,49 @@ export default class RoomClient
|
||||||
this._recvTransport = null;
|
this._recvTransport = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
store.dispatch(roomActions.setRoomState('connecting'));
|
||||||
|
});
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
producerActions.removeProducer(this._screenSharingProducer.id));
|
||||||
|
|
||||||
|
this._screenSharingProducer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._webcamProducer)
|
||||||
|
{
|
||||||
|
this._webcamProducer.close();
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
producerActions.removeProducer(this._webcamProducer.id));
|
||||||
|
|
||||||
|
this._webcamProducer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._micProducer)
|
||||||
|
{
|
||||||
|
this._micProducer.close();
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
producerActions.removeProducer(this._micProducer.id));
|
||||||
|
|
||||||
|
this._micProducer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._sendTransport)
|
||||||
|
{
|
||||||
|
this._sendTransport.close();
|
||||||
|
|
||||||
|
this._sendTransport = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._recvTransport)
|
||||||
|
{
|
||||||
|
this._recvTransport.close();
|
||||||
|
|
||||||
|
this._recvTransport = null;
|
||||||
|
}
|
||||||
|
|
||||||
store.dispatch(roomActions.setRoomState('connecting'));
|
store.dispatch(roomActions.setRoomState('connecting'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2539,6 +2628,13 @@ export default class RoomClient
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'moderator:lowerHand':
|
||||||
|
{
|
||||||
|
this.setRaisedHand(false);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'gotRole':
|
case 'gotRole':
|
||||||
{
|
{
|
||||||
const { peerId, role } = notification.data;
|
const { peerId, role } = notification.data;
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,12 @@ export const setPeerRaisedHand = (peerId, raisedHand, raisedHandTimestamp) =>
|
||||||
payload : { peerId, raisedHand, raisedHandTimestamp }
|
payload : { peerId, raisedHand, raisedHandTimestamp }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setPeerRaisedHandInProgress = (peerId, flag) =>
|
||||||
|
({
|
||||||
|
type : 'SET_PEER_RAISED_HAND_IN_PROGRESS',
|
||||||
|
payload : { peerId, flag }
|
||||||
|
});
|
||||||
|
|
||||||
export const setPeerPicture = (peerId, picture) =>
|
export const setPeerPicture = (peerId, picture) =>
|
||||||
({
|
({
|
||||||
type : 'SET_PEER_PICTURE',
|
type : 'SET_PEER_PICTURE',
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as appPropTypes from '../../appPropTypes';
|
import * as appPropTypes from '../../appPropTypes';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
|
@ -23,7 +24,7 @@ const styles = (theme) =>
|
||||||
{
|
{
|
||||||
borderRadius : '50%',
|
borderRadius : '50%',
|
||||||
height : '2rem',
|
height : '2rem',
|
||||||
marginTop : theme.spacing(1)
|
marginTop : theme.spacing(0.5)
|
||||||
},
|
},
|
||||||
peerInfo :
|
peerInfo :
|
||||||
{
|
{
|
||||||
|
|
@ -33,6 +34,10 @@ const styles = (theme) =>
|
||||||
flexGrow : 1,
|
flexGrow : 1,
|
||||||
alignItems : 'center'
|
alignItems : 'center'
|
||||||
},
|
},
|
||||||
|
buttons :
|
||||||
|
{
|
||||||
|
padding : theme.spacing(1)
|
||||||
|
},
|
||||||
green :
|
green :
|
||||||
{
|
{
|
||||||
color : 'rgba(0, 153, 0, 1)'
|
color : 'rgba(0, 153, 0, 1)'
|
||||||
|
|
@ -71,7 +76,9 @@ const ListMe = (props) =>
|
||||||
id : 'tooltip.raisedHand',
|
id : 'tooltip.raisedHand',
|
||||||
defaultMessage : 'Raise hand'
|
defaultMessage : 'Raise hand'
|
||||||
})}
|
})}
|
||||||
className={me.raisedHand ? classes.green : null}
|
className={
|
||||||
|
classnames(me.raisedHand ? classes.green : null, classes.buttons)
|
||||||
|
}
|
||||||
disabled={me.raisedHandInProgress}
|
disabled={me.raisedHandInProgress}
|
||||||
color='primary'
|
color='primary'
|
||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
|
||||||
import * as appPropTypes from '../../appPropTypes';
|
import * as appPropTypes from '../../appPropTypes';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import { green } from '@material-ui/core/colors';
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
import VideocamIcon from '@material-ui/icons/Videocam';
|
import VideocamIcon from '@material-ui/icons/Videocam';
|
||||||
|
|
@ -17,6 +18,7 @@ import ScreenOffIcon from '@material-ui/icons/StopScreenShare';
|
||||||
import ExitIcon from '@material-ui/icons/ExitToApp';
|
import ExitIcon from '@material-ui/icons/ExitToApp';
|
||||||
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
||||||
import PanIcon from '@material-ui/icons/PanTool';
|
import PanIcon from '@material-ui/icons/PanTool';
|
||||||
|
import RecordVoiceOverIcon from '@material-ui/icons/RecordVoiceOver';
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
|
|
@ -31,7 +33,7 @@ const styles = (theme) =>
|
||||||
{
|
{
|
||||||
borderRadius : '50%',
|
borderRadius : '50%',
|
||||||
height : '2rem',
|
height : '2rem',
|
||||||
marginTop : theme.spacing(1)
|
marginTop : theme.spacing(0.5)
|
||||||
},
|
},
|
||||||
peerInfo :
|
peerInfo :
|
||||||
{
|
{
|
||||||
|
|
@ -44,11 +46,16 @@ const styles = (theme) =>
|
||||||
indicators :
|
indicators :
|
||||||
{
|
{
|
||||||
display : 'flex',
|
display : 'flex',
|
||||||
padding : theme.spacing(1.5)
|
padding : theme.spacing(1)
|
||||||
|
},
|
||||||
|
buttons :
|
||||||
|
{
|
||||||
|
padding : theme.spacing(1)
|
||||||
},
|
},
|
||||||
green :
|
green :
|
||||||
{
|
{
|
||||||
color : 'rgba(0, 153, 0, 1)'
|
color : 'rgba(0, 153, 0, 1)',
|
||||||
|
marginLeft : theme.spacing(2)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -59,6 +66,7 @@ const ListPeer = (props) =>
|
||||||
const {
|
const {
|
||||||
roomClient,
|
roomClient,
|
||||||
isModerator,
|
isModerator,
|
||||||
|
spotlight,
|
||||||
peer,
|
peer,
|
||||||
micConsumer,
|
micConsumer,
|
||||||
webcamConsumer,
|
webcamConsumer,
|
||||||
|
|
@ -94,11 +102,30 @@ const ListPeer = (props) =>
|
||||||
<div className={classes.peerInfo}>
|
<div className={classes.peerInfo}>
|
||||||
{peer.displayName}
|
{peer.displayName}
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.indicators}>
|
{ peer.raisedHand &&
|
||||||
{ peer.raisedHand &&
|
<IconButton
|
||||||
<PanIcon className={classes.green} />
|
className={classes.buttons}
|
||||||
}
|
style={{ color: green[500] }}
|
||||||
</div>
|
disabled={!isModerator || peer.raisedHandInProgress}
|
||||||
|
onClick={(e) =>
|
||||||
|
{
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
roomClient.lowerPeerHand(peer.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PanIcon />
|
||||||
|
</IconButton>
|
||||||
|
}
|
||||||
|
{ spotlight &&
|
||||||
|
<IconButton
|
||||||
|
className={classes.buttons}
|
||||||
|
style={{ color: green[500] }}
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<RecordVoiceOverIcon />
|
||||||
|
</IconButton>
|
||||||
|
}
|
||||||
{ screenConsumer &&
|
{ screenConsumer &&
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={intl.formatMessage({
|
title={intl.formatMessage({
|
||||||
|
|
@ -114,6 +141,7 @@ const ListPeer = (props) =>
|
||||||
})}
|
})}
|
||||||
color={screenVisible ? 'primary' : 'secondary'}
|
color={screenVisible ? 'primary' : 'secondary'}
|
||||||
disabled={peer.peerScreenInProgress}
|
disabled={peer.peerScreenInProgress}
|
||||||
|
className={classes.buttons}
|
||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -145,6 +173,7 @@ const ListPeer = (props) =>
|
||||||
})}
|
})}
|
||||||
color={webcamEnabled ? 'primary' : 'secondary'}
|
color={webcamEnabled ? 'primary' : 'secondary'}
|
||||||
disabled={peer.peerVideoInProgress}
|
disabled={peer.peerVideoInProgress}
|
||||||
|
className={classes.buttons}
|
||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -175,6 +204,7 @@ const ListPeer = (props) =>
|
||||||
})}
|
})}
|
||||||
color={micEnabled ? 'primary' : 'secondary'}
|
color={micEnabled ? 'primary' : 'secondary'}
|
||||||
disabled={peer.peerAudioInProgress}
|
disabled={peer.peerAudioInProgress}
|
||||||
|
className={classes.buttons}
|
||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -205,6 +235,7 @@ const ListPeer = (props) =>
|
||||||
defaultMessage : 'Kick out participant'
|
defaultMessage : 'Kick out participant'
|
||||||
})}
|
})}
|
||||||
disabled={peer.peerKickInProgress}
|
disabled={peer.peerKickInProgress}
|
||||||
|
className={classes.buttons}
|
||||||
color='secondary'
|
color='secondary'
|
||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
{
|
{
|
||||||
|
|
@ -227,6 +258,7 @@ ListPeer.propTypes =
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
isModerator : PropTypes.bool,
|
isModerator : PropTypes.bool,
|
||||||
|
spotlight : PropTypes.bool,
|
||||||
peer : appPropTypes.Peer.isRequired,
|
peer : appPropTypes.Peer.isRequired,
|
||||||
micConsumer : appPropTypes.Consumer,
|
micConsumer : appPropTypes.Consumer,
|
||||||
webcamConsumer : appPropTypes.Consumer,
|
webcamConsumer : appPropTypes.Consumer,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
passivePeersSelector,
|
participantListSelector
|
||||||
spotlightSortedPeersSelector
|
|
||||||
} from '../../Selectors';
|
} from '../../Selectors';
|
||||||
import classNames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { Flipper, Flipped } from 'react-flip-toolkit';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import ListPeer from './ListPeer';
|
import ListPeer from './ListPeer';
|
||||||
import ListMe from './ListMe';
|
import ListMe from './ListMe';
|
||||||
|
|
@ -76,9 +76,9 @@ class ParticipantList extends React.PureComponent
|
||||||
roomClient,
|
roomClient,
|
||||||
advancedMode,
|
advancedMode,
|
||||||
isModerator,
|
isModerator,
|
||||||
passivePeers,
|
participants,
|
||||||
|
spotlights,
|
||||||
selectedPeerId,
|
selectedPeerId,
|
||||||
spotlightPeers,
|
|
||||||
classes
|
classes
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
|
@ -107,50 +107,42 @@ class ParticipantList extends React.PureComponent
|
||||||
<ul className={classes.list}>
|
<ul className={classes.list}>
|
||||||
<li className={classes.listheader}>
|
<li className={classes.listheader}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='room.spotlights'
|
id='label.participants'
|
||||||
defaultMessage='Participants in Spotlight'
|
defaultMessage='Participants'
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
{ spotlightPeers.map((peer) => (
|
<Flipper
|
||||||
<li
|
flipKey={participants}
|
||||||
key={peer.id}
|
>
|
||||||
className={classNames(classes.listItem, {
|
{ participants.map((peer) => (
|
||||||
selected : peer.id === selectedPeerId
|
<Flipped key={peer.id} flipId={peer.id}>
|
||||||
})}
|
<li
|
||||||
onClick={() => roomClient.setSelectedPeer(peer.id)}
|
key={peer.id}
|
||||||
>
|
className={classnames(classes.listItem, {
|
||||||
<ListPeer
|
selected : peer.id === selectedPeerId
|
||||||
id={peer.id}
|
})}
|
||||||
advancedMode={advancedMode}
|
onClick={() => roomClient.setSelectedPeer(peer.id)}
|
||||||
isModerator={isModerator}
|
>
|
||||||
>
|
{ spotlights.includes(peer.id) ?
|
||||||
<Volume small id={peer.id} />
|
<ListPeer
|
||||||
</ListPeer>
|
id={peer.id}
|
||||||
</li>
|
advancedMode={advancedMode}
|
||||||
))}
|
isModerator={isModerator}
|
||||||
</ul>
|
spotlight
|
||||||
<ul className={classes.list}>
|
>
|
||||||
<li className={classes.listheader}>
|
<Volume small id={peer.id} />
|
||||||
<FormattedMessage
|
</ListPeer>
|
||||||
id='room.passive'
|
:
|
||||||
defaultMessage='Passive Participants'
|
<ListPeer
|
||||||
/>
|
id={peer.id}
|
||||||
</li>
|
advancedMode={advancedMode}
|
||||||
{ passivePeers.map((peer) => (
|
isModerator={isModerator}
|
||||||
<li
|
/>
|
||||||
key={peer.id}
|
}
|
||||||
className={classNames(classes.listItem, {
|
</li>
|
||||||
selected : peer.id === selectedPeerId
|
</Flipped>
|
||||||
})}
|
))}
|
||||||
onClick={() => roomClient.setSelectedPeer(peer.id)}
|
</Flipper>
|
||||||
>
|
|
||||||
<ListPeer
|
|
||||||
id={peer.id}
|
|
||||||
advancedMode={advancedMode}
|
|
||||||
isModerator={isModerator}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -162,9 +154,9 @@ ParticipantList.propTypes =
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
isModerator : PropTypes.bool,
|
isModerator : PropTypes.bool,
|
||||||
passivePeers : PropTypes.array,
|
participants : PropTypes.array,
|
||||||
|
spotlights : PropTypes.array,
|
||||||
selectedPeerId : PropTypes.string,
|
selectedPeerId : PropTypes.string,
|
||||||
spotlightPeers : PropTypes.array,
|
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -174,9 +166,9 @@ const mapStateToProps = (state) =>
|
||||||
isModerator :
|
isModerator :
|
||||||
state.me.roles.some((role) =>
|
state.me.roles.some((role) =>
|
||||||
state.room.permissionsFromRoles.MODERATE_ROOM.includes(role)),
|
state.room.permissionsFromRoles.MODERATE_ROOM.includes(role)),
|
||||||
passivePeers : passivePeersSelector(state),
|
participants : participantListSelector(state),
|
||||||
selectedPeerId : state.room.selectedPeerId,
|
spotlights : state.room.spotlights,
|
||||||
spotlightPeers : spotlightSortedPeersSelector(state)
|
selectedPeerId : state.room.selectedPeerId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@ const peersValueSelector = createSelector(
|
||||||
(peers) => Object.values(peers)
|
(peers) => Object.values(peers)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const peersValueSelector = createSelector(
|
||||||
|
peersSelector,
|
||||||
|
(peers) => Object.values(peers)
|
||||||
|
);
|
||||||
|
|
||||||
export const lobbyPeersKeySelector = createSelector(
|
export const lobbyPeersKeySelector = createSelector(
|
||||||
lobbyPeersSelector,
|
lobbyPeersSelector,
|
||||||
(lobbyPeers) => Object.keys(lobbyPeers)
|
(lobbyPeers) => Object.keys(lobbyPeers)
|
||||||
|
|
@ -113,8 +118,31 @@ export const spotlightPeersSelector = createSelector(
|
||||||
export const spotlightSortedPeersSelector = createSelector(
|
export const spotlightSortedPeersSelector = createSelector(
|
||||||
spotlightsSelector,
|
spotlightsSelector,
|
||||||
peersValueSelector,
|
peersValueSelector,
|
||||||
(spotlights, peers) => peers.filter((peer) => spotlights.includes(peer.id))
|
(spotlights, peers) =>
|
||||||
.sort((a, b) => String(a.displayName || '').localeCompare(String(b.displayName || '')))
|
peers.filter((peer) => spotlights.includes(peer.id) && !peer.raisedHand)
|
||||||
|
.sort((a, b) => String(a.displayName || '').localeCompare(String(b.displayName || '')))
|
||||||
|
);
|
||||||
|
|
||||||
|
const raisedHandSortedPeers = createSelector(
|
||||||
|
peersValueSelector,
|
||||||
|
(peers) => peers.filter((peer) => peer.raisedHand)
|
||||||
|
.sort((a, b) => a.raisedHandTimestamp - b.raisedHandTimestamp)
|
||||||
|
);
|
||||||
|
|
||||||
|
const peersSortedSelector = createSelector(
|
||||||
|
spotlightsSelector,
|
||||||
|
peersValueSelector,
|
||||||
|
(spotlights, peers) =>
|
||||||
|
peers.filter((peer) => !spotlights.includes(peer.id) && !peer.raisedHand)
|
||||||
|
.sort((a, b) => String(a.displayName || '').localeCompare(String(b.displayName || '')))
|
||||||
|
);
|
||||||
|
|
||||||
|
export const participantListSelector = createSelector(
|
||||||
|
raisedHandSortedPeers,
|
||||||
|
spotlightSortedPeersSelector,
|
||||||
|
peersSortedSelector,
|
||||||
|
(raisedHands, spotlights, peers) =>
|
||||||
|
[ ...raisedHands, ...spotlights, ...peers ]
|
||||||
);
|
);
|
||||||
|
|
||||||
export const peersLengthSelector = createSelector(
|
export const peersLengthSelector = createSelector(
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,12 @@ const peer = (state = {}, action) =>
|
||||||
raisedHand : action.payload.raisedHand,
|
raisedHand : action.payload.raisedHand,
|
||||||
raisedHandTimestamp : action.payload.raisedHandTimestamp
|
raisedHandTimestamp : action.payload.raisedHandTimestamp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case 'SET_PEER_RAISED_HAND_IN_PROGRESS':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
raisedHandInProgress : action.payload.flag
|
||||||
|
};
|
||||||
|
|
||||||
case 'ADD_CONSUMER':
|
case 'ADD_CONSUMER':
|
||||||
{
|
{
|
||||||
|
|
@ -91,6 +97,7 @@ const peers = (state = {}, action) =>
|
||||||
case 'SET_PEER_AUDIO_IN_PROGRESS':
|
case 'SET_PEER_AUDIO_IN_PROGRESS':
|
||||||
case 'SET_PEER_SCREEN_IN_PROGRESS':
|
case 'SET_PEER_SCREEN_IN_PROGRESS':
|
||||||
case 'SET_PEER_RAISED_HAND':
|
case 'SET_PEER_RAISED_HAND':
|
||||||
|
case 'SET_PEER_RAISED_HAND_IN_PROGRESS':
|
||||||
case 'SET_PEER_PICTURE':
|
case 'SET_PEER_PICTURE':
|
||||||
case 'ADD_CONSUMER':
|
case 'ADD_CONSUMER':
|
||||||
case 'ADD_PEER_ROLE':
|
case 'ADD_PEER_ROLE':
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,8 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
this._peers = {};
|
this._peers = {};
|
||||||
|
|
||||||
|
this._selfDestructTimeout = null;
|
||||||
|
|
||||||
// Array of mediasoup Router instances.
|
// Array of mediasoup Router instances.
|
||||||
this._mediasoupRouters = mediasoupRouters;
|
this._mediasoupRouters = mediasoupRouters;
|
||||||
|
|
||||||
|
|
@ -146,6 +148,11 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
this._closed = true;
|
this._closed = true;
|
||||||
|
|
||||||
|
if (this._selfDestructTimeout)
|
||||||
|
clearTimeout(this._selfDestructTimeout);
|
||||||
|
|
||||||
|
this._selfDestructTimeout = null;
|
||||||
|
|
||||||
this._chatHistory = null;
|
this._chatHistory = null;
|
||||||
|
|
||||||
this._fileHistory = null;
|
this._fileHistory = null;
|
||||||
|
|
@ -411,7 +418,10 @@ class Room extends EventEmitter
|
||||||
{
|
{
|
||||||
logger.debug('selfDestructCountdown() started');
|
logger.debug('selfDestructCountdown() started');
|
||||||
|
|
||||||
setTimeout(() =>
|
if (this._selfDestructTimeout)
|
||||||
|
clearTimeout(this._selfDestructTimeout);
|
||||||
|
|
||||||
|
this._selfDestructTimeout = setTimeout(() =>
|
||||||
{
|
{
|
||||||
if (this._closed)
|
if (this._closed)
|
||||||
return;
|
return;
|
||||||
|
|
@ -1430,6 +1440,29 @@ class Room extends EventEmitter
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'moderator:lowerHand':
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!peer.roles.some(
|
||||||
|
(role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
|
const { peerId } = request.data;
|
||||||
|
|
||||||
|
const lowerPeer = this._peers[peerId];
|
||||||
|
|
||||||
|
if (!lowerPeer)
|
||||||
|
throw new Error(`peer with id "${peerId}" not found`);
|
||||||
|
|
||||||
|
this._notification(lowerPeer.socket, 'moderator:lowerHand');
|
||||||
|
|
||||||
|
cb();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
logger.error('unknown request.method "%s"', request.method);
|
logger.error('unknown request.method "%s"', request.method);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue