ref(stage-filmstrip) Refactor as new layout

Fixes screensharing selection issues. Now when there’s a screen share we just use the old VERTICAL_FILMSTRIP_VIEW layout
Add THUMBAIL_TYPE to determine how to display thumbnails
This commit is contained in:
robertpin 2022-04-12 16:19:10 +03:00 committed by Hristo Terezov
parent dde8c586da
commit 0abefa87aa
27 changed files with 452 additions and 355 deletions

View File

@ -1,175 +1,177 @@
.vertical-filmstrip span:not(.tile-view) .filmstrip {
&.hide-videos {
.remote-videos {
& > div {
opacity: 0;
pointer-events: none;
.vertical-filmstrip, .stage-filmstrip {
span:not(.tile-view) .filmstrip {
&.hide-videos {
.remote-videos {
& > div {
opacity: 0;
pointer-events: none;
}
}
}
}
/*
* Firefox sets flex items to min-height: auto and min-width: auto,
* preventing flex children from shrinking like they do on other browsers.
* Setting min-height and min-width 0 is a workaround for the issue so
* Firefox behaves like other browsers.
* https://bugzilla.mozilla.org/show_bug.cgi?id=1043520
*/
@mixin minHWAutoFix() {
min-height: 0;
min-width: 0;
}
/*
* Firefox sets flex items to min-height: auto and min-width: auto,
* preventing flex children from shrinking like they do on other browsers.
* Setting min-height and min-width 0 is a workaround for the issue so
* Firefox behaves like other browsers.
* https://bugzilla.mozilla.org/show_bug.cgi?id=1043520
*/
@mixin minHWAutoFix() {
min-height: 0;
min-width: 0;
}
@extend %align-right;
align-items: flex-end;
bottom: 0;
box-sizing: border-box;
display: flex;
flex-direction: column-reverse;
height: 100%;
width: 100%;
padding: 0;
/**
* fixed positioning is necessary for remote menus and tooltips to pop
* out of the scrolling filmstrip. AtlasKit dialogs and tooltips use
* a library called popper which will position its elements fixed if
* any parent is also fixed.
*/
position: fixed;
top: 0;
right: 0;
z-index: $filmstripVideosZ;
&.no-vertical-padding {
padding: 0;
}
/**
* Hide videos by making them slight to the right.
*/
.filmstrip__videos {
@extend %align-right;
align-items: flex-end;
bottom: 0;
box-sizing: border-box;
display: flex;
flex-direction: column-reverse;
height: 100%;
width: 100%;
padding: 0;
position:relative;
/**
* fixed positioning is necessary for remote menus and tooltips to pop
* out of the scrolling filmstrip. AtlasKit dialogs and tooltips use
* a library called popper which will position its elements fixed if
* any parent is also fixed.
*/
position: fixed;
top: 0;
right: 0;
width: auto;
z-index: $filmstripVideosZ;
&.no-vertical-padding {
padding: 0;
}
/**
* An id selector is used to match id specificity with existing
* filmstrip styles.
* Hide videos by making them slight to the right.
*/
&#remoteVideos {
border: $thumbnailsBorder solid transparent;
padding-left: 0;
border-left: 0;
.filmstrip__videos {
@extend %align-right;
bottom: 0;
padding: 0;
position:relative;
right: 0;
width: auto;
/**
* An id selector is used to match id specificity with existing
* filmstrip styles.
*/
&#remoteVideos {
border: $thumbnailsBorder solid transparent;
padding-left: 0;
border-left: 0;
width: 100%;
height: 100%;
justify-content: center;
}
}
/**
* Re-styles the local Video to better fit vertical filmstrip layout.
*/
#filmstripLocalVideo {
align-self: initial;
margin-bottom: 5px;
display: flex;
flex-direction: column-reverse;
height: auto;
justify-content: flex-start;
width: 100%;
height: 100%;
justify-content: center;
}
}
/**
* Re-styles the local Video to better fit vertical filmstrip layout.
*/
#filmstripLocalVideo {
align-self: initial;
margin-bottom: 5px;
display: flex;
flex-direction: column-reverse;
height: auto;
justify-content: flex-start;
width: 100%;
#filmstripLocalVideoThumbnail {
width: calc(100% - 15px);
#filmstripLocalVideoThumbnail {
width: calc(100% - 15px);
.videocontainer {
height: 0px;
width: 100%;
.videocontainer {
height: 0px;
width: 100%;
}
}
}
}
#filmstripLocalScreenShare {
align-self: initial;
margin-bottom: 5px;
display: flex;
flex-direction: column-reverse;
height: auto;
justify-content: flex-start;
width: 100%;
#filmstripLocalScreenShare {
align-self: initial;
margin-bottom: 5px;
display: flex;
flex-direction: column-reverse;
height: auto;
justify-content: flex-start;
width: 100%;
#filmstripLocalScreenShareThumbnail {
width: calc(100% - 15px);
#filmstripLocalScreenShareThumbnail {
width: calc(100% - 15px);
.videocontainer {
height: 0px;
width: 100%;
.videocontainer {
height: 0px;
width: 100%;
}
}
}
}
/**
* Remove unnecssary padding that is normally used to prevent horizontal
* filmstrip from overlapping the left edge of the screen.
*/
#filmstripLocalVideo,
#filmstripLocalScreenShare,
.remote-videos {
padding: 0;
}
#remoteVideos {
@include minHWAutoFix();
flex-direction: column;
flex-grow: 1;
}
.resizable-filmstrip #remoteVideos .videocontainer {
border-left: 0;
margin: 0;
}
&.reduce-height {
height: calc(100% - calc(#{$newToolbarSizeWithPadding} + #{$scrollHeight}));
}
.filmstrip__videos.vertical-view-grid#remoteVideos {
align-items: 'center';
border: 0px;
padding-right: 7px;
&.has-scroll {
padding-right: 0px;
/**
* Remove unnecssary padding that is normally used to prevent horizontal
* filmstrip from overlapping the left edge of the screen.
*/
#filmstripLocalVideo,
#filmstripLocalScreenShare,
.remote-videos {
padding: 0;
}
.remote-videos > div {
left: 0px; // fixes an issue on FF - the div is aligned to the right by default for some reason
#remoteVideos {
@include minHWAutoFix();
flex-direction: column;
flex-grow: 1;
}
.videocontainer {
.resizable-filmstrip #remoteVideos .videocontainer {
border-left: 0;
margin: 0;
}
&.reduce-height {
height: calc(100% - calc(#{$newToolbarSizeWithPadding} + #{$scrollHeight}));
}
.filmstrip__videos.vertical-view-grid#remoteVideos {
align-items: 'center';
border: 0px;
margin: 2px;
}
}
padding-right: 7px;
.remote-videos {
display: flex;
overscroll-behavior: contain;
&.has-scroll {
padding-right: 0px;
}
&.height-transition {
transition: height .3s ease-in;
.remote-videos > div {
left: 0px; // fixes an issue on FF - the div is aligned to the right by default for some reason
}
.videocontainer {
border: 0px;
margin: 2px;
}
}
& > div {
position: absolute;
transition: opacity 1s;
}
.remote-videos {
display: flex;
overscroll-behavior: contain;
&.is-not-overflowing > div {
bottom: 0px;
&.height-transition {
transition: height .3s ease-in;
}
& > div {
position: absolute;
transition: opacity 1s;
}
&.is-not-overflowing > div {
bottom: 0px;
}
}
}
}

View File

@ -3,14 +3,17 @@
* clashing with the filmstrip.
*/
.vertical-filmstrip #etherpad,
.vertical-filmstrip #sharedvideo {
.stage-filmstrip #etherpad,
.vertical-filmstrip #sharedvideo,
.stage-filmstrip #sharedvideo {
text-align: left;
}
/**
* Overrides for small videos in vertical filmstrip mode.
*/
.vertical-filmstrip .filmstrip__videos .videocontainer {
.vertical-filmstrip .filmstrip__videos .videocontainer,
.stage-filmstrip .filmstrip__videos .videocontainer {
.self-view-mobile-portrait video {
object-fit: contain;
}
@ -27,7 +30,8 @@
* The class opening is for when the filmstrip is transitioning from hidden
* to visible.
*/
.vertical-filmstrip .large-video-labels {
.vertical-filmstrip .large-video-labels,
.stage-filmstrip .large-video-labels {
&.with-filmstrip {
right: 150px;
}
@ -47,6 +51,7 @@
* Overrides for self view when in portrait mode on mobile.
* This is done in order to keep the aspect ratio.
*/
.vertical-filmstrip .self-view-mobile-portrait #localVideo_container {
.vertical-filmstrip .self-view-mobile-portrait #localVideo_container,
.stage-filmstrip .self-view-mobile-portrait #localVideo_container {
object-fit: contain;
}

View File

@ -6,7 +6,7 @@ import ReactDOM from 'react-dom';
import { browser } from '../../../react/features/base/lib-jitsi-meet';
import { isTestModeEnabled } from '../../../react/features/base/testing';
import { FILMSTRIP_BREAKPOINT, shouldDisplayStageFilmstrip } from '../../../react/features/filmstrip';
import { FILMSTRIP_BREAKPOINT } from '../../../react/features/filmstrip';
import { ORIENTATION, LargeVideoBackground, updateLastLargeVideoMediaEvent } from '../../../react/features/large-video';
import { LAYOUTS, getCurrentLayout } from '../../../react/features/video-layout';
/* eslint-enable no-unused-vars */
@ -414,7 +414,7 @@ export class VideoContainer extends LargeContainer {
const verticalFilmstripWidth = state['features/filmstrip'].width?.current;
if (currentLayout === LAYOUTS.TILE_VIEW || shouldDisplayStageFilmstrip(state)) {
if (currentLayout === LAYOUTS.TILE_VIEW || currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW) {
// We don't need to resize the large video since it won't be displayed and we'll resize when returning back
// to stage view.
return;

View File

@ -4,7 +4,7 @@ import { getGravatarURL } from '@jitsi/js-utils/avatar';
import type { Store } from 'redux';
import { i18next } from '../../base/i18n';
import { isStageFilmstripEnabled } from '../../filmstrip/functions';
import { isStageFilmstripAvailable } from '../../filmstrip/functions';
import { GRAVATAR_BASE_URL, isCORSAvatarURL } from '../avatar';
import { getSourceNameSignalingFeatureFlag } from '../config';
import { JitsiParticipantConnectionStatus } from '../lib-jitsi-meet';
@ -372,7 +372,7 @@ export function getRemoteParticipantsSorted(stateful: Object | Function) {
export function getPinnedParticipant(stateful: Object | Function) {
const state = toState(stateful);
const { pinnedParticipant } = state['features/base/participants'];
const stageFilmstrip = isStageFilmstripEnabled(state);
const stageFilmstrip = isStageFilmstripAvailable(state);
if (stageFilmstrip) {
const { activeParticipants } = state['features/filmstrip'];

View File

@ -1,6 +1,5 @@
// @flow
import clsx from 'clsx';
import _ from 'lodash';
import React from 'react';
@ -12,7 +11,7 @@ import { translate } from '../../../base/i18n';
import { connect as reactReduxConnect } from '../../../base/redux';
import { setColorAlpha } from '../../../base/util';
import { Chat } from '../../../chat';
import { MainFilmstrip, StageFilmstrip, shouldDisplayStageFilmstrip } from '../../../filmstrip';
import { MainFilmstrip, StageFilmstrip } from '../../../filmstrip';
import { CalleeInfoContainer } from '../../../invite';
import { LargeVideo } from '../../../large-video';
import { LobbyScreen } from '../../../lobby';
@ -59,7 +58,8 @@ const FULL_SCREEN_EVENTS = [
export const LAYOUT_CLASSNAMES = {
[LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW]: 'horizontal-filmstrip',
[LAYOUTS.TILE_VIEW]: 'tile-view',
[LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'vertical-filmstrip'
[LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'vertical-filmstrip',
[LAYOUTS.STAGE_FILMSTRIP_VIEW]: 'stage-filmstrip'
};
/**
@ -103,11 +103,6 @@ type Props = AbstractProps & {
*/
_showPrejoin: boolean,
/**
* Whether or not the stage filmstrip should be displayed.
*/
_showStageFilmstrip: boolean,
dispatch: Function,
t: Function
}
@ -220,8 +215,7 @@ class Conference extends AbstractConference<Props, *> {
_notificationsVisible,
_overflowDrawer,
_showLobby,
_showPrejoin,
_showStageFilmstrip
_showPrejoin
} = this.props;
return (
@ -233,7 +227,7 @@ class Conference extends AbstractConference<Props, *> {
ref = { this._setBackground }>
<Chat />
<div
className = { clsx(_layoutClassName, _showStageFilmstrip && 'stage-filmstrip') }
className = { _layoutClassName }
id = 'videoconference_page'
onMouseMove = { isMobileBrowser() ? undefined : this._onShowToolbar }>
<ConferenceInfo />
@ -242,7 +236,7 @@ class Conference extends AbstractConference<Props, *> {
id = 'videospace'
onTouchStart = { this._onVidespaceTouchStart }>
<LargeVideo />
{_showStageFilmstrip && <StageFilmstrip />}
<StageFilmstrip />
<MainFilmstrip />
</div>
@ -402,8 +396,7 @@ function _mapStateToProps(state) {
_overflowDrawer: overflowDrawer,
_roomName: getConferenceNameForTitle(state),
_showLobby: getIsLobbyVisible(state),
_showPrejoin: isPrejoinPageVisible(state),
_showStageFilmstrip: shouldDisplayStageFilmstrip(state)
_showPrejoin: isPrejoinPageVisible(state)
};
}

View File

@ -36,11 +36,6 @@ type Props = {
*/
allowEditing: boolean,
/**
* The current layout of the filmstrip.
*/
currentLayout: string,
/**
* Invoked to update the participant's display name.
*/
@ -70,7 +65,12 @@ type Props = {
/**
* Invoked to obtain translated strings.
*/
t: Function
t: Function,
/**
* The type of thumbnail.
*/
thumbnailType: string
};
/**
@ -183,11 +183,11 @@ class DisplayName extends Component<Props, State> {
const {
_nameToDisplay,
allowEditing,
currentLayout,
displayNameSuffix,
classes,
elementID,
t
t,
thumbnailType
} = this.props;
if (allowEditing && this.state.isEditing) {
@ -211,7 +211,7 @@ class DisplayName extends Component<Props, State> {
return (
<Tooltip
content = { appendSuffix(_nameToDisplay, displayNameSuffix) }
position = { getIndicatorsTooltipPosition(currentLayout) }>
position = { getIndicatorsTooltipPosition(thumbnailType) }>
<span
className = { `displayname ${classes.displayName}` }
id = { elementID }

View File

@ -171,7 +171,6 @@ export const SET_STAGE_PARTICIPANTS = 'SET_STAGE_PARTICIPANTS';
*/
export const SET_MAX_STAGE_PARTICIPANTS = 'SET_MAX_STAGE_PARTICIPANTS';
/**
* The type of Redux action which toggles the pin state of stage participants.
* {
@ -180,3 +179,11 @@ export const SET_MAX_STAGE_PARTICIPANTS = 'SET_MAX_STAGE_PARTICIPANTS';
* }
*/
export const TOGGLE_PIN_STAGE_PARTICIPANT = 'TOGGLE_PIN_STAGE_PARTICIPANT';
/**
* The type of Redux action which clears the list of stage participants.
* {
* type: CLEAR_STAGE_PARTICIPANTS
* }
*/
export const CLEAR_STAGE_PARTICIPANTS = 'CLEAR_STAGE_PARTICIPANTS';

View File

@ -24,7 +24,8 @@ import {
SET_VERTICAL_VIEW_DIMENSIONS,
SET_VOLUME,
SET_MAX_STAGE_PARTICIPANTS,
TOGGLE_PIN_STAGE_PARTICIPANT
TOGGLE_PIN_STAGE_PARTICIPANT,
CLEAR_STAGE_PARTICIPANTS
} from './actionTypes';
import {
HORIZONTAL_FILMSTRIP_MARGIN,
@ -266,8 +267,6 @@ export function setStageFilmstripViewDimensions() {
const state = getState();
const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
const {
disableResponsiveTiles,
disableTileEnlargement,
tileView = {}
} = state['features/base/config'];
const { visible } = state['features/filmstrip'];
@ -282,17 +281,15 @@ export function setStageFilmstripViewDimensions() {
width,
columns,
rows
} = disableResponsiveTiles
? calculateNonResponsiveTileViewDimensions(state, true)
: calculateResponsiveTileViewDimensions({
clientWidth: availableWidth,
clientHeight,
disableTileEnlargement,
maxColumns,
noHorizontalContainerMargin: verticalWidth > 0,
numberOfParticipants,
numberOfVisibleTiles
});
} = calculateResponsiveTileViewDimensions({
clientWidth: availableWidth,
clientHeight,
disableTileEnlargement: false,
maxColumns,
noHorizontalContainerMargin: verticalWidth > 0,
numberOfParticipants,
numberOfVisibleTiles
});
const thumbnailsTotalHeight = rows * (TILE_VERTICAL_MARGIN + height);
const hasScroll = clientHeight < thumbnailsTotalHeight;
const filmstripWidth
@ -469,3 +466,14 @@ export function togglePinStageParticipant(participantId) {
participantId
};
}
/**
* Clears the stage participants list.
*
* @returns {Object}
*/
export function clearStageParticipants() {
return {
type: CLEAR_STAGE_PARTICIPANTS
};
}

View File

@ -20,7 +20,7 @@ import { connect } from '../../../base/redux';
import { shouldHideSelfView } from '../../../base/settings/functions.any';
import { showToolbox } from '../../../toolbox/actions.web';
import { isButtonEnabled, isToolboxVisible } from '../../../toolbox/functions.web';
import { LAYOUTS } from '../../../video-layout';
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
import {
setFilmstripVisible,
setVisibleRemoteParticipants,
@ -110,7 +110,7 @@ type Props = {
/**
* The local screen share participant. This prop is behind the sourceNameSignaling feature flag.
*/
_localScreenShare: Object,
_localScreenShare: Object,
/**
* The maximum width of the vertical filmstrip.
@ -318,32 +318,29 @@ class Filmstrip extends PureComponent <Props, State> {
const { isMouseDown } = this.state;
const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
switch (_currentLayout) {
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW: {
if (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && _stageFilmstrip) {
if (_visible) {
filmstripStyle.maxWidth = `calc(100% - ${_verticalViewMaxWidth}px)`;
}
} else if (_currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
|| (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && !_stageFilmstrip)) {
filmstripStyle.maxWidth = _verticalViewMaxWidth;
if (!_visible) {
filmstripStyle.right = `-${filmstripStyle.maxWidth}px`;
}
break;
}
case LAYOUTS.TILE_VIEW: {
if (_stageFilmstrip && _visible) {
filmstripStyle.maxWidth = `calc(100% - ${_verticalViewMaxWidth}px)`;
}
break;
}
}
let toolbar = null;
if (!this.props._iAmRecorder && this.props._isFilmstripButtonEnabled && _currentLayout !== LAYOUTS.TILE_VIEW) {
if (!this.props._iAmRecorder && this.props._isFilmstripButtonEnabled
&& _currentLayout !== LAYOUTS.TILE_VIEW && !_stageFilmstrip) {
toolbar = this._renderToggleButton();
}
const filmstrip = (<>
<div
className = { clsx(this.props._videosClassName,
!tileViewActive && !_resizableFilmstrip && 'filmstrip-hover',
!tileViewActive && !_stageFilmstrip && !_resizableFilmstrip && 'filmstrip-hover',
_verticalViewGrid && 'vertical-view-grid') }
id = 'remoteVideos'>
{!_disableSelfView && !_verticalViewGrid && (
@ -351,7 +348,7 @@ class Filmstrip extends PureComponent <Props, State> {
className = 'filmstrip__videos'
id = 'filmstripLocalVideo'>
{
!tileViewActive && <div id = 'filmstripLocalVideoThumbnail'>
!tileViewActive && !_stageFilmstrip && <div id = 'filmstripLocalVideoThumbnail'>
<Thumbnail
key = 'local' />
</div>
@ -364,7 +361,7 @@ class Filmstrip extends PureComponent <Props, State> {
id = 'filmstripLocalScreenShare'>
<div id = 'filmstripLocalScreenShareThumbnail'>
{
!tileViewActive && <Thumbnail
!tileViewActive && !_stageFilmstrip && <Thumbnail
key = 'localScreenShare'
participantID = { _localScreenShare.id } />
@ -604,6 +601,7 @@ class Filmstrip extends PureComponent <Props, State> {
_filmstripHeight,
_filmstripWidth,
_hasScroll,
_isVerticalFilmstrip,
_remoteParticipantsLength,
_resizableFilmstrip,
_rows,
@ -619,7 +617,7 @@ class Filmstrip extends PureComponent <Props, State> {
return null;
}
if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid) {
if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid || _stageFilmstrip) {
return (
<FixedSizeGrid
className = 'filmstrip__videos remote-videos'
@ -669,7 +667,7 @@ class Filmstrip extends PureComponent <Props, State> {
props.className += ' is-not-overflowing';
}
} else if (_currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW) {
} else if (_isVerticalFilmstrip) {
const itemSize = _thumbnailHeight + TILE_VERTICAL_MARGIN;
const isNotOverflowing = !_hasScroll;
@ -820,15 +818,20 @@ function _mapStateToProps(state, ownProps) {
shouldReduceHeight ? 'reduce-height' : ''
} ${shiftRight ? 'shift-right' : ''} ${collapseTileView ? 'collapse' : ''} ${visible ? '' : 'hidden'}`.trim();
const _currentLayout = getCurrentLayout(state);
const _isVerticalFilmstrip = _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
|| (!ownProps._stageFilmstrip && _currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW);
return {
_className: className,
_chatOpen: state['features/chat'].isOpen,
_currentLayout,
_disableSelfView: disableSelfView,
_hasScroll,
_iAmRecorder: Boolean(iAmRecorder),
_isFilmstripButtonEnabled: isButtonEnabled('filmstrip', state),
_isToolboxVisible: isToolboxVisible(state),
_isVerticalFilmstrip: ownProps._currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW,
_isVerticalFilmstrip,
_localScreenShare: getSourceNameSignalingFeatureFlag(state) && localScreenShare,
_maxFilmstripWidth: clientWidth - MIN_STAGE_VIEW_WIDTH,
_thumbnailsReordered: enableThumbnailReordering,

View File

@ -17,11 +17,6 @@ import Filmstrip from './Filmstrip';
type Props = {
/**
* The current layout of the filmstrip.
*/
_currentLayout: string,
/**
* The number of columns in tile view.
*/
@ -143,7 +138,8 @@ function _mapStateToProps(state) {
&& clientWidth <= ASPECT_RATIO_BREAKPOINT;
const shouldReduceHeight = reduceHeight && (
isMobileBrowser() || _currentLayout !== LAYOUTS.VERTICAL_FILMSTRIP_VIEW);
isMobileBrowser() || (_currentLayout !== LAYOUTS.VERTICAL_FILMSTRIP_VIEW
&& _currentLayout !== LAYOUTS.STAGE_FILMSTRIP_VIEW));
let _thumbnailSize, remoteFilmstripHeight, remoteFilmstripWidth;
@ -154,7 +150,8 @@ function _mapStateToProps(state) {
remoteFilmstripHeight = filmstripHeight - (collapseTileView && filmstripPadding > 0 ? filmstripPadding : 0);
remoteFilmstripWidth = filmstripWidth;
break;
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW: {
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
case LAYOUTS.STAGE_FILMSTRIP_VIEW: {
const {
remote,
remoteVideosContainer,
@ -189,7 +186,6 @@ function _mapStateToProps(state) {
return {
_columns: gridDimensions.columns,
_currentLayout,
_filmstripHeight: remoteFilmstripHeight,
_filmstripWidth: remoteFilmstripWidth,
_hasScroll,

View File

@ -92,11 +92,10 @@ type Props = {
_visible: boolean
};
const StageFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW && (
const StageFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && (
<span className = { LAYOUT_CLASSNAMES[LAYOUTS.TILE_VIEW] }>
<Filmstrip
{ ...props }
_currentLayout = { LAYOUTS.TILE_VIEW }
_stageFilmstrip = { true } />
</span>
);

View File

@ -6,7 +6,6 @@ import { MEDIA_TYPE } from '../../../base/media';
import { getParticipantByIdOrUndefined, PARTICIPANT_ROLE } from '../../../base/participants';
import { connect } from '../../../base/redux';
import { getTrackByMediaTypeAndParticipant, isLocalTrackMuted, isRemoteTrackMuted } from '../../../base/tracks';
import { getCurrentLayout } from '../../../video-layout';
import { getIndicatorsTooltipPosition } from '../../functions.web';
import AudioMutedIndicator from './AudioMutedIndicator';
@ -20,11 +19,6 @@ declare var interfaceConfig: Object;
*/
type Props = {
/**
* The current layout of the filmstrip.
*/
_currentLayout: string,
/**
* Indicates if the audio muted indicator should be visible or not.
*/
@ -43,7 +37,12 @@ type Props = {
/**
* The ID of the participant for which the status bar is rendered.
*/
participantID: String
participantID: String,
/**
* The type of thumbnail.
*/
thumbnailType: string
};
/**
@ -60,12 +59,12 @@ class StatusIndicators extends Component<Props> {
*/
render() {
const {
_currentLayout,
_showAudioMutedIndicator,
_showModeratorIndicator,
_showScreenShareIndicator
_showScreenShareIndicator,
thumbnailType
} = this.props;
const tooltipPosition = getIndicatorsTooltipPosition(_currentLayout);
const tooltipPosition = getIndicatorsTooltipPosition(thumbnailType);
return (
<>
@ -111,7 +110,6 @@ function _mapStateToProps(state, ownProps) {
const { disableModeratorIndicator } = state['features/base/config'];
return {
_currentLayout: getCurrentLayout(state),
_showAudioMutedIndicator: isAudioMuted && audio,
_showModeratorIndicator:
!disableModeratorIndicator && participant && participant.role === PARTICIPANT_ROLE.MODERATOR && moderator,

View File

@ -36,6 +36,7 @@ import {
DISPLAY_MODE_TO_CLASS_NAME,
DISPLAY_VIDEO,
SHOW_TOOLBAR_CONTEXT_MENU_AFTER,
THUMBNAIL_TYPE,
VIDEO_TEST_EVENTS
} from '../../constants';
import {
@ -44,9 +45,9 @@ import {
getDisplayModeInput,
isVideoPlayable,
showGridInVerticalView,
isStageFilmstripEnabled,
shouldDisplayStageFilmstrip
isStageFilmstripAvailable
} from '../../functions';
import { getThumbnailTypeFromLayout } from '../../functions.web';
import FakeScreenShareParticipant from './FakeScreenShareParticipant';
import ThumbnailAudioIndicator from './ThumbnailAudioIndicator';
@ -91,11 +92,6 @@ export type Props = {|
*/
_audioTrack: ?Object,
/**
* The current layout of the filmstrip.
*/
_currentLayout: string,
/**
* Indicates whether the local video flip feature is disabled or not.
*/
@ -189,9 +185,9 @@ export type Props = {|
_raisedHand: boolean,
/**
* Whether or not the stage filmstrip is disabled.
* Whether or not the current layout is stage filmstrip layout.
*/
_stageFilmstripDisabled: boolean,
_stageFilmstripLayout: boolean,
/**
* Whether or not the participants are displayed on stage.
@ -200,6 +196,11 @@ export type Props = {|
*/
_stageParticipantsVisible: boolean,
/**
* The type of thumbnail to display.
*/
_thumbnailType: string,
/**
* The video object position for the participant.
*/
@ -447,15 +448,15 @@ class Thumbnail extends Component<Props, State> {
*/
_maybeSendScreenSharingIssueEvents(input) {
const {
_currentLayout,
_isAudioOnly,
_isScreenSharing
_isScreenSharing,
_thumbnailType
} = this.props;
const { displayMode } = this.state;
const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
const isTileType = _thumbnailType === THUMBNAIL_TYPE.TILE;
if (!(DISPLAY_VIDEO === displayMode)
&& tileViewActive
&& isTileType
&& _isScreenSharing
&& !_isAudioOnly) {
sendAnalytics(createScreenSharingIssueEvent({
@ -530,9 +531,9 @@ class Thumbnail extends Component<Props, State> {
* @returns {void}
*/
_hidePopover() {
const { _currentLayout } = this.props;
const { _thumbnailType } = this.props;
if (_currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW) {
if (_thumbnailType === THUMBNAIL_TYPE.VERTICAL) {
this.setState({
isHovered: false
});
@ -550,13 +551,13 @@ class Thumbnail extends Component<Props, State> {
_getStyles(): Object {
const { canPlayEventReceived } = this.state;
const {
_currentLayout,
_disableTileEnlargement,
_height,
_isFakeScreenShareParticipant,
_isHidden,
_isScreenSharing,
_participant,
_thumbnailType,
_videoObjectPosition,
_videoTrack,
_width,
@ -564,7 +565,7 @@ class Thumbnail extends Component<Props, State> {
style
} = this.props;
const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
const isTileType = _thumbnailType === THUMBNAIL_TYPE.TILE;
const jitsiVideoTrack = _videoTrack?.jitsiTrack;
const track = jitsiVideoTrack?.track;
const isPortraitVideo = ((track && track.getSettings()?.aspectRatio) || 1) < 1;
@ -587,7 +588,7 @@ class Thumbnail extends Component<Props, State> {
}
let videoStyles = null;
const doNotStretchVideo = (isPortraitVideo && tileViewActive)
const doNotStretchVideo = (isPortraitVideo && isTileType)
|| _disableTileEnlargement
|| _isScreenSharing;
@ -636,13 +637,13 @@ class Thumbnail extends Component<Props, State> {
* @returns {void}
*/
_onClick() {
const { _participant, dispatch, _stageFilmstripDisabled } = this.props;
const { _participant, dispatch, _stageFilmstripLayout } = this.props;
const { id, pinned } = _participant;
if (_stageFilmstripDisabled) {
dispatch(pinParticipant(pinned ? null : id));
} else {
if (_stageFilmstripLayout) {
dispatch(togglePinStageParticipant(id));
} else {
dispatch(pinParticipant(pinned ? null : id));
}
}
@ -790,8 +791,8 @@ class Thumbnail extends Component<Props, State> {
const {
_isDominantSpeakerDisabled,
_participant,
_currentLayout,
_raisedHand,
_thumbnailType,
classes
} = this.props;
@ -804,7 +805,7 @@ class Thumbnail extends Component<Props, State> {
if (!_isDominantSpeakerDisabled && _participant?.dominantSpeaker) {
className += ` ${classes.activeSpeaker} dominant-speaker`;
}
if (_currentLayout !== LAYOUTS.TILE_VIEW && _participant?.pinned) {
if (_thumbnailType !== THUMBNAIL_TYPE.TILE && _participant?.pinned) {
className += ' videoContainerFocused';
}
@ -902,16 +903,16 @@ class Thumbnail extends Component<Props, State> {
_renderParticipant(local = false) {
const {
_audioTrack,
_currentLayout,
_disableLocalVideoFlip,
_gifSrc,
_isMobile,
_isMobilePortrait,
_isScreenSharing,
_isTestModeEnabled,
_localFlipX,
_participant,
_thumbnailType,
_videoTrack,
_gifSrc,
classes,
stageFilmstrip
} = this.props;
@ -975,28 +976,28 @@ class Thumbnail extends Component<Props, State> {
<div
className = { clsx(classes.indicatorsContainer,
classes.indicatorsTopContainer,
_currentLayout === LAYOUTS.TILE_VIEW && 'tile-view-mode'
_thumbnailType === THUMBNAIL_TYPE.TILE && 'tile-view-mode'
) }>
<ThumbnailTopIndicators
currentLayout = { _currentLayout }
hidePopover = { this._hidePopover }
indicatorsClassName = { classes.indicatorsBackground }
isHovered = { isHovered }
local = { local }
participantId = { id }
popoverVisible = { popoverVisible }
showPopover = { this._showPopover } />
showPopover = { this._showPopover }
thumbnailType = { _thumbnailType } />
</div>
<div
className = { clsx(classes.indicatorsContainer,
classes.indicatorsBottomContainer,
_currentLayout === LAYOUTS.TILE_VIEW && 'tile-view-mode'
_thumbnailType === THUMBNAIL_TYPE.TILE && 'tile-view-mode'
) }>
<ThumbnailBottomIndicators
className = { classes.indicatorsBackground }
currentLayout = { _currentLayout }
local = { local }
participantId = { id } />
participantId = { id }
thumbnailType = { _thumbnailType } />
</div>
{!_gifSrc && this._renderAvatar(styles.avatar) }
{ !local && (
@ -1103,7 +1104,7 @@ function _mapStateToProps(state, ownProps): Object {
}
const _audioTrack = isLocal
? getLocalAudioTrack(tracks) : getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, participantID);
const _currentLayout = stageFilmstrip ? LAYOUTS.TILE_VIEW : getCurrentLayout(state);
const _currentLayout = getCurrentLayout(state);
let size = {};
let _isMobilePortrait = false;
const {
@ -1116,10 +1117,12 @@ function _mapStateToProps(state, ownProps): Object {
const { localFlipX } = state['features/base/settings'];
const _isMobile = isMobileBrowser();
const activeParticipants = getActiveParticipantsIds(state);
const tileType = getThumbnailTypeFromLayout(_currentLayout, stageFilmstrip);
switch (_currentLayout) {
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW: {
switch (tileType) {
case THUMBNAIL_TYPE.VERTICAL:
case THUMBNAIL_TYPE.HORIZONTAL: {
const {
horizontalViewDimensions = {
local: {},
@ -1133,7 +1136,7 @@ function _mapStateToProps(state, ownProps): Object {
} = state['features/filmstrip'];
const _verticalViewGrid = showGridInVerticalView(state);
const { local, remote }
= _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
= tileType === THUMBNAIL_TYPE.VERTICAL
? verticalViewDimensions : horizontalViewDimensions;
const { width, height } = (isLocal ? local : remote) ?? {};
@ -1155,7 +1158,7 @@ function _mapStateToProps(state, ownProps): Object {
break;
}
case LAYOUTS.TILE_VIEW: {
case THUMBNAIL_TYPE.TILE: {
const { thumbnailSize } = state['features/filmstrip'].tileViewDimensions;
const {
stageFilmstripDimensions = {
@ -1186,7 +1189,6 @@ function _mapStateToProps(state, ownProps): Object {
return {
_audioTrack,
_currentLayout,
_defaultLocalDisplayName: defaultLocalDisplayName,
_disableLocalVideoFlip: Boolean(disableLocalVideoFlip),
_disableTileEnlargement: Boolean(disableTileEnlargement),
@ -1204,8 +1206,9 @@ function _mapStateToProps(state, ownProps): Object {
_localFlipX: Boolean(localFlipX),
_participant: participant,
_raisedHand: hasRaisedHand(participant),
_stageFilmstripDisabled: !isStageFilmstripEnabled(state),
_stageParticipantsVisible: shouldDisplayStageFilmstrip(state, 1),
_stageFilmstripLayout: isStageFilmstripAvailable(state),
_stageParticipantsVisible: _currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW,
_thumbnailType: tileType,
_videoObjectPosition: getVideoObjectPosition(state, participant?.id),
_videoTrack,
...size,

View File

@ -6,7 +6,7 @@ import { useSelector } from 'react-redux';
import { isDisplayNameVisible, isNameReadOnly } from '../../../base/config/functions.any';
import DisplayName from '../../../display-name/components/web/DisplayName';
import { LAYOUTS } from '../../../video-layout';
import { THUMBNAIL_TYPE } from '../../constants';
import StatusIndicators from './StatusIndicators';
@ -14,11 +14,6 @@ declare var interfaceConfig: Object;
type Props = {
/**
* The current layout of the filmstrip.
*/
currentLayout: string,
/**
* Class name for indicators container.
*/
@ -37,7 +32,12 @@ type Props = {
/**
* Whether or not to show the status indicators.
*/
showStatusIndicators: string
showStatusIndicators: string,
/**
* The type of thumbnail.
*/
thumbnailType: string
}
const useStyles = makeStyles(() => {
@ -61,10 +61,10 @@ const useStyles = makeStyles(() => {
const ThumbnailBottomIndicators = ({
className,
currentLayout,
local,
participantId,
showStatusIndicators = true
showStatusIndicators = true,
thumbnailType
}: Props) => {
const styles = useStyles();
const _allowEditing = !useSelector(isNameReadOnly);
@ -77,17 +77,18 @@ const ThumbnailBottomIndicators = ({
audio = { true }
moderator = { true }
participantID = { participantId }
screenshare = { currentLayout === LAYOUTS.TILE_VIEW } />
screenshare = { thumbnailType === THUMBNAIL_TYPE.TILE }
thumbnailType = { thumbnailType } />
}
{
_showDisplayName && (
<span className = { styles.nameContainer }>
<DisplayName
allowEditing = { local ? _allowEditing : false }
currentLayout = { currentLayout }
displayNameSuffix = { local ? _defaultLocalDisplayName : '' }
elementID = { local ? 'localDisplayName' : `participant_${participantId}_name` }
participantID = { participantId } />
participantID = { participantId }
thumbnailType = { thumbnailType } />
</span>
)
}

View File

@ -8,8 +8,7 @@ import { useSelector } from 'react-redux';
import { getSourceNameSignalingFeatureFlag } from '../../../base/config';
import { isMobileBrowser } from '../../../base/environment/utils';
import ConnectionIndicator from '../../../connection-indicator/components/web/ConnectionIndicator';
import { LAYOUTS } from '../../../video-layout';
import { STATS_POPOVER_POSITION } from '../../constants';
import { STATS_POPOVER_POSITION, THUMBNAIL_TYPE } from '../../constants';
import { getIndicatorsTooltipPosition } from '../../functions.web';
import PinnedIndicator from './PinnedIndicator';
@ -21,11 +20,6 @@ declare var interfaceConfig: Object;
type Props = {
/**
* The current layout of the filmstrip.
*/
currentLayout: string,
/**
* Hide popover callback.
*/
@ -64,7 +58,12 @@ type Props = {
/**
* Show popover callback.
*/
showPopover: Function
showPopover: Function,
/**
* The type of thumbnail.
*/
thumbnailType: string
}
const useStyles = makeStyles(() => {
@ -80,7 +79,6 @@ const useStyles = makeStyles(() => {
});
const ThumbnailTopIndicators = ({
currentLayout,
hidePopover,
indicatorsClassName,
isFakeScreenShareParticipant,
@ -88,7 +86,8 @@ const ThumbnailTopIndicators = ({
local,
participantId,
popoverVisible,
showPopover
showPopover,
thumbnailType
}: Props) => {
const styles = useStyles();
@ -111,32 +110,34 @@ const ThumbnailTopIndicators = ({
enableStatsDisplay = { true }
iconSize = { _indicatorIconSize }
participantId = { participantId }
statsPopoverPosition = { STATS_POPOVER_POSITION[currentLayout] } />
statsPopoverPosition = { STATS_POPOVER_POSITION[thumbnailType] } />
}
</div>
);
}
const tooltipPosition = getIndicatorsTooltipPosition(thumbnailType);
return (
<>
<div className = { styles.container }>
<PinnedIndicator
iconSize = { _indicatorIconSize }
participantId = { participantId }
tooltipPosition = { getIndicatorsTooltipPosition(currentLayout) } />
tooltipPosition = { tooltipPosition } />
{!_connectionIndicatorDisabled
&& <ConnectionIndicator
alwaysVisible = { showConnectionIndicator }
enableStatsDisplay = { true }
iconSize = { _indicatorIconSize }
participantId = { participantId }
statsPopoverPosition = { STATS_POPOVER_POSITION[currentLayout] } />
statsPopoverPosition = { STATS_POPOVER_POSITION[thumbnailType] } />
}
<RaisedHandIndicator
iconSize = { _indicatorIconSize }
participantId = { participantId }
tooltipPosition = { getIndicatorsTooltipPosition(currentLayout) } />
{currentLayout !== LAYOUTS.TILE_VIEW && (
tooltipPosition = { tooltipPosition } />
{thumbnailType !== THUMBNAIL_TYPE.TILE && (
<div className = { clsx(indicatorsClassName, 'top-indicators') }>
<StatusIndicators
participantID = { participantId }
@ -146,12 +147,12 @@ const ThumbnailTopIndicators = ({
</div>
<div className = { styles.container }>
<VideoMenuTriggerButton
currentLayout = { currentLayout }
hidePopover = { hidePopover }
local = { local }
participantId = { participantId }
popoverVisible = { popoverVisible }
showPopover = { showPopover }
thumbnailType = { thumbnailType }
visible = { isHovered } />
</div>
</>);

View File

@ -6,11 +6,6 @@ import { LocalVideoMenuTriggerButton, RemoteVideoMenuTriggerButton } from '../..
type Props = {
/**
* The current layout of the filmstrip.
*/
currentLayout: string,
/**
* Hide popover callback.
*/
@ -36,6 +31,11 @@ type Props = {
*/
showPopover: Function,
/**
* The type of thumbnail.
*/
thumbnailType: string,
/**
* Whether or not the component is visible.
*/
@ -44,33 +44,33 @@ type Props = {
// eslint-disable-next-line no-confusing-arrow
const VideoMenuTriggerButton = ({
currentLayout,
hidePopover,
local,
participantId,
popoverVisible,
showPopover,
thumbnailType,
visible
}: Props) => local
? (
<span id = 'localvideomenu'>
<LocalVideoMenuTriggerButton
buttonVisible = { visible }
currentLayout = { currentLayout }
hidePopover = { hidePopover }
popoverVisible = { popoverVisible }
showPopover = { showPopover } />
showPopover = { showPopover }
thumbnailType = { thumbnailType } />
</span>
)
: (
<span id = 'remotevideomenu'>
<RemoteVideoMenuTriggerButton
buttonVisible = { visible }
currentLayout = { currentLayout }
hidePopover = { hidePopover }
participantID = { participantId }
popoverVisible = { popoverVisible }
showPopover = { showPopover } />
showPopover = { showPopover }
thumbnailType = { thumbnailType } />
</span>
);

View File

@ -1,7 +1,6 @@
// @flow
import { BoxModel } from '../base/styles';
import { LAYOUTS } from '../video-layout/constants';
/**
* The size (height and width) of the small (not tile view) thumbnails.
@ -228,22 +227,31 @@ export const SHOW_TOOLBAR_CONTEXT_MENU_AFTER = 600;
*/
export const TILE_MARGIN = 10;
/**
* The types of thumbnails for filmstrip.
*/
export const THUMBNAIL_TYPE = {
TILE: 'TILE',
VERTICAL: 'VERTICAL',
HORIZONTAL: 'HORIZONTAL'
};
/**
* The popover position for the connection stats table.
*/
export const STATS_POPOVER_POSITION = {
[LAYOUTS.TILE_VIEW]: 'right-start',
[LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'left-start',
[LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW]: 'top-end'
[THUMBNAIL_TYPE.TILE]: 'right-start',
[THUMBNAIL_TYPE.VERTICAL]: 'left-start',
[THUMBNAIL_TYPE.HORIZONTAL]: 'top-end'
};
/**
* The tooltip position for the indicators on the thumbnail.
*/
export const INDICATORS_TOOLTIP_POSITION = {
[LAYOUTS.TILE_VIEW]: 'right',
[LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'left',
[LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW]: 'top'
[THUMBNAIL_TYPE.TILE]: 'right',
[THUMBNAIL_TYPE.VERTICAL]: 'left',
[THUMBNAIL_TYPE.HORIZONTAL]: 'top'
};
/**

View File

@ -103,11 +103,11 @@ export function isReorderingEnabled(state) {
}
/**
* Whether the stage filmstrip is disabled or not.
* Whether the stage filmstrip is available or not.
*
* @param {Object} state - Redux state.
* @returns {boolean}
*/
export function isStageFilmstripEnabled() {
export function isStageFilmstripAvailable() {
return false;
}

View File

@ -35,6 +35,7 @@ import {
INDICATORS_TOOLTIP_POSITION,
SCROLL_SIZE,
SQUARE_TILE_ASPECT_RATIO,
THUMBNAIL_TYPE,
TILE_ASPECT_RATIO,
TILE_HORIZONTAL_MARGIN,
TILE_MIN_HEIGHT_LARGE,
@ -243,18 +244,16 @@ export function getNumberOfPartipantsForTileView(state) {
* disabled.
*
* @param {Object} state - The redux store state.
* @param {boolean} stageFilmstrip - Whether the dimensions should be calculated for the stage filmstrip.
* @returns {Object} - The dimensions.
*/
export function calculateNonResponsiveTileViewDimensions(state, stageFilmstrip = false) {
export function calculateNonResponsiveTileViewDimensions(state) {
const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
const { disableTileEnlargement } = state['features/base/config'];
const { columns: c, minVisibleRows, rows: r } = getNotResponsiveTileViewGridDimensions(state, stageFilmstrip);
const filmstripWidth = getVerticalViewMaxWidth(state);
const { columns: c, minVisibleRows, rows: r } = getNotResponsiveTileViewGridDimensions(state);
const size = calculateThumbnailSizeForTileView({
columns: c,
minVisibleRows,
clientWidth: clientWidth - (stageFilmstrip ? filmstripWidth : 0),
clientWidth,
clientHeight,
disableTileEnlargement,
disableResponsiveTiles: true
@ -515,6 +514,7 @@ export function computeDisplayModeFromInput(input: Object) {
canPlayEventReceived,
isRemoteParticipant,
stageParticipantsVisible,
stageFilmstrip,
tileViewActive
} = input;
const adjustedIsVideoPlayable = input.isVideoPlayable && (!isRemoteParticipant || canPlayEventReceived);
@ -524,7 +524,7 @@ export function computeDisplayModeFromInput(input: Object) {
}
if (!tileViewActive && ((isScreenSharing && isRemoteParticipant)
|| (stageParticipantsVisible && isActiveParticipant))) {
|| (stageParticipantsVisible && isActiveParticipant && !stageFilmstrip))) {
return DISPLAY_AVATAR;
} else if (isCurrentlyOnLargeVideo && !tileViewActive) {
// Display name is always and only displayed when user is on the stage
@ -556,7 +556,8 @@ export function getDisplayModeInput(props: Object, state: Object) {
_isVideoPlayable,
_participant,
_stageParticipantsVisible,
_videoTrack
_videoTrack,
stageFilmstrip
} = props;
const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
const { canPlayEventReceived } = state;
@ -574,6 +575,7 @@ export function getDisplayModeInput(props: Object, state: Object) {
isScreenSharing: _isScreenSharing,
isFakeScreenShareParticipant: _isFakeScreenShareParticipant,
stageParticipantsVisible: _stageParticipantsVisible,
stageFilmstrip,
videoStreamMuted: _videoTrack ? _videoTrack.muted : 'no stream'
};
}
@ -581,11 +583,11 @@ export function getDisplayModeInput(props: Object, state: Object) {
/**
* Gets the tooltip position for the thumbnail indicators.
*
* @param {string} currentLayout - The current layout of the app.
* @param {string} thumbnailType - The current thumbnail type.
* @returns {string}
*/
export function getIndicatorsTooltipPosition(currentLayout: string) {
return INDICATORS_TOOLTIP_POSITION[currentLayout] || 'top';
export function getIndicatorsTooltipPosition(thumbnailType: string) {
return INDICATORS_TOOLTIP_POSITION[thumbnailType] || 'top';
}
/**
@ -599,7 +601,7 @@ export function isFilmstripResizable(state: Object) {
const _currentLayout = getCurrentLayout(state);
return !filmstrip?.disableResizable && !isMobileBrowser()
&& _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW;
&& (_currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW || _currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW);
}
/**
@ -665,7 +667,8 @@ export function isFilmstripScrollVisible(state) {
case LAYOUTS.TILE_VIEW:
({ hasScroll = false } = state['features/filmstrip'].tileViewDimensions);
break;
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW: {
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
case LAYOUTS.STAGE_FILMSTRIP_VIEW: {
({ hasScroll = false } = state['features/filmstrip'].verticalViewDimensions);
break;
}
@ -703,21 +706,30 @@ export function getPinnedActiveParticipants(state) {
}
/**
* Get whether or not the stage filmstrip should be displayed.
* Get whether or not the stage filmstrip is available (enabled & can be used).
*
* @param {Object} state - Redux state.
* @param {number} minParticipantCount - The min number of participants for the stage filmstrip
* to be displayed.
* @returns {boolean}
*/
export function shouldDisplayStageFilmstrip(state, minParticipantCount = 2) {
export function isStageFilmstripAvailable(state, minParticipantCount = 0) {
const { activeParticipants } = state['features/filmstrip'];
const { remoteScreenShares } = state['features/video-layout'];
const currentLayout = getCurrentLayout(state);
const sharedVideo = isSharingStatus(state['features/shared-video']?.status);
return isStageFilmstripEnabled(state) && remoteScreenShares.length === 0 && !sharedVideo
&& activeParticipants.length >= minParticipantCount && currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW;
&& activeParticipants.length >= minParticipantCount;
}
/**
* Get whether or not the stage filmstrip should be displayed.
*
* @param {Object} state - Redux state.
* @returns {boolean}
*/
export function shouldDisplayStageFilmstrip(state) {
return isStageFilmstripAvailable(state, 2);
}
/**
@ -731,3 +743,27 @@ export function isStageFilmstripEnabled(state) {
return !(filmstrip?.disableStageFilmstrip ?? true) && interfaceConfig.VERTICAL_FILMSTRIP;
}
/**
* Gets the thumbnail type by filmstrip type.
*
* @param {string} currentLayout - Current app layout.
* @param {boolean} isStageFilmstrip - Whether the filmstrip is stage filmstrip or not.
* @returns {string}
*/
export function getThumbnailTypeFromLayout(currentLayout, isStageFilmstrip = false) {
switch (currentLayout) {
case LAYOUTS.TILE_VIEW:
return THUMBNAIL_TYPE.TILE;
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
return THUMBNAIL_TYPE.VERTICAL;
case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW:
return THUMBNAIL_TYPE.HORIZONTAL;
case LAYOUTS.STAGE_FILMSTRIP_VIEW:
if (isStageFilmstrip) {
return THUMBNAIL_TYPE.TILE;
}
return THUMBNAIL_TYPE.VERTICAL;
}
}

View File

@ -22,6 +22,7 @@ import {
import {
ADD_STAGE_PARTICIPANT,
CLEAR_STAGE_PARTICIPANTS,
REMOVE_STAGE_PARTICIPANT,
SET_MAX_STAGE_PARTICIPANTS,
SET_USER_FILMSTRIP_WIDTH,
@ -42,10 +43,12 @@ import {
import {
isFilmstripResizable,
updateRemoteParticipants,
updateRemoteParticipantsOnLeave
updateRemoteParticipantsOnLeave,
getActiveParticipantsIds,
getPinnedActiveParticipants,
isStageFilmstripAvailable
} from './functions';
import './subscriber';
import { getActiveParticipantsIds, getPinnedActiveParticipants, isStageFilmstripEnabled } from './functions.web';
/**
* Map of timers.
@ -202,15 +205,15 @@ MiddlewareRegistry.register(store => next => action => {
case DOMINANT_SPEAKER_CHANGED: {
const { id } = action.participant;
const state = store.getState();
const stageFilmstrip = isStageFilmstripEnabled(state);
const currentLayout = getCurrentLayout(state);
const stageFilmstrip = isStageFilmstripAvailable(state);
const local = getLocalParticipant(state);
const currentLayout = getCurrentLayout(state);
if (id === local.id) {
if (id === local.id || currentLayout === LAYOUTS.TILE_VIEW) {
break;
}
if (stageFilmstrip && currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW) {
if (stageFilmstrip) {
const isPinned = getPinnedActiveParticipants(state).some(p => p.participantId === id);
store.dispatch(addStageParticipant(id, Boolean(isPinned)));
@ -276,7 +279,17 @@ MiddlewareRegistry.register(store => next => action => {
} else {
dispatch(addStageParticipant(participantId, true));
}
break;
}
case CLEAR_STAGE_PARTICIPANTS: {
const activeParticipants = getActiveParticipantsIds(store.getState());
activeParticipants.forEach(pId => {
const tid = timers.get(pId);
clearTimeout(tid);
timers.delete(pId);
});
}
}

View File

@ -18,7 +18,8 @@ import {
SET_VERTICAL_VIEW_DIMENSIONS,
SET_VISIBLE_REMOTE_PARTICIPANTS,
SET_VOLUME,
SET_MAX_STAGE_PARTICIPANTS
SET_MAX_STAGE_PARTICIPANTS,
CLEAR_STAGE_PARTICIPANTS
} from './actionTypes';
const DEFAULT_STATE = {
@ -273,6 +274,12 @@ ReducerRegistry.register(
maxStageParticipants: action.maxParticipants
};
}
case CLEAR_STAGE_PARTICIPANTS: {
return {
...state,
activeParticipants: []
};
}
}
return state;

View File

@ -1,7 +1,7 @@
// @flow
import { isMobileBrowser } from '../base/environment/utils';
import { getParticipantCountWithFake } from '../base/participants';
import { getParticipantCountWithFake, pinParticipant } from '../base/participants';
import { StateListenerRegistry } from '../base/redux';
import { clientResized } from '../base/responsive-ui';
import { shouldHideSelfView } from '../base/settings';
@ -17,6 +17,7 @@ import {
setTileViewDimensions,
setVerticalViewDimensions
} from './actions';
import { clearStageParticipants } from './actions.web';
import {
ASPECT_RATIO_BREAKPOINT,
DISPLAY_DRAWER_THRESHOLD
@ -24,7 +25,6 @@ import {
import {
isFilmstripResizable,
isFilmstripScrollVisible,
shouldDisplayStageFilmstrip,
updateRemoteParticipants
} from './functions';
@ -74,6 +74,12 @@ StateListenerRegistry.register(
break;
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
store.dispatch(setVerticalViewDimensions());
if (store.getState()['features/filmstrip'].activeParticipants.length > 1) {
store.dispatch(clearStageParticipants());
}
break;
case LAYOUTS.STAGE_FILMSTRIP_VIEW:
store.dispatch(pinParticipant(null));
break;
}
}, {
@ -177,7 +183,7 @@ StateListenerRegistry.register(
};
},
/* listener */(_, store) => {
if (shouldDisplayStageFilmstrip(store.getState())) {
if (getCurrentLayout(store.getState()) === LAYOUTS.STAGE_FILMSTRIP_VIEW) {
store.dispatch(setStageFilmstripViewDimensions());
}
}, {

View File

@ -9,7 +9,8 @@ import {
getPinnedParticipant,
getRemoteParticipants
} from '../base/participants';
import { isStageFilmstripEnabled } from '../filmstrip/functions';
import { isStageFilmstripAvailable } from '../filmstrip/functions';
import { shouldDisplayStageFilmstrip } from '../filmstrip/functions.web';
import {
SELECT_LARGE_VIDEO_PARTICIPANT,
@ -30,6 +31,10 @@ export function selectParticipantInLargeVideo(participant: ?string) {
return (dispatch: Dispatch<any>, getState: Function) => {
const state = getState();
if (shouldDisplayStageFilmstrip(state)) {
return;
}
// Keep Etherpad open.
if (state['features/etherpad'].editing) {
return;
@ -103,7 +108,7 @@ function _electLastVisibleRemoteVideo(tracks) {
* @returns {(string|undefined)}
*/
function _electParticipantInLargeVideo(state) {
const stageFilmstrip = isStageFilmstripEnabled(state);
const stageFilmstrip = isStageFilmstripAvailable(state);
let participant;
if (!stageFilmstrip) {

View File

@ -6,5 +6,6 @@
export const LAYOUTS = {
HORIZONTAL_FILMSTRIP_VIEW: 'horizontal-filmstrip-view',
TILE_VIEW: 'tile-view',
VERTICAL_FILMSTRIP_VIEW: 'vertical-filmstrip-view'
VERTICAL_FILMSTRIP_VIEW: 'vertical-filmstrip-view',
STAGE_FILMSTRIP_VIEW: 'stage-filmstrip-view'
};

View File

@ -7,6 +7,7 @@ import {
getParticipantCount,
pinParticipant
} from '../base/participants';
import { shouldDisplayStageFilmstrip } from '../filmstrip/functions.web';
import { isVideoPlaying } from '../shared-video/functions';
import { VIDEO_QUALITY_LEVELS } from '../video-quality/constants';
@ -40,6 +41,10 @@ export function getCurrentLayout(state: Object) {
if (shouldDisplayTileView(state)) {
return LAYOUTS.TILE_VIEW;
} else if (interfaceConfig.VERTICAL_FILMSTRIP) {
if (shouldDisplayStageFilmstrip(state)) {
return LAYOUTS.STAGE_FILMSTRIP_VIEW;
}
return LAYOUTS.VERTICAL_FILMSTRIP_VIEW;
}

View File

@ -18,8 +18,8 @@ import { setParticipantContextMenuOpen } from '../../../base/responsive-ui/actio
import { getHideSelfView } from '../../../base/settings';
import { getLocalVideoTrack } from '../../../base/tracks';
import ConnectionIndicatorContent from '../../../connection-indicator/components/web/ConnectionIndicatorContent';
import { THUMBNAIL_TYPE } from '../../../filmstrip';
import { isStageFilmstripEnabled } from '../../../filmstrip/functions.web';
import { LAYOUTS } from '../../../video-layout';
import { renderConnectionStatus } from '../../actions.web';
import ConnectionStatusButton from './ConnectionStatusButton';
@ -274,7 +274,7 @@ class LocalVideoMenuTriggerButton extends Component<Props> {
* @returns {Props}
*/
function _mapStateToProps(state, ownProps) {
const { currentLayout } = ownProps;
const { thumbnailType } = ownProps;
const localParticipant = getLocalParticipant(state);
const { disableLocalVideoFlip, disableSelfViewSettings } = state['features/base/config'];
const videoTrack = getLocalVideoTrack(state['features/base/tracks']);
@ -284,14 +284,14 @@ function _mapStateToProps(state, ownProps) {
let _menuPosition;
switch (currentLayout) {
case LAYOUTS.TILE_VIEW:
switch (thumbnailType) {
case THUMBNAIL_TYPE.TILE:
_menuPosition = 'left-start';
break;
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
case THUMBNAIL_TYPE.VERTICAL:
_menuPosition = 'left-start';
break;
case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW:
case THUMBNAIL_TYPE.HORIZONTAL:
_menuPosition = 'top-start';
break;
default:

View File

@ -14,7 +14,7 @@ import { getParticipantById } from '../../../base/participants';
import { Popover } from '../../../base/popover';
import { connect } from '../../../base/redux';
import { setParticipantContextMenuOpen } from '../../../base/responsive-ui/actions';
import { LAYOUTS } from '../../../video-layout';
import { THUMBNAIL_TYPE } from '../../../filmstrip';
import { renderConnectionStatus } from '../../actions.web';
import ParticipantContextMenu from './ParticipantContextMenu';
@ -265,7 +265,7 @@ class RemoteVideoMenuTriggerButton extends Component<Props> {
* @returns {Props}
*/
function _mapStateToProps(state, ownProps) {
const { participantID, currentLayout } = ownProps;
const { participantID, thumbnailType } = ownProps;
let _remoteControlState = null;
const participant = getParticipantById(state, participantID);
const _participantDisplayName = participant?.name;
@ -291,14 +291,14 @@ function _mapStateToProps(state, ownProps) {
let _menuPosition;
switch (currentLayout) {
case LAYOUTS.TILE_VIEW:
switch (thumbnailType) {
case THUMBNAIL_TYPE.TILE:
_menuPosition = 'left-start';
break;
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
case THUMBNAIL_TYPE.VERTICAL:
_menuPosition = 'left-end';
break;
case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW:
case THUMBNAIL_TYPE.HORIZONTAL:
_menuPosition = 'top';
break;
default: