From c727b603af478faff83d98f1d5027ebe9d0d08b0 Mon Sep 17 00:00:00 2001 From: Jaya Allamsetty <54324652+jallamsetty1@users.noreply.github.com> Date: Wed, 20 Jul 2022 15:51:47 -0400 Subject: [PATCH] fix(filmstrip) Make dominant speaker visible at all times (#11874) * fix(filmstrip) Make dominant speaker visible at all times. * squash: address review comments. --- react/features/base/participants/functions.js | 53 +++++++++++++++++++ react/features/filmstrip/functions.any.js | 18 +++---- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/react/features/base/participants/functions.js b/react/features/base/participants/functions.js index 352a55900..3aabc95b8 100644 --- a/react/features/base/participants/functions.js +++ b/react/features/base/participants/functions.js @@ -46,6 +46,59 @@ const AVATAR_CHECKER_FUNCTIONS = [ ]; /* eslint-enable arrow-body-style, no-unused-vars */ +/** + * Returns the list of active speakers that should be moved to the top of the sorted list of participants so that the + * dominant speaker is visible always on the vertical filmstrip in stage layout. + * + * @param {Function | Object} stateful - The (whole) redux state, or redux's {@code getState} function to be used to + * retrieve the state. + * @returns {Array} + */ +export function getActiveSpeakersToBeDisplayed(stateful: Object | Function) { + const state = toState(stateful); + const { + fakeParticipants, + sortedRemoteScreenshares, + sortedRemoteVirtualScreenshareParticipants, + speakersList + } = state['features/base/participants']; + const { visibleRemoteParticipants } = state['features/filmstrip']; + + // Do not re-sort the active speakers if all of them are currently visible. + if (typeof visibleRemoteParticipants === 'undefined' || speakersList.size <= visibleRemoteParticipants.size) { + return speakersList; + } + const activeSpeakers = new Map(speakersList); + let availableSlotsForActiveSpeakers = visibleRemoteParticipants.size; + + // Remove screenshares from the count. + if (getMultipleVideoSupportFeatureFlag(state)) { + if (sortedRemoteVirtualScreenshareParticipants) { + availableSlotsForActiveSpeakers -= sortedRemoteVirtualScreenshareParticipants.size * 2; + for (const screenshare of Array.from(sortedRemoteVirtualScreenshareParticipants.keys())) { + const ownerId = getVirtualScreenshareParticipantOwnerId(screenshare); + + activeSpeakers.delete(ownerId); + } + } + } else if (sortedRemoteScreenshares) { + availableSlotsForActiveSpeakers -= sortedRemoteScreenshares.size; + for (const id of Array.from(sortedRemoteScreenshares.keys())) { + activeSpeakers.delete(id); + } + } + + // Remove shared video from the count. + if (fakeParticipants) { + availableSlotsForActiveSpeakers -= fakeParticipants.size; + } + const truncatedSpeakersList = Array.from(activeSpeakers).slice(0, availableSlotsForActiveSpeakers); + + truncatedSpeakersList.sort((a, b) => a[1].localeCompare(b[1])); + + return new Map(truncatedSpeakersList); +} + /** * Resolves the first loadable avatar URL for a participant. * diff --git a/react/features/filmstrip/functions.any.js b/react/features/filmstrip/functions.any.js index b9d8472dc..d971a0ed8 100644 --- a/react/features/filmstrip/functions.any.js +++ b/react/features/filmstrip/functions.any.js @@ -1,7 +1,7 @@ // @flow -import { getSourceNameSignalingFeatureFlag } from '../base/config'; -import { getVirtualScreenshareParticipantOwnerId } from '../base/participants'; +import { getMultipleVideoSupportFeatureFlag } from '../base/config'; +import { getActiveSpeakersToBeDisplayed, getVirtualScreenshareParticipantOwnerId } from '../base/participants'; import { setRemoteParticipants } from './actions'; import { isFilmstripScrollVisible } from './functions'; @@ -34,42 +34,36 @@ export function updateRemoteParticipants(store: Object, participantId: ?number) const { fakeParticipants, sortedRemoteParticipants, - sortedRemoteScreenshares, - speakersList + sortedRemoteScreenshares } = state['features/base/participants']; const remoteParticipants = new Map(sortedRemoteParticipants); const screenShares = new Map(sortedRemoteScreenshares); const screenShareParticipants = sortedRemoteVirtualScreenshareParticipants ? [ ...sortedRemoteVirtualScreenshareParticipants.keys() ] : []; const sharedVideos = fakeParticipants ? Array.from(fakeParticipants.keys()) : []; - const speakers = new Map(speakersList); + const speakers = getActiveSpeakersToBeDisplayed(state); - if (getSourceNameSignalingFeatureFlag(state)) { + if (getMultipleVideoSupportFeatureFlag(state)) { for (const screenshare of screenShareParticipants) { const ownerId = getVirtualScreenshareParticipantOwnerId(screenshare); remoteParticipants.delete(ownerId); remoteParticipants.delete(screenshare); - - speakers.delete(ownerId); - speakers.delete(screenshare); } } else { for (const screenshare of screenShares.keys()) { remoteParticipants.delete(screenshare); - speakers.delete(screenshare); } } for (const sharedVideo of sharedVideos) { remoteParticipants.delete(sharedVideo); - speakers.delete(sharedVideo); } for (const speaker of speakers.keys()) { remoteParticipants.delete(speaker); } - if (getSourceNameSignalingFeatureFlag(state)) { + if (getMultipleVideoSupportFeatureFlag(state)) { // Always update the order of the thumnails. const participantsWithScreenShare = screenShareParticipants.reduce((acc, screenshare) => { const ownerId = getVirtualScreenshareParticipantOwnerId(screenshare);