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.
This commit is contained in:
Lyubo Marinov 2018-05-22 14:30:51 -05:00
parent fcca15c827
commit 983e62ffe9
6 changed files with 99 additions and 78 deletions

View File

@ -1688,6 +1688,7 @@ export default {
const displayName = user.getDisplayName(); const displayName = user.getDisplayName();
APP.store.dispatch(participantJoined({ APP.store.dispatch(participantJoined({
conference: room,
id, id,
name: displayName, name: displayName,
role: user.getRole() role: user.getRole()

View File

@ -306,6 +306,7 @@ export default class SharedVideoManager {
SHARED_VIDEO_CONTAINER_TYPE, self.sharedVideo); SHARED_VIDEO_CONTAINER_TYPE, self.sharedVideo);
APP.store.dispatch(participantJoined({ APP.store.dispatch(participantJoined({
conference: APP.conference,
id: self.url, id: self.url,
isBot: true, isBot: true,
name: 'YouTube' name: 'YouTube'

View File

@ -140,6 +140,7 @@ function _addConferenceListeners(conference, dispatch) {
conference.on( conference.on(
JitsiConferenceEvents.USER_JOINED, JitsiConferenceEvents.USER_JOINED,
(id, user) => dispatch(participantJoined({ (id, user) => dispatch(participantJoined({
conference,
id, id,
name: user.getDisplayName(), name: user.getDisplayName(),
role: user.getRole() role: user.getRole()

View File

@ -214,11 +214,16 @@ export function participantDisplayNameChanged(id, displayName = '') {
* *
* @param {Participant} participant - Information about participant. * @param {Participant} participant - Information about participant.
* @returns {{ * @returns {{
* type: PARTICIPANT_JOINED, * type: PARTICIPANT_JOINED,
* participant: Participant * participant: Participant
* }} * }}
*/ */
export function participantJoined(participant) { export function participantJoined(participant) {
if (!participant.local && !participant.conference) {
throw Error(
'A remote participant must be associated with a JitsiConference!');
}
return { return {
type: PARTICIPANT_JOINED, type: PARTICIPANT_JOINED,
participant participant
@ -393,7 +398,5 @@ export function showParticipantJoinedNotification(displayName) {
joinedParticipantsNames.push( joinedParticipantsNames.push(
displayName || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME); displayName || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME);
return dispatch => { return dispatch => _throttledNotifyParticipantConnected(dispatch);
_throttledNotifyParticipantConnected(dispatch);
};
} }

View File

@ -139,15 +139,15 @@ MiddlewareRegistry.register(store => next => action => {
*/ */
function _localParticipantJoined({ getState, dispatch }, next, action) { function _localParticipantJoined({ getState, dispatch }, next, action) {
const result = next(action); const result = next(action);
const settings = getState()['features/base/settings']; const settings = getState()['features/base/settings'];
const localParticipant = {
dispatch(localParticipantJoined({
avatarID: settings.avatarID, avatarID: settings.avatarID,
avatarURL: settings.avatarURL, avatarURL: settings.avatarURL,
email: settings.email, email: settings.email,
name: settings.displayName name: settings.displayName
}; }));
dispatch(localParticipantJoined(localParticipant));
return result; return result;
} }
@ -204,7 +204,10 @@ function _participantJoinedOrUpdated({ getState }, next, action) {
if (local) { if (local) {
const { conference } = getState()['features/base/conference']; const { conference } = getState()['features/base/conference'];
conference.setLocalParticipantProperty('raisedHand', raisedHand); conference
&& conference.setLocalParticipantProperty(
'raisedHand',
raisedHand);
} }
if (typeof APP === 'object') { if (typeof APP === 'object') {

View File

@ -39,6 +39,35 @@ declare var APP: Object;
const PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE const PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE
= [ 'dominantSpeaker', 'id', 'local', 'pinned' ]; = [ '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. * Reducer function for a single participant.
* *
@ -67,52 +96,6 @@ function _participant(state: Object = {}, action) {
} }
break; 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: { case PARTICIPANT_UPDATED: {
const { participant } = action; // eslint-disable-line no-shadow const { participant } = action; // eslint-disable-line no-shadow
let { id } = participant; 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 * Reduces a specific redux action of type {@link PARTICIPANT_JOINED} in the
* the conference. * feature base/participants.
* *
* @param {Participant[]} state - List of participants to be modified. * @param {Action} action - The redux action of type {@code PARTICIPANT_JOINED}
* @param {Object} action - Action object. * to reduce.
* @param {string} action.type - Type of action. * @private
* @param {Participant} action.participant - Information about participant to be * @returns {Object} The new participant derived from the payload of the
* added/removed/modified. * specified {@code action} to be added into the redux state of the feature
* @returns {Participant[]} * base/participants after the reduction of the specified
* {@code action}.
*/ */
ReducerRegistry.register('features/base/participants', (state = [], action) => { function _participantJoined({ participant }) {
switch (action.type) { const {
case DOMINANT_SPEAKER_CHANGED: avatarURL,
case PARTICIPANT_ID_CHANGED: connectionStatus,
case PARTICIPANT_UPDATED: dominantSpeaker,
case PIN_PARTICIPANT: email,
return state.map(p => _participant(p, action)); isBot,
local,
name,
pinned,
role
} = participant;
let { avatarID, conference, id } = participant;
case PARTICIPANT_JOINED: if (local) {
return [ ...state, _participant(undefined, action) ]; // avatarID
//
// TODO Get the avatarID of the local participant from localStorage.
avatarID || (avatarID = randomHexString(32));
case PARTICIPANT_LEFT: // conference
return state.filter(p => p.id !== action.participant.id); //
// 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: // id
return state; 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
};
}