commit
6cec6e5155
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||||
import ResizeObserver from 'resize-observer-polyfill';
|
import ResizeObserver from 'resize-observer-polyfill';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import * as stateActions from '../redux/stateActions';
|
||||||
import Peer from './Peer';
|
import Peer from './Peer';
|
||||||
|
|
||||||
class Filmstrip extends Component
|
class Filmstrip extends Component
|
||||||
|
|
@ -15,15 +16,32 @@ class Filmstrip extends Component
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
selectedPeerName : null,
|
|
||||||
lastSpeaker : null,
|
lastSpeaker : null,
|
||||||
width : 400
|
width : 400
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find the name of the peer which is currently speaking. This is either
|
// Find the name of the peer which is currently speaking. This is either
|
||||||
// the latest active speaker, or the manually selected peer.
|
// the latest active speaker, or the manually selected peer, or, if no
|
||||||
|
// person has spoken yet, the first peer in the list of peers.
|
||||||
getActivePeerName = () =>
|
getActivePeerName = () =>
|
||||||
this.state.selectedPeerName || this.state.lastSpeaker;
|
{
|
||||||
|
if (this.props.selectedPeerName)
|
||||||
|
{
|
||||||
|
return this.props.selectedPeerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.lastSpeaker)
|
||||||
|
{
|
||||||
|
return this.state.lastSpeaker;
|
||||||
|
}
|
||||||
|
|
||||||
|
const peerNames = Object.keys(this.props.peers);
|
||||||
|
|
||||||
|
if (peerNames.length > 0)
|
||||||
|
{
|
||||||
|
return peerNames[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
isSharingCamera = (peerName) => this.props.peers[peerName] &&
|
isSharingCamera = (peerName) => this.props.peers[peerName] &&
|
||||||
this.props.peers[peerName].consumers.some((consumer) =>
|
this.props.peers[peerName].consumers.some((consumer) =>
|
||||||
|
|
@ -92,14 +110,6 @@ class Filmstrip extends Component
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectPeer = (selectedPeerName) =>
|
|
||||||
{
|
|
||||||
this.setState((oldState) => ({
|
|
||||||
selectedPeerName : oldState.selectedPeerName === selectedPeerName ?
|
|
||||||
null : selectedPeerName
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
render()
|
render()
|
||||||
{
|
{
|
||||||
const { peers, advancedMode } = this.props;
|
const { peers, advancedMode } = this.props;
|
||||||
|
|
@ -130,9 +140,9 @@ class Filmstrip extends Component
|
||||||
{Object.keys(peers).map((peerName) => (
|
{Object.keys(peers).map((peerName) => (
|
||||||
<div
|
<div
|
||||||
key={peerName}
|
key={peerName}
|
||||||
onClick={() => this.handleSelectPeer(peerName)}
|
onClick={() => this.props.setSelectedPeer(peerName)}
|
||||||
className={classnames('film', {
|
className={classnames('film', {
|
||||||
selected : this.state.selectedPeerName === peerName,
|
selected : this.props.selectedPeerName === peerName,
|
||||||
active : this.state.lastSpeaker === peerName
|
active : this.state.lastSpeaker === peerName
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|
@ -156,17 +166,24 @@ Filmstrip.propTypes = {
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
peers : PropTypes.object.isRequired,
|
peers : PropTypes.object.isRequired,
|
||||||
consumers : PropTypes.object.isRequired,
|
consumers : PropTypes.object.isRequired,
|
||||||
myName : PropTypes.string.isRequired
|
myName : PropTypes.string.isRequired,
|
||||||
|
selectedPeerName : PropTypes.string,
|
||||||
|
setSelectedPeer : PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) => ({
|
||||||
({
|
|
||||||
activeSpeakerName : state.room.activeSpeakerName,
|
activeSpeakerName : state.room.activeSpeakerName,
|
||||||
|
selectedPeerName : state.room.selectedPeerName,
|
||||||
peers : state.peers,
|
peers : state.peers,
|
||||||
consumers : state.consumers,
|
consumers : state.consumers,
|
||||||
myName : state.me.name
|
myName : state.me.name
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
setSelectedPeer : stateActions.setSelectedPeer
|
||||||
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
)(Filmstrip);
|
)(Filmstrip);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ const ListMe = ({ me }) =>
|
||||||
const picture = me.picture || 'resources/images/avatar-empty.jpeg';
|
const picture = me.picture || 'resources/images/avatar-empty.jpeg';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className='list-item'>
|
<li className='list-item me'>
|
||||||
<div data-component='ListPeer'>
|
<div data-component='ListPeer'>
|
||||||
<img className='avatar' src={picture} />
|
<img className='avatar' src={picture} />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,38 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import classNames from 'classnames';
|
||||||
import * as appPropTypes from '../appPropTypes';
|
import * as appPropTypes from '../appPropTypes';
|
||||||
import * as requestActions from '../../redux/requestActions';
|
|
||||||
import * as stateActions from '../../redux/stateActions';
|
import * as stateActions from '../../redux/stateActions';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ListPeer from './ListPeer';
|
import ListPeer from './ListPeer';
|
||||||
import ListMe from './ListMe';
|
import ListMe from './ListMe';
|
||||||
|
|
||||||
class ParticipantList extends React.Component
|
const ParticipantList = ({ advancedMode, peers, setSelectedPeer, selectedPeerName }) => (
|
||||||
{
|
|
||||||
constructor(props)
|
|
||||||
{
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
render()
|
|
||||||
{
|
|
||||||
const {
|
|
||||||
advancedMode,
|
|
||||||
peers
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div data-component='ParticipantList'>
|
<div data-component='ParticipantList'>
|
||||||
<ul className='list'>
|
<ul className='list'>
|
||||||
<ListMe />
|
<ListMe />
|
||||||
|
|
||||||
{
|
{peers.map((peer) => (
|
||||||
peers.map((peer) =>
|
<li
|
||||||
{
|
key={peer.name}
|
||||||
return (
|
className={classNames('list-item', {
|
||||||
<li key={peer.name} className='list-item'>
|
selected : peer.name === selectedPeerName
|
||||||
|
})}
|
||||||
|
onClick={() => setSelectedPeer(peer.name)}
|
||||||
|
>
|
||||||
<ListPeer name={peer.name} advancedMode={advancedMode} />
|
<ListPeer name={peer.name} advancedMode={advancedMode} />
|
||||||
</li>
|
</li>
|
||||||
);
|
))}
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ParticipantList.propTypes =
|
ParticipantList.propTypes =
|
||||||
{
|
{
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
peers : PropTypes.arrayOf(appPropTypes.Peer).isRequired
|
peers : PropTypes.arrayOf(appPropTypes.Peer).isRequired,
|
||||||
|
setSelectedPeer : PropTypes.func.isRequired,
|
||||||
|
selectedPeerName : PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
|
|
@ -53,26 +40,13 @@ const mapStateToProps = (state) =>
|
||||||
const peersArray = Object.values(state.peers);
|
const peersArray = Object.values(state.peers);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
peers : peersArray
|
peers : peersArray,
|
||||||
|
selectedPeerName : state.room.selectedPeerName
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) =>
|
const mapDispatchToProps = {
|
||||||
{
|
setSelectedPeer : stateActions.setSelectedPeer
|
||||||
return {
|
|
||||||
handleChangeWebcam : (device) =>
|
|
||||||
{
|
|
||||||
dispatch(requestActions.changeWebcam(device.value));
|
|
||||||
},
|
|
||||||
handleChangeAudioDevice : (device) =>
|
|
||||||
{
|
|
||||||
dispatch(requestActions.changeAudioDevice(device.value));
|
|
||||||
},
|
|
||||||
onToggleAdvancedMode : () =>
|
|
||||||
{
|
|
||||||
dispatch(stateActions.toggleAdvancedMode());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ParticipantListContainer = connect(
|
const ParticipantListContainer = connect(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ const initialState =
|
||||||
advancedMode : false,
|
advancedMode : false,
|
||||||
fullScreenConsumer : null, // ConsumerID
|
fullScreenConsumer : null, // ConsumerID
|
||||||
toolbarsVisible : true,
|
toolbarsVisible : true,
|
||||||
mode : 'democratic'
|
mode : 'democratic',
|
||||||
|
selectedPeerName : null
|
||||||
};
|
};
|
||||||
|
|
||||||
const room = (state = initialState, action) =>
|
const room = (state = initialState, action) =>
|
||||||
|
|
@ -70,6 +71,18 @@ const room = (state = initialState, action) =>
|
||||||
case 'SET_DISPLAY_MODE':
|
case 'SET_DISPLAY_MODE':
|
||||||
return { ...state, mode: action.payload.mode };
|
return { ...state, mode: action.payload.mode };
|
||||||
|
|
||||||
|
case 'SET_SELECTED_PEER':
|
||||||
|
{
|
||||||
|
const { selectedPeerName } = action.payload;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
|
||||||
|
selectedPeerName : state.selectedPeerName === selectedPeerName ?
|
||||||
|
null : selectedPeerName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -473,3 +473,8 @@ export const loggedIn = () =>
|
||||||
({
|
({
|
||||||
type : 'LOGGED_IN'
|
type : 'LOGGED_IN'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setSelectedPeer = (selectedPeerName) => ({
|
||||||
|
type : 'SET_SELECTED_PEER',
|
||||||
|
payload : { selectedPeerName }
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,26 @@
|
||||||
|
|
||||||
> .list-item {
|
> .list-item {
|
||||||
padding: 0.5vmin;
|
padding: 0.5vmin;
|
||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #CBCBCB;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.me {
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border-bottom-color: #377EFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-component='ListPeer'] {
|
[data-component='ListPeer'] {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
> .indicators {
|
> .indicators {
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue