feat: Add screenshare filmstrip (#11714)
Add new screen share layout with resizable top panel Only enable new layout in large meetings (min 50 participants - configurable)
This commit is contained in:
parent
bac1347961
commit
21cf7f23c2
12
config.js
12
config.js
|
@ -1205,7 +1205,8 @@ var config = {
|
|||
// 'transcribing',
|
||||
// 'video-quality',
|
||||
// 'insecure-room',
|
||||
// 'highlight-moment'
|
||||
// 'highlight-moment',
|
||||
// 'top-panel-toggle'
|
||||
// ]
|
||||
// },
|
||||
|
||||
|
@ -1399,7 +1400,14 @@ var config = {
|
|||
|
||||
// // Disables the stage filmstrip
|
||||
// // (displaying multiple participants on stage besides the vertical filmstrip)
|
||||
// disableStageFilmstrip: false
|
||||
// disableStageFilmstrip: false,
|
||||
|
||||
// // Disables the top panel (only shown when a user is sharing their screen).
|
||||
// disableTopPanel: false,
|
||||
|
||||
// // The minimum number of participants that must be in the call for
|
||||
// // the top panel layout to be used.
|
||||
// minParticipantCountForTopPanel: 50
|
||||
// },
|
||||
|
||||
// Tile view related config options.
|
||||
|
|
|
@ -1033,6 +1033,7 @@
|
|||
"termsView": {
|
||||
"header": "Terms"
|
||||
},
|
||||
"toggleTopPanelLabel": "Toggle top panel",
|
||||
"toolbar": {
|
||||
"Settings": "Settings",
|
||||
"accessibilityLabel": {
|
||||
|
|
|
@ -7888,9 +7888,9 @@
|
|||
"integrity": "sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
|
||||
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
|
@ -26086,9 +26086,9 @@
|
|||
"integrity": "sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
|
||||
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ export const CONFERENCE_INFO = {
|
|||
'e2ee',
|
||||
'transcribing',
|
||||
'video-quality',
|
||||
'insecure-room'
|
||||
'insecure-room',
|
||||
'top-panel-toggle'
|
||||
]
|
||||
};
|
||||
|
|
|
@ -11,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 } from '../../../filmstrip';
|
||||
import { MainFilmstrip, StageFilmstrip, ScreenshareFilmstrip } from '../../../filmstrip';
|
||||
import { CalleeInfoContainer } from '../../../invite';
|
||||
import { LargeVideo } from '../../../large-video';
|
||||
import { LobbyScreen } from '../../../lobby';
|
||||
|
@ -239,6 +239,7 @@ class Conference extends AbstractConference<Props, *> {
|
|||
{
|
||||
_showPrejoin || _showLobby || (<>
|
||||
<StageFilmstrip />
|
||||
<ScreenshareFilmstrip />
|
||||
<MainFilmstrip />
|
||||
</>)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import InsecureRoomNameLabel from './InsecureRoomNameLabel';
|
|||
import ParticipantsCount from './ParticipantsCount';
|
||||
import RaisedHandsCountLabel from './RaisedHandsCountLabel';
|
||||
import SubjectText from './SubjectText';
|
||||
import ToggleTopPanelLabel from './ToggleTopPanelLabel';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link Subject}.
|
||||
|
@ -82,6 +83,10 @@ const COMPONENTS = [
|
|||
{
|
||||
Component: InsecureRoomNameLabel,
|
||||
id: 'insecure-room'
|
||||
},
|
||||
{
|
||||
Component: ToggleTopPanelLabel,
|
||||
id: 'top-panel-toggle'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
// @ts-ignore
|
||||
import { IconMenuDown } from '../../../base/icons';
|
||||
// @ts-ignore
|
||||
import { Label } from '../../../base/label';
|
||||
// @ts-ignore
|
||||
import { Tooltip } from '../../../base/tooltip';
|
||||
// @ts-ignore
|
||||
import { setTopPanelVisible } from '../../../filmstrip/actions.web';
|
||||
|
||||
const ToggleTopPanelLabel = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const topPanelHidden = !useSelector((state: any) => state['features/filmstrip'].topPanelVisible);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(setTopPanelVisible(true));
|
||||
}, []);
|
||||
|
||||
return topPanelHidden && (<Tooltip
|
||||
content={t('toggleTopPanelLabel') }
|
||||
position = { 'bottom' }>
|
||||
<Label
|
||||
icon={IconMenuDown}
|
||||
onClick = { onClick }/>
|
||||
</Tooltip>);
|
||||
};
|
||||
|
||||
export default ToggleTopPanelLabel;
|
|
@ -92,6 +92,15 @@ export const SET_VOLUME = 'SET_VOLUME';
|
|||
*/
|
||||
export const SET_VISIBLE_REMOTE_PARTICIPANTS = 'SET_VISIBLE_REMOTE_PARTICIPANTS';
|
||||
|
||||
/**
|
||||
* The type of action which sets the height for the top panel filmstrip.
|
||||
* {
|
||||
* type: SET_FILMSTRIP_HEIGHT,
|
||||
* height: number
|
||||
* }
|
||||
*/
|
||||
export const SET_FILMSTRIP_HEIGHT = 'SET_FILMSTRIP_HEIGHT';
|
||||
|
||||
/**
|
||||
* The type of action which sets the width for the vertical filmstrip.
|
||||
* {
|
||||
|
@ -101,6 +110,15 @@ export const SET_VISIBLE_REMOTE_PARTICIPANTS = 'SET_VISIBLE_REMOTE_PARTICIPANTS'
|
|||
*/
|
||||
export const SET_FILMSTRIP_WIDTH = 'SET_FILMSTRIP_WIDTH';
|
||||
|
||||
/**
|
||||
* The type of action which sets the height for the top panel filmstrip (user resized).
|
||||
* {
|
||||
* type: SET_USER_FILMSTRIP_HEIGHT,
|
||||
* height: number
|
||||
* }
|
||||
*/
|
||||
export const SET_USER_FILMSTRIP_HEIGHT = 'SET_USER_FILMSTRIP_HEIGHT';
|
||||
|
||||
/**
|
||||
* The type of action which sets the width for the vertical filmstrip (user resized).
|
||||
* {
|
||||
|
@ -187,3 +205,19 @@ export const TOGGLE_PIN_STAGE_PARTICIPANT = 'TOGGLE_PIN_STAGE_PARTICIPANT';
|
|||
* }
|
||||
*/
|
||||
export const CLEAR_STAGE_PARTICIPANTS = 'CLEAR_STAGE_PARTICIPANTS';
|
||||
|
||||
/**
|
||||
* The type of Redux action which sets the dimensions of the screenshare tile.
|
||||
* {
|
||||
* type: SET_SCREENSHARING_TILE_DIMENSIONS
|
||||
* }
|
||||
*/
|
||||
export const SET_SCREENSHARING_TILE_DIMENSIONS = 'SET_SCREENSHARING_TILE_DIMENSIONS';
|
||||
|
||||
/**
|
||||
* The type of Redux action which sets the visibility of the top panel.
|
||||
* {
|
||||
* type: SET_TOP_PANEL_VISIBILITY
|
||||
* }
|
||||
*/
|
||||
export const SET_TOP_PANEL_VISIBILITY = 'SET_TOP_PANEL_VISIBILITY';
|
||||
|
|
|
@ -25,7 +25,11 @@ import {
|
|||
SET_VOLUME,
|
||||
SET_MAX_STAGE_PARTICIPANTS,
|
||||
TOGGLE_PIN_STAGE_PARTICIPANT,
|
||||
CLEAR_STAGE_PARTICIPANTS
|
||||
CLEAR_STAGE_PARTICIPANTS,
|
||||
SET_SCREENSHARING_TILE_DIMENSIONS,
|
||||
SET_USER_FILMSTRIP_HEIGHT,
|
||||
SET_FILMSTRIP_HEIGHT,
|
||||
SET_TOP_PANEL_VISIBILITY
|
||||
} from './actionTypes';
|
||||
import {
|
||||
HORIZONTAL_FILMSTRIP_MARGIN,
|
||||
|
@ -33,11 +37,13 @@ import {
|
|||
SCROLL_SIZE,
|
||||
STAGE_VIEW_THUMBNAIL_VERTICAL_BORDER,
|
||||
TILE_HORIZONTAL_MARGIN,
|
||||
TILE_MIN_HEIGHT_SMALL,
|
||||
TILE_VERTICAL_CONTAINER_HORIZONTAL_MARGIN,
|
||||
TILE_VERTICAL_MARGIN,
|
||||
TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES,
|
||||
TILE_VIEW_GRID_HORIZONTAL_MARGIN,
|
||||
TILE_VIEW_GRID_VERTICAL_MARGIN,
|
||||
TOP_FILMSTRIP_HEIGHT,
|
||||
VERTICAL_FILMSTRIP_VERTICAL_MARGIN
|
||||
} from './constants';
|
||||
import {
|
||||
|
@ -48,6 +54,7 @@ import {
|
|||
getNumberOfPartipantsForTileView,
|
||||
getVerticalViewMaxWidth,
|
||||
isFilmstripResizable,
|
||||
isStageFilmstripTopPanel,
|
||||
showGridInVerticalView
|
||||
} from './functions';
|
||||
import { isStageFilmstripAvailable } from './functions.web';
|
||||
|
@ -270,7 +277,7 @@ export function setStageFilmstripViewDimensions() {
|
|||
const {
|
||||
tileView = {}
|
||||
} = state['features/base/config'];
|
||||
const { visible } = state['features/filmstrip'];
|
||||
const { visible, topPanelHeight } = state['features/filmstrip'];
|
||||
const verticalWidth = visible ? getVerticalViewMaxWidth(state) : 0;
|
||||
const { numberOfVisibleTiles = MAX_ACTIVE_PARTICIPANTS } = tileView;
|
||||
const numberOfParticipants = state['features/filmstrip'].activeParticipants.length;
|
||||
|
@ -280,6 +287,7 @@ export function setStageFilmstripViewDimensions() {
|
|||
disableResponsiveTiles: false,
|
||||
disableTileEnlargement: false
|
||||
});
|
||||
const topPanel = isStageFilmstripTopPanel(state);
|
||||
|
||||
const {
|
||||
height,
|
||||
|
@ -288,12 +296,13 @@ export function setStageFilmstripViewDimensions() {
|
|||
rows
|
||||
} = calculateResponsiveTileViewDimensions({
|
||||
clientWidth: availableWidth,
|
||||
clientHeight,
|
||||
clientHeight: topPanel ? topPanelHeight?.current || TOP_FILMSTRIP_HEIGHT : clientHeight,
|
||||
disableTileEnlargement: false,
|
||||
maxColumns,
|
||||
noHorizontalContainerMargin: verticalWidth > 0,
|
||||
numberOfParticipants,
|
||||
numberOfVisibleTiles
|
||||
numberOfVisibleTiles,
|
||||
minTileHeight: topPanel ? TILE_MIN_HEIGHT_SMALL : null
|
||||
});
|
||||
const thumbnailsTotalHeight = rows * (TILE_VERTICAL_MARGIN + height);
|
||||
const hasScroll = clientHeight < thumbnailsTotalHeight;
|
||||
|
@ -368,6 +377,22 @@ export function setVolume(participantId: string, volume: number) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the top filmstrip's height.
|
||||
*
|
||||
* @param {number} height - The new height of the filmstrip.
|
||||
* @returns {{
|
||||
* type: SET_FILMSTRIP_HEIGHT,
|
||||
* height: number
|
||||
* }}
|
||||
*/
|
||||
export function setFilmstripHeight(height: number) {
|
||||
return {
|
||||
type: SET_FILMSTRIP_HEIGHT,
|
||||
height
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filmstrip's width.
|
||||
*
|
||||
|
@ -384,6 +409,22 @@ export function setFilmstripWidth(width: number) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filmstrip's height and the user preferred height.
|
||||
*
|
||||
* @param {number} height - The new height of the filmstrip.
|
||||
* @returns {{
|
||||
* type: SET_USER_FILMSTRIP_WIDTH,
|
||||
* height: number
|
||||
* }}
|
||||
*/
|
||||
export function setUserFilmstripHeight(height: number) {
|
||||
return {
|
||||
type: SET_USER_FILMSTRIP_HEIGHT,
|
||||
height
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filmstrip's width and the user preferred width.
|
||||
*
|
||||
|
@ -490,3 +531,45 @@ export function clearStageParticipants() {
|
|||
type: CLEAR_STAGE_PARTICIPANTS
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the screensharing tile dimensions.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function setScreensharingTileDimensions() {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
|
||||
const { visible, topPanelHeight, topPanelVisible } = state['features/filmstrip'];
|
||||
const verticalWidth = visible ? getVerticalViewMaxWidth(state) : 0;
|
||||
const availableWidth = clientWidth - verticalWidth;
|
||||
const topPanel = isStageFilmstripTopPanel(state) && topPanelVisible;
|
||||
const availableHeight = clientHeight - (topPanel ? topPanelHeight?.current || TOP_FILMSTRIP_HEIGHT : 0);
|
||||
|
||||
dispatch({
|
||||
type: SET_SCREENSHARING_TILE_DIMENSIONS,
|
||||
dimensions: {
|
||||
filmstripHeight: availableHeight,
|
||||
filmstripWidth: availableWidth,
|
||||
thumbnailSize: {
|
||||
width: availableWidth - TILE_HORIZONTAL_MARGIN,
|
||||
height: availableHeight - TILE_VERTICAL_MARGIN
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the visibility of the top panel.
|
||||
*
|
||||
* @param {boolean} visible - Whether it should be visible or not.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function setTopPanelVisible(visible) {
|
||||
return {
|
||||
type: SET_TOP_PANEL_VISIBILITY,
|
||||
visible
|
||||
};
|
||||
}
|
||||
|
|
|
@ -23,20 +23,26 @@ import { isButtonEnabled, isToolboxVisible } from '../../../toolbox/functions.we
|
|||
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
|
||||
import {
|
||||
setFilmstripVisible,
|
||||
setVisibleRemoteParticipants,
|
||||
setUserFilmstripHeight,
|
||||
setUserFilmstripWidth,
|
||||
setUserIsResizing
|
||||
setUserIsResizing,
|
||||
setTopPanelVisible,
|
||||
setVisibleRemoteParticipants
|
||||
} from '../../actions';
|
||||
import {
|
||||
ASPECT_RATIO_BREAKPOINT,
|
||||
DEFAULT_FILMSTRIP_WIDTH,
|
||||
FILMSTRIP_TYPE,
|
||||
MIN_STAGE_VIEW_HEIGHT,
|
||||
MIN_STAGE_VIEW_WIDTH,
|
||||
TILE_HORIZONTAL_MARGIN,
|
||||
TILE_VERTICAL_MARGIN
|
||||
TILE_VERTICAL_MARGIN,
|
||||
TOP_FILMSTRIP_HEIGHT
|
||||
} from '../../constants';
|
||||
import {
|
||||
getVerticalViewMaxWidth,
|
||||
shouldRemoteVideosBeVisible
|
||||
shouldRemoteVideosBeVisible,
|
||||
isStageFilmstripTopPanel
|
||||
} from '../../functions';
|
||||
|
||||
import AudioTracksContainer from './AudioTracksContainer';
|
||||
|
@ -112,11 +118,21 @@ type Props = {
|
|||
*/
|
||||
_localScreenShare: Object,
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip videos should currently be displayed.
|
||||
*/
|
||||
_mainFilmstripVisible: boolean,
|
||||
|
||||
/**
|
||||
* The maximum width of the vertical filmstrip.
|
||||
*/
|
||||
_maxFilmstripWidth: number,
|
||||
|
||||
/**
|
||||
* The maximum height of the top panel.
|
||||
*/
|
||||
_maxTopPanelHeight: number,
|
||||
|
||||
/**
|
||||
* The participants in the call.
|
||||
*/
|
||||
|
@ -137,11 +153,6 @@ type Props = {
|
|||
*/
|
||||
_rows: number,
|
||||
|
||||
/**
|
||||
* Whether or not this is the stage filmstrip.
|
||||
*/
|
||||
_stageFilmstrip: boolean,
|
||||
|
||||
/**
|
||||
* The height of the thumbnail.
|
||||
*/
|
||||
|
@ -157,6 +168,26 @@ type Props = {
|
|||
*/
|
||||
_thumbnailsReordered: Boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip is top panel.
|
||||
*/
|
||||
_topPanelFilmstrip: boolean,
|
||||
|
||||
/**
|
||||
* The max height of the top panel.
|
||||
*/
|
||||
_topPanelMaxHeight: number,
|
||||
|
||||
/**
|
||||
* The height of the top panel (user resized).
|
||||
*/
|
||||
_topPanelHeight: ?number,
|
||||
|
||||
/**
|
||||
* Whether or not the top panel is visible.
|
||||
*/
|
||||
_topPanelVisible: boolean,
|
||||
|
||||
/**
|
||||
* The width of the vertical filmstrip (user resized).
|
||||
*/
|
||||
|
@ -182,11 +213,6 @@ type Props = {
|
|||
*/
|
||||
_videosClassName: string,
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip videos should currently be displayed.
|
||||
*/
|
||||
_visible: boolean,
|
||||
|
||||
/**
|
||||
* An object containing the CSS classes.
|
||||
*/
|
||||
|
@ -197,6 +223,11 @@ type Props = {
|
|||
*/
|
||||
dispatch: Dispatch<any>,
|
||||
|
||||
/**
|
||||
* The type of filmstrip to be displayed.
|
||||
*/
|
||||
filmstripType: string,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
|
@ -218,7 +249,12 @@ type State = {
|
|||
/**
|
||||
* Initial filmstrip width on drag handle mouse down.
|
||||
*/
|
||||
dragFilmstripWidth: ?number
|
||||
dragFilmstripWidth: ?number,
|
||||
|
||||
/**
|
||||
* Initial top panel height on drag handle mouse down.
|
||||
*/
|
||||
dragFilmstripHeight: ?number
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -307,25 +343,45 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
_currentLayout,
|
||||
_disableSelfView,
|
||||
_localScreenShare,
|
||||
_mainFilmstripVisible,
|
||||
_resizableFilmstrip,
|
||||
_stageFilmstrip,
|
||||
_visible,
|
||||
_topPanelFilmstrip,
|
||||
_topPanelMaxHeight,
|
||||
_topPanelVisible,
|
||||
_verticalViewBackground,
|
||||
_verticalViewGrid,
|
||||
_verticalViewMaxWidth,
|
||||
classes
|
||||
classes,
|
||||
filmstripType
|
||||
} = this.props;
|
||||
const { isMouseDown } = this.state;
|
||||
const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
|
||||
|
||||
if (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && _stageFilmstrip) {
|
||||
if (_visible) {
|
||||
if (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && filmstripType === FILMSTRIP_TYPE.STAGE) {
|
||||
if (_topPanelFilmstrip) {
|
||||
filmstripStyle.maxHeight = `${_topPanelMaxHeight}px`;
|
||||
filmstripStyle.zIndex = 1;
|
||||
|
||||
if (!_topPanelVisible) {
|
||||
filmstripStyle.top = `-${_topPanelMaxHeight}px`;
|
||||
}
|
||||
}
|
||||
if (_mainFilmstripVisible) {
|
||||
filmstripStyle.maxWidth = `calc(100% - ${_verticalViewMaxWidth}px)`;
|
||||
}
|
||||
} else if (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && filmstripType === FILMSTRIP_TYPE.SCREENSHARE) {
|
||||
if (_mainFilmstripVisible) {
|
||||
filmstripStyle.maxWidth = `calc(100% - ${_verticalViewMaxWidth}px)`;
|
||||
}
|
||||
if (_topPanelVisible) {
|
||||
filmstripStyle.maxHeight = `calc(100% - ${_topPanelMaxHeight}px)`;
|
||||
}
|
||||
filmstripStyle.bottom = 0;
|
||||
filmstripStyle.top = 'auto';
|
||||
} else if (_currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
|
||||
|| (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && !_stageFilmstrip)) {
|
||||
|| (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && filmstripType === FILMSTRIP_TYPE.MAIN)) {
|
||||
filmstripStyle.maxWidth = _verticalViewMaxWidth;
|
||||
if (!_visible) {
|
||||
if (!_mainFilmstripVisible) {
|
||||
filmstripStyle.right = `-${filmstripStyle.maxWidth}px`;
|
||||
}
|
||||
}
|
||||
|
@ -333,14 +389,17 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
let toolbar = null;
|
||||
|
||||
if (!this.props._iAmRecorder && this.props._isFilmstripButtonEnabled
|
||||
&& _currentLayout !== LAYOUTS.TILE_VIEW && !_stageFilmstrip) {
|
||||
&& _currentLayout !== LAYOUTS.TILE_VIEW && (filmstripType === FILMSTRIP_TYPE.MAIN
|
||||
|| (filmstripType === FILMSTRIP_TYPE.STAGE && _topPanelFilmstrip))) {
|
||||
toolbar = this._renderToggleButton();
|
||||
}
|
||||
|
||||
const filmstrip = (<>
|
||||
<div
|
||||
className = { clsx(this.props._videosClassName,
|
||||
!tileViewActive && !_stageFilmstrip && !_resizableFilmstrip && 'filmstrip-hover',
|
||||
!tileViewActive && (filmstripType === FILMSTRIP_TYPE.MAIN
|
||||
|| (filmstripType === FILMSTRIP_TYPE.STAGE && _topPanelFilmstrip))
|
||||
&& !_resizableFilmstrip && 'filmstrip-hover',
|
||||
_verticalViewGrid && 'vertical-view-grid') }
|
||||
id = 'remoteVideos'>
|
||||
{!_disableSelfView && !_verticalViewGrid && (
|
||||
|
@ -348,8 +407,10 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
className = 'filmstrip__videos'
|
||||
id = 'filmstripLocalVideo'>
|
||||
{
|
||||
!tileViewActive && !_stageFilmstrip && <div id = 'filmstripLocalVideoThumbnail'>
|
||||
!tileViewActive && filmstripType === FILMSTRIP_TYPE.MAIN
|
||||
&& <div id = 'filmstripLocalVideoThumbnail'>
|
||||
<Thumbnail
|
||||
filmstripType = { FILMSTRIP_TYPE.MAIN }
|
||||
key = 'local' />
|
||||
</div>
|
||||
}
|
||||
|
@ -361,10 +422,9 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
id = 'filmstripLocalScreenShare'>
|
||||
<div id = 'filmstripLocalScreenShareThumbnail'>
|
||||
{
|
||||
!tileViewActive && !_stageFilmstrip && <Thumbnail
|
||||
!tileViewActive && filmstripType === FILMSTRIP_TYPE.MAIN && <Thumbnail
|
||||
key = 'localScreenShare'
|
||||
participantID = { _localScreenShare.id } />
|
||||
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -385,11 +445,14 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
style = { filmstripStyle }>
|
||||
{ toolbar }
|
||||
{_resizableFilmstrip
|
||||
? <div className = { clsx('resizable-filmstrip', classes.resizableFilmstripContainer) }>
|
||||
? <div
|
||||
className = { clsx('resizable-filmstrip', classes.resizableFilmstripContainer,
|
||||
_topPanelFilmstrip && 'top-panel-filmstrip') }>
|
||||
<div
|
||||
className = { clsx('dragHandleContainer',
|
||||
classes.dragHandleContainer,
|
||||
isMouseDown && 'visible')
|
||||
isMouseDown && 'visible',
|
||||
_topPanelFilmstrip && 'top-panel')
|
||||
}
|
||||
onMouseDown = { this._onDragHandleMouseDown }>
|
||||
<div className = { clsx(classes.dragHandle, 'dragHandle') } />
|
||||
|
@ -412,10 +475,13 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_onDragHandleMouseDown(e) {
|
||||
const { _topPanelFilmstrip, _topPanelHeight, _verticalFilmstripWidth } = this.props;
|
||||
|
||||
this.setState({
|
||||
isMouseDown: true,
|
||||
mousePosition: e.clientX,
|
||||
dragFilmstripWidth: this.props._verticalFilmstripWidth || DEFAULT_FILMSTRIP_WIDTH
|
||||
mousePosition: _topPanelFilmstrip ? e.clientY : e.clientX,
|
||||
dragFilmstripWidth: _verticalFilmstripWidth || DEFAULT_FILMSTRIP_WIDTH,
|
||||
dragFilmstripHeight: _topPanelHeight || TOP_FILMSTRIP_HEIGHT
|
||||
});
|
||||
this.props.dispatch(setUserIsResizing(true));
|
||||
}
|
||||
|
@ -446,16 +512,36 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
*/
|
||||
_onFilmstripResize(e) {
|
||||
if (this.state.isMouseDown) {
|
||||
const { dispatch, _verticalFilmstripWidth, _maxFilmstripWidth } = this.props;
|
||||
const { dragFilmstripWidth, mousePosition } = this.state;
|
||||
const diff = mousePosition - e.clientX;
|
||||
const width = Math.max(
|
||||
Math.min(dragFilmstripWidth + diff, _maxFilmstripWidth),
|
||||
DEFAULT_FILMSTRIP_WIDTH
|
||||
);
|
||||
const {
|
||||
dispatch,
|
||||
_verticalFilmstripWidth,
|
||||
_maxFilmstripWidth,
|
||||
_topPanelHeight,
|
||||
_maxTopPanelHeight,
|
||||
_topPanelFilmstrip
|
||||
} = this.props;
|
||||
const { dragFilmstripWidth, dragFilmstripHeight, mousePosition } = this.state;
|
||||
|
||||
if (width !== _verticalFilmstripWidth) {
|
||||
dispatch(setUserFilmstripWidth(width));
|
||||
if (_topPanelFilmstrip) {
|
||||
const diff = e.clientY - mousePosition;
|
||||
const height = Math.max(
|
||||
Math.min(dragFilmstripHeight + diff, _maxTopPanelHeight),
|
||||
TOP_FILMSTRIP_HEIGHT
|
||||
);
|
||||
|
||||
if (height !== _topPanelHeight) {
|
||||
dispatch(setUserFilmstripHeight(height));
|
||||
}
|
||||
} else {
|
||||
const diff = mousePosition - e.clientX;
|
||||
const width = Math.max(
|
||||
Math.min(dragFilmstripWidth + diff, _maxFilmstripWidth),
|
||||
DEFAULT_FILMSTRIP_WIDTH
|
||||
);
|
||||
|
||||
if (width !== _verticalFilmstripWidth) {
|
||||
dispatch(setUserFilmstripWidth(width));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -495,7 +581,7 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_onTabIn() {
|
||||
if (!this.props._isToolboxVisible && this.props._visible) {
|
||||
if (!this.props._isToolboxVisible && this.props._mainFilmstripVisible) {
|
||||
this.props.dispatch(showToolbox());
|
||||
}
|
||||
}
|
||||
|
@ -605,10 +691,10 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
_remoteParticipantsLength,
|
||||
_resizableFilmstrip,
|
||||
_rows,
|
||||
_stageFilmstrip,
|
||||
_thumbnailHeight,
|
||||
_thumbnailWidth,
|
||||
_verticalViewGrid
|
||||
_verticalViewGrid,
|
||||
filmstripType
|
||||
} = this.props;
|
||||
|
||||
if (!_thumbnailWidth || isNaN(_thumbnailWidth) || !_thumbnailHeight
|
||||
|
@ -617,7 +703,7 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid || _stageFilmstrip) {
|
||||
if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid || filmstripType !== FILMSTRIP_TYPE.MAIN) {
|
||||
return (
|
||||
<FixedSizeGrid
|
||||
className = 'filmstrip__videos remote-videos'
|
||||
|
@ -626,7 +712,7 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
height = { _filmstripHeight }
|
||||
initialScrollLeft = { 0 }
|
||||
initialScrollTop = { 0 }
|
||||
itemData = {{ stageFilmstrip: _stageFilmstrip }}
|
||||
itemData = {{ filmstripType }}
|
||||
itemKey = { this._gridItemKey }
|
||||
onItemsRendered = { this._onGridItemsRendered }
|
||||
overscanRowCount = { 1 }
|
||||
|
@ -694,7 +780,11 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_doToggleFilmstrip() {
|
||||
this.props.dispatch(setFilmstripVisible(!this.props._visible));
|
||||
const { dispatch, _mainFilmstripVisible, _topPanelFilmstrip, _topPanelVisible } = this.props;
|
||||
|
||||
_topPanelFilmstrip
|
||||
? dispatch(setTopPanelVisible(!_topPanelVisible))
|
||||
: dispatch(setFilmstripVisible(!_mainFilmstripVisible));
|
||||
}
|
||||
|
||||
_onShortcutToggleFilmstrip: () => void;
|
||||
|
@ -710,7 +800,7 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.filmstrip',
|
||||
{
|
||||
enable: this.props._visible
|
||||
enable: this.props._mainFilmstripVisible
|
||||
}));
|
||||
|
||||
this._doToggleFilmstrip();
|
||||
|
@ -729,7 +819,7 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
sendAnalytics(createToolbarEvent(
|
||||
'toggle.filmstrip.button',
|
||||
{
|
||||
enable: this.props._visible
|
||||
enable: this.props._mainFilmstripVisible
|
||||
}));
|
||||
|
||||
this._doToggleFilmstrip();
|
||||
|
@ -758,8 +848,15 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderToggleButton() {
|
||||
const icon = this.props._visible ? IconMenuDown : IconMenuUp;
|
||||
const { t, classes, _isVerticalFilmstrip } = this.props;
|
||||
const {
|
||||
t,
|
||||
classes,
|
||||
_isVerticalFilmstrip,
|
||||
_mainFilmstripVisible,
|
||||
_topPanelFilmstrip,
|
||||
_topPanelVisible
|
||||
} = this.props;
|
||||
const icon = (_topPanelFilmstrip ? _topPanelVisible : _mainFilmstripVisible) ? IconMenuDown : IconMenuUp;
|
||||
const actions = isMobileBrowser()
|
||||
? { onTouchStart: this._onToggleButtonTouch }
|
||||
: { onClick: this._onToolbarToggleFilmstrip };
|
||||
|
@ -768,9 +865,11 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
<div
|
||||
className = { clsx(classes.toggleFilmstripContainer,
|
||||
_isVerticalFilmstrip && classes.toggleVerticalFilmstripContainer,
|
||||
_topPanelFilmstrip && classes.toggleTopPanelContainer,
|
||||
_topPanelFilmstrip && !_topPanelVisible && classes.toggleTopPanelContainerHidden,
|
||||
'toggleFilmstripContainer') }>
|
||||
<button
|
||||
aria-expanded = { this.props._visible }
|
||||
aria-expanded = { this.props._mainFilmstripVisible }
|
||||
aria-label = { t('toolbar.accessibilityLabel.toggleFilmstrip') }
|
||||
className = { classes.toggleFilmstripButton }
|
||||
id = 'toggleFilmstripButton'
|
||||
|
@ -795,32 +894,38 @@ class Filmstrip extends PureComponent <Props, State> {
|
|||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
const { _hasScroll = false } = ownProps;
|
||||
const { _hasScroll = false, filmstripType, _topPanelFilmstrip, _remoteParticipants } = ownProps;
|
||||
const toolbarButtons = getToolbarButtons(state);
|
||||
const { testing = {}, iAmRecorder } = state['features/base/config'];
|
||||
const enableThumbnailReordering = testing.enableThumbnailReordering ?? true;
|
||||
const { visible, width: verticalFilmstripWidth } = state['features/filmstrip'];
|
||||
const { topPanelHeight, topPanelVisible, visible, width: verticalFilmstripWidth } = state['features/filmstrip'];
|
||||
const { localScreenShare } = state['features/base/participants'];
|
||||
const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
|
||||
const remoteVideosVisible = shouldRemoteVideosBeVisible(state);
|
||||
const { isOpen: shiftRight } = state['features/chat'];
|
||||
const disableSelfView = shouldHideSelfView(state);
|
||||
const { clientWidth } = state['features/base/responsive-ui'];
|
||||
const { clientWidth, clientHeight } = state['features/base/responsive-ui'];
|
||||
|
||||
const collapseTileView = reduceHeight
|
||||
&& isMobileBrowser()
|
||||
&& clientWidth <= ASPECT_RATIO_BREAKPOINT;
|
||||
|
||||
const shouldReduceHeight = reduceHeight && isMobileBrowser();
|
||||
const _topPanelVisible = isStageFilmstripTopPanel(state) && topPanelVisible;
|
||||
|
||||
const videosClassName = `filmstrip__videos${visible ? '' : ' hidden'}${_hasScroll ? ' has-scroll' : ''}`;
|
||||
let isVisible = visible || filmstripType !== FILMSTRIP_TYPE.MAIN;
|
||||
|
||||
if (_topPanelFilmstrip) {
|
||||
isVisible = _topPanelVisible;
|
||||
}
|
||||
const videosClassName = `filmstrip__videos${isVisible ? '' : ' hidden'}${_hasScroll ? ' has-scroll' : ''}`;
|
||||
const className = `${remoteVideosVisible || ownProps._verticalViewGrid ? '' : 'hide-videos'} ${
|
||||
shouldReduceHeight ? 'reduce-height' : ''
|
||||
} ${shiftRight ? 'shift-right' : ''} ${collapseTileView ? 'collapse' : ''} ${visible ? '' : 'hidden'}`.trim();
|
||||
} ${shiftRight ? 'shift-right' : ''} ${collapseTileView ? 'collapse' : ''} ${isVisible ? '' : 'hidden'}`.trim();
|
||||
|
||||
const _currentLayout = getCurrentLayout(state);
|
||||
const _isVerticalFilmstrip = _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
|
||||
|| (!ownProps._stageFilmstrip && _currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW);
|
||||
|| (filmstripType === FILMSTRIP_TYPE.MAIN && _currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW);
|
||||
|
||||
return {
|
||||
_className: className,
|
||||
|
@ -833,8 +938,14 @@ function _mapStateToProps(state, ownProps) {
|
|||
_isToolboxVisible: isToolboxVisible(state),
|
||||
_isVerticalFilmstrip,
|
||||
_localScreenShare: getSourceNameSignalingFeatureFlag(state) && localScreenShare,
|
||||
_mainFilmstripVisible: visible,
|
||||
_maxFilmstripWidth: clientWidth - MIN_STAGE_VIEW_WIDTH,
|
||||
_maxTopPanelHeight: clientHeight - MIN_STAGE_VIEW_HEIGHT,
|
||||
_remoteParticipantsLength: _remoteParticipants.length,
|
||||
_thumbnailsReordered: enableThumbnailReordering,
|
||||
_topPanelHeight: topPanelHeight.current,
|
||||
_topPanelMaxHeight: topPanelHeight.current || TOP_FILMSTRIP_HEIGHT,
|
||||
_topPanelVisible,
|
||||
_verticalFilmstripWidth: verticalFilmstripWidth.current,
|
||||
_verticalViewMaxWidth: getVerticalViewMaxWidth(state),
|
||||
_videosClassName: videosClassName
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
ASPECT_RATIO_BREAKPOINT,
|
||||
FILMSTRIP_BREAKPOINT,
|
||||
FILMSTRIP_BREAKPOINT_OFFSET,
|
||||
FILMSTRIP_TYPE,
|
||||
TOOLBAR_HEIGHT,
|
||||
TOOLBAR_HEIGHT_MOBILE } from '../../constants';
|
||||
import { isFilmstripResizable, showGridInVerticalView } from '../../functions.web';
|
||||
|
@ -85,15 +86,16 @@ type Props = {
|
|||
/**
|
||||
* Additional CSS class names to add to the container of all the thumbnails.
|
||||
*/
|
||||
_videosClassName: string,
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip videos should currently be displayed.
|
||||
*/
|
||||
_visible: boolean
|
||||
_videosClassName: string
|
||||
};
|
||||
|
||||
const MainFilmstrip = (props: Props) => <span><Filmstrip { ...props } /></span>;
|
||||
const MainFilmstrip = (props: Props) => (
|
||||
<span>
|
||||
<Filmstrip
|
||||
{ ...props }
|
||||
filmstripType = { FILMSTRIP_TYPE.MAIN } />
|
||||
</span>
|
||||
);
|
||||
|
||||
/**
|
||||
* Maps (parts of) the Redux state to the associated {@code Filmstrip}'s props.
|
||||
|
@ -104,7 +106,7 @@ const MainFilmstrip = (props: Props) => <span><Filmstrip { ...props } /></span>;
|
|||
*/
|
||||
function _mapStateToProps(state) {
|
||||
const toolbarButtons = getToolbarButtons(state);
|
||||
const { visible, remoteParticipants, width: verticalFilmstripWidth } = state['features/filmstrip'];
|
||||
const { remoteParticipants, width: verticalFilmstripWidth } = state['features/filmstrip'];
|
||||
const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
|
||||
const {
|
||||
gridDimensions: dimensions = {},
|
||||
|
@ -189,13 +191,11 @@ function _mapStateToProps(state) {
|
|||
_filmstripHeight: remoteFilmstripHeight,
|
||||
_filmstripWidth: remoteFilmstripWidth,
|
||||
_hasScroll,
|
||||
_remoteParticipantsLength: remoteParticipants.length,
|
||||
_remoteParticipants: remoteParticipants,
|
||||
_resizableFilmstrip,
|
||||
_rows: gridDimensions.rows,
|
||||
_thumbnailWidth: _thumbnailSize?.width,
|
||||
_thumbnailHeight: _thumbnailSize?.height,
|
||||
_visible: visible,
|
||||
_verticalViewGrid,
|
||||
_verticalViewBackground: verticalFilmstripWidth.current + FILMSTRIP_BREAKPOINT_OFFSET >= FILMSTRIP_BREAKPOINT
|
||||
};
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
|
||||
import { connect } from '../../../base/redux';
|
||||
import { LAYOUT_CLASSNAMES } from '../../../conference/components/web/Conference';
|
||||
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
|
||||
import {
|
||||
FILMSTRIP_TYPE
|
||||
} from '../../constants';
|
||||
|
||||
import Filmstrip from './Filmstrip';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The current layout of the filmstrip.
|
||||
*/
|
||||
_currentLayout: string,
|
||||
|
||||
/**
|
||||
* The number of columns in tile view.
|
||||
*/
|
||||
_columns: number,
|
||||
|
||||
/**
|
||||
* The width of the filmstrip.
|
||||
*/
|
||||
_filmstripWidth: number,
|
||||
|
||||
/**
|
||||
* The height of the filmstrip.
|
||||
*/
|
||||
_filmstripHeight: number,
|
||||
|
||||
/**
|
||||
* Whether or not the current layout is vertical filmstrip.
|
||||
*/
|
||||
_isVerticalFilmstrip: boolean,
|
||||
|
||||
/**
|
||||
* The participants in the call.
|
||||
*/
|
||||
_remoteParticipants: Array<Object>,
|
||||
|
||||
/**
|
||||
* The length of the remote participants array.
|
||||
*/
|
||||
_remoteParticipantsLength: number,
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip should be user-resizable.
|
||||
*/
|
||||
_resizableFilmstrip: boolean,
|
||||
|
||||
/**
|
||||
* The number of rows in tile view.
|
||||
*/
|
||||
_rows: number,
|
||||
|
||||
/**
|
||||
* The height of the thumbnail.
|
||||
*/
|
||||
_thumbnailHeight: number,
|
||||
|
||||
/**
|
||||
* The width of the thumbnail.
|
||||
*/
|
||||
_thumbnailWidth: number,
|
||||
|
||||
/**
|
||||
* Whether or not the vertical filmstrip should have a background color.
|
||||
*/
|
||||
_verticalViewBackground: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the vertical filmstrip should be displayed as grid.
|
||||
*/
|
||||
_verticalViewGrid: boolean,
|
||||
|
||||
/**
|
||||
* Additional CSS class names to add to the container of all the thumbnails.
|
||||
*/
|
||||
_videosClassName: string
|
||||
};
|
||||
|
||||
const ScreenshareFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW
|
||||
&& props._remoteParticipantsLength === 1 && (
|
||||
<span className = { LAYOUT_CLASSNAMES[LAYOUTS.TILE_VIEW] }>
|
||||
<Filmstrip
|
||||
{ ...props }
|
||||
filmstripType = { FILMSTRIP_TYPE.SCREENSHARE } />
|
||||
</span>
|
||||
);
|
||||
|
||||
/**
|
||||
* Maps (parts of) the Redux state to the associated {@code Filmstrip}'s props.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
const {
|
||||
filmstripHeight,
|
||||
filmstripWidth,
|
||||
thumbnailSize
|
||||
} = state['features/filmstrip'].screenshareFilmstripDimensions;
|
||||
const screenshares = state['features/video-layout'].remoteScreenShares;
|
||||
|
||||
return {
|
||||
_columns: 1,
|
||||
_currentLayout: getCurrentLayout(state),
|
||||
_filmstripHeight: filmstripHeight,
|
||||
_filmstripWidth: filmstripWidth,
|
||||
_remoteParticipants: screenshares.length ? [ screenshares[0] ] : [],
|
||||
_resizableFilmstrip: false,
|
||||
_rows: 1,
|
||||
_thumbnailWidth: thumbnailSize?.width,
|
||||
_thumbnailHeight: thumbnailSize?.height,
|
||||
_verticalViewGrid: false,
|
||||
_verticalViewBackground: false
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(ScreenshareFilmstrip);
|
|
@ -8,9 +8,11 @@ import { LAYOUT_CLASSNAMES } from '../../../conference/components/web/Conference
|
|||
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
|
||||
import {
|
||||
ASPECT_RATIO_BREAKPOINT,
|
||||
FILMSTRIP_TYPE,
|
||||
TOOLBAR_HEIGHT_MOBILE
|
||||
} from '../../constants';
|
||||
import { getActiveParticipantsIds } from '../../functions';
|
||||
import { isFilmstripResizable, isStageFilmstripTopPanel } from '../../functions.web';
|
||||
|
||||
import Filmstrip from './Filmstrip';
|
||||
|
||||
|
@ -84,19 +86,14 @@ type Props = {
|
|||
/**
|
||||
* Additional CSS class names to add to the container of all the thumbnails.
|
||||
*/
|
||||
_videosClassName: string,
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip videos should currently be displayed.
|
||||
*/
|
||||
_visible: boolean
|
||||
_videosClassName: string
|
||||
};
|
||||
|
||||
const StageFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && (
|
||||
<span className = { LAYOUT_CLASSNAMES[LAYOUTS.TILE_VIEW] }>
|
||||
<Filmstrip
|
||||
{ ...props }
|
||||
_stageFilmstrip = { true } />
|
||||
filmstripType = { FILMSTRIP_TYPE.STAGE } />
|
||||
</span>
|
||||
);
|
||||
|
||||
|
@ -109,7 +106,6 @@ const StageFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.STAGE_
|
|||
*/
|
||||
function _mapStateToProps(state) {
|
||||
const toolbarButtons = getToolbarButtons(state);
|
||||
const { visible } = state['features/filmstrip'];
|
||||
const activeParticipants = getActiveParticipantsIds(state);
|
||||
const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
|
||||
const {
|
||||
|
@ -139,19 +135,19 @@ function _mapStateToProps(state) {
|
|||
&& clientWidth <= ASPECT_RATIO_BREAKPOINT;
|
||||
|
||||
const remoteFilmstripHeight = filmstripHeight - (collapseTileView && filmstripPadding > 0 ? filmstripPadding : 0);
|
||||
const _topPanelFilmstrip = isStageFilmstripTopPanel(state);
|
||||
|
||||
return {
|
||||
_columns: gridDimensions.columns,
|
||||
_currentLayout: getCurrentLayout(state),
|
||||
_filmstripHeight: remoteFilmstripHeight,
|
||||
_filmstripWidth: filmstripWidth,
|
||||
_remoteParticipantsLength: activeParticipants.length,
|
||||
_remoteParticipants: activeParticipants,
|
||||
_resizableFilmstrip: false,
|
||||
_resizableFilmstrip: isFilmstripResizable(state) && _topPanelFilmstrip,
|
||||
_rows: gridDimensions.rows,
|
||||
_thumbnailWidth: thumbnailSize?.width,
|
||||
_thumbnailHeight: thumbnailSize?.height,
|
||||
_visible: visible,
|
||||
_topPanelFilmstrip,
|
||||
_verticalViewGrid: false,
|
||||
_verticalViewBackground: false
|
||||
};
|
||||
|
|
|
@ -37,6 +37,7 @@ import { togglePinStageParticipant } from '../../actions';
|
|||
import {
|
||||
DISPLAY_MODE_TO_CLASS_NAME,
|
||||
DISPLAY_VIDEO,
|
||||
FILMSTRIP_TYPE,
|
||||
SHOW_TOOLBAR_CONTEXT_MENU_AFTER,
|
||||
THUMBNAIL_TYPE,
|
||||
VIDEO_TEST_EVENTS
|
||||
|
@ -234,6 +235,11 @@ export type Props = {|
|
|||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* The type of filmstrip the tile is displayed in.
|
||||
*/
|
||||
filmstripType: string,
|
||||
|
||||
/**
|
||||
* The horizontal offset in px for the thumbnail. Used to center the thumbnails from the last row in tile view.
|
||||
*/
|
||||
|
@ -244,11 +250,6 @@ export type Props = {|
|
|||
*/
|
||||
participantID: ?string,
|
||||
|
||||
/**
|
||||
* Whether the tile is displayed in the stage filmstrip or not.
|
||||
*/
|
||||
stageFilmstrip: boolean,
|
||||
|
||||
/**
|
||||
* Styles that will be set to the Thumbnail's main span element.
|
||||
*/
|
||||
|
@ -993,7 +994,7 @@ class Thumbnail extends Component<Props, State> {
|
|||
_thumbnailType,
|
||||
_videoTrack,
|
||||
classes,
|
||||
stageFilmstrip
|
||||
filmstripType
|
||||
} = this.props;
|
||||
const { id } = _participant || {};
|
||||
const { isHovered, popoverVisible } = this.state;
|
||||
|
@ -1031,8 +1032,8 @@ class Thumbnail extends Component<Props, State> {
|
|||
<span
|
||||
className = { containerClassName }
|
||||
id = { local
|
||||
? `localVideoContainer${stageFilmstrip ? '_stage' : ''}`
|
||||
: `participant_${id}${stageFilmstrip ? '_stage' : ''}`
|
||||
? `localVideoContainer${filmstripType === FILMSTRIP_TYPE.MAIN ? '' : `_${filmstripType}`}`
|
||||
: `participant_${id}${filmstripType === FILMSTRIP_TYPE.MAIN ? '' : `_${filmstripType}`}`
|
||||
}
|
||||
{ ...(_isMobile
|
||||
? {
|
||||
|
@ -1168,7 +1169,7 @@ class Thumbnail extends Component<Props, State> {
|
|||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps): Object {
|
||||
const { participantID, stageFilmstrip } = ownProps;
|
||||
const { participantID, filmstripType = FILMSTRIP_TYPE.MAIN } = ownProps;
|
||||
|
||||
const participant = getParticipantByIdOrUndefined(state, participantID);
|
||||
const id = participant?.id;
|
||||
|
@ -1199,7 +1200,7 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
const { localFlipX } = state['features/base/settings'];
|
||||
const _isMobile = isMobileBrowser();
|
||||
const activeParticipants = getActiveParticipantsIds(state);
|
||||
const tileType = getThumbnailTypeFromLayout(_currentLayout, stageFilmstrip);
|
||||
const tileType = getThumbnailTypeFromLayout(_currentLayout, filmstripType);
|
||||
|
||||
switch (tileType) {
|
||||
case THUMBNAIL_TYPE.VERTICAL:
|
||||
|
@ -1244,7 +1245,8 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
const {
|
||||
stageFilmstripDimensions = {
|
||||
thumbnailSize: {}
|
||||
}
|
||||
},
|
||||
screenshareFilmstripDimensions
|
||||
} = state['features/filmstrip'];
|
||||
|
||||
size = {
|
||||
|
@ -1252,9 +1254,16 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
_height: thumbnailSize?.height
|
||||
};
|
||||
|
||||
if (stageFilmstrip) {
|
||||
if (filmstripType === FILMSTRIP_TYPE.STAGE) {
|
||||
const { width: _width, height: _height } = stageFilmstripDimensions.thumbnailSize;
|
||||
|
||||
size = {
|
||||
_width,
|
||||
_height
|
||||
};
|
||||
} else if (filmstripType === FILMSTRIP_TYPE.SCREENSHARE) {
|
||||
const { width: _width, height: _height } = screenshareFilmstripDimensions.thumbnailSize;
|
||||
|
||||
size = {
|
||||
_width,
|
||||
_height
|
||||
|
|
|
@ -7,7 +7,7 @@ import { getLocalParticipant } from '../../../base/participants';
|
|||
import { connect } from '../../../base/redux';
|
||||
import { shouldHideSelfView } from '../../../base/settings/functions.any';
|
||||
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
|
||||
import { TILE_ASPECT_RATIO, TILE_HORIZONTAL_MARGIN } from '../../constants';
|
||||
import { TILE_ASPECT_RATIO, TILE_HORIZONTAL_MARGIN, FILMSTRIP_TYPE } from '../../constants';
|
||||
import { showGridInVerticalView, getActiveParticipantsIds } from '../../functions';
|
||||
|
||||
import Thumbnail from './Thumbnail';
|
||||
|
@ -22,6 +22,11 @@ type Props = {
|
|||
*/
|
||||
_disableSelfView: boolean,
|
||||
|
||||
/**
|
||||
* The type of filmstrip this thumbnail is displayed in.
|
||||
*/
|
||||
_filmstripType: string,
|
||||
|
||||
/**
|
||||
* The horizontal offset in px for the thumbnail. Used to center the thumbnails in the last row in tile view.
|
||||
*/
|
||||
|
@ -37,11 +42,6 @@ type Props = {
|
|||
*/
|
||||
_isLocalScreenShare: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip is used a stage filmstrip.
|
||||
*/
|
||||
_stageFilmstrip: boolean,
|
||||
|
||||
/**
|
||||
* The width of the thumbnail. Used for expanding the width of the thumbnails on last row in case
|
||||
* there is empty space.
|
||||
|
@ -97,10 +97,10 @@ class ThumbnailWrapper extends Component<Props> {
|
|||
render() {
|
||||
const {
|
||||
_disableSelfView,
|
||||
_filmstripType = FILMSTRIP_TYPE.MAIN,
|
||||
_isLocalScreenShare = false,
|
||||
_horizontalOffset = 0,
|
||||
_participantID,
|
||||
_stageFilmstrip,
|
||||
_thumbnailWidth,
|
||||
style
|
||||
} = this.props;
|
||||
|
@ -112,9 +112,9 @@ class ThumbnailWrapper extends Component<Props> {
|
|||
if (_participantID === 'local') {
|
||||
return _disableSelfView ? null : (
|
||||
<Thumbnail
|
||||
filmstripType = { _filmstripType }
|
||||
horizontalOffset = { _horizontalOffset }
|
||||
key = 'local'
|
||||
stageFilmstrip = { _stageFilmstrip }
|
||||
style = { style }
|
||||
width = { _thumbnailWidth } />);
|
||||
}
|
||||
|
@ -122,20 +122,20 @@ class ThumbnailWrapper extends Component<Props> {
|
|||
if (_isLocalScreenShare) {
|
||||
return _disableSelfView ? null : (
|
||||
<Thumbnail
|
||||
filmstripType = { _filmstripType }
|
||||
horizontalOffset = { _horizontalOffset }
|
||||
key = 'localScreenShare'
|
||||
participantID = { _participantID }
|
||||
stageFilmstrip = { _stageFilmstrip }
|
||||
style = { style }
|
||||
width = { _thumbnailWidth } />);
|
||||
}
|
||||
|
||||
return (
|
||||
<Thumbnail
|
||||
filmstripType = { _filmstripType }
|
||||
horizontalOffset = { _horizontalOffset }
|
||||
key = { `remote_${_participantID}` }
|
||||
participantID = { _participantID }
|
||||
stageFilmstrip = { _stageFilmstrip }
|
||||
style = { style }
|
||||
width = { _thumbnailWidth } />);
|
||||
}
|
||||
|
@ -158,7 +158,8 @@ function _mapStateToProps(state, ownProps) {
|
|||
const enableThumbnailReordering = testing.enableThumbnailReordering ?? true;
|
||||
const sourceNameSignalingEnabled = getSourceNameSignalingFeatureFlag(state);
|
||||
const _verticalViewGrid = showGridInVerticalView(state);
|
||||
const stageFilmstrip = ownProps.data?.stageFilmstrip;
|
||||
const filmstripType = ownProps.data?.filmstripType;
|
||||
const stageFilmstrip = filmstripType === FILMSTRIP_TYPE.STAGE;
|
||||
const sortedActiveParticipants = activeParticipants.sort();
|
||||
const remoteParticipants = stageFilmstrip ? sortedActiveParticipants : remote;
|
||||
const remoteParticipantsLength = remoteParticipants.length;
|
||||
|
@ -235,9 +236,9 @@ function _mapStateToProps(state, ownProps) {
|
|||
if (stageFilmstrip) {
|
||||
return {
|
||||
_disableSelfView: disableSelfView,
|
||||
_filmstripType: filmstripType,
|
||||
_participantID: remoteParticipants[index] === localId ? 'local' : remoteParticipants[index],
|
||||
_horizontalOffset: horizontalOffset,
|
||||
_stageFilmstrip: stageFilmstrip,
|
||||
_thumbnailWidth: thumbnailWidth
|
||||
};
|
||||
}
|
||||
|
@ -260,6 +261,7 @@ function _mapStateToProps(state, ownProps) {
|
|||
if (!iAmRecorder && index === localIndex) {
|
||||
return {
|
||||
_disableSelfView: disableSelfView,
|
||||
_filmstripType: filmstripType,
|
||||
_participantID: 'local',
|
||||
_horizontalOffset: horizontalOffset,
|
||||
_thumbnailWidth: thumbnailWidth
|
||||
|
@ -269,6 +271,7 @@ function _mapStateToProps(state, ownProps) {
|
|||
if (sourceNameSignalingEnabled && !iAmRecorder && localScreenShare && index === localScreenShareIndex) {
|
||||
return {
|
||||
_disableSelfView: disableSelfView,
|
||||
_filmstripType: filmstripType,
|
||||
_isLocalScreenShare: true,
|
||||
_participantID: localScreenShare?.id,
|
||||
_horizontalOffset: horizontalOffset,
|
||||
|
@ -277,12 +280,22 @@ function _mapStateToProps(state, ownProps) {
|
|||
}
|
||||
|
||||
return {
|
||||
_filmstripType: filmstripType,
|
||||
_participantID: remoteParticipants[remoteIndex],
|
||||
_horizontalOffset: horizontalOffset,
|
||||
_thumbnailWidth: thumbnailWidth
|
||||
};
|
||||
}
|
||||
|
||||
if (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && filmstripType === FILMSTRIP_TYPE.SCREENSHARE) {
|
||||
const { remoteScreenShares } = state['features/video-layout'];
|
||||
|
||||
return {
|
||||
_filmstripType: filmstripType,
|
||||
_participantID: remoteScreenShares[remoteScreenShares.length - 1]
|
||||
};
|
||||
}
|
||||
|
||||
const { index } = ownProps;
|
||||
|
||||
if (typeof index !== 'number' || remoteParticipantsLength <= index) {
|
||||
|
|
|
@ -5,6 +5,7 @@ export { default as Filmstrip } from './Filmstrip';
|
|||
export { default as MainFilmstrip } from './MainFilmstrip';
|
||||
export { default as ModeratorIndicator } from './ModeratorIndicator';
|
||||
export { default as RaisedHandIndicator } from './RaisedHandIndicator';
|
||||
export { default as ScreenshareFilmstrip } from './ScreenshareFilmstrip';
|
||||
export { default as StageFilmstrip } from './StageFilmstrip';
|
||||
export { default as StatusIndicators } from './StatusIndicators';
|
||||
export { default as Thumbnail } from './Thumbnail';
|
||||
|
|
|
@ -23,6 +23,7 @@ export const styles = theme => {
|
|||
left: 'calc(50% - 16px)',
|
||||
opacity: 0,
|
||||
transition: 'opacity .3s',
|
||||
zIndex: 1,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.ui02
|
||||
|
@ -53,8 +54,18 @@ export const styles = theme => {
|
|||
top: 'calc(50% - 12px)'
|
||||
},
|
||||
|
||||
toggleTopPanelContainer: {
|
||||
transform: 'rotate(180deg)',
|
||||
bottom: 'calc(-24px - 6px)',
|
||||
top: 'auto'
|
||||
},
|
||||
|
||||
toggleTopPanelContainerHidden: {
|
||||
visibility: 'hidden'
|
||||
},
|
||||
|
||||
filmstrip: {
|
||||
transition: 'background .2s ease-in-out, right 1s, bottom 1s, height .3s ease-in',
|
||||
transition: 'background .2s ease-in-out, right 1s, bottom 1s, top 1s, height .3s ease-in',
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
|
||||
|
@ -111,6 +122,10 @@ export const styles = theme => {
|
|||
'& .avatar-container': {
|
||||
maxWidth: 'initial',
|
||||
maxHeight: 'initial'
|
||||
},
|
||||
|
||||
'&.top-panel-filmstrip': {
|
||||
flexDirection: 'column'
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -137,6 +152,18 @@ export const styles = theme => {
|
|||
'& .dragHandle': {
|
||||
backgroundColor: theme.palette.icon01
|
||||
}
|
||||
},
|
||||
|
||||
'&.top-panel': {
|
||||
order: 2,
|
||||
width: '100%',
|
||||
height: '9px',
|
||||
cursor: 'row-resize',
|
||||
|
||||
'& .dragHandle': {
|
||||
height: '3px',
|
||||
width: '100px'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -281,6 +281,12 @@ export const FILMSTRIP_GRID_BREAKPOINT = 300;
|
|||
*/
|
||||
export const FILMSTRIP_BREAKPOINT_OFFSET = 5;
|
||||
|
||||
/**
|
||||
* The minimum height for the stage view
|
||||
* (used to determine the maximum height of the user-resizable top panel).
|
||||
*/
|
||||
export const MIN_STAGE_VIEW_HEIGHT = 700;
|
||||
|
||||
/**
|
||||
* The minimum width for the stage view
|
||||
* (used to determine the maximum width of the user-resizable vertical filmstrip).
|
||||
|
@ -298,7 +304,21 @@ export const VERTICAL_VIEW_HORIZONTAL_MARGIN = VERTICAL_FILMSTRIP_MIN_HORIZONTAL
|
|||
*/
|
||||
export const ACTIVE_PARTICIPANT_TIMEOUT = 1000 * 60;
|
||||
|
||||
/**
|
||||
* The types of filmstrip.
|
||||
*/
|
||||
export const FILMSTRIP_TYPE = {
|
||||
MAIN: 'main',
|
||||
STAGE: 'stage',
|
||||
SCREENSHARE: 'screenshare'
|
||||
};
|
||||
|
||||
/**
|
||||
* The max number of participants to be displayed on the stage filmstrip.
|
||||
*/
|
||||
export const MAX_ACTIVE_PARTICIPANTS = 6;
|
||||
|
||||
/**
|
||||
* Top filmstrip default height.
|
||||
*/
|
||||
export const TOP_FILMSTRIP_HEIGHT = 180;
|
||||
|
|
|
@ -6,6 +6,7 @@ import { MEDIA_TYPE } from '../base/media';
|
|||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantById,
|
||||
getParticipantCount,
|
||||
getParticipantCountWithFake,
|
||||
getPinnedParticipant
|
||||
} from '../base/participants';
|
||||
|
@ -32,6 +33,7 @@ import {
|
|||
DISPLAY_AVATAR,
|
||||
DISPLAY_VIDEO,
|
||||
FILMSTRIP_GRID_BREAKPOINT,
|
||||
FILMSTRIP_TYPE,
|
||||
INDICATORS_TOOLTIP_POSITION,
|
||||
SCROLL_SIZE,
|
||||
SQUARE_TILE_ASPECT_RATIO,
|
||||
|
@ -296,7 +298,8 @@ export function calculateResponsiveTileViewDimensions({
|
|||
noHorizontalContainerMargin = false,
|
||||
maxColumns,
|
||||
numberOfParticipants,
|
||||
desiredNumberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES
|
||||
desiredNumberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES,
|
||||
minTileHeight
|
||||
}) {
|
||||
let height, width;
|
||||
let columns, rows;
|
||||
|
@ -324,7 +327,8 @@ export function calculateResponsiveTileViewDimensions({
|
|||
clientHeight,
|
||||
disableTileEnlargement,
|
||||
disableResponsiveTiles: false,
|
||||
noHorizontalContainerMargin
|
||||
noHorizontalContainerMargin,
|
||||
minTileHeight
|
||||
});
|
||||
|
||||
if (size) {
|
||||
|
@ -413,10 +417,11 @@ export function calculateThumbnailSizeForTileView({
|
|||
clientHeight,
|
||||
disableResponsiveTiles = false,
|
||||
disableTileEnlargement = false,
|
||||
noHorizontalContainerMargin = false
|
||||
noHorizontalContainerMargin = false,
|
||||
minTileHeight
|
||||
}: Object) {
|
||||
const aspectRatio = getTileDefaultAspectRatio(disableResponsiveTiles, disableTileEnlargement, clientWidth);
|
||||
const minHeight = getThumbnailMinHeight(clientWidth);
|
||||
const minHeight = minTileHeight || getThumbnailMinHeight(clientWidth);
|
||||
const viewWidth = clientWidth - (columns * TILE_HORIZONTAL_MARGIN)
|
||||
- (noHorizontalContainerMargin ? SCROLL_SIZE : TILE_VIEW_GRID_HORIZONTAL_MARGIN);
|
||||
const availableHeight = clientHeight - TILE_VIEW_GRID_VERTICAL_MARGIN;
|
||||
|
@ -506,6 +511,7 @@ export function getVerticalFilmstripVisibleAreaWidth() {
|
|||
*/
|
||||
export function computeDisplayModeFromInput(input: Object) {
|
||||
const {
|
||||
filmstripType,
|
||||
isActiveParticipant,
|
||||
isAudioOnly,
|
||||
isCurrentlyOnLargeVideo,
|
||||
|
@ -515,7 +521,6 @@ export function computeDisplayModeFromInput(input: Object) {
|
|||
isRemoteParticipant,
|
||||
multipleVideoSupport,
|
||||
stageParticipantsVisible,
|
||||
stageFilmstrip,
|
||||
tileViewActive
|
||||
} = input;
|
||||
const adjustedIsVideoPlayable = input.isVideoPlayable && (!isRemoteParticipant || canPlayEventReceived);
|
||||
|
@ -534,8 +539,8 @@ export function computeDisplayModeFromInput(input: Object) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!tileViewActive && ((isScreenSharing && isRemoteParticipant)
|
||||
|| (stageParticipantsVisible && isActiveParticipant && !stageFilmstrip))) {
|
||||
if (!tileViewActive && filmstripType === FILMSTRIP_TYPE.MAIN && ((isScreenSharing && isRemoteParticipant)
|
||||
|| (stageParticipantsVisible && isActiveParticipant))) {
|
||||
return DISPLAY_AVATAR;
|
||||
} else if (isCurrentlyOnLargeVideo && !tileViewActive) {
|
||||
// Display name is always and only displayed when user is on the stage
|
||||
|
@ -569,12 +574,13 @@ export function getDisplayModeInput(props: Object, state: Object) {
|
|||
_participant,
|
||||
_stageParticipantsVisible,
|
||||
_videoTrack,
|
||||
stageFilmstrip
|
||||
filmstripType = FILMSTRIP_TYPE.MAIN
|
||||
} = props;
|
||||
const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
|
||||
const { canPlayEventReceived } = state;
|
||||
|
||||
return {
|
||||
filmstripType,
|
||||
isActiveParticipant: _isActiveParticipant,
|
||||
isCurrentlyOnLargeVideo: _isCurrentlyOnLargeVideo,
|
||||
isAudioOnly: _isAudioOnly,
|
||||
|
@ -588,7 +594,6 @@ export function getDisplayModeInput(props: Object, state: Object) {
|
|||
isVirtualScreenshareParticipant: _isVirtualScreenshareParticipant,
|
||||
multipleVideoSupport: _multipleVideoSupport,
|
||||
stageParticipantsVisible: _stageParticipantsVisible,
|
||||
stageFilmstrip,
|
||||
videoStreamMuted: _videoTrack ? _videoTrack.muted : 'no stream'
|
||||
};
|
||||
}
|
||||
|
@ -717,8 +722,24 @@ export function isStageFilmstripAvailable(state, minParticipantCount = 0) {
|
|||
const { remoteScreenShares } = state['features/video-layout'];
|
||||
const sharedVideo = isSharingStatus(state['features/shared-video']?.status);
|
||||
|
||||
return isStageFilmstripEnabled(state) && remoteScreenShares.length === 0 && !sharedVideo
|
||||
&& activeParticipants.length >= minParticipantCount;
|
||||
return isStageFilmstripEnabled(state) && !sharedVideo
|
||||
&& activeParticipants.length >= minParticipantCount
|
||||
&& (isTopPanelEnabled(state) || remoteScreenShares.length === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the stage filmstrip should be displayed on the top.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @param {number} minParticipantCount - The min number of participants for the stage filmstrip
|
||||
* to be displayed.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isStageFilmstripTopPanel(state, minParticipantCount = 0) {
|
||||
const { remoteScreenShares } = state['features/video-layout'];
|
||||
|
||||
return isTopPanelEnabled(state)
|
||||
&& isStageFilmstripAvailable(state, minParticipantCount) && remoteScreenShares.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -737,10 +758,10 @@ export function isStageFilmstripEnabled(state) {
|
|||
* Gets the thumbnail type by filmstrip type.
|
||||
*
|
||||
* @param {string} currentLayout - Current app layout.
|
||||
* @param {boolean} isStageFilmstrip - Whether the filmstrip is stage filmstrip or not.
|
||||
* @param {string} filmstripType - The current filmstrip type.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getThumbnailTypeFromLayout(currentLayout, isStageFilmstrip = false) {
|
||||
export function getThumbnailTypeFromLayout(currentLayout, filmstripType) {
|
||||
switch (currentLayout) {
|
||||
case LAYOUTS.TILE_VIEW:
|
||||
return THUMBNAIL_TYPE.TILE;
|
||||
|
@ -749,10 +770,24 @@ export function getThumbnailTypeFromLayout(currentLayout, isStageFilmstrip = fal
|
|||
case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW:
|
||||
return THUMBNAIL_TYPE.HORIZONTAL;
|
||||
case LAYOUTS.STAGE_FILMSTRIP_VIEW:
|
||||
if (isStageFilmstrip) {
|
||||
if (filmstripType !== FILMSTRIP_TYPE.MAIN) {
|
||||
return THUMBNAIL_TYPE.TILE;
|
||||
}
|
||||
|
||||
return THUMBNAIL_TYPE.VERTICAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the top panel is enabled.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isTopPanelEnabled(state) {
|
||||
const { filmstrip } = state['features/base/config'];
|
||||
const participantsCount = getParticipantCount(state);
|
||||
|
||||
return !filmstrip?.disableTopPanel && participantsCount >= (filmstrip?.minParticipantCountForTopPanel ?? 50);
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import {
|
|||
import {
|
||||
addStageParticipant,
|
||||
removeStageParticipant,
|
||||
setFilmstripHeight,
|
||||
setFilmstripWidth,
|
||||
setStageParticipants
|
||||
} from './actions';
|
||||
|
@ -38,7 +39,9 @@ import {
|
|||
ACTIVE_PARTICIPANT_TIMEOUT,
|
||||
DEFAULT_FILMSTRIP_WIDTH,
|
||||
MAX_ACTIVE_PARTICIPANTS,
|
||||
MIN_STAGE_VIEW_WIDTH
|
||||
MIN_STAGE_VIEW_HEIGHT,
|
||||
MIN_STAGE_VIEW_WIDTH,
|
||||
TOP_FILMSTRIP_HEIGHT
|
||||
} from './constants';
|
||||
import {
|
||||
isFilmstripResizable,
|
||||
|
@ -77,19 +80,27 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
const state = store.getState();
|
||||
|
||||
if (isFilmstripResizable(state)) {
|
||||
const { width: filmstripWidth } = state['features/filmstrip'];
|
||||
const { clientWidth } = action;
|
||||
let width;
|
||||
const { width: filmstripWidth, topPanelHeight } = state['features/filmstrip'];
|
||||
const { clientWidth, clientHeight } = action;
|
||||
let height, width;
|
||||
|
||||
if (filmstripWidth.current > clientWidth - MIN_STAGE_VIEW_WIDTH) {
|
||||
width = Math.max(clientWidth - MIN_STAGE_VIEW_WIDTH, DEFAULT_FILMSTRIP_WIDTH);
|
||||
} else {
|
||||
width = Math.min(clientWidth - MIN_STAGE_VIEW_WIDTH, filmstripWidth.userSet);
|
||||
}
|
||||
|
||||
if (width !== filmstripWidth.current) {
|
||||
store.dispatch(setFilmstripWidth(width));
|
||||
}
|
||||
|
||||
if (topPanelHeight.current > clientHeight - MIN_STAGE_VIEW_HEIGHT) {
|
||||
height = Math.max(clientHeight - MIN_STAGE_VIEW_HEIGHT, TOP_FILMSTRIP_HEIGHT);
|
||||
} else {
|
||||
height = Math.min(clientHeight - MIN_STAGE_VIEW_HEIGHT, topPanelHeight.userSet);
|
||||
}
|
||||
if (height !== topPanelHeight.current) {
|
||||
store.dispatch(setFilmstripHeight(height));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,11 @@ import {
|
|||
SET_VISIBLE_REMOTE_PARTICIPANTS,
|
||||
SET_VOLUME,
|
||||
SET_MAX_STAGE_PARTICIPANTS,
|
||||
CLEAR_STAGE_PARTICIPANTS
|
||||
CLEAR_STAGE_PARTICIPANTS,
|
||||
SET_SCREENSHARING_TILE_DIMENSIONS,
|
||||
SET_USER_FILMSTRIP_HEIGHT,
|
||||
SET_FILMSTRIP_HEIGHT,
|
||||
SET_TOP_PANEL_VISIBILITY
|
||||
} from './actionTypes';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
|
@ -76,6 +80,11 @@ const DEFAULT_STATE = {
|
|||
*/
|
||||
remoteParticipants: [],
|
||||
|
||||
/**
|
||||
* The dimensions of the screenshare filmstrip.
|
||||
*/
|
||||
screenshareFilmstripDimensions: {},
|
||||
|
||||
/**
|
||||
* The stage filmstrip view dimensions.
|
||||
*
|
||||
|
@ -92,6 +101,27 @@ const DEFAULT_STATE = {
|
|||
*/
|
||||
tileViewDimensions: {},
|
||||
|
||||
/**
|
||||
* The height of the resizable top panel.
|
||||
*/
|
||||
topPanelHeight: {
|
||||
/**
|
||||
* Current height. Affected by: user top panel resize,
|
||||
* window resize.
|
||||
*/
|
||||
current: null,
|
||||
|
||||
/**
|
||||
* Height set by user resize. Used as the preferred height.
|
||||
*/
|
||||
userSet: null
|
||||
},
|
||||
|
||||
/**
|
||||
* The indicator determines if the top panel is visible.
|
||||
*/
|
||||
topPanelVisible: true,
|
||||
|
||||
/**
|
||||
* The vertical view dimensions.
|
||||
*
|
||||
|
@ -227,6 +257,15 @@ ReducerRegistry.register(
|
|||
...state
|
||||
};
|
||||
}
|
||||
case SET_FILMSTRIP_HEIGHT:{
|
||||
return {
|
||||
...state,
|
||||
topPanelHeight: {
|
||||
...state.topPanelHeight,
|
||||
current: action.height
|
||||
}
|
||||
};
|
||||
}
|
||||
case SET_FILMSTRIP_WIDTH: {
|
||||
return {
|
||||
...state,
|
||||
|
@ -236,6 +275,17 @@ ReducerRegistry.register(
|
|||
}
|
||||
};
|
||||
}
|
||||
case SET_USER_FILMSTRIP_HEIGHT: {
|
||||
const { height } = action;
|
||||
|
||||
return {
|
||||
...state,
|
||||
topPanelHeight: {
|
||||
current: height,
|
||||
userSet: height
|
||||
}
|
||||
};
|
||||
}
|
||||
case SET_USER_FILMSTRIP_WIDTH: {
|
||||
const { width } = action;
|
||||
|
||||
|
@ -283,6 +333,18 @@ ReducerRegistry.register(
|
|||
activeParticipants: []
|
||||
};
|
||||
}
|
||||
case SET_SCREENSHARING_TILE_DIMENSIONS: {
|
||||
return {
|
||||
...state,
|
||||
screenshareFilmstripDimensions: action.dimensions
|
||||
};
|
||||
}
|
||||
case SET_TOP_PANEL_VISIBILITY: {
|
||||
return {
|
||||
...state,
|
||||
topPanelVisible: action.visible
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
@ -14,6 +14,7 @@ import { getCurrentLayout, shouldDisplayTileView, LAYOUTS } from '../video-layou
|
|||
import {
|
||||
clearStageParticipants,
|
||||
setHorizontalViewDimensions,
|
||||
setScreensharingTileDimensions,
|
||||
setStageFilmstripViewDimensions,
|
||||
setTileViewDimensions,
|
||||
setVerticalViewDimensions
|
||||
|
@ -23,7 +24,8 @@ import {
|
|||
DISPLAY_DRAWER_THRESHOLD
|
||||
} from './constants';
|
||||
import {
|
||||
isFilmstripResizable
|
||||
isFilmstripResizable,
|
||||
isTopPanelEnabled
|
||||
} from './functions';
|
||||
|
||||
import './subscriber.any';
|
||||
|
@ -176,7 +178,8 @@ StateListenerRegistry.register(
|
|||
visible: state['features/filmstrip'].visible,
|
||||
clientWidth: state['features/base/responsive-ui'].clientWidth,
|
||||
clientHeight: state['features/base/responsive-ui'].clientHeight,
|
||||
tileView: state['features/video-layout'].tileViewEnabled
|
||||
tileView: state['features/video-layout'].tileViewEnabled,
|
||||
height: state['features/filmstrip'].topPanelHeight?.current
|
||||
};
|
||||
},
|
||||
/* listener */(_, store) => {
|
||||
|
@ -198,3 +201,27 @@ StateListenerRegistry.register(
|
|||
store.dispatch(selectParticipantInLargeVideo());
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Listens for changes to determine the size of the screenshare filmstrip.
|
||||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => {
|
||||
return {
|
||||
length: state['features/video-layout'].remoteScreenShares.length,
|
||||
clientWidth: state['features/base/responsive-ui'].clientWidth,
|
||||
clientHeight: state['features/base/responsive-ui'].clientHeight,
|
||||
height: state['features/filmstrip'].topPanelHeight?.current,
|
||||
width: state['features/filmstrip'].width?.current,
|
||||
visible: state['features/filmstrip'].visible,
|
||||
topPanelVisible: state['features/filmstrip'].topPanelVisible
|
||||
};
|
||||
},
|
||||
/* listener */({ length }, store) => {
|
||||
if (length >= 1 && isTopPanelEnabled(store.getState())) {
|
||||
store.dispatch(setScreensharingTileDimensions());
|
||||
}
|
||||
}, {
|
||||
deepEquals: true
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue