fix(external_api): Fix number of participants in meeting (#12052)
This commit is contained in:
parent
17008237dc
commit
5f62acc67c
|
@ -55,7 +55,7 @@ import {
|
||||||
removeBreakoutRoom,
|
removeBreakoutRoom,
|
||||||
sendParticipantToRoom
|
sendParticipantToRoom
|
||||||
} from '../../react/features/breakout-rooms/actions';
|
} from '../../react/features/breakout-rooms/actions';
|
||||||
import { getBreakoutRooms } from '../../react/features/breakout-rooms/functions';
|
import { getBreakoutRooms, getRoomsInfo } from '../../react/features/breakout-rooms/functions';
|
||||||
import {
|
import {
|
||||||
sendMessage,
|
sendMessage,
|
||||||
setPrivateMessageRecipient,
|
setPrivateMessageRecipient,
|
||||||
|
@ -846,6 +846,10 @@ function initCommands() {
|
||||||
callback(getBreakoutRooms(APP.store.getState()));
|
callback(getBreakoutRooms(APP.store.getState()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'rooms-info': {
|
||||||
|
callback(getRoomsInfo(APP.store.getState()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -619,6 +619,9 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||||
case 'video-quality-changed':
|
case 'video-quality-changed':
|
||||||
this._videoQuality = data.videoQuality;
|
this._videoQuality = data.videoQuality;
|
||||||
break;
|
break;
|
||||||
|
case 'breakout-rooms-updated':
|
||||||
|
this.updateNumberOfParticipants(data.rooms);
|
||||||
|
break;
|
||||||
case 'local-storage-changed':
|
case 'local-storage-changed':
|
||||||
jitsiLocalStorage.setItem('jitsiLocalStorage', data.localStorageContent);
|
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.
|
* 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.
|
* participant is included.
|
||||||
*
|
*
|
||||||
* @returns {int} The number of participants in the conference.
|
* @returns {int} The number of participants in the conference.
|
||||||
|
|
|
@ -132,7 +132,7 @@ export function commonUserLeftHandling(
|
||||||
} else {
|
} else {
|
||||||
const isReplaced = user.isReplaced && user.isReplaced();
|
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
|
* with the participant identified by the specified {@code id}. Only the local
|
||||||
* participant is allowed to not specify an associated {@code JitsiConference}
|
* participant is allowed to not specify an associated {@code JitsiConference}
|
||||||
* instance.
|
* instance.
|
||||||
* @param {boolean} isReplaced - Whether the participant is to be replaced in the meeting.
|
* @param {Object} participantLeftProps - Other participant properties.
|
||||||
* @param {boolean} isVirtualScreenshareParticipant - Whether the participant is a virtual screen share participant.
|
* @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 {{
|
* @returns {{
|
||||||
* type: PARTICIPANT_LEFT,
|
* type: PARTICIPANT_LEFT,
|
||||||
* participant: {
|
* participant: {
|
||||||
|
@ -391,15 +396,15 @@ export function hiddenParticipantLeft(id: string) {
|
||||||
* }
|
* }
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function participantLeft(id: string, conference: any,
|
export function participantLeft(id: string, conference: any, participantLeftProps: any = {}) {
|
||||||
isReplaced?: boolean, isVirtualScreenshareParticipant?: boolean) {
|
|
||||||
return {
|
return {
|
||||||
type: PARTICIPANT_LEFT,
|
type: PARTICIPANT_LEFT,
|
||||||
participant: {
|
participant: {
|
||||||
conference,
|
conference,
|
||||||
id,
|
id,
|
||||||
isReplaced,
|
isReplaced: participantLeftProps.isReplaced,
|
||||||
isVirtualScreenshareParticipant
|
isVirtualScreenshareParticipant: participantLeftProps.isVirtualScreenshareParticipant,
|
||||||
|
isFakeParticipant: participantLeftProps.isFakeParticipant
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -369,7 +369,9 @@ StateListenerRegistry.register(
|
||||||
batch(() => {
|
batch(() => {
|
||||||
for (const [ id, p ] of getRemoteParticipants(getState())) {
|
for (const [ id, p ] of getRemoteParticipants(getState())) {
|
||||||
(!conference || p.conference !== conference)
|
(!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) {
|
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);
|
const addedScreenshareSourceNames = _.difference(currentScreenshareSourceNames, previousScreenshareSourceNames);
|
||||||
|
|
||||||
if (removedScreenshareSourceNames.length) {
|
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) {
|
if (addedScreenshareSourceNames.length) {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { getCurrentConference } from '../base/conference';
|
import { getCurrentConference } from '../base/conference';
|
||||||
import { getParticipantCount, isLocalParticipantModerator } from '../base/participants';
|
import { getParticipantById, getParticipantCount, isLocalParticipantModerator } from '../base/participants';
|
||||||
import { toState } from '../base/redux';
|
import { toState } from '../base/redux';
|
||||||
|
|
||||||
import { FEATURE_KEY } from './constants';
|
import { FEATURE_KEY } from './constants';
|
||||||
|
@ -30,6 +30,69 @@ export const getMainRoom = (stateful: Function | Object) => {
|
||||||
return _.find(rooms, (room: Object) => room.isMainRoom);
|
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.
|
* Returns the room by Jid.
|
||||||
*
|
*
|
||||||
|
|
|
@ -150,19 +150,32 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
{ id: action.kicker });
|
{ id: action.kicker });
|
||||||
break;
|
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);
|
APP.API.notifyUserLeft(action.participant.id);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case PARTICIPANT_JOINED: {
|
case PARTICIPANT_JOINED: {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const { defaultRemoteDisplayName } = state['features/base/config'];
|
const { defaultRemoteDisplayName } = state['features/base/config'];
|
||||||
const { participant } = action;
|
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 version of external api outside of middleware did not emit
|
||||||
// the local participant being created.
|
// the local participant being created.
|
||||||
if (!local) {
|
if (!local) {
|
||||||
|
// Skip sending participant joined event for fake or virtual screenshare participants.
|
||||||
|
if (isFakeParticipant || isVirtualScreenshareParticipant) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
APP.API.notifyUserJoined(id, {
|
APP.API.notifyUserJoined(id, {
|
||||||
displayName: name,
|
displayName: name,
|
||||||
formattedDisplayName: appendSuffix(
|
formattedDisplayName: appendSuffix(
|
||||||
|
|
|
@ -10,7 +10,8 @@ import {
|
||||||
getLocalParticipant,
|
getLocalParticipant,
|
||||||
participantJoined,
|
participantJoined,
|
||||||
participantLeft,
|
participantLeft,
|
||||||
pinParticipant
|
pinParticipant,
|
||||||
|
getParticipantById
|
||||||
} from '../base/participants';
|
} from '../base/participants';
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
import { MiddlewareRegistry } from '../base/redux';
|
||||||
|
|
||||||
|
@ -51,7 +52,12 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
if (isSharingStatus(sharedVideoStatus)) {
|
if (isSharingStatus(sharedVideoStatus)) {
|
||||||
handleSharingVideoStatus(store, value, attributes, conference);
|
handleSharingVideoStatus(store, value, attributes, conference);
|
||||||
} else if (sharedVideoStatus === 'stop') {
|
} else if (sharedVideoStatus === 'stop') {
|
||||||
dispatch(participantLeft(value, conference));
|
const videoParticipant = getParticipantById(state, value);
|
||||||
|
|
||||||
|
dispatch(participantLeft(value, conference, {
|
||||||
|
isFakeParticipant: videoParticipant?.isFakeParticipant
|
||||||
|
}));
|
||||||
|
|
||||||
if (localParticipantId !== from) {
|
if (localParticipantId !== from) {
|
||||||
dispatch(resetSharedVideoStatus());
|
dispatch(resetSharedVideoStatus());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue