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 + }; +}