Reduce the number of unnecessary Redux state changes
This commit is contained in:
parent
3fa62c3757
commit
4d335e086b
|
@ -46,7 +46,7 @@ ReducerRegistry.register('features/base/connection',
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
connectionOptions: {
|
connectionOptions: {
|
||||||
...(state.connectionOptions || {}),
|
...state.connectionOptions,
|
||||||
...buildConnectionOptions(action.domain)
|
...buildConnectionOptions(action.domain)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,8 +25,9 @@ import {
|
||||||
* @property {boolean} local - If true, participant is local.
|
* @property {boolean} local - If true, participant is local.
|
||||||
* @property {boolean} pinned - If true, participant is currently a
|
* @property {boolean} pinned - If true, participant is currently a
|
||||||
* "PINNED_ENDPOINT".
|
* "PINNED_ENDPOINT".
|
||||||
* @property {boolean} speaking - If true, participant is currently a dominant
|
* @property {boolean} dominantSpeaker - If this participant is the dominant
|
||||||
* speaker.
|
* speaker in the (associated) conference, <tt>true</tt>; otherwise,
|
||||||
|
* <tt>false</tt>.
|
||||||
* @property {string} email - Participant email.
|
* @property {string} email - Participant email.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ import {
|
||||||
* @type {string[]}
|
* @type {string[]}
|
||||||
*/
|
*/
|
||||||
const PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE
|
const PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE
|
||||||
= [ 'id', 'local', 'pinned', 'speaking' ];
|
= [ 'dominantSpeaker', 'id', 'local', 'pinned' ];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reducer function for a single participant.
|
* Reducer function for a single participant.
|
||||||
|
@ -53,10 +54,11 @@ function participant(state, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case DOMINANT_SPEAKER_CHANGED:
|
case DOMINANT_SPEAKER_CHANGED:
|
||||||
// Only one dominant speaker is allowed.
|
// Only one dominant speaker is allowed.
|
||||||
return {
|
return (
|
||||||
...state,
|
_setStateProperty(
|
||||||
speaking: state.id === action.participant.id
|
state,
|
||||||
};
|
'dominantSpeaker',
|
||||||
|
state.id === action.participant.id));
|
||||||
|
|
||||||
case PARTICIPANT_ID_CHANGED:
|
case PARTICIPANT_ID_CHANGED:
|
||||||
if (state.id === action.oldValue) {
|
if (state.id === action.oldValue) {
|
||||||
|
@ -68,8 +70,7 @@ function participant(state, action) {
|
||||||
avatar: state.avatar || _getAvatarURL(id, state.email)
|
avatar: state.avatar || _getAvatarURL(id, state.email)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
return state;
|
|
||||||
|
|
||||||
case PARTICIPANT_JOINED: {
|
case PARTICIPANT_JOINED: {
|
||||||
const participant = action.participant; // eslint-disable-line no-shadow
|
const participant = action.participant; // eslint-disable-line no-shadow
|
||||||
|
@ -94,7 +95,7 @@ function participant(state, action) {
|
||||||
name,
|
name,
|
||||||
pinned: participant.pinned || false,
|
pinned: participant.pinned || false,
|
||||||
role: participant.role || PARTICIPANT_ROLE.NONE,
|
role: participant.role || PARTICIPANT_ROLE.NONE,
|
||||||
speaking: participant.speaking || false
|
dominantSpeaker: participant.dominantSpeaker || false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,19 +118,18 @@ function participant(state, action) {
|
||||||
|
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
return state;
|
|
||||||
|
|
||||||
case PIN_PARTICIPANT:
|
case PIN_PARTICIPANT:
|
||||||
// Currently, only one pinned participant is allowed.
|
// Currently, only one pinned participant is allowed.
|
||||||
return {
|
return (
|
||||||
...state,
|
_setStateProperty(
|
||||||
pinned: state.id === action.participant.id
|
state,
|
||||||
};
|
'pinned',
|
||||||
|
state.id === action.participant.id));
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -201,3 +201,30 @@ function _getAvatarURL(participantId, email) {
|
||||||
|
|
||||||
return urlPref + avatarId + urlSuf;
|
return urlPref + avatarId + urlSuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a specific property of a specific state to a specific value. Prevents
|
||||||
|
* unnecessary state changes (when the specified <tt>value</tt> is equal to the
|
||||||
|
* value of the specified <tt>property</tt> of the specified <tt>state</tt>).
|
||||||
|
*
|
||||||
|
* @param {Object} state - The (Redux) state from which a new state is to be
|
||||||
|
* constructed by setting the specified <tt>property</tt> to the specified
|
||||||
|
* <tt>value</tt>.
|
||||||
|
* @param {string} property - The property of <tt>state</tt> which is to be
|
||||||
|
* assigned the specified <tt>value</tt> (in the new state).
|
||||||
|
* @param {*} value - The value to assign to the specified <tt>property</tt>.
|
||||||
|
* @returns {Object} The specified <tt>state</tt> if the value of the specified
|
||||||
|
* <tt>property</tt> equals the specified <tt>value/tt>; otherwise, a new state
|
||||||
|
* constructed from the specified <tt>state</tt> by setting the specified
|
||||||
|
* <tt>property</tt> to the specified <tt>value</tt>.
|
||||||
|
*/
|
||||||
|
function _setStateProperty(state, property, value) {
|
||||||
|
if (state[property] !== value) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[property]: value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
|
@ -81,8 +81,9 @@ class Thumbnail extends Component {
|
||||||
// participants would be hearing themselves.
|
// participants would be hearing themselves.
|
||||||
const audioMuted = !audioTrack || audioTrack.muted;
|
const audioMuted = !audioTrack || audioTrack.muted;
|
||||||
const renderAudio = !audioMuted && !audioTrack.local;
|
const renderAudio = !audioMuted && !audioTrack.local;
|
||||||
|
const participantId = participant.id;
|
||||||
const participantNotInLargeVideo
|
const participantNotInLargeVideo
|
||||||
= participant.id !== largeVideo.participantId;
|
= participantId !== largeVideo.participantId;
|
||||||
const videoMuted = !videoTrack || videoTrack.muted;
|
const videoMuted = !videoTrack || videoTrack.muted;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -96,7 +97,7 @@ class Thumbnail extends Component {
|
||||||
= { audioTrack.jitsiTrack.getOriginalStream() } /> }
|
= { audioTrack.jitsiTrack.getOriginalStream() } /> }
|
||||||
|
|
||||||
<ParticipantView
|
<ParticipantView
|
||||||
participantId = { participant.id }
|
participantId = { participantId }
|
||||||
showAvatar = { participantNotInLargeVideo }
|
showAvatar = { participantNotInLargeVideo }
|
||||||
showVideo = { participantNotInLargeVideo }
|
showVideo = { participantNotInLargeVideo }
|
||||||
zOrder = { 1 } />
|
zOrder = { 1 } />
|
||||||
|
@ -104,7 +105,7 @@ class Thumbnail extends Component {
|
||||||
{ participant.role === PARTICIPANT_ROLE.MODERATOR
|
{ participant.role === PARTICIPANT_ROLE.MODERATOR
|
||||||
&& <ModeratorIndicator /> }
|
&& <ModeratorIndicator /> }
|
||||||
|
|
||||||
{ participant.speaking
|
{ participant.dominantSpeaker
|
||||||
&& <DominantSpeakerIndicator /> }
|
&& <DominantSpeakerIndicator /> }
|
||||||
|
|
||||||
{ audioMuted
|
{ audioMuted
|
||||||
|
|
|
@ -26,13 +26,17 @@ export function selectParticipant() {
|
||||||
const largeVideo = state['features/largeVideo'];
|
const largeVideo = state['features/largeVideo'];
|
||||||
const tracks = state['features/base/tracks'];
|
const tracks = state['features/base/tracks'];
|
||||||
|
|
||||||
const videoTrack = getTrackByMediaTypeAndParticipant(
|
const id = largeVideo.participantId;
|
||||||
tracks, MEDIA_TYPE.VIDEO, largeVideo.participantId);
|
const videoTrack
|
||||||
|
= getTrackByMediaTypeAndParticipant(
|
||||||
|
tracks,
|
||||||
|
MEDIA_TYPE.VIDEO,
|
||||||
|
id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
conference.selectParticipant(
|
conference.selectParticipant(
|
||||||
videoTrack && videoTrack.videoType === VIDEO_TYPE.CAMERA
|
videoTrack && videoTrack.videoType === VIDEO_TYPE.CAMERA
|
||||||
? largeVideo.participantId
|
? id
|
||||||
: null);
|
: null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
_handleParticipantError(err);
|
_handleParticipantError(err);
|
||||||
|
@ -51,11 +55,8 @@ export function selectParticipant() {
|
||||||
export function selectParticipantInLargeVideo() {
|
export function selectParticipantInLargeVideo() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const participants = state['features/base/participants'];
|
const participantId = _electParticipantInLargeVideo(state);
|
||||||
const tracks = state['features/base/tracks'];
|
|
||||||
const largeVideo = state['features/largeVideo'];
|
const largeVideo = state['features/largeVideo'];
|
||||||
const participantId
|
|
||||||
= _electParticipantInLargeVideo(participants, tracks);
|
|
||||||
|
|
||||||
if (participantId !== largeVideo.participantId) {
|
if (participantId !== largeVideo.participantId) {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -77,57 +78,54 @@ export function selectParticipantInLargeVideo() {
|
||||||
* @returns {(Track|undefined)}
|
* @returns {(Track|undefined)}
|
||||||
*/
|
*/
|
||||||
function _electLastVisibleVideo(tracks) {
|
function _electLastVisibleVideo(tracks) {
|
||||||
let videoTrack;
|
|
||||||
|
|
||||||
// First we try to get most recent remote video track.
|
// First we try to get most recent remote video track.
|
||||||
for (let i = tracks.length - 1; i >= 0; i--) {
|
for (let i = tracks.length - 1; i >= 0; --i) {
|
||||||
if (tracks[i].mediaType === MEDIA_TYPE.VIDEO && !tracks[i].local) {
|
const track = tracks[i];
|
||||||
videoTrack = tracks[i];
|
|
||||||
break;
|
if (!track.local && track.mediaType === MEDIA_TYPE.VIDEO) {
|
||||||
|
return track;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// And if no remote video tracks are available, we select the local one.
|
// And if no remote video tracks are available, we select the local one.
|
||||||
if (!videoTrack) {
|
return getLocalVideoTrack(tracks);
|
||||||
videoTrack = getLocalVideoTrack(tracks);
|
|
||||||
}
|
|
||||||
|
|
||||||
return videoTrack;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the participant ID who is to be on the stage i.e. should be displayed
|
* Returns the identifier of the participant who is to be on the stage i.e.
|
||||||
* in LargeVideo.
|
* should be displayed in <tt>LargeVideo</tt>.
|
||||||
*
|
*
|
||||||
* @param {Participant[]} participants - All participants.
|
* @param {Object} state - The Redux state from which the participant to be
|
||||||
* @param {Track[]} tracks - All tracks.
|
* displayed in <tt>LargeVideo</tt> is to be elected.
|
||||||
* @private
|
* @private
|
||||||
* @returns {(string|undefined)}
|
* @returns {(string|undefined)}
|
||||||
*/
|
*/
|
||||||
function _electParticipantInLargeVideo(participants, tracks) {
|
function _electParticipantInLargeVideo(state) {
|
||||||
// First get the pinned participant. If local participant is pinned, he will
|
// First get the pinned participant. If the local participant is pinned,
|
||||||
// be shown in LargeVideo.
|
// he/she will be shown in LargeVideo.
|
||||||
|
const participants = state['features/base/participants'];
|
||||||
let participant = participants.find(p => p.pinned);
|
let participant = participants.find(p => p.pinned);
|
||||||
let id = participant ? participant.id : undefined;
|
let id = participant ? participant.id : undefined;
|
||||||
|
|
||||||
// If no participant is pinned, get the dominant speaker. But local
|
|
||||||
// participant won't be displayed in LargeVideo even if he is the dominant
|
|
||||||
// speaker.
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
participant = participants.find(p => p.speaking && !p.local);
|
// No participant is pinned so get the dominant speaker. But the local
|
||||||
|
// participant won't be displayed in LargeVideo even if he/she is the
|
||||||
|
// dominant speaker.
|
||||||
|
participant = participants.find(p => p.dominantSpeaker && !p.local);
|
||||||
if (participant) {
|
if (participant) {
|
||||||
id = participant.id;
|
id = participant.id;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If no participant is pinned and no dominant speaker, just get the
|
|
||||||
// participant with last visible video track. This may turn out to be local
|
|
||||||
// participant.
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
|
// There is no dominant speaker so get the participant with the last
|
||||||
|
// visible video track. This may turn out to be the local
|
||||||
|
// participant.
|
||||||
|
const tracks = state['features/base/tracks'];
|
||||||
const videoTrack = _electLastVisibleVideo(tracks);
|
const videoTrack = _electLastVisibleVideo(tracks);
|
||||||
|
|
||||||
id = videoTrack && videoTrack.participantId;
|
id = videoTrack && videoTrack.participantId;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,16 +24,14 @@ ReducerRegistry.register(
|
||||||
participantId: action.newValue
|
participantId: action.newValue
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
return state;
|
|
||||||
|
|
||||||
case LARGE_VIDEO_PARTICIPANT_CHANGED:
|
case LARGE_VIDEO_PARTICIPANT_CHANGED:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
participantId: action.participantId
|
participantId: action.participantId
|
||||||
};
|
};
|
||||||
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue