From 983e62ffe98dae24d724ecb314f896bbb7db488f Mon Sep 17 00:00:00 2001 From: Lyubo Marinov Date: Tue, 22 May 2018 14:30:51 -0500 Subject: [PATCH] Associate remote participant w/ JitsiConference (_JOINED) The plan set in motion here is to associate remote participants with the JitsiConference instances that created them in order to be able to remove remote participants when a JitsiConference is no longer the primary focus of the jitsi-meet app. And that's supposed to alleviate a problem with multiplying remote thumbnails. Doing all of the above in a single commit is a bit of a high order. So I'm splitting the whole into multiple successive commits for the purposes of observability, comprehension. Each commit is supposed to be safe even if subsequent commits are not accepted, are reverted, whatever. Obviously, without the successive commits, a commit may be "unused". One of the important pieces of the multiplying remote thumbnails "fix" offered is removing remote participants who are no longer of interest i.e. PARTICIPANT_LEFT. But in order for _LEFT to be implemented, _JOINED must be implemented first. --- conference.js | 1 + modules/UI/shared_video/SharedVideo.js | 1 + react/features/base/conference/actions.js | 1 + react/features/base/participants/actions.js | 13 +- .../features/base/participants/middleware.js | 13 +- react/features/base/participants/reducer.js | 148 ++++++++++-------- 6 files changed, 99 insertions(+), 78 deletions(-) diff --git a/conference.js b/conference.js index 9968946f5..76a729eaa 100644 --- a/conference.js +++ b/conference.js @@ -1688,6 +1688,7 @@ export default { const displayName = user.getDisplayName(); APP.store.dispatch(participantJoined({ + conference: room, id, name: displayName, role: user.getRole() diff --git a/modules/UI/shared_video/SharedVideo.js b/modules/UI/shared_video/SharedVideo.js index 34c4f797e..c29e23015 100644 --- a/modules/UI/shared_video/SharedVideo.js +++ b/modules/UI/shared_video/SharedVideo.js @@ -306,6 +306,7 @@ export default class SharedVideoManager { SHARED_VIDEO_CONTAINER_TYPE, self.sharedVideo); APP.store.dispatch(participantJoined({ + conference: APP.conference, id: self.url, isBot: true, name: 'YouTube' diff --git a/react/features/base/conference/actions.js b/react/features/base/conference/actions.js index c2b56573d..5cae20a97 100644 --- a/react/features/base/conference/actions.js +++ b/react/features/base/conference/actions.js @@ -140,6 +140,7 @@ function _addConferenceListeners(conference, dispatch) { conference.on( JitsiConferenceEvents.USER_JOINED, (id, user) => dispatch(participantJoined({ + conference, id, name: user.getDisplayName(), role: user.getRole() diff --git a/react/features/base/participants/actions.js b/react/features/base/participants/actions.js index f3bbe98f0..a67f4ca20 100644 --- a/react/features/base/participants/actions.js +++ b/react/features/base/participants/actions.js @@ -214,11 +214,16 @@ export function participantDisplayNameChanged(id, displayName = '') { * * @param {Participant} participant - Information about participant. * @returns {{ - * type: PARTICIPANT_JOINED, - * participant: Participant + * type: PARTICIPANT_JOINED, + * participant: Participant * }} */ export function participantJoined(participant) { + if (!participant.local && !participant.conference) { + throw Error( + 'A remote participant must be associated with a JitsiConference!'); + } + return { type: PARTICIPANT_JOINED, participant @@ -393,7 +398,5 @@ export function showParticipantJoinedNotification(displayName) { joinedParticipantsNames.push( displayName || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME); - return dispatch => { - _throttledNotifyParticipantConnected(dispatch); - }; + return dispatch => _throttledNotifyParticipantConnected(dispatch); } diff --git a/react/features/base/participants/middleware.js b/react/features/base/participants/middleware.js index 6198667cd..61735812d 100644 --- a/react/features/base/participants/middleware.js +++ b/react/features/base/participants/middleware.js @@ -139,15 +139,15 @@ MiddlewareRegistry.register(store => next => action => { */ function _localParticipantJoined({ getState, dispatch }, next, action) { const result = next(action); + const settings = getState()['features/base/settings']; - const localParticipant = { + + dispatch(localParticipantJoined({ avatarID: settings.avatarID, avatarURL: settings.avatarURL, email: settings.email, name: settings.displayName - }; - - dispatch(localParticipantJoined(localParticipant)); + })); return result; } @@ -204,7 +204,10 @@ function _participantJoinedOrUpdated({ getState }, next, action) { if (local) { const { conference } = getState()['features/base/conference']; - conference.setLocalParticipantProperty('raisedHand', raisedHand); + conference + && conference.setLocalParticipantProperty( + 'raisedHand', + raisedHand); } if (typeof APP === 'object') { diff --git a/react/features/base/participants/reducer.js b/react/features/base/participants/reducer.js index aa40e4449..5af3dc33a 100644 --- a/react/features/base/participants/reducer.js +++ b/react/features/base/participants/reducer.js @@ -39,6 +39,35 @@ declare var APP: Object; const PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE = [ 'dominantSpeaker', 'id', 'local', 'pinned' ]; +/** + * Listen for actions which add, remove, or update the set of participants in + * the conference. + * + * @param {Participant[]} state - List of participants to be modified. + * @param {Object} action - Action object. + * @param {string} action.type - Type of action. + * @param {Participant} action.participant - Information about participant to be + * added/removed/modified. + * @returns {Participant[]} + */ +ReducerRegistry.register('features/base/participants', (state = [], action) => { + switch (action.type) { + case DOMINANT_SPEAKER_CHANGED: + case PARTICIPANT_ID_CHANGED: + case PARTICIPANT_UPDATED: + case PIN_PARTICIPANT: + return state.map(p => _participant(p, action)); + + case PARTICIPANT_JOINED: + return [ ...state, _participantJoined(action) ]; + + case PARTICIPANT_LEFT: + return state.filter(p => p.id !== action.participant.id); + } + + return state; +}); + /** * Reducer function for a single participant. * @@ -67,52 +96,6 @@ function _participant(state: Object = {}, action) { } break; - case PARTICIPANT_JOINED: { - const { participant } = action; // eslint-disable-line no-shadow - const { - avatarURL, - connectionStatus, - dominantSpeaker, - email, - isBot, - local, - name, - pinned, - role - } = participant; - let { avatarID, id } = participant; - - // avatarID - // - // TODO Get the avatarID of the local participant from localStorage. - if (!avatarID && local) { - avatarID = randomHexString(32); - } - - // id - // - // XXX The situation of not having an ID for a remote participant should - // not happen. Maybe we should raise an error in this case or generate a - // random ID. - if (!id && local) { - id = LOCAL_PARTICIPANT_DEFAULT_ID; - } - - return { - avatarID, - avatarURL, - connectionStatus, - dominantSpeaker: dominantSpeaker || false, - email, - id, - isBot, - local: local || false, - name, - pinned: pinned || false, - role: role || PARTICIPANT_ROLE.NONE - }; - } - case PARTICIPANT_UPDATED: { const { participant } = action; // eslint-disable-line no-shadow let { id } = participant; @@ -147,31 +130,60 @@ function _participant(state: Object = {}, action) { } /** - * Listen for actions which add, remove, or update the set of participants in - * the conference. + * Reduces a specific redux action of type {@link PARTICIPANT_JOINED} in the + * feature base/participants. * - * @param {Participant[]} state - List of participants to be modified. - * @param {Object} action - Action object. - * @param {string} action.type - Type of action. - * @param {Participant} action.participant - Information about participant to be - * added/removed/modified. - * @returns {Participant[]} + * @param {Action} action - The redux action of type {@code PARTICIPANT_JOINED} + * to reduce. + * @private + * @returns {Object} The new participant derived from the payload of the + * specified {@code action} to be added into the redux state of the feature + * base/participants after the reduction of the specified + * {@code action}. */ -ReducerRegistry.register('features/base/participants', (state = [], action) => { - switch (action.type) { - case DOMINANT_SPEAKER_CHANGED: - case PARTICIPANT_ID_CHANGED: - case PARTICIPANT_UPDATED: - case PIN_PARTICIPANT: - return state.map(p => _participant(p, action)); +function _participantJoined({ participant }) { + const { + avatarURL, + connectionStatus, + dominantSpeaker, + email, + isBot, + local, + name, + pinned, + role + } = participant; + let { avatarID, conference, id } = participant; - case PARTICIPANT_JOINED: - return [ ...state, _participant(undefined, action) ]; + if (local) { + // avatarID + // + // TODO Get the avatarID of the local participant from localStorage. + avatarID || (avatarID = randomHexString(32)); - case PARTICIPANT_LEFT: - return state.filter(p => p.id !== action.participant.id); + // conference + // + // XXX The local participant is not identified in association with a + // JitsiConference because it is identified by the very fact that it is + // the local participant. + conference = undefined; - default: - return state; + // id + id || (id = LOCAL_PARTICIPANT_DEFAULT_ID); } -}); + + return { + avatarID, + avatarURL, + conference, + connectionStatus, + dominantSpeaker: dominantSpeaker || false, + email, + id, + isBot, + local: local || false, + name, + pinned: pinned || false, + role: role || PARTICIPANT_ROLE.NONE + }; +}