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 } );