From 21cf7f23c2b71eab5c36ff2f6c8c9a33fbd10953 Mon Sep 17 00:00:00 2001 From: Robert Pintilii Date: Wed, 29 Jun 2022 16:59:49 +0300 Subject: [PATCH] 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) --- config.js | 12 +- lang/main.json | 1 + package-lock.json | 12 +- .../conference/components/constants.js | 3 +- .../conference/components/web/Conference.js | 3 +- .../components/web/ConferenceInfo.js | 5 + .../components/web/ToggleTopPanelLabel.tsx | 31 +++ react/features/filmstrip/actionTypes.ts | 34 +++ react/features/filmstrip/actions.web.js | 91 ++++++- .../filmstrip/components/web/Filmstrip.js | 225 +++++++++++++----- .../filmstrip/components/web/MainFilmstrip.js | 20 +- .../components/web/ScreenshareFilmstrip.js | 125 ++++++++++ .../components/web/StageFilmstrip.js | 18 +- .../filmstrip/components/web/Thumbnail.js | 33 ++- .../components/web/ThumbnailWrapper.js | 37 ++- .../filmstrip/components/web/index.js | 1 + .../filmstrip/components/web/styles.js | 29 ++- react/features/filmstrip/constants.js | 20 ++ react/features/filmstrip/functions.web.js | 63 +++-- react/features/filmstrip/middleware.web.js | 21 +- react/features/filmstrip/reducer.js | 64 ++++- react/features/filmstrip/subscriber.web.js | 31 ++- 22 files changed, 740 insertions(+), 139 deletions(-) create mode 100644 react/features/conference/components/web/ToggleTopPanelLabel.tsx create mode 100644 react/features/filmstrip/components/web/ScreenshareFilmstrip.js diff --git a/config.js b/config.js index c0e1c9a58..ad7185aec 100644 --- a/config.js +++ b/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. diff --git a/lang/main.json b/lang/main.json index 7b80c7248..123818672 100644 --- a/lang/main.json +++ b/lang/main.json @@ -1033,6 +1033,7 @@ "termsView": { "header": "Terms" }, + "toggleTopPanelLabel": "Toggle top panel", "toolbar": { "Settings": "Settings", "accessibilityLabel": { diff --git a/package-lock.json b/package-lock.json index 48dc671e6..708697693 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" } diff --git a/react/features/conference/components/constants.js b/react/features/conference/components/constants.js index 243d92e6d..914ba8a94 100644 --- a/react/features/conference/components/constants.js +++ b/react/features/conference/components/constants.js @@ -8,6 +8,7 @@ export const CONFERENCE_INFO = { 'e2ee', 'transcribing', 'video-quality', - 'insecure-room' + 'insecure-room', + 'top-panel-toggle' ] }; diff --git a/react/features/conference/components/web/Conference.js b/react/features/conference/components/web/Conference.js index 90be0b0b5..450856447 100644 --- a/react/features/conference/components/web/Conference.js +++ b/react/features/conference/components/web/Conference.js @@ -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 { { _showPrejoin || _showLobby || (<> + ) } diff --git a/react/features/conference/components/web/ConferenceInfo.js b/react/features/conference/components/web/ConferenceInfo.js index a911f954a..96ecfbfa1 100644 --- a/react/features/conference/components/web/ConferenceInfo.js +++ b/react/features/conference/components/web/ConferenceInfo.js @@ -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' } ]; diff --git a/react/features/conference/components/web/ToggleTopPanelLabel.tsx b/react/features/conference/components/web/ToggleTopPanelLabel.tsx new file mode 100644 index 000000000..f19e2c885 --- /dev/null +++ b/react/features/conference/components/web/ToggleTopPanelLabel.tsx @@ -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 && ( + ); +}; + +export default ToggleTopPanelLabel; diff --git a/react/features/filmstrip/actionTypes.ts b/react/features/filmstrip/actionTypes.ts index f3c22ab9a..d18de4929 100644 --- a/react/features/filmstrip/actionTypes.ts +++ b/react/features/filmstrip/actionTypes.ts @@ -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'; diff --git a/react/features/filmstrip/actions.web.js b/react/features/filmstrip/actions.web.js index c3e327f3b..53cf09f77 100644 --- a/react/features/filmstrip/actions.web.js +++ b/react/features/filmstrip/actions.web.js @@ -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, 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 + }; +} diff --git a/react/features/filmstrip/components/web/Filmstrip.js b/react/features/filmstrip/components/web/Filmstrip.js index 6a5f00549..cafd11c87 100644 --- a/react/features/filmstrip/components/web/Filmstrip.js +++ b/react/features/filmstrip/components/web/Filmstrip.js @@ -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, + /** + * 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 { _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 { 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 = (<>
{!_disableSelfView && !_verticalViewGrid && ( @@ -348,8 +407,10 @@ class Filmstrip extends PureComponent { className = 'filmstrip__videos' id = 'filmstripLocalVideo'> { - !tileViewActive && !_stageFilmstrip &&
+ !tileViewActive && filmstripType === FILMSTRIP_TYPE.MAIN + &&
} @@ -361,10 +422,9 @@ class Filmstrip extends PureComponent { id = 'filmstripLocalScreenShare'>
{ - !tileViewActive && !_stageFilmstrip && - }
@@ -385,11 +445,14 @@ class Filmstrip extends PureComponent { style = { filmstripStyle }> { toolbar } {_resizableFilmstrip - ?
+ ?
@@ -412,10 +475,13 @@ class Filmstrip extends PureComponent { * @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 { */ _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 { * @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 { _remoteParticipantsLength, _resizableFilmstrip, _rows, - _stageFilmstrip, _thumbnailHeight, _thumbnailWidth, - _verticalViewGrid + _verticalViewGrid, + filmstripType } = this.props; if (!_thumbnailWidth || isNaN(_thumbnailWidth) || !_thumbnailHeight @@ -617,7 +703,7 @@ class Filmstrip extends PureComponent { return null; } - if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid || _stageFilmstrip) { + if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid || filmstripType !== FILMSTRIP_TYPE.MAIN) { return ( { 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 { * @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 { sendAnalytics(createShortcutEvent( 'toggle.filmstrip', { - enable: this.props._visible + enable: this.props._mainFilmstripVisible })); this._doToggleFilmstrip(); @@ -729,7 +819,7 @@ class Filmstrip extends PureComponent { sendAnalytics(createToolbarEvent( 'toggle.filmstrip.button', { - enable: this.props._visible + enable: this.props._mainFilmstripVisible })); this._doToggleFilmstrip(); @@ -758,8 +848,15 @@ class Filmstrip extends PureComponent { * @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 {