fix(external_api): Fix number of participants in meeting (#12052)

pull/12129/head jitsi-meet_7742
Duduman Bogdan Vlad 2 years ago committed by GitHub
parent 17008237dc
commit 5f62acc67c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      modules/API/API.js
  2. 39
      modules/API/external/external_api.js
  3. 2
      react/features/base/conference/functions.js
  4. 17
      react/features/base/participants/actions.ts
  5. 4
      react/features/base/participants/middleware.js
  6. 10
      react/features/base/participants/subscriber.js
  7. 65
      react/features/breakout-rooms/functions.js
  8. 19
      react/features/external-api/middleware.js
  9. 10
      react/features/shared-video/middleware.any.js

@ -55,7 +55,7 @@ import {
removeBreakoutRoom,
sendParticipantToRoom
} from '../../react/features/breakout-rooms/actions';
import { getBreakoutRooms } from '../../react/features/breakout-rooms/functions';
import { getBreakoutRooms, getRoomsInfo } from '../../react/features/breakout-rooms/functions';
import {
sendMessage,
setPrivateMessageRecipient,
@ -846,6 +846,10 @@ function initCommands() {
callback(getBreakoutRooms(APP.store.getState()));
break;
}
case 'rooms-info': {
callback(getRoomsInfo(APP.store.getState()));
break;
}
default:
return false;
}

@ -619,6 +619,9 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
case 'video-quality-changed':
this._videoQuality = data.videoQuality;
break;
case 'breakout-rooms-updated':
this.updateNumberOfParticipants(data.rooms);
break;
case 'local-storage-changed':
jitsiLocalStorage.setItem('jitsiLocalStorage', data.localStorageContent);
@ -638,6 +641,40 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
});
}
/**
* Update number of participants based on all rooms.
*
* @param {Object} rooms - Rooms available rooms in the conference.
* @returns {void}
*/
updateNumberOfParticipants(rooms) {
if (!rooms || !Object.keys(rooms).length) {
return;
}
const allParticipants = Object.keys(rooms).reduce((prev, roomItemKey) => {
if (rooms[roomItemKey]?.participants) {
return Object.keys(rooms[roomItemKey].participants).length + prev;
}
return prev;
}, 0);
this._numberOfParticipants = allParticipants;
}
/**
* Returns the rooms info in the conference.
*
* @returns {Object} Rooms info.
*/
async getRoomsInfo() {
return this._transport.sendRequest({
name: 'rooms-info'
});
}
/**
* Adds event listener to Meet Jitsi.
*
@ -1101,7 +1138,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
}
/**
* Returns the number of participants in the conference. The local
* Returns the number of participants in the conference from all rooms. The local
* participant is included.
*
* @returns {int} The number of participants in the conference.

@ -132,7 +132,7 @@ export function commonUserLeftHandling(
} else {
const isReplaced = user.isReplaced && user.isReplaced();
dispatch(participantLeft(id, conference, isReplaced));
dispatch(participantLeft(id, conference, { isReplaced }));
}
}

@ -381,8 +381,13 @@ export function hiddenParticipantLeft(id: string) {
* with the participant identified by the specified {@code id}. Only the local
* participant is allowed to not specify an associated {@code JitsiConference}
* instance.
* @param {boolean} isReplaced - Whether the participant is to be replaced in the meeting.
* @param {boolean} isVirtualScreenshareParticipant - Whether the participant is a virtual screen share participant.
* @param {Object} participantLeftProps - Other participant properties.
* @typedef {Object} participantLeftProps
* @param {boolean} participantLeftProps.isReplaced - Whether the participant is to be replaced in the meeting.
* @param {boolean} participantLeftProps.isVirtualScreenshareParticipant - Whether the participant is a
* virtual screen share participant.
* @param {boolean} participantLeftProps.isFakeParticipant - Whether the participant is a fake participant.
*
* @returns {{
* type: PARTICIPANT_LEFT,
* participant: {
@ -391,15 +396,15 @@ export function hiddenParticipantLeft(id: string) {
* }
* }}
*/
export function participantLeft(id: string, conference: any,
isReplaced?: boolean, isVirtualScreenshareParticipant?: boolean) {
export function participantLeft(id: string, conference: any, participantLeftProps: any = {}) {
return {
type: PARTICIPANT_LEFT,
participant: {
conference,
id,
isReplaced,
isVirtualScreenshareParticipant
isReplaced: participantLeftProps.isReplaced,
isVirtualScreenshareParticipant: participantLeftProps.isVirtualScreenshareParticipant,
isFakeParticipant: participantLeftProps.isFakeParticipant
}
};
}

@ -369,7 +369,9 @@ StateListenerRegistry.register(
batch(() => {
for (const [ id, p ] of getRemoteParticipants(getState())) {
(!conference || p.conference !== conference)
&& dispatch(participantLeft(id, p.conference, p.isReplaced));
&& dispatch(participantLeft(id, p.conference, {
isReplaced: p.isReplaced
}));
}
});
});

@ -56,7 +56,10 @@ function _updateScreenshareParticipants({ getState, dispatch }) {
}
if (localScreenShare && !newLocalSceenshareSourceName) {
dispatch(participantLeft(localScreenShare.id, conference, undefined, true));
dispatch(participantLeft(localScreenShare.id, conference, {
isReplaced: undefined,
isVirtualScreenshareParticipant: true
}));
}
}
@ -64,7 +67,10 @@ function _updateScreenshareParticipants({ getState, dispatch }) {
const addedScreenshareSourceNames = _.difference(currentScreenshareSourceNames, previousScreenshareSourceNames);
if (removedScreenshareSourceNames.length) {
removedScreenshareSourceNames.forEach(id => dispatch(participantLeft(id, conference, undefined, true)));
removedScreenshareSourceNames.forEach(id => dispatch(participantLeft(id, conference, {
isReplaced: undefined,
isVirtualScreenshareParticipant: true
})));
}
if (addedScreenshareSourceNames.length) {

@ -3,7 +3,7 @@
import _ from 'lodash';
import { getCurrentConference } from '../base/conference';
import { getParticipantCount, isLocalParticipantModerator } from '../base/participants';
import { getParticipantById, getParticipantCount, isLocalParticipantModerator } from '../base/participants';
import { toState } from '../base/redux';
import { FEATURE_KEY } from './constants';
@ -30,6 +30,69 @@ export const getMainRoom = (stateful: Function | Object) => {
return _.find(rooms, (room: Object) => room.isMainRoom);
};
export const getRoomsInfo = (stateful: Function | Object) => {
const breakoutRooms = getBreakoutRooms(stateful);
const conference = getCurrentConference(stateful);
const initialRoomsInfo = {
rooms: []
};
// only main roomn
if (!breakoutRooms || Object.keys(breakoutRooms).length === 0) {
return {
...initialRoomsInfo,
rooms: [ {
isMainRoom: true,
id: conference?.room?.roomjid,
jid: conference?.room?.myroomjid,
participants: conference && conference.participants && Object.keys(conference.participants).length
? Object.keys(conference.participants).map(participantId => {
const participantItem = conference?.participants[participantId];
const storeParticipant = getParticipantById(stateful, participantItem._id);
return {
jid: participantItem._jid,
role: participantItem._role,
displayName: participantItem._displayName,
avatarUrl: storeParticipant?.loadableAvatarUrl,
id: participantItem._id
};
}) : []
} ]
};
}
return {
...initialRoomsInfo,
rooms: Object.keys(breakoutRooms).map(breakoutRoomKey => {
const breakoutRoomItem = breakoutRooms[breakoutRoomKey];
return {
isMainRoom: Boolean(breakoutRoomItem.isMainRoom),
id: breakoutRoomItem.id,
jid: breakoutRoomItem.jid,
participants: breakoutRoomItem.participants && Object.keys(breakoutRoomItem.participants).length
? Object.keys(breakoutRoomItem.participants).map(participantLongId => {
const participantItem = breakoutRoomItem.participants[participantLongId];
const ids = participantLongId.split('/');
const storeParticipant = getParticipantById(stateful,
ids.length > 1 ? ids[1] : participantItem.jid);
return {
jid: participantItem?.jid,
role: participantItem?.role,
displayName: participantItem?.displayName,
avatarUrl: storeParticipant?.loadableAvatarUrl,
id: storeParticipant ? storeParticipant.id
: participantLongId
};
}) : []
};
})
};
};
/**
* Returns the room by Jid.
*

@ -150,19 +150,32 @@ MiddlewareRegistry.register(store => next => action => {
{ id: action.kicker });
break;
case PARTICIPANT_LEFT:
case PARTICIPANT_LEFT: {
const { participant } = action;
const { isFakeParticipant, isVirtualScreenshareParticipant } = participant;
// Skip sending participant left event for fake or virtual screenshare participants.
if (isFakeParticipant || isVirtualScreenshareParticipant) {
break;
}
APP.API.notifyUserLeft(action.participant.id);
break;
}
case PARTICIPANT_JOINED: {
const state = store.getState();
const { defaultRemoteDisplayName } = state['features/base/config'];
const { participant } = action;
const { id, local, name } = participant;
const { id, isFakeParticipant, isVirtualScreenshareParticipant, local, name } = participant;
// The version of external api outside of middleware did not emit
// the local participant being created.
if (!local) {
// Skip sending participant joined event for fake or virtual screenshare participants.
if (isFakeParticipant || isVirtualScreenshareParticipant) {
break;
}
APP.API.notifyUserJoined(id, {
displayName: name,
formattedDisplayName: appendSuffix(

@ -10,7 +10,8 @@ import {
getLocalParticipant,
participantJoined,
participantLeft,
pinParticipant
pinParticipant,
getParticipantById
} from '../base/participants';
import { MiddlewareRegistry } from '../base/redux';
@ -51,7 +52,12 @@ MiddlewareRegistry.register(store => next => action => {
if (isSharingStatus(sharedVideoStatus)) {
handleSharingVideoStatus(store, value, attributes, conference);
} else if (sharedVideoStatus === 'stop') {
dispatch(participantLeft(value, conference));
const videoParticipant = getParticipantById(state, value);
dispatch(participantLeft(value, conference, {
isFakeParticipant: videoParticipant?.isFakeParticipant
}));
if (localParticipantId !== from) {
dispatch(resetSharedVideoStatus());
}

Loading…
Cancel
Save