From d7c8164b7494d06fb4cb908b5f539020bb12c851 Mon Sep 17 00:00:00 2001 From: Robert Pintilii Date: Tue, 5 Apr 2022 16:00:32 +0300 Subject: [PATCH] fix(follow-me) Make follow me work with stage filmstrip (#11306) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On follow me enabled all participants will see the participants pinned by the moderator Fix pinned indicator to work when stage filmstrip is disabled Fix add participant on dominant speaker change: if the participant was already pinned keep it as pinned Don’t add local participant on stage (on automatic selection) --- .../filmstrip/components/web/PinnedIndicator.js | 9 ++++++--- react/features/filmstrip/functions.web.js | 12 ++++++++++++ react/features/filmstrip/middleware.web.js | 11 +++++++++-- react/features/follow-me/middleware.js | 11 +++++++++++ react/features/follow-me/subscriber.js | 15 ++++++++++++++- react/features/video-quality/subscriber.js | 5 +++-- 6 files changed, 55 insertions(+), 8 deletions(-) diff --git a/react/features/filmstrip/components/web/PinnedIndicator.js b/react/features/filmstrip/components/web/PinnedIndicator.js index c0982914b..d1be1721d 100644 --- a/react/features/filmstrip/components/web/PinnedIndicator.js +++ b/react/features/filmstrip/components/web/PinnedIndicator.js @@ -5,7 +5,9 @@ import React from 'react'; import { useSelector } from 'react-redux'; import { IconPinParticipant } from '../../../base/icons'; +import { getParticipantById } from '../../../base/participants'; import { BaseIndicator } from '../../../base/react'; +import { getPinnedActiveParticipants, isStageFilmstripEnabled } from '../../functions.web'; /** * The type of the React {@code Component} props of {@link PinnedIndicator}. @@ -52,11 +54,12 @@ const PinnedIndicator = ({ participantId, tooltipPosition }: Props) => { - const isPinned = useSelector(state => state['features/filmstrip'].activeParticipants) - .find(p => p.id === participantId && p.pinned); + const stageFilmstrip = useSelector(isStageFilmstripEnabled); + const pinned = useSelector(state => getParticipantById(state, participantId))?.pinned; + const isPinned = useSelector(getPinnedActiveParticipants).find(p => p.participantId === participantId); const styles = useStyles(); - if (!isPinned) { + if ((stageFilmstrip && !isPinned) || (!stageFilmstrip && !pinned)) { return null; } diff --git a/react/features/filmstrip/functions.web.js b/react/features/filmstrip/functions.web.js index 89739e124..97c8cc5e5 100644 --- a/react/features/filmstrip/functions.web.js +++ b/react/features/filmstrip/functions.web.js @@ -670,6 +670,18 @@ export function getActiveParticipantsIds(state) { return activeParticipants.map(p => p.participantId); } +/** + * Gets the ids of the active participants. + * + * @param {Object} state - Redux state. + * @returns {Array} + */ +export function getPinnedActiveParticipants(state) { + const { activeParticipants } = state['features/filmstrip']; + + return activeParticipants.filter(p => p.pinned); +} + /** * Get whether or not the stage filmstrip should be displayed. * diff --git a/react/features/filmstrip/middleware.web.js b/react/features/filmstrip/middleware.web.js index d6a6930ce..b8e52ee28 100644 --- a/react/features/filmstrip/middleware.web.js +++ b/react/features/filmstrip/middleware.web.js @@ -36,7 +36,7 @@ import { updateRemoteParticipantsOnLeave } from './functions'; import './subscriber'; -import { getActiveParticipantsIds, isStageFilmstripEnabled } from './functions.web'; +import { getActiveParticipantsIds, getPinnedActiveParticipants, isStageFilmstripEnabled } from './functions.web'; /** * Map of timers. @@ -187,9 +187,16 @@ MiddlewareRegistry.register(store => next => action => { const state = store.getState(); const stageFilmstrip = isStageFilmstripEnabled(state); const currentLayout = getCurrentLayout(state); + const local = getLocalParticipant(state); + + if (id === local.id) { + break; + } if (stageFilmstrip && currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW) { - store.dispatch(addStageParticipant(id)); + const isPinned = getPinnedActiveParticipants(state).some(p => p.participantId === id); + + store.dispatch(addStageParticipant(id, Boolean(isPinned))); } break; } diff --git a/react/features/follow-me/middleware.js b/react/features/follow-me/middleware.js index b2971ad83..e570d0662 100644 --- a/react/features/follow-me/middleware.js +++ b/react/features/follow-me/middleware.js @@ -1,5 +1,7 @@ // @flow +import _ from 'lodash'; + import { CONFERENCE_WILL_JOIN } from '../base/conference/actionTypes'; import { getParticipantById, @@ -9,6 +11,7 @@ import { } from '../base/participants'; import { MiddlewareRegistry } from '../base/redux'; import { setFilmstripVisible } from '../filmstrip'; +import { addStageParticipant } from '../filmstrip/actions.web'; import { setTileView } from '../video-layout'; import { @@ -178,6 +181,14 @@ function _onFollowMeCommand(attributes = {}, id, store) { } else if (typeof idOfParticipantToPin === 'undefined' && pinnedParticipant) { store.dispatch(pinParticipant(null)); } + + if (attributes.pinnedStageParticipants !== undefined) { + const stageParticipants = JSON.parse(attributes.pinnedStageParticipants); + + if (!_.isEqual(stageParticipants, oldState.pinnedStageParticipants)) { + stageParticipants.forEach(p => store.dispatch(addStageParticipant(p.participantId, true))); + } + } } /** diff --git a/react/features/follow-me/subscriber.js b/react/features/follow-me/subscriber.js index 0ab7b9267..c2dcc0fb6 100644 --- a/react/features/follow-me/subscriber.js +++ b/react/features/follow-me/subscriber.js @@ -6,6 +6,7 @@ import { isLocalParticipantModerator } from '../base/participants'; import { StateListenerRegistry } from '../base/redux'; +import { getPinnedActiveParticipants, isStageFilmstripEnabled } from '../filmstrip/functions.web'; import { shouldDisplayTileView } from '../video-layout/functions'; import { FOLLOW_ME_COMMAND } from './constants'; @@ -51,6 +52,16 @@ StateListenerRegistry.register( /* selector */ state => state['features/filmstrip'].visible, /* listener */ _sendFollowMeCommand); +/** + * Subscribes to changes to the stage filmstrip participants. + */ +StateListenerRegistry.register( + /* selector */ getPinnedActiveParticipants, + /* listener */ _sendFollowMeCommand, + { + deepEquals: true + }); + /** * Subscribes to changes to the tile view setting in the user interface of the * local participant. @@ -68,10 +79,12 @@ StateListenerRegistry.register( */ function _getFollowMeState(state) { const pinnedParticipant = getPinnedParticipant(state); + const stageFilmstrip = isStageFilmstripEnabled(state); return { filmstripVisible: state['features/filmstrip'].visible, - nextOnStage: pinnedParticipant && pinnedParticipant.id, + nextOnStage: stageFilmstrip ? undefined : pinnedParticipant && pinnedParticipant.id, + pinnedStageParticipants: stageFilmstrip ? JSON.stringify(getPinnedActiveParticipants(state)) : undefined, sharedDocumentVisible: state['features/etherpad'].editing, tileViewEnabled: shouldDisplayTileView(state) }; diff --git a/react/features/video-quality/subscriber.js b/react/features/video-quality/subscriber.js index 32fc9a176..efd6a0fb2 100644 --- a/react/features/video-quality/subscriber.js +++ b/react/features/video-quality/subscriber.js @@ -98,10 +98,11 @@ StateListenerRegistry.register( * Updates the receiver constraints when the stage participants change. */ StateListenerRegistry.register( - state => getActiveParticipantsIds(state).sort() - .join(), + state => getActiveParticipantsIds(state).sort(), (_, store) => { _updateReceiverVideoConstraints(store); + }, { + deepEquals: true } );