diff --git a/css/filmstrip/_tile_view.scss b/css/filmstrip/_tile_view.scss index 339c22687..4bc64ef71 100644 --- a/css/filmstrip/_tile_view.scss +++ b/css/filmstrip/_tile_view.scss @@ -73,6 +73,10 @@ display: block; } + .filmstrip__videos.has-scroll { + padding-left: 7px; + } + .remote-videos { box-sizing: border-box; @@ -90,7 +94,6 @@ margin-top: auto; margin-bottom: auto; justify-content: center; - position: absolute; .videocontainer { border: 0; diff --git a/react/features/base/config/configWhitelist.js b/react/features/base/config/configWhitelist.js index 7bb49d9ab..07d13d9c9 100644 --- a/react/features/base/config/configWhitelist.js +++ b/react/features/base/config/configWhitelist.js @@ -216,6 +216,7 @@ export default [ 'testing', 'toolbarButtons', 'toolbarConfig', + 'tileView', 'transcribingEnabled', 'useHostPageLocalStorage', 'useTurnUdp', diff --git a/react/features/filmstrip/actions.web.js b/react/features/filmstrip/actions.web.js index 774343d60..4f85bb8e6 100644 --- a/react/features/filmstrip/actions.web.js +++ b/react/features/filmstrip/actions.web.js @@ -1,9 +1,13 @@ // @flow import type { Dispatch } from 'redux'; -import { getLocalParticipant, getParticipantById, pinParticipant } from '../base/participants'; +import { + getLocalParticipant, + getParticipantById, + pinParticipant +} from '../base/participants'; import { shouldHideSelfView } from '../base/settings/functions.any'; -import { getTileViewGridDimensions } from '../video-layout'; +import { getMaxColumnCount } from '../video-layout'; import { SET_FILMSTRIP_WIDTH, @@ -21,15 +25,20 @@ import { TILE_HORIZONTAL_MARGIN, 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, VERTICAL_FILMSTRIP_VERTICAL_MARGIN } from './constants'; import { + calculateNotResponsiveTileViewDimensions, + calculateResponsiveTileViewDimensions, calculateThumbnailSizeForHorizontalView, - calculateThumbnailSizeForTileView, calculateThumbnailSizeForVerticalView, isFilmstripResizable, showGridInVerticalView } from './functions'; +import { getNumberOfPartipantsForTileView } from './functions.web'; export * from './actions.any'; @@ -41,37 +50,56 @@ export * from './actions.any'; * resolved to Redux state using the {@code toState} function. * @returns {Function} */ -export function setTileViewDimensions(dimensions: Object) { +export function setTileViewDimensions() { return (dispatch: Dispatch, getState: Function) => { const state = getState(); const { clientHeight, clientWidth } = state['features/base/responsive-ui']; - const { disableResponsiveTiles, disableTileEnlargement } = state['features/base/config']; + const { + disableResponsiveTiles, + disableTileEnlargement, + tileView = {} + } = state['features/base/config']; + const { numberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES } = tileView; + const numberOfParticipants = getNumberOfPartipantsForTileView(state); + const maxColumns = getMaxColumnCount(state); + const { height, - width - } = calculateThumbnailSizeForTileView({ - ...dimensions, - clientWidth, - clientHeight, - disableResponsiveTiles, - disableTileEnlargement - }); - const { columns, rows } = dimensions; + width, + columns, + rows + } = disableResponsiveTiles + ? calculateNotResponsiveTileViewDimensions(state) + : calculateResponsiveTileViewDimensions({ + clientWidth, + clientHeight, + disableTileEnlargement, + isVerticalFilmstrip: false, + maxColumns, + numberOfParticipants, + numberOfVisibleTiles + }); const thumbnailsTotalHeight = rows * (TILE_VERTICAL_MARGIN + height); const hasScroll = clientHeight < thumbnailsTotalHeight; - const filmstripWidth = (columns * (TILE_HORIZONTAL_MARGIN + width)) + (hasScroll ? SCROLL_SIZE : 0); - const filmstripHeight = Math.min(clientHeight, thumbnailsTotalHeight); + const filmstripWidth + = Math.min(clientWidth - TILE_VIEW_GRID_HORIZONTAL_MARGIN, columns * (TILE_HORIZONTAL_MARGIN + width)) + + (hasScroll ? SCROLL_SIZE : 0); + const filmstripHeight = Math.min(clientHeight - TILE_VIEW_GRID_VERTICAL_MARGIN, thumbnailsTotalHeight); dispatch({ type: SET_TILE_VIEW_DIMENSIONS, dimensions: { - gridDimensions: dimensions, + gridDimensions: { + columns, + rows + }, thumbnailSize: { height, width }, filmstripHeight, - filmstripWidth + filmstripWidth, + hasScroll } }); }; @@ -97,26 +125,34 @@ export function setVerticalViewDimensions() { // grid view in the vertical filmstrip if (_verticalViewGrid) { - const dimensions = getTileViewGridDimensions(state, filmstripWidth.current); + const { tileView = {} } = state['features/base/config']; + const { numberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES } = tileView; + const numberOfParticipants = getNumberOfPartipantsForTileView(state); + const maxColumns = getMaxColumnCount(state); const { height, - width - } = calculateThumbnailSizeForTileView({ - ...dimensions, + width, + columns, + rows + } = calculateResponsiveTileViewDimensions({ clientWidth: filmstripWidth.current, clientHeight, - disableResponsiveTiles: false, disableTileEnlargement: false, - isVerticalFilmstrip: true + isVerticalFilmstrip: true, + maxColumns, + numberOfParticipants, + numberOfVisibleTiles }); - const { columns, rows } = dimensions; const thumbnailsTotalHeight = rows * (TILE_VERTICAL_MARGIN + height); const hasScroll = clientHeight < thumbnailsTotalHeight; const widthOfFilmstrip = (columns * (TILE_HORIZONTAL_MARGIN + width)) + (hasScroll ? SCROLL_SIZE : 0); const filmstripHeight = Math.min(clientHeight, thumbnailsTotalHeight); gridView = { - gridDimensions: dimensions, + gridDimensions: { + columns, + rows + }, thumbnailSize: { height, width diff --git a/react/features/filmstrip/components/web/Filmstrip.js b/react/features/filmstrip/components/web/Filmstrip.js index 25b851bc7..6280ab8da 100644 --- a/react/features/filmstrip/components/web/Filmstrip.js +++ b/react/features/filmstrip/components/web/Filmstrip.js @@ -767,6 +767,7 @@ function _mapStateToProps(state) { gridDimensions: dimensions = {}, filmstripHeight, filmstripWidth, + hasScroll = false, thumbnailSize: tileViewThumbnailSize } = state['features/filmstrip'].tileViewDimensions; const _currentLayout = getCurrentLayout(state); @@ -796,7 +797,7 @@ function _mapStateToProps(state) { const shouldReduceHeight = reduceHeight && ( isMobileBrowser() || _currentLayout !== LAYOUTS.VERTICAL_FILMSTRIP_VIEW); - const videosClassName = `filmstrip__videos${visible ? '' : ' hidden'}`; + let videosClassName = `filmstrip__videos${visible ? '' : ' hidden'}`; const className = `${remoteVideosVisible || _verticalViewGrid ? '' : 'hide-videos'} ${ shouldReduceHeight ? 'reduce-height' : '' } ${shiftRight ? 'shift-right' : ''} ${collapseTileView ? 'collapse' : ''} ${visible ? '' : 'hidden'}`.trim(); @@ -804,6 +805,9 @@ function _mapStateToProps(state) { switch (_currentLayout) { case LAYOUTS.TILE_VIEW: + if (hasScroll) { + videosClassName += ' has-scroll'; + } _thumbnailSize = tileViewThumbnailSize; remoteFilmstripHeight = filmstripHeight - (collapseTileView && filmstripPadding > 0 ? filmstripPadding : 0); remoteFilmstripWidth = filmstripWidth; diff --git a/react/features/filmstrip/constants.js b/react/features/filmstrip/constants.js index 3e6a9d773..dd4923d6d 100644 --- a/react/features/filmstrip/constants.js +++ b/react/features/filmstrip/constants.js @@ -28,16 +28,6 @@ export const SQUARE_TILE_ASPECT_RATIO = 1; */ export const DISPLAY_DRAWER_THRESHOLD = 512; -/** - * Breakpoint past which a single column view is enforced in tile view. - */ -export const SINGLE_COLUMN_BREAKPOINT = 300; - -/** - * Breakpoint past which a two column view is enforced in tile view. - */ -export const TWO_COLUMN_BREAKPOINT = 1000; - /** * Breakpoint past which the aspect ratio is switched in tile view. * Also, past this breakpoint, if there are two participants in the conference, we enforce @@ -57,9 +47,14 @@ export const TILE_MIN_HEIGHT_SMALL = 150; export const TILE_MIN_HEIGHT_LARGE = 200; /** - * Aspect ratio for portrait tiles. (height / width). + * Aspect ratio for portrait tiles. */ -export const TILE_PORTRAIT_ASPECT_RATIO = 1.3; +export const TILE_PORTRAIT_ASPECT_RATIO = 1 / 1.3; + +/** + * The default number of visible tiles for tile view. + */ +export const TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES = 25; /** * The default number of columns for tile view. @@ -150,14 +145,14 @@ export const TILE_VERTICAL_CONTAINER_HORIZONTAL_MARGIN = 2; * * @type {number} */ -export const TILE_VIEW_GRID_VERTICAL_MARGIN = 12; +export const TILE_VIEW_GRID_VERTICAL_MARGIN = 14; /** * The horizontal margin of the tile grid container. * * @type {number} */ -export const TILE_VIEW_GRID_HORIZONTAL_MARGIN = 12; +export const TILE_VIEW_GRID_HORIZONTAL_MARGIN = 14; /** * The height of the whole toolbar. diff --git a/react/features/filmstrip/functions.web.js b/react/features/filmstrip/functions.web.js index 78f1b4ed0..81ee7a8ce 100644 --- a/react/features/filmstrip/functions.web.js +++ b/react/features/filmstrip/functions.web.js @@ -10,6 +10,7 @@ import { getPinnedParticipant } from '../base/participants'; import { toState } from '../base/redux'; +import { shouldHideSelfView } from '../base/settings/functions.any'; import { getLocalVideoTrack, getTrackByMediaTypeAndParticipant, @@ -17,7 +18,11 @@ import { isRemoteTrackMuted } from '../base/tracks/functions'; import { isTrackStreamingStatusActive, isParticipantConnectionStatusActive } from '../connection-indicator/functions'; -import { getCurrentLayout, LAYOUTS } from '../video-layout'; +import { + getCurrentLayout, + getNotResponsiveTileViewGridDimensions, + LAYOUTS +} from '../video-layout'; import { ASPECT_RATIO_BREAKPOINT, @@ -27,7 +32,6 @@ import { DISPLAY_VIDEO, FILMSTRIP_GRID_BREAKPOINT, INDICATORS_TOOLTIP_POSITION, - SCROLL_SIZE, SQUARE_TILE_ASPECT_RATIO, TILE_ASPECT_RATIO, TILE_HORIZONTAL_MARGIN, @@ -35,6 +39,7 @@ import { TILE_MIN_HEIGHT_SMALL, TILE_PORTRAIT_ASPECT_RATIO, TILE_VERTICAL_MARGIN, + TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES, TILE_VIEW_GRID_HORIZONTAL_MARGIN, TILE_VIEW_GRID_VERTICAL_MARGIN, VERTICAL_VIEW_HORIZONTAL_MARGIN @@ -187,6 +192,211 @@ export function calculateThumbnailSizeForVerticalView(clientWidth: number = 0, }; } +/** + * Returns the minimum height of a thumbnail. + * + * @param {number} clientWidth - The width of the window. + * @returns {number} The minimum height of a thumbnail. + */ +export function getThumbnailMinHeight(clientWidth) { + return clientWidth < ASPECT_RATIO_BREAKPOINT ? TILE_MIN_HEIGHT_SMALL : TILE_MIN_HEIGHT_LARGE; +} + +/** + * Returns the default aspect ratio for a tile. + * + * @param {boolean} disableResponsiveTiles - Indicates whether the responsive tiles functionality is disabled. + * @param {boolean} disableTileEnlargement - Indicates whether the tiles enlargement functionality is disabled. + * @param {number} clientWidth - The width of the window. + * @returns {number} The default aspect ratio for a tile. + */ +export function getTileDefaultAspectRatio(disableResponsiveTiles, disableTileEnlargement, clientWidth) { + if (!disableResponsiveTiles && disableTileEnlargement && clientWidth < ASPECT_RATIO_BREAKPOINT) { + return SQUARE_TILE_ASPECT_RATIO; + } + + return TILE_ASPECT_RATIO; +} + +/** + * Returns the number of participants that will be displayed in tile view. + * + * @param {Object} state - The redux store state. + * @returns {number} The number of participants that will be displayed in tile view. + */ +export function getNumberOfPartipantsForTileView(state) { + const { iAmRecorder } = state['features/base/config']; + const disableSelfView = shouldHideSelfView(state); + const numberOfParticipants = getParticipantCountWithFake(state) + - (iAmRecorder ? 1 : 0) + - (disableSelfView ? 1 : 0); + + return numberOfParticipants; +} + +/** + * Calculates the dimensions (thumbnail width/height and columns/row) for tile view when the responsive tiles are + * disabled. + * + * @param {Object} state - The redux store state. + * @returns {Object} - The dimensions. + */ +export function calculateNotResponsiveTileViewDimensions(state) { + const { clientHeight, clientWidth } = state['features/base/responsive-ui']; + const { disableTileEnlargement } = state['features/base/config']; + const { columns: c, minVisibleRows, rows: r } = getNotResponsiveTileViewGridDimensions(state); + const size = calculateThumbnailSizeForTileView({ + columns: c, + minVisibleRows, + clientWidth, + clientHeight, + disableTileEnlargement, + disableResponsiveTiles: true + }); + + if (typeof size === 'undefined') { // The columns don't fit into the screen. We will have horizontal scroll. + const aspectRatio = disableTileEnlargement + ? getTileDefaultAspectRatio(true, disableTileEnlargement, clientWidth) + : TILE_PORTRAIT_ASPECT_RATIO; + + const height = getThumbnailMinHeight(clientWidth); + + return { + height, + width: aspectRatio * height, + columns: c, + rows: r + }; + } + + return { + height: size.height, + width: size.width, + columns: c, + rows: r + }; +} + +/** + * Calculates the dimensions (thumbnail width/height and columns/row) for tile view when the responsive tiles are + * enabled. + * + * @param {Object} state - The redux store state. + * @returns {Object} - The dimensions. + */ +export function calculateResponsiveTileViewDimensions({ + clientWidth, + clientHeight, + disableTileEnlargement = false, + isVerticalFilmstrip = false, + maxColumns, + numberOfParticipants, + numberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES +}) { + let height, width; + let columns, rows; + + let dimensions = { + maxArea: 0 + }; + let minHeightEnforcedDimensions = { + maxArea: 0 + }; + let zeroVisibleRowsDimensions = { + maxArea: 0 + }; + + for (let c = 1; c <= Math.min(maxColumns, numberOfParticipants); c++) { + const r = Math.ceil(numberOfParticipants / c); + + // we want to display as much as possible tumbnails up to numberOfVisibleTiles + const visibleRows + = numberOfParticipants <= numberOfVisibleTiles ? r : Math.floor(numberOfVisibleTiles / c); + + const size = calculateThumbnailSizeForTileView({ + columns: c, + minVisibleRows: visibleRows, + clientWidth, + clientHeight, + disableTileEnlargement, + disableResponsiveTiles: false, + isVerticalFilmstrip + }); + + // eslint-disable-next-line no-negated-condition + if (typeof size !== 'undefined') { + + const { height: currentHeight, width: currentWidth, minHeightEnforced, maxVisibleRows } = size; + + console.error(`Num col = ${c}, visibleRows=${visibleRows}, hxw=${ + currentHeight}x${currentWidth}, maxVisibleRows=${maxVisibleRows}`); + let area = currentHeight * currentWidth * Math.min(c * maxVisibleRows, numberOfParticipants); + + // we have a preference to show more columns if possible, that's why even if the area is equal we + // overwrite. + if (!minHeightEnforced && area > dimensions.maxArea) { + dimensions = { + maxArea: area, + height: currentHeight, + width: currentWidth, + columns: c, + rows: r + }; + } else if (minHeightEnforced) { + // eslint-disable-next-line max-depth + if (area > minHeightEnforcedDimensions.maxArea) { + minHeightEnforcedDimensions = { + maxArea: area, + height: currentHeight, + width: currentWidth, + columns: c, + rows: r + }; + } else if (maxVisibleRows === 0) { + area = currentHeight * currentWidth * Math.min(c, numberOfParticipants); + + // eslint-disable-next-line max-depth + if (area > zeroVisibleRowsDimensions.maxArea) { + zeroVisibleRowsDimensions = { + maxArea: area, + height: currentHeight, + width: currentWidth, + columns: c, + rows: r + }; + } + } + } + } else { + console.error(`Num col = ${c}, visibleRows=${visibleRows} not possible`); + } + } + + if (dimensions.maxArea > 0) { + ({ height, width, columns, rows } = dimensions); + } else if (minHeightEnforcedDimensions.maxArea > 0) { + ({ height, width, columns, rows } = minHeightEnforcedDimensions); + } else if (zeroVisibleRowsDimensions.maxArea > 0) { + ({ height, width, columns, rows } = zeroVisibleRowsDimensions); + } else { // This would mean that we can't fit even one thumbnail with minimal size. + const aspectRatio = disableTileEnlargement + ? getTileDefaultAspectRatio(false, disableTileEnlargement, clientWidth) + : TILE_PORTRAIT_ASPECT_RATIO; + + height = getThumbnailMinHeight(clientWidth); + width = aspectRatio * height; + columns = 1; + rows = numberOfParticipants; + } + + return { + height, + width, + columns, + rows + }; +} + /** * Calculates the size for thumbnails when in tile view layout. * @@ -196,90 +406,78 @@ export function calculateThumbnailSizeForVerticalView(clientWidth: number = 0, export function calculateThumbnailSizeForTileView({ columns, minVisibleRows, - rows, clientWidth, clientHeight, - disableResponsiveTiles, - disableTileEnlargement, + disableResponsiveTiles = false, + disableTileEnlargement = false, isVerticalFilmstrip = false }: Object) { - let aspectRatio = TILE_ASPECT_RATIO; - - if (!disableResponsiveTiles && clientWidth < ASPECT_RATIO_BREAKPOINT) { - aspectRatio = SQUARE_TILE_ASPECT_RATIO; - } - - const minHeight = clientWidth < ASPECT_RATIO_BREAKPOINT ? TILE_MIN_HEIGHT_SMALL : TILE_MIN_HEIGHT_LARGE; + const aspectRatio = getTileDefaultAspectRatio(disableResponsiveTiles, disableTileEnlargement, clientWidth); + const minHeight = getThumbnailMinHeight(clientWidth); const viewWidth = clientWidth - (columns * TILE_HORIZONTAL_MARGIN) - (isVerticalFilmstrip ? 0 : TILE_VIEW_GRID_HORIZONTAL_MARGIN); const viewHeight = clientHeight - (minVisibleRows * TILE_VERTICAL_MARGIN) - TILE_VIEW_GRID_VERTICAL_MARGIN; const initialWidth = viewWidth / columns; - const initialHeight = viewHeight / minVisibleRows; - const aspectRatioHeight = initialWidth / aspectRatio; - const noScrollHeight = (clientHeight / rows) - TILE_VERTICAL_MARGIN; - const scrollInitialWidth = (viewWidth - SCROLL_SIZE) / columns; - let height = Math.floor(Math.min(aspectRatioHeight, initialHeight)); - let width = Math.floor(aspectRatio * height); + let initialHeight = viewHeight / minVisibleRows; + let minHeightEnforced = false; - if (height > noScrollHeight && width > scrollInitialWidth) { // we will have scroll and we need more space for it. - const scrollAspectRatioHeight = scrollInitialWidth / aspectRatio; - - // Recalculating width/height to fit the available space when a scroll is displayed. - // NOTE: Math.min(scrollAspectRatioHeight, initialHeight) would be enough to recalculate but since the new - // height value can theoretically be dramatically smaller and the scroll may not be neccessary anymore we need - // to compare it with noScrollHeight( the optimal height to fit all thumbnails without scroll) and get the - // bigger one. This way we ensure that we always strech the thumbnails as close as we can to the edges of the - // window. - height = Math.floor(Math.max(Math.min(scrollAspectRatioHeight, initialHeight), noScrollHeight)); - width = Math.floor(aspectRatio * height); - - return { - height, - width - }; + if (initialHeight < minHeight) { + minHeightEnforced = true; + initialHeight = minHeight; } if (disableTileEnlargement) { - return { - height, - width - }; - } + const aspectRatioHeight = initialWidth / aspectRatio; - if (initialHeight > noScrollHeight) { - height = Math.max(height, viewHeight / rows, minHeight); - width = Math.max(width, initialWidth); - } else { - height = Math.max(initialHeight, minHeight); - width = initialWidth; - } - - if (height > width) { - const heightFromWidth = TILE_PORTRAIT_ASPECT_RATIO * width; - - if (height > heightFromWidth && heightFromWidth < minHeight) { - return { - height, - width: height / TILE_PORTRAIT_ASPECT_RATIO - }; + if (aspectRatioHeight < minHeight) { // we can't fit the required number of columns. + return; } - return { - height: Math.min(height, heightFromWidth), - width - }; - } else if (height < width) { + const height = Math.floor(Math.min(aspectRatioHeight, initialHeight)); + return { height, - width: Math.min(width, aspectRatio * height) + width: Math.floor(aspectRatio * height), + minHeightEnforced, + maxVisibleRows: Math.floor(viewHeight / height) }; } - return { - height, - width - }; + const initialRatio = initialWidth / initialHeight; + let height = Math.floor(initialHeight); + // The biggest area of the grid will be when the grid's height is equal to clientHeight or when the grid's width is + // equal to clientWidth. + + if (initialRatio > aspectRatio) { + return { + height, + width: Math.floor(initialHeight * aspectRatio), + minHeightEnforced, + maxVisibleRows: Math.floor(viewHeight / height) + }; + } else if (initialRatio >= TILE_PORTRAIT_ASPECT_RATIO) { + return { + height, + width: Math.floor(initialWidth), + minHeightEnforced, + maxVisibleRows: Math.floor(viewHeight / height) + }; + } else if (!minHeightEnforced) { + height = Math.floor(initialWidth / TILE_PORTRAIT_ASPECT_RATIO); + + if (height >= minHeight) { + return { + height, + width: Math.floor(initialWidth), + minHeightEnforced, + maxVisibleRows: Math.floor(viewHeight / height) + }; + } + } + + // else + // We can't fit that number of columns with the desired min height and aspect ratio. } /** diff --git a/react/features/filmstrip/middleware.web.js b/react/features/filmstrip/middleware.web.js index e541aafad..337712967 100644 --- a/react/features/filmstrip/middleware.web.js +++ b/react/features/filmstrip/middleware.web.js @@ -44,9 +44,7 @@ MiddlewareRegistry.register(store => next => action => { switch (layout) { case LAYOUTS.TILE_VIEW: { - const { gridDimensions } = state['features/filmstrip'].tileViewDimensions; - - store.dispatch(setTileViewDimensions(gridDimensions)); + store.dispatch(setTileViewDimensions()); break; } case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW: diff --git a/react/features/filmstrip/subscriber.native.js b/react/features/filmstrip/subscriber.native.js index e4f247840..b516888ce 100644 --- a/react/features/filmstrip/subscriber.native.js +++ b/react/features/filmstrip/subscriber.native.js @@ -2,7 +2,7 @@ import { getParticipantCountWithFake } from '../base/participants'; import { StateListenerRegistry } from '../base/redux'; -import { getTileViewGridDimensions, shouldDisplayTileView } from '../video-layout'; +import { shouldDisplayTileView } from '../video-layout'; import { setTileViewDimensions } from './actions'; import './subscriber.any'; @@ -34,9 +34,7 @@ StateListenerRegistry.register( StateListenerRegistry.register( /* selector */ state => shouldDisplayTileView(state), /* listener */ (isTileView, store) => { - const state = store.getState(); - if (isTileView) { - store.dispatch(setTileViewDimensions(getTileViewGridDimensions(state))); + store.dispatch(setTileViewDimensions()); } }); diff --git a/react/features/filmstrip/subscriber.web.js b/react/features/filmstrip/subscriber.web.js index c3c69c538..5c5fefd9a 100644 --- a/react/features/filmstrip/subscriber.web.js +++ b/react/features/filmstrip/subscriber.web.js @@ -2,13 +2,13 @@ import { isMobileBrowser } from '../base/environment/utils'; import { getParticipantCountWithFake } from '../base/participants'; -import { StateListenerRegistry, equals } from '../base/redux'; +import { StateListenerRegistry } from '../base/redux'; import { clientResized } from '../base/responsive-ui'; import { shouldHideSelfView } from '../base/settings'; import { setFilmstripVisible } from '../filmstrip/actions'; import { getParticipantsPaneOpen } from '../participants-pane/functions'; import { setOverflowDrawer } from '../toolbox/actions.web'; -import { getCurrentLayout, getTileViewGridDimensions, shouldDisplayTileView, LAYOUTS } from '../video-layout'; +import { getCurrentLayout, shouldDisplayTileView, LAYOUTS } from '../video-layout'; import { setHorizontalViewDimensions, @@ -17,9 +17,7 @@ import { } from './actions'; import { ASPECT_RATIO_BREAKPOINT, - DISPLAY_DRAWER_THRESHOLD, - SINGLE_COLUMN_BREAKPOINT, - TWO_COLUMN_BREAKPOINT + DISPLAY_DRAWER_THRESHOLD } from './constants'; import { isFilmstripResizable } from './functions.web'; import './subscriber.any'; @@ -40,12 +38,7 @@ StateListenerRegistry.register( const resizableFilmstrip = isFilmstripResizable(state); if (shouldDisplayTileView(state)) { - const gridDimensions = getTileViewGridDimensions(state); - const oldGridDimensions = state['features/filmstrip'].tileViewDimensions.gridDimensions; - - if (!equals(gridDimensions, oldGridDimensions)) { - store.dispatch(setTileViewDimensions(gridDimensions)); - } + store.dispatch(setTileViewDimensions()); } if (resizableFilmstrip) { store.dispatch(setVerticalViewDimensions()); @@ -60,11 +53,9 @@ StateListenerRegistry.register( StateListenerRegistry.register( /* selector */ state => getCurrentLayout(state), /* listener */ (layout, store) => { - const state = store.getState(); - switch (layout) { case LAYOUTS.TILE_VIEW: - store.dispatch(setTileViewDimensions(getTileViewGridDimensions(state))); + store.dispatch(setTileViewDimensions()); break; case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW: store.dispatch(setHorizontalViewDimensions()); @@ -132,50 +123,6 @@ StateListenerRegistry.register( } }); -/** - * Symbol mapping used for the tile view responsiveness computation. - */ -const responsiveColumnMapping = { - multipleColumns: Symbol('multipleColumns'), - singleColumn: Symbol('singleColumn'), - twoColumns: Symbol('twoColumns'), - twoParticipantsSingleColumn: Symbol('twoParticipantsSingleColumn') -}; - -/** - * Listens for changes in the screen size to recompute - * the dimensions of the tile view grid and the tiles for responsiveness. - */ -StateListenerRegistry.register( - /* selector */ state => { - const { clientWidth } = state['features/base/responsive-ui']; - - if (clientWidth < TWO_COLUMN_BREAKPOINT && clientWidth >= ASPECT_RATIO_BREAKPOINT) { - // Forcing the recomputation of tiles when screen switches in or out of - // the (TWO_COLUMN_BREAKPOINT, ASPECT_RATIO_BREAKPOINT] interval. - return responsiveColumnMapping.twoColumns; - } else if (clientWidth < ASPECT_RATIO_BREAKPOINT && clientWidth >= SINGLE_COLUMN_BREAKPOINT) { - // Forcing the recomputation of tiles when screen switches in or out of - // the (ASPECT_RATIO_BREAKPOINT, SINGLE_COLUMN_BREAKPOINT] interval. - return responsiveColumnMapping.twoParticipantsSingleColumn; - } else if (clientWidth < SINGLE_COLUMN_BREAKPOINT) { - // Forcing the recomputation of tiles when screen switches below SINGLE_COLUMN_BREAKPOINT. - return responsiveColumnMapping.singleColumn; - } - - // Forcing the recomputation of tiles when screen switches above TWO_COLUMN_BREAKPOINT. - return responsiveColumnMapping.multipleColumns; - }, - /* listener */ (_, store) => { - const state = store.getState(); - - if (shouldDisplayTileView(state)) { - const gridDimensions = getTileViewGridDimensions(state); - - store.dispatch(setTileViewDimensions(gridDimensions)); - } - }); - /** * Listens for changes in the filmstrip width to determine the size of the tiles. */ diff --git a/react/features/video-layout/functions.js b/react/features/video-layout/functions.js index bf842caf6..63bb713e3 100644 --- a/react/features/video-layout/functions.js +++ b/react/features/video-layout/functions.js @@ -5,17 +5,13 @@ import { getFeatureFlag, TILE_VIEW_ENABLED } from '../base/flags'; import { getPinnedParticipant, getParticipantCount, - pinParticipant, - getParticipantCountWithFake + pinParticipant } from '../base/participants'; -import { shouldHideSelfView } from '../base/settings/functions.any'; import { - ASPECT_RATIO_BREAKPOINT, DEFAULT_MAX_COLUMNS, - ABSOLUTE_MAX_COLUMNS, - SINGLE_COLUMN_BREAKPOINT, - TWO_COLUMN_BREAKPOINT + ABSOLUTE_MAX_COLUMNS } from '../filmstrip/constants'; +import { getNumberOfPartipantsForTileView } from '../filmstrip/functions.web'; import { isVideoPlaying } from '../shared-video/functions'; import { LAYOUTS } from './constants'; @@ -62,32 +58,10 @@ export function getCurrentLayout(state: Object) { * @param {number} width - Custom width to use for calculation. * @returns {number} */ -export function getMaxColumnCount(state: Object, width: ?number) { +export function getMaxColumnCount() { const configuredMax = (typeof interfaceConfig === 'undefined' ? DEFAULT_MAX_COLUMNS : interfaceConfig.TILE_VIEW_MAX_COLUMNS) || DEFAULT_MAX_COLUMNS; - const { disableResponsiveTiles } = state['features/base/config']; - - if (!disableResponsiveTiles) { - const { clientWidth } = state['features/base/responsive-ui']; - const widthToUse = width || clientWidth; - const participantCount = getParticipantCount(state); - - // If there are just two participants in a conference, enforce single-column view for mobile size. - if (participantCount === 2 && widthToUse < ASPECT_RATIO_BREAKPOINT) { - return Math.min(1, Math.max(configuredMax, 1)); - } - - // Enforce single column view at very small screen widths. - if (widthToUse < SINGLE_COLUMN_BREAKPOINT) { - return Math.min(1, Math.max(configuredMax, 1)); - } - - // Enforce two column view below breakpoint. - if (widthToUse < TWO_COLUMN_BREAKPOINT) { - return Math.min(2, Math.max(configuredMax, 1)); - } - } return Math.min(Math.max(configuredMax, 1), ABSOLUTE_MAX_COLUMNS); } @@ -102,22 +76,10 @@ export function getMaxColumnCount(state: Object, width: ?number) { * @returns {Object} An object is return with the desired number of columns, * rows, and visible rows (the rest should overflow) for the tile view layout. */ -export function getTileViewGridDimensions(state: Object, width: ?number) { - const maxColumns = getMaxColumnCount(state, width); - - // When in tile view mode, we must discount ourselves (the local participant) because our - // tile is not visible. - const { iAmRecorder } = state['features/base/config']; - const disableSelfView = shouldHideSelfView(state); - const numberOfParticipants = getParticipantCountWithFake(state) - - (iAmRecorder ? 1 : 0) - - (disableSelfView ? 1 : 0); - const isWeb = navigator.product !== 'ReactNative'; - - // When there are 3 participants in the call we want them to be placed on a single row unless the maxColumn setting - // is lower. - const columnsToMaintainASquare - = isWeb && numberOfParticipants === 3 ? 3 : Math.ceil(Math.sqrt(numberOfParticipants)); +export function getNotResponsiveTileViewGridDimensions(state: Object) { + const maxColumns = getMaxColumnCount(state); + const numberOfParticipants = getNumberOfPartipantsForTileView(state); + const columnsToMaintainASquare = Math.ceil(Math.sqrt(numberOfParticipants)); const columns = Math.min(columnsToMaintainASquare, maxColumns); const rows = Math.ceil(numberOfParticipants / columns); const minVisibleRows = Math.min(maxColumns, rows);