feat(filmstrip) Make filmstrip user resizable (#10884)
Make conference info and toolbar appear on top of the filmstrip After a breakpoint, filmstrip pushes over the stage view instead of appearing on top On user resize make tiles wider; after a breakpoint show grid view in the filmstrip On filmstrip visibility toggle animate stage view resize Added config for filmstrip with disableResizableFilmstrip
This commit is contained in:
parent
fde33b72d0
commit
2dda749b1f
|
@ -1256,6 +1256,13 @@ var config = {
|
|||
// Prevent the filmstrip from autohiding when screen width is under a certain threshold
|
||||
// disableFilmstripAutohiding: false,
|
||||
|
||||
// filmstrip: {
|
||||
// // Disables user resizable filmstrip. Also, allows configuration of the filmstrip
|
||||
// // (width, tiles aspect ratios) through the interfaceConfig options.
|
||||
// disableResizable: false,
|
||||
// }
|
||||
|
||||
|
||||
// Specifies whether the chat emoticons are disabled or not
|
||||
// disableChatSmileys: false,
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.subject {
|
||||
color: #fff;
|
||||
transition: opacity .6s ease-in-out;
|
||||
z-index: $zindex3;
|
||||
z-index: $toolbarZ + 2;
|
||||
margin-top: 20px;
|
||||
opacity: 0;
|
||||
|
||||
|
|
|
@ -78,6 +78,10 @@
|
|||
#largeVideoContainer {
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
|
||||
&.transition {
|
||||
transition: width 1s, height 1s, top 1s;
|
||||
}
|
||||
}
|
||||
|
||||
#largeVideoContainer {
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
flex-direction: column-reverse;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: ($desktopAppDragBarHeight - 5px) 5px calc(env(safe-area-inset-bottom, 0) + 10px);
|
||||
padding: 0;
|
||||
/**
|
||||
* fixed positioning is necessary for remote menus and tooltips to pop
|
||||
* out of the scrolling filmstrip. AtlasKit dialogs and tooltips use
|
||||
|
@ -40,6 +40,10 @@
|
|||
right: 0;
|
||||
z-index: $filmstripVideosZ;
|
||||
|
||||
&.no-vertical-padding {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide videos by making them slight to the right.
|
||||
*/
|
||||
|
@ -58,7 +62,10 @@
|
|||
&#remoteVideos {
|
||||
border: $thumbnailsBorder solid transparent;
|
||||
padding-left: 0;
|
||||
border-left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,11 +74,12 @@
|
|||
*/
|
||||
#filmstripLocalVideo {
|
||||
align-self: initial;
|
||||
bottom: 5px;
|
||||
margin-bottom: 5px;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
height: auto;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
|
||||
#filmstripLocalVideoThumbnail {
|
||||
width: calc(100% - 15px);
|
||||
|
@ -100,15 +108,27 @@
|
|||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.resizable-filmstrip #remoteVideos .videocontainer {
|
||||
border-left: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.reduce-height {
|
||||
height: calc(100% - calc(#{$newToolbarSizeWithPadding} + #{$scrollHeight}));
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
display: flex;
|
||||
transition: height .3s ease-in;
|
||||
overscroll-behavior: contain;
|
||||
|
||||
&.height-transition {
|
||||
transition: height .3s ease-in;
|
||||
}
|
||||
|
||||
&.vertical-grid-margin > div {
|
||||
margin-right: $scrollHeight;
|
||||
}
|
||||
|
||||
& > div {
|
||||
position: absolute;
|
||||
transition: opacity 1s;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: $toolbarZ + 1;
|
||||
z-index: $toolbarZ + 2;
|
||||
|
||||
.action-btn {
|
||||
border-radius: 6px;
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
isTrackStreamingStatusInactive,
|
||||
isTrackStreamingStatusInterrupted
|
||||
} from '../../../react/features/connection-indicator/functions';
|
||||
import { FILMSTRIP_BREAKPOINT, isFilmstripResizable } from '../../../react/features/filmstrip';
|
||||
import {
|
||||
updateKnownLargeVideoResolution
|
||||
} from '../../../react/features/large-video/actions';
|
||||
|
@ -401,7 +402,9 @@ export default class LargeVideoManager {
|
|||
let widthToUse = this.preferredWidth || window.innerWidth;
|
||||
const state = APP.store.getState();
|
||||
const { isOpen } = state['features/chat'];
|
||||
const { width: filmstripWidth, visible } = state['features/filmstrip'];
|
||||
const isParticipantsPaneOpen = getParticipantsPaneOpen(state);
|
||||
const resizableFilmstrip = isFilmstripResizable(state);
|
||||
|
||||
if (isParticipantsPaneOpen) {
|
||||
widthToUse -= theme.participantsPaneWidth;
|
||||
|
@ -415,6 +418,10 @@ export default class LargeVideoManager {
|
|||
widthToUse -= CHAT_SIZE;
|
||||
}
|
||||
|
||||
if (resizableFilmstrip && visible && filmstripWidth.current >= FILMSTRIP_BREAKPOINT) {
|
||||
widthToUse -= filmstripWidth.current;
|
||||
}
|
||||
|
||||
this.width = widthToUse;
|
||||
this.height = this.preferredHeight || window.innerHeight;
|
||||
}
|
||||
|
|
|
@ -154,6 +154,7 @@ export default [
|
|||
'failICE',
|
||||
'feedbackPercentage',
|
||||
'fileRecordingsEnabled',
|
||||
'filmstrip',
|
||||
'firefox_fake_device',
|
||||
'forceJVB121Ratio',
|
||||
'forceTurnRelay',
|
||||
|
|
|
@ -91,3 +91,21 @@ export const SET_VOLUME = 'SET_VOLUME';
|
|||
* }
|
||||
*/
|
||||
export const SET_VISIBLE_REMOTE_PARTICIPANTS = 'SET_VISIBLE_REMOTE_PARTICIPANTS';
|
||||
|
||||
/**
|
||||
* The type of action which sets the width for the vertical filmstrip.
|
||||
* {
|
||||
* type: SET_FILMSTRIP_WIDTH,
|
||||
* width: number
|
||||
* }
|
||||
*/
|
||||
export const SET_FILMSTRIP_WIDTH = 'SET_FILMSTRIP_WIDTH';
|
||||
|
||||
/**
|
||||
* The type of action which sets the width for the vertical filmstrip (user resized).
|
||||
* {
|
||||
* type: SET_USER_FILMSTRIP_WIDTH,
|
||||
* width: number
|
||||
* }
|
||||
*/
|
||||
export const SET_USER_FILMSTRIP_WIDTH = 'SET_USER_FILMSTRIP_WIDTH';
|
||||
|
|
|
@ -3,26 +3,32 @@ import type { Dispatch } from 'redux';
|
|||
|
||||
import { getLocalParticipant, getParticipantById, pinParticipant } from '../base/participants';
|
||||
import { shouldHideSelfView } from '../base/settings/functions.any';
|
||||
import { getTileViewGridDimensions } from '../video-layout';
|
||||
|
||||
import {
|
||||
SET_FILMSTRIP_WIDTH,
|
||||
SET_HORIZONTAL_VIEW_DIMENSIONS,
|
||||
SET_TILE_VIEW_DIMENSIONS,
|
||||
SET_USER_FILMSTRIP_WIDTH,
|
||||
SET_VERTICAL_VIEW_DIMENSIONS,
|
||||
SET_VOLUME
|
||||
} from './actionTypes';
|
||||
import {
|
||||
HORIZONTAL_FILMSTRIP_MARGIN,
|
||||
SCROLL_SIZE,
|
||||
STAGE_VIEW_THUMBNAIL_HORIZONTAL_BORDER,
|
||||
STAGE_VIEW_THUMBNAIL_VERTICAL_BORDER,
|
||||
TILE_HORIZONTAL_MARGIN,
|
||||
TILE_VERTICAL_CONTAINER_HORIZONTAL_MARGIN,
|
||||
TILE_VERTICAL_MARGIN,
|
||||
VERTICAL_FILMSTRIP_VERTICAL_MARGIN
|
||||
} from './constants';
|
||||
import {
|
||||
calculateThumbnailSizeForHorizontalView,
|
||||
calculateThumbnailSizeForTileView,
|
||||
calculateThumbnailSizeForVerticalView
|
||||
calculateThumbnailSizeForVerticalView,
|
||||
calculateThumbnailSizeForResizableVerticalView,
|
||||
isFilmstripResizable,
|
||||
showGridInVerticalView
|
||||
} from './functions';
|
||||
|
||||
export * from './actions.any';
|
||||
|
@ -80,21 +86,65 @@ export function setVerticalViewDimensions() {
|
|||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const { clientHeight = 0, clientWidth = 0 } = state['features/base/responsive-ui'];
|
||||
const { width: filmstripWidth } = state['features/filmstrip'];
|
||||
const disableSelfView = shouldHideSelfView(state);
|
||||
const thumbnails = calculateThumbnailSizeForVerticalView(clientWidth);
|
||||
const resizableFilmstrip = isFilmstripResizable(state);
|
||||
const _verticalViewGrid = showGridInVerticalView(state);
|
||||
|
||||
let gridView = {};
|
||||
let thumbnails = {};
|
||||
let filmstripDimensions = {};
|
||||
|
||||
// grid view in the vertical filmstrip
|
||||
if (_verticalViewGrid) {
|
||||
const dimensions = getTileViewGridDimensions(state, filmstripWidth.current);
|
||||
const {
|
||||
height,
|
||||
width
|
||||
} = calculateThumbnailSizeForTileView({
|
||||
...dimensions,
|
||||
clientWidth: filmstripWidth.current,
|
||||
clientHeight,
|
||||
disableResponsiveTiles: false,
|
||||
disableTileEnlargement: false,
|
||||
isVerticalFilmstrip: true
|
||||
});
|
||||
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,
|
||||
thumbnailSize: {
|
||||
height,
|
||||
width
|
||||
}
|
||||
};
|
||||
|
||||
filmstripDimensions = {
|
||||
height: filmstripHeight,
|
||||
width: widthOfFilmstrip
|
||||
};
|
||||
} else {
|
||||
thumbnails = resizableFilmstrip
|
||||
? calculateThumbnailSizeForResizableVerticalView(clientWidth, filmstripWidth.current)
|
||||
: calculateThumbnailSizeForVerticalView(clientWidth);
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: SET_VERTICAL_VIEW_DIMENSIONS,
|
||||
dimensions: {
|
||||
...thumbnails,
|
||||
remoteVideosContainer: {
|
||||
remoteVideosContainer: _verticalViewGrid ? filmstripDimensions : {
|
||||
width: thumbnails?.local?.width
|
||||
+ TILE_HORIZONTAL_MARGIN + STAGE_VIEW_THUMBNAIL_HORIZONTAL_BORDER + SCROLL_SIZE,
|
||||
+ TILE_VERTICAL_CONTAINER_HORIZONTAL_MARGIN + SCROLL_SIZE,
|
||||
height: clientHeight - (disableSelfView ? 0 : thumbnails?.local?.height)
|
||||
- VERTICAL_FILMSTRIP_VERTICAL_MARGIN
|
||||
}
|
||||
},
|
||||
gridView
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -163,3 +213,35 @@ export function setVolume(participantId: string, volume: number) {
|
|||
volume
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filmstrip's width.
|
||||
*
|
||||
* @param {number} width - The new width of the filmstrip.
|
||||
* @returns {{
|
||||
* type: SET_FILMSTRIP_WIDTH,
|
||||
* width: number
|
||||
* }}
|
||||
*/
|
||||
export function setFilmstripWidth(width: number) {
|
||||
return {
|
||||
type: SET_FILMSTRIP_WIDTH,
|
||||
width
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filmstrip's width and the user preferred width.
|
||||
*
|
||||
* @param {number} width - The new width of the filmstrip.
|
||||
* @returns {{
|
||||
* type: SET_USER_FILMSTRIP_WIDTH,
|
||||
* width: number
|
||||
* }}
|
||||
*/
|
||||
export function setUserFilmstripWidth(width: number) {
|
||||
return {
|
||||
type: SET_USER_FILMSTRIP_WIDTH,
|
||||
width
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { withStyles } from '@material-ui/styles';
|
||||
import clsx from 'clsx';
|
||||
import _ from 'lodash';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { FixedSizeList, FixedSizeGrid } from 'react-window';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
@ -20,19 +21,28 @@ import { shouldHideSelfView } from '../../../base/settings/functions.any';
|
|||
import { showToolbox } from '../../../toolbox/actions.web';
|
||||
import { isButtonEnabled, isToolboxVisible } from '../../../toolbox/functions.web';
|
||||
import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
|
||||
import { setFilmstripVisible, setVisibleRemoteParticipants } from '../../actions';
|
||||
import { setFilmstripVisible, setVisibleRemoteParticipants, setUserFilmstripWidth } from '../../actions';
|
||||
import {
|
||||
ASPECT_RATIO_BREAKPOINT,
|
||||
DEFAULT_FILMSTRIP_WIDTH,
|
||||
FILMSTRIP_BREAKPOINT,
|
||||
FILMSTRIP_BREAKPOINT_OFFSET,
|
||||
MIN_STAGE_VIEW_WIDTH,
|
||||
TILE_HORIZONTAL_MARGIN,
|
||||
TILE_VERTICAL_MARGIN,
|
||||
TOOLBAR_HEIGHT,
|
||||
TOOLBAR_HEIGHT_MOBILE
|
||||
} from '../../constants';
|
||||
import { shouldRemoteVideosBeVisible } from '../../functions';
|
||||
import {
|
||||
isFilmstripResizable,
|
||||
shouldRemoteVideosBeVisible,
|
||||
showGridInVerticalView
|
||||
} from '../../functions';
|
||||
|
||||
import AudioTracksContainer from './AudioTracksContainer';
|
||||
import Thumbnail from './Thumbnail';
|
||||
import ThumbnailWrapper from './ThumbnailWrapper';
|
||||
import { styles } from './styles';
|
||||
|
||||
declare var APP: Object;
|
||||
declare var interfaceConfig: Object;
|
||||
|
@ -82,11 +92,21 @@ type Props = {
|
|||
*/
|
||||
_isFilmstripButtonEnabled: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the toolbox is displayed.
|
||||
*/
|
||||
_isToolboxVisible: Boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the current layout is vertical filmstrip.
|
||||
*/
|
||||
_isVerticalFilmstrip: boolean,
|
||||
|
||||
/**
|
||||
* The maximum width of the vertical filmstrip.
|
||||
*/
|
||||
_maxFilmstripWidth: number,
|
||||
|
||||
/**
|
||||
* The participants in the call.
|
||||
*/
|
||||
|
@ -97,6 +117,11 @@ type Props = {
|
|||
*/
|
||||
_remoteParticipantsLength: number,
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip should be user-resizable.
|
||||
*/
|
||||
_resizableFilmstrip: boolean,
|
||||
|
||||
/**
|
||||
* The number of rows in tile view.
|
||||
*/
|
||||
|
@ -117,6 +142,16 @@ type Props = {
|
|||
*/
|
||||
_thumbnailsReordered: Boolean,
|
||||
|
||||
/**
|
||||
* The width of the vertical filmstrip (user resized).
|
||||
*/
|
||||
_verticalFilmstripWidth: ?number,
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -127,11 +162,6 @@ type Props = {
|
|||
*/
|
||||
_visible: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the toolbox is displayed.
|
||||
*/
|
||||
_isToolboxVisible: Boolean,
|
||||
|
||||
/**
|
||||
* An object containing the CSS classes.
|
||||
*/
|
||||
|
@ -148,83 +178,23 @@ type Props = {
|
|||
t: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the styles for the component.
|
||||
*
|
||||
* @param {Object} theme - The current theme.
|
||||
* @returns {Object}
|
||||
*/
|
||||
const styles = theme => {
|
||||
return {
|
||||
toggleFilmstripContainer: {
|
||||
display: 'flex',
|
||||
flexWrap: 'nowrap',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'rgba(0, 0, 0, .6)',
|
||||
width: '32px',
|
||||
height: '24px',
|
||||
position: 'absolute',
|
||||
borderRadius: '4px',
|
||||
top: 'calc(-24px - 2px)',
|
||||
left: 'calc(50% - 16px)',
|
||||
opacity: 0,
|
||||
transition: 'opacity .3s'
|
||||
},
|
||||
type State = {
|
||||
|
||||
toggleFilmstripButton: {
|
||||
fontSize: '14px',
|
||||
lineHeight: 1.2,
|
||||
textAlign: 'center',
|
||||
background: 'transparent',
|
||||
height: 'auto',
|
||||
width: '100%',
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
border: 'none',
|
||||
/**
|
||||
* Whether or not the mouse is pressed.
|
||||
*/
|
||||
isMouseDown: boolean,
|
||||
|
||||
'-webkit-appearance': 'none',
|
||||
/**
|
||||
* Initial mouse position on drag handle mouse down.
|
||||
*/
|
||||
mousePosition: ?number,
|
||||
|
||||
'& svg': {
|
||||
fill: theme.palette.icon02
|
||||
}
|
||||
},
|
||||
|
||||
toggleVerticalFilmstripContainer: {
|
||||
transform: 'rotate(-90deg)',
|
||||
left: 'calc(-24px - 2px - 5px)',
|
||||
top: 'calc(50% - 16px)'
|
||||
},
|
||||
|
||||
filmstrip: {
|
||||
transition: 'background .2s ease-in-out, right 1s, bottom 1s, height .3s ease-in',
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(0, 0, 0, .6)',
|
||||
|
||||
'& .toggleFilmstripContainer': {
|
||||
opacity: 1
|
||||
}
|
||||
},
|
||||
|
||||
'.horizontal-filmstrip &.hidden': {
|
||||
bottom: '-50px',
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: 'transparent'
|
||||
}
|
||||
},
|
||||
|
||||
'&.hidden': {
|
||||
'& .toggleFilmstripContainer': {
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Initial filmstrip width on drag handle mouse down.
|
||||
*/
|
||||
dragFilmstripWidth: ?number
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a React {@link Component} which represents the filmstrip on
|
||||
|
@ -232,7 +202,9 @@ const styles = theme => {
|
|||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class Filmstrip extends PureComponent <Props> {
|
||||
class Filmstrip extends PureComponent <Props, State> {
|
||||
|
||||
_throttledResize: Function;
|
||||
|
||||
/**
|
||||
* Initializes a new {@code Filmstrip} instance.
|
||||
|
@ -243,6 +215,12 @@ class Filmstrip extends PureComponent <Props> {
|
|||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isMouseDown: false,
|
||||
mousePosition: null,
|
||||
dragFilmstripWidth: null
|
||||
};
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onShortcutToggleFilmstrip = this._onShortcutToggleFilmstrip.bind(this);
|
||||
this._onToolbarToggleFilmstrip = this._onToolbarToggleFilmstrip.bind(this);
|
||||
|
@ -252,6 +230,17 @@ class Filmstrip extends PureComponent <Props> {
|
|||
this._onGridItemsRendered = this._onGridItemsRendered.bind(this);
|
||||
this._onListItemsRendered = this._onListItemsRendered.bind(this);
|
||||
this._onToggleButtonTouch = this._onToggleButtonTouch.bind(this);
|
||||
this._onDragHandleMouseDown = this._onDragHandleMouseDown.bind(this);
|
||||
this._onDragMouseUp = this._onDragMouseUp.bind(this);
|
||||
this._onFilmstripResize = this._onFilmstripResize.bind(this);
|
||||
|
||||
this._throttledResize = _.throttle(
|
||||
this._onFilmstripResize,
|
||||
50,
|
||||
{
|
||||
leading: true,
|
||||
trailing: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,6 +255,8 @@ class Filmstrip extends PureComponent <Props> {
|
|||
this._onShortcutToggleFilmstrip,
|
||||
'keyboardShortcuts.toggleFilmstrip'
|
||||
);
|
||||
document.addEventListener('mouseup', this._onDragMouseUp);
|
||||
document.addEventListener('mousemove', this._throttledResize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -275,6 +266,8 @@ class Filmstrip extends PureComponent <Props> {
|
|||
*/
|
||||
componentWillUnmount() {
|
||||
APP.keyboardshortcut.unregisterShortcut('F');
|
||||
document.removeEventListener('mouseup', this._onDragMouseUp);
|
||||
document.removeEventListener('mousemove', this._throttledResize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -285,17 +278,32 @@ class Filmstrip extends PureComponent <Props> {
|
|||
*/
|
||||
render() {
|
||||
const filmstripStyle = { };
|
||||
const { _currentLayout, _disableSelfView, classes, _visible } = this.props;
|
||||
const {
|
||||
_currentLayout,
|
||||
_disableSelfView,
|
||||
_resizableFilmstrip,
|
||||
_verticalFilmstripWidth,
|
||||
_visible,
|
||||
_verticalViewGrid,
|
||||
classes
|
||||
} = this.props;
|
||||
const { isMouseDown } = this.state;
|
||||
const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
|
||||
let maxWidth;
|
||||
|
||||
switch (_currentLayout) {
|
||||
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
|
||||
// Adding 18px for the 2px margins, 2px borders on the left and right and 5px padding on the left and right.
|
||||
// Also adding 7px for the scrollbar.
|
||||
filmstripStyle.maxWidth = (interfaceConfig.FILM_STRIP_MAX_HEIGHT || 120) + 25;
|
||||
maxWidth = _resizableFilmstrip
|
||||
? _verticalFilmstripWidth || DEFAULT_FILMSTRIP_WIDTH
|
||||
: interfaceConfig.FILM_STRIP_MAX_HEIGHT || DEFAULT_FILMSTRIP_WIDTH;
|
||||
|
||||
// Adding 4px for the border-right and margin-right.
|
||||
// On non-resizable filmstrip add 4px for the left margin and border.
|
||||
// Also adding 7px for the scrollbar. Also adding 9px for the drag handle.
|
||||
filmstripStyle.maxWidth = maxWidth + (_verticalViewGrid ? 0 : 11) + (_resizableFilmstrip ? 9 : 4);
|
||||
|
||||
if (!_visible) {
|
||||
filmstripStyle.right = `-${filmstripStyle.maxWidth + 2}px`;
|
||||
filmstripStyle.right = `-${filmstripStyle.maxWidth}px`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -306,37 +314,113 @@ class Filmstrip extends PureComponent <Props> {
|
|||
toolbar = this._renderToggleButton();
|
||||
}
|
||||
|
||||
const filmstrip = (<>
|
||||
<div
|
||||
className = { clsx(this.props._videosClassName,
|
||||
!tileViewActive && !_resizableFilmstrip && 'filmstrip-hover') }
|
||||
id = 'remoteVideos'>
|
||||
{!_disableSelfView && !_verticalViewGrid && (
|
||||
<div
|
||||
className = 'filmstrip__videos'
|
||||
id = 'filmstripLocalVideo'>
|
||||
<div id = 'filmstripLocalVideoThumbnail'>
|
||||
{
|
||||
!tileViewActive && <Thumbnail
|
||||
key = 'local' />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
this._renderRemoteParticipants()
|
||||
}
|
||||
</div>
|
||||
</>);
|
||||
|
||||
return (
|
||||
<div
|
||||
className = { clsx('filmstrip',
|
||||
this.props._className,
|
||||
classes.filmstrip) }
|
||||
classes.filmstrip,
|
||||
_verticalViewGrid && 'no-vertical-padding',
|
||||
_verticalFilmstripWidth + FILMSTRIP_BREAKPOINT_OFFSET >= FILMSTRIP_BREAKPOINT
|
||||
&& classes.filmstripBackground) }
|
||||
style = { filmstripStyle }>
|
||||
{ toolbar }
|
||||
<div
|
||||
className = { this.props._videosClassName }
|
||||
id = 'remoteVideos'>
|
||||
{!_disableSelfView && (
|
||||
{_resizableFilmstrip
|
||||
? <div className = { clsx('resizable-filmstrip', classes.resizableFilmstripContainer) }>
|
||||
<div
|
||||
className = 'filmstrip__videos'
|
||||
id = 'filmstripLocalVideo'>
|
||||
<div id = 'filmstripLocalVideoThumbnail'>
|
||||
{
|
||||
!tileViewActive && <Thumbnail
|
||||
key = 'local' />
|
||||
}
|
||||
</div>
|
||||
className = { clsx('dragHandleContainer',
|
||||
classes.dragHandleContainer,
|
||||
isMouseDown && 'visible')
|
||||
}
|
||||
onMouseDown = { this._onDragHandleMouseDown }>
|
||||
<div className = { clsx(classes.dragHandle, 'dragHandle') } />
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
this._renderRemoteParticipants()
|
||||
}
|
||||
</div>
|
||||
{filmstrip}
|
||||
</div>
|
||||
: filmstrip
|
||||
}
|
||||
<AudioTracksContainer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_onDragHandleMouseDown: (MouseEvent) => void;
|
||||
|
||||
/**
|
||||
* Handles mouse down on the drag handle.
|
||||
*
|
||||
* @param {MouseEvent} e - The mouse down event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onDragHandleMouseDown(e) {
|
||||
this.setState({
|
||||
isMouseDown: true,
|
||||
mousePosition: e.clientX,
|
||||
dragFilmstripWidth: this.props._verticalFilmstripWidth || DEFAULT_FILMSTRIP_WIDTH
|
||||
});
|
||||
}
|
||||
|
||||
_onDragMouseUp: () => void;
|
||||
|
||||
/**
|
||||
* Drag handle mouse up handler.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onDragMouseUp() {
|
||||
if (this.state.isMouseDown) {
|
||||
this.setState({
|
||||
isMouseDown: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_onFilmstripResize: (MouseEvent) => void;
|
||||
|
||||
/**
|
||||
* Handles drag handle mouse move.
|
||||
*
|
||||
* @param {MouseEvent} e - The mousemove event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_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
|
||||
);
|
||||
|
||||
if (width !== _verticalFilmstripWidth) {
|
||||
dispatch(setUserFilmstripWidth(width));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the start and stop indices based on whether the thumbnails need to be reordered in the filmstrip.
|
||||
*
|
||||
|
@ -480,7 +564,8 @@ class Filmstrip extends PureComponent <Props> {
|
|||
_remoteParticipantsLength,
|
||||
_rows,
|
||||
_thumbnailHeight,
|
||||
_thumbnailWidth
|
||||
_thumbnailWidth,
|
||||
_verticalViewGrid
|
||||
} = this.props;
|
||||
|
||||
if (!_thumbnailWidth || isNaN(_thumbnailWidth) || !_thumbnailHeight
|
||||
|
@ -489,7 +574,7 @@ class Filmstrip extends PureComponent <Props> {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (_currentLayout === LAYOUTS.TILE_VIEW) {
|
||||
if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid) {
|
||||
return (
|
||||
<FixedSizeGrid
|
||||
className = 'filmstrip__videos remote-videos'
|
||||
|
@ -514,7 +599,7 @@ class Filmstrip extends PureComponent <Props> {
|
|||
|
||||
const props = {
|
||||
itemCount: _remoteParticipantsLength,
|
||||
className: 'filmstrip__videos remote-videos',
|
||||
className: 'filmstrip__videos remote-videos height-transition',
|
||||
height: _filmstripHeight,
|
||||
itemKey: this._listItemKey,
|
||||
itemSize: 0,
|
||||
|
@ -668,18 +753,21 @@ function _mapStateToProps(state) {
|
|||
const toolbarButtons = getToolbarButtons(state);
|
||||
const { testing = {}, iAmRecorder } = state['features/base/config'];
|
||||
const enableThumbnailReordering = testing.enableThumbnailReordering ?? true;
|
||||
const { visible, remoteParticipants } = state['features/filmstrip'];
|
||||
const { visible, remoteParticipants, width: verticalFilmstripWidth } = state['features/filmstrip'];
|
||||
const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
|
||||
const remoteVideosVisible = shouldRemoteVideosBeVisible(state);
|
||||
const { isOpen: shiftRight } = state['features/chat'];
|
||||
const {
|
||||
gridDimensions = {},
|
||||
gridDimensions: dimensions = {},
|
||||
filmstripHeight,
|
||||
filmstripWidth,
|
||||
thumbnailSize: tileViewThumbnailSize
|
||||
} = state['features/filmstrip'].tileViewDimensions;
|
||||
const _currentLayout = getCurrentLayout(state);
|
||||
const disableSelfView = shouldHideSelfView(state);
|
||||
const _resizableFilmstrip = isFilmstripResizable(state);
|
||||
const _verticalViewGrid = showGridInVerticalView(state);
|
||||
let gridDimensions = dimensions;
|
||||
|
||||
const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
|
||||
const availableSpace = clientHeight - filmstripHeight;
|
||||
|
@ -703,7 +791,7 @@ function _mapStateToProps(state) {
|
|||
isMobileBrowser() || _currentLayout !== LAYOUTS.VERTICAL_FILMSTRIP_VIEW);
|
||||
|
||||
const videosClassName = `filmstrip__videos${visible ? '' : ' hidden'}`;
|
||||
const className = `${remoteVideosVisible ? '' : 'hide-videos'} ${
|
||||
const className = `${remoteVideosVisible || _verticalViewGrid ? '' : 'hide-videos'} ${
|
||||
shouldReduceHeight ? 'reduce-height' : ''
|
||||
} ${shiftRight ? 'shift-right' : ''} ${collapseTileView ? 'collapse' : ''} ${visible ? '' : 'hidden'}`.trim();
|
||||
let _thumbnailSize, remoteFilmstripHeight, remoteFilmstripWidth;
|
||||
|
@ -715,11 +803,18 @@ function _mapStateToProps(state) {
|
|||
remoteFilmstripWidth = filmstripWidth;
|
||||
break;
|
||||
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW: {
|
||||
const { remote, remoteVideosContainer } = state['features/filmstrip'].verticalViewDimensions;
|
||||
const { remote, remoteVideosContainer, gridView } = state['features/filmstrip'].verticalViewDimensions;
|
||||
|
||||
_thumbnailSize = remote;
|
||||
remoteFilmstripHeight = remoteVideosContainer?.height - (shouldReduceHeight ? TOOLBAR_HEIGHT : 0);
|
||||
remoteFilmstripHeight = remoteVideosContainer?.height - (!_verticalViewGrid && shouldReduceHeight
|
||||
? TOOLBAR_HEIGHT : 0);
|
||||
remoteFilmstripWidth = remoteVideosContainer?.width;
|
||||
|
||||
if (_verticalViewGrid) {
|
||||
gridDimensions = gridView.gridDimensions;
|
||||
_thumbnailSize = gridView.thumbnailSize;
|
||||
} else {
|
||||
_thumbnailSize = remote;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW: {
|
||||
|
@ -741,16 +836,20 @@ function _mapStateToProps(state) {
|
|||
_filmstripWidth: remoteFilmstripWidth,
|
||||
_iAmRecorder: Boolean(iAmRecorder),
|
||||
_isFilmstripButtonEnabled: isButtonEnabled('filmstrip', state),
|
||||
_isToolboxVisible: isToolboxVisible(state),
|
||||
_isVerticalFilmstrip: _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW,
|
||||
_maxFilmstripWidth: clientWidth - MIN_STAGE_VIEW_WIDTH,
|
||||
_remoteParticipantsLength: remoteParticipants.length,
|
||||
_remoteParticipants: remoteParticipants,
|
||||
_resizableFilmstrip,
|
||||
_rows: gridDimensions.rows,
|
||||
_thumbnailWidth: _thumbnailSize?.width,
|
||||
_thumbnailHeight: _thumbnailSize?.height,
|
||||
_thumbnailsReordered: enableThumbnailReordering,
|
||||
_verticalFilmstripWidth: verticalFilmstripWidth.current,
|
||||
_videosClassName: videosClassName,
|
||||
_visible: visible,
|
||||
_isToolboxVisible: isToolboxVisible(state),
|
||||
_isVerticalFilmstrip: _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
|
||||
_verticalViewGrid
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -28,10 +28,15 @@ import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
|
|||
import {
|
||||
DISPLAY_MODE_TO_CLASS_NAME,
|
||||
DISPLAY_VIDEO,
|
||||
VIDEO_TEST_EVENTS,
|
||||
SHOW_TOOLBAR_CONTEXT_MENU_AFTER
|
||||
SHOW_TOOLBAR_CONTEXT_MENU_AFTER,
|
||||
VIDEO_TEST_EVENTS
|
||||
} from '../../constants';
|
||||
import { isVideoPlayable, computeDisplayModeFromInput, getDisplayModeInput } from '../../functions';
|
||||
import {
|
||||
computeDisplayModeFromInput,
|
||||
getDisplayModeInput,
|
||||
isVideoPlayable,
|
||||
showGridInVerticalView
|
||||
} from '../../functions';
|
||||
|
||||
import ThumbnailAudioIndicator from './ThumbnailAudioIndicator';
|
||||
import ThumbnailBottomIndicators from './ThumbnailBottomIndicators';
|
||||
|
@ -480,7 +485,6 @@ class Thumbnail extends Component<Props, State> {
|
|||
style
|
||||
} = this.props;
|
||||
|
||||
|
||||
const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
|
||||
const jitsiVideoTrack = _videoTrack?.jitsiTrack;
|
||||
const track = jitsiVideoTrack?.track;
|
||||
|
@ -949,19 +953,30 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
},
|
||||
verticalViewDimensions = {
|
||||
local: {},
|
||||
remote: {}
|
||||
remote: {},
|
||||
gridView: {}
|
||||
}
|
||||
} = state['features/filmstrip'];
|
||||
const _verticalViewGrid = showGridInVerticalView(state);
|
||||
const { local, remote }
|
||||
= _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
|
||||
? verticalViewDimensions : horizontalViewDimensions;
|
||||
const { width, height } = isLocal ? local : remote;
|
||||
const { width, height } = (isLocal ? local : remote) ?? {};
|
||||
|
||||
size = {
|
||||
_width: width,
|
||||
_height: height
|
||||
};
|
||||
|
||||
if (_verticalViewGrid) {
|
||||
const { width: _width, height: _height } = verticalViewDimensions.gridView.thumbnailSize;
|
||||
|
||||
size = {
|
||||
_width,
|
||||
_height
|
||||
};
|
||||
}
|
||||
|
||||
_isMobilePortrait = _isMobile && state['features/base/responsive-ui'].aspectRatio === ASPECT_RATIO_NARROW;
|
||||
|
||||
break;
|
||||
|
|
|
@ -5,6 +5,7 @@ import { shouldComponentUpdate } from 'react-window';
|
|||
import { connect } from '../../../base/redux';
|
||||
import { shouldHideSelfView } from '../../../base/settings/functions.any';
|
||||
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
|
||||
import { showGridInVerticalView } from '../../functions';
|
||||
|
||||
import Thumbnail from './Thumbnail';
|
||||
|
||||
|
@ -120,10 +121,14 @@ function _mapStateToProps(state, ownProps) {
|
|||
const { testing = {} } = state['features/base/config'];
|
||||
const disableSelfView = shouldHideSelfView(state);
|
||||
const enableThumbnailReordering = testing.enableThumbnailReordering ?? true;
|
||||
const _verticalViewGrid = showGridInVerticalView(state);
|
||||
|
||||
if (_currentLayout === LAYOUTS.TILE_VIEW) {
|
||||
if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid) {
|
||||
const { columnIndex, rowIndex } = ownProps;
|
||||
const { gridDimensions = {}, thumbnailSize } = state['features/filmstrip'].tileViewDimensions;
|
||||
const { gridDimensions: dimensions = {}, thumbnailSize: size } = state['features/filmstrip'].tileViewDimensions;
|
||||
const { gridView } = state['features/filmstrip'].verticalViewDimensions;
|
||||
const gridDimensions = _verticalViewGrid ? gridView.gridDimensions : dimensions;
|
||||
const thumbnailSize = _verticalViewGrid ? gridView.thumbnailSize : size;
|
||||
const { columns, rows } = gridDimensions;
|
||||
const index = (rowIndex * columns) + columnIndex;
|
||||
let horizontalOffset;
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
|
||||
const BACKGROUND_COLOR = 'rgba(51, 51, 51, .5)';
|
||||
|
||||
/**
|
||||
* Creates the styles for the component.
|
||||
*
|
||||
* @param {Object} theme - The current theme.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export const styles = theme => {
|
||||
return {
|
||||
toggleFilmstripContainer: {
|
||||
display: 'flex',
|
||||
flexWrap: 'nowrap',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: BACKGROUND_COLOR,
|
||||
width: '32px',
|
||||
height: '24px',
|
||||
position: 'absolute',
|
||||
borderRadius: '4px',
|
||||
top: 'calc(-24px - 3px)',
|
||||
left: 'calc(50% - 16px)',
|
||||
opacity: 0,
|
||||
transition: 'opacity .3s',
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.ui02
|
||||
}
|
||||
},
|
||||
|
||||
toggleFilmstripButton: {
|
||||
fontSize: '14px',
|
||||
lineHeight: 1.2,
|
||||
textAlign: 'center',
|
||||
background: 'transparent',
|
||||
height: 'auto',
|
||||
width: '100%',
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
border: 'none',
|
||||
|
||||
'-webkit-appearance': 'none',
|
||||
|
||||
'& svg': {
|
||||
fill: theme.palette.icon01
|
||||
}
|
||||
},
|
||||
|
||||
toggleVerticalFilmstripContainer: {
|
||||
transform: 'rotate(-90deg)',
|
||||
left: 'calc(-24px - 3px - 4px)',
|
||||
top: 'calc(50% - 12px)'
|
||||
},
|
||||
|
||||
filmstrip: {
|
||||
transition: 'background .2s ease-in-out, right 1s, bottom 1s, height .3s ease-in',
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
|
||||
'&:hover': {
|
||||
'& .resizable-filmstrip': {
|
||||
backgroundColor: BACKGROUND_COLOR
|
||||
},
|
||||
|
||||
'& .filmstrip-hover': {
|
||||
backgroundColor: BACKGROUND_COLOR
|
||||
},
|
||||
|
||||
'& .toggleFilmstripContainer': {
|
||||
opacity: 1
|
||||
},
|
||||
|
||||
'& .dragHandleContainer': {
|
||||
visibility: 'visible'
|
||||
}
|
||||
},
|
||||
|
||||
'.horizontal-filmstrip &.hidden': {
|
||||
bottom: '-50px',
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: 'transparent'
|
||||
}
|
||||
},
|
||||
|
||||
'&.hidden': {
|
||||
'& .toggleFilmstripContainer': {
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
filmstripBackground: {
|
||||
backgroundColor: theme.palette.uiBackground,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.uiBackground
|
||||
}
|
||||
},
|
||||
|
||||
resizableFilmstripContainer: {
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
transition: 'background .2s ease-in-out',
|
||||
|
||||
'& .avatar-container': {
|
||||
maxWidth: 'initial',
|
||||
maxHeight: 'initial'
|
||||
}
|
||||
},
|
||||
|
||||
dragHandleContainer: {
|
||||
height: '100%',
|
||||
width: '9px',
|
||||
backgroundColor: 'transparent',
|
||||
position: 'relative',
|
||||
cursor: 'col-resize',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
visibility: 'hidden',
|
||||
|
||||
'&:hover': {
|
||||
'& .dragHandle': {
|
||||
backgroundColor: theme.palette.icon01
|
||||
}
|
||||
},
|
||||
|
||||
'&.visible': {
|
||||
visibility: 'visible',
|
||||
|
||||
'& .dragHandle': {
|
||||
backgroundColor: theme.palette.icon01
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
dragHandle: {
|
||||
backgroundColor: theme.palette.icon02,
|
||||
height: '100px',
|
||||
width: '3px',
|
||||
borderRadius: '1px'
|
||||
}
|
||||
};
|
||||
};
|
|
@ -137,6 +137,14 @@ export const TILE_VERTICAL_MARGIN = 4;
|
|||
*/
|
||||
export const TILE_HORIZONTAL_MARGIN = 4;
|
||||
|
||||
/**
|
||||
* The horizontal margin of a vertical filmstrip tile container.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
export const TILE_VERTICAL_CONTAINER_HORIZONTAL_MARGIN = 2;
|
||||
|
||||
|
||||
/**
|
||||
* The vertical margin of the tile grid container.
|
||||
*
|
||||
|
@ -189,7 +197,7 @@ export const SCROLL_SIZE = 7;
|
|||
*
|
||||
* @type {number}
|
||||
*/
|
||||
export const VERTICAL_FILMSTRIP_VERTICAL_MARGIN = 60;
|
||||
export const VERTICAL_FILMSTRIP_VERTICAL_MARGIN = 26;
|
||||
|
||||
/**
|
||||
* The min horizontal space between the thumbnails container and the edges of the window.
|
||||
|
@ -242,3 +250,37 @@ export const INDICATORS_TOOLTIP_POSITION = {
|
|||
[LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'left',
|
||||
[LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW]: 'top'
|
||||
};
|
||||
|
||||
/**
|
||||
* The default (and minimum) width for the vertical filmstrip (user resizable).
|
||||
*/
|
||||
export const DEFAULT_FILMSTRIP_WIDTH = 120;
|
||||
|
||||
/**
|
||||
* The width of the filmstrip at which it no longer goes above the stage view, but it pushes it.
|
||||
*/
|
||||
export const FILMSTRIP_BREAKPOINT = 180;
|
||||
|
||||
/**
|
||||
* The width of the filmstrip at which the display mode changes from column to grid.
|
||||
*/
|
||||
export const FILMSTRIP_GRID_BREAKPOINT = 300;
|
||||
|
||||
/**
|
||||
* How much before the breakpoint should we display the background.
|
||||
* (We display the opaque background before we resize the stage view to make sure
|
||||
* the resize is not visible behind the filmstrip).
|
||||
*/
|
||||
export const FILMSTRIP_BREAKPOINT_OFFSET = 5;
|
||||
|
||||
/**
|
||||
* The minimum width for the stage view
|
||||
* (used to determine the maximum width of the user-resizable vertical filmstrip).
|
||||
*/
|
||||
export const MIN_STAGE_VIEW_WIDTH = 800;
|
||||
|
||||
/**
|
||||
* Horizontal margin used for the vertical filmstrip.
|
||||
*/
|
||||
export const VERTICAL_VIEW_HORIZONTAL_MARGIN = VERTICAL_FILMSTRIP_MIN_HORIZONTAL_MARGIN
|
||||
+ SCROLL_SIZE + TILE_HORIZONTAL_MARGIN + STAGE_VIEW_THUMBNAIL_HORIZONTAL_BORDER;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import { getSourceNameSignalingFeatureFlag } from '../base/config';
|
||||
import { isMobileBrowser } from '../base/environment/utils';
|
||||
import { MEDIA_TYPE } from '../base/media';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
|
@ -16,25 +17,26 @@ import {
|
|||
isRemoteTrackMuted
|
||||
} from '../base/tracks/functions';
|
||||
import { isTrackStreamingStatusActive, isParticipantConnectionStatusActive } from '../connection-indicator/functions';
|
||||
import { LAYOUTS } from '../video-layout';
|
||||
import { getCurrentLayout, LAYOUTS } from '../video-layout';
|
||||
|
||||
import {
|
||||
ASPECT_RATIO_BREAKPOINT,
|
||||
DEFAULT_FILMSTRIP_WIDTH,
|
||||
DISPLAY_AVATAR,
|
||||
DISPLAY_VIDEO,
|
||||
FILMSTRIP_GRID_BREAKPOINT,
|
||||
INDICATORS_TOOLTIP_POSITION,
|
||||
SCROLL_SIZE,
|
||||
SQUARE_TILE_ASPECT_RATIO,
|
||||
STAGE_VIEW_THUMBNAIL_HORIZONTAL_BORDER,
|
||||
TILE_ASPECT_RATIO,
|
||||
TILE_HORIZONTAL_MARGIN,
|
||||
TILE_MIN_HEIGHT_LARGE,
|
||||
TILE_MIN_HEIGHT_SMALL,
|
||||
TILE_PORTRAIT_ASPECT_RATIO,
|
||||
TILE_VERTICAL_MARGIN,
|
||||
TILE_VIEW_GRID_HORIZONTAL_MARGIN,
|
||||
TILE_VIEW_GRID_VERTICAL_MARGIN,
|
||||
VERTICAL_FILMSTRIP_MIN_HORIZONTAL_MARGIN,
|
||||
TILE_MIN_HEIGHT_LARGE,
|
||||
TILE_MIN_HEIGHT_SMALL,
|
||||
TILE_PORTRAIT_ASPECT_RATIO
|
||||
VERTICAL_VIEW_HORIZONTAL_MARGIN
|
||||
} from './constants';
|
||||
|
||||
export * from './functions.any';
|
||||
|
@ -139,7 +141,8 @@ export function isVideoPlayable(stateful: Object | Function, id: String) {
|
|||
*/
|
||||
export function calculateThumbnailSizeForHorizontalView(clientHeight: number = 0) {
|
||||
const topBottomMargin = 15;
|
||||
const availableHeight = Math.min(clientHeight, (interfaceConfig.FILM_STRIP_MAX_HEIGHT || 120) + topBottomMargin);
|
||||
const availableHeight = Math.min(clientHeight,
|
||||
(interfaceConfig.FILM_STRIP_MAX_HEIGHT || DEFAULT_FILMSTRIP_WIDTH) + topBottomMargin);
|
||||
const height = availableHeight - topBottomMargin;
|
||||
|
||||
return {
|
||||
|
@ -161,12 +164,9 @@ export function calculateThumbnailSizeForHorizontalView(clientHeight: number = 0
|
|||
* @returns {{local: {height, width}, remote: {height, width}}}
|
||||
*/
|
||||
export function calculateThumbnailSizeForVerticalView(clientWidth: number = 0) {
|
||||
const horizontalMargin
|
||||
= VERTICAL_FILMSTRIP_MIN_HORIZONTAL_MARGIN + SCROLL_SIZE
|
||||
+ TILE_HORIZONTAL_MARGIN + STAGE_VIEW_THUMBNAIL_HORIZONTAL_BORDER;
|
||||
const availableWidth = Math.min(
|
||||
Math.max(clientWidth - horizontalMargin, 0),
|
||||
interfaceConfig.FILM_STRIP_MAX_HEIGHT || 120);
|
||||
Math.max(clientWidth - VERTICAL_VIEW_HORIZONTAL_MARGIN, 0),
|
||||
interfaceConfig.FILM_STRIP_MAX_HEIGHT || DEFAULT_FILMSTRIP_WIDTH);
|
||||
|
||||
return {
|
||||
local: {
|
||||
|
@ -180,6 +180,31 @@ export function calculateThumbnailSizeForVerticalView(clientWidth: number = 0) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the size for thumbnails when in vertical view layout
|
||||
* and the filmstrip is resizable.
|
||||
*
|
||||
* @param {number} clientWidth - The height of the app window.
|
||||
* @param {number} filmstripWidth - The width of the filmstrip.
|
||||
* @returns {{local: {height, width}, remote: {height, width}}}
|
||||
*/
|
||||
export function calculateThumbnailSizeForResizableVerticalView(clientWidth: number = 0, filmstripWidth: number = 0) {
|
||||
const availableWidth = Math.min(
|
||||
Math.max(clientWidth - VERTICAL_VIEW_HORIZONTAL_MARGIN, 0),
|
||||
filmstripWidth || DEFAULT_FILMSTRIP_WIDTH);
|
||||
|
||||
return {
|
||||
local: {
|
||||
height: DEFAULT_FILMSTRIP_WIDTH,
|
||||
width: availableWidth
|
||||
},
|
||||
remote: {
|
||||
height: DEFAULT_FILMSTRIP_WIDTH,
|
||||
width: availableWidth
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the size for thumbnails when in tile view layout.
|
||||
*
|
||||
|
@ -193,7 +218,8 @@ export function calculateThumbnailSizeForTileView({
|
|||
clientWidth,
|
||||
clientHeight,
|
||||
disableResponsiveTiles,
|
||||
disableTileEnlargement
|
||||
disableTileEnlargement,
|
||||
isVerticalFilmstrip = false
|
||||
}: Object) {
|
||||
let aspectRatio = TILE_ASPECT_RATIO;
|
||||
|
||||
|
@ -202,7 +228,8 @@ export function calculateThumbnailSizeForTileView({
|
|||
}
|
||||
|
||||
const minHeight = clientWidth < ASPECT_RATIO_BREAKPOINT ? TILE_MIN_HEIGHT_SMALL : TILE_MIN_HEIGHT_LARGE;
|
||||
const viewWidth = clientWidth - (columns * TILE_HORIZONTAL_MARGIN) - TILE_VIEW_GRID_HORIZONTAL_MARGIN;
|
||||
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;
|
||||
|
@ -285,7 +312,7 @@ export function getVerticalFilmstripVisibleAreaWidth() {
|
|||
// TODO: Check if we can remove the left margins and paddings from the CSS.
|
||||
// FIXME: This function is used to calculate the size of the large video, etherpad or shared video. Once everything
|
||||
// is reactified this calculation will need to move to the corresponding components.
|
||||
const filmstripMaxWidth = (interfaceConfig.FILM_STRIP_MAX_HEIGHT || 120) + 18;
|
||||
const filmstripMaxWidth = (interfaceConfig.FILM_STRIP_MAX_HEIGHT || DEFAULT_FILMSTRIP_WIDTH) + 18;
|
||||
|
||||
return Math.min(filmstripMaxWidth, window.innerWidth);
|
||||
}
|
||||
|
@ -365,3 +392,30 @@ export function getDisplayModeInput(props: Object, state: Object) {
|
|||
export function getIndicatorsTooltipPosition(currentLayout: string) {
|
||||
return INDICATORS_TOOLTIP_POSITION[currentLayout] || 'top';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the filmstrip is resizable.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isFilmstripResizable(state: Object) {
|
||||
const { filmstrip } = state['features/base/config'];
|
||||
const _currentLayout = getCurrentLayout(state);
|
||||
|
||||
return !filmstrip?.disableResizable && !isMobileBrowser()
|
||||
&& _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not grid should be displayed in the vertical filmstrip.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function showGridInVerticalView(state) {
|
||||
const resizableFilmstrip = isFilmstripResizable(state);
|
||||
const { width } = state['features/filmstrip'];
|
||||
|
||||
return resizableFilmstrip && ((width.current ?? 0) > FILMSTRIP_GRID_BREAKPOINT);
|
||||
}
|
||||
|
|
|
@ -10,12 +10,16 @@ import {
|
|||
LAYOUTS
|
||||
} from '../video-layout';
|
||||
|
||||
import { SET_USER_FILMSTRIP_WIDTH } from './actionTypes';
|
||||
import {
|
||||
setFilmstripWidth,
|
||||
setHorizontalViewDimensions,
|
||||
setTileViewDimensions,
|
||||
setVerticalViewDimensions
|
||||
} from './actions';
|
||||
import { DEFAULT_FILMSTRIP_WIDTH, MIN_STAGE_VIEW_WIDTH } from './constants';
|
||||
import { updateRemoteParticipants, updateRemoteParticipantsOnLeave } from './functions';
|
||||
import { isFilmstripResizable } from './functions.web';
|
||||
import './subscriber';
|
||||
|
||||
/**
|
||||
|
@ -53,6 +57,22 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
store.dispatch(setVerticalViewDimensions());
|
||||
break;
|
||||
}
|
||||
|
||||
if (isFilmstripResizable(state)) {
|
||||
const { width: filmstripWidth } = state['features/filmstrip'];
|
||||
const { clientWidth } = action;
|
||||
let 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));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PARTICIPANT_JOINED: {
|
||||
|
@ -66,6 +86,9 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case SET_USER_FILMSTRIP_WIDTH: {
|
||||
VideoLayout.refreshLayout();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -6,9 +6,11 @@ import { ReducerRegistry } from '../base/redux';
|
|||
import {
|
||||
SET_FILMSTRIP_ENABLED,
|
||||
SET_FILMSTRIP_VISIBLE,
|
||||
SET_FILMSTRIP_WIDTH,
|
||||
SET_HORIZONTAL_VIEW_DIMENSIONS,
|
||||
SET_REMOTE_PARTICIPANTS,
|
||||
SET_TILE_VIEW_DIMENSIONS,
|
||||
SET_USER_FILMSTRIP_WIDTH,
|
||||
SET_VERTICAL_VIEW_DIMENSIONS,
|
||||
SET_VISIBLE_REMOTE_PARTICIPANTS,
|
||||
SET_VOLUME
|
||||
|
@ -92,7 +94,26 @@ const DEFAULT_STATE = {
|
|||
* @public
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
visibleRemoteParticipants: new Set()
|
||||
visibleRemoteParticipants: new Set(),
|
||||
|
||||
/**
|
||||
* The width of the resizable filmstrip.
|
||||
*
|
||||
* @public
|
||||
* @type {Object}
|
||||
*/
|
||||
width: {
|
||||
/**
|
||||
* Current width. Affected by: user filmstrip resize,
|
||||
* window resize, panels open/ close.
|
||||
*/
|
||||
current: null,
|
||||
|
||||
/**
|
||||
* Width set by user resize. Used as the preferred width.
|
||||
*/
|
||||
userSet: null
|
||||
}
|
||||
};
|
||||
|
||||
ReducerRegistry.register(
|
||||
|
@ -167,6 +188,26 @@ ReducerRegistry.register(
|
|||
...state
|
||||
};
|
||||
}
|
||||
case SET_FILMSTRIP_WIDTH: {
|
||||
return {
|
||||
...state,
|
||||
width: {
|
||||
...state.width,
|
||||
current: action.width
|
||||
}
|
||||
};
|
||||
}
|
||||
case SET_USER_FILMSTRIP_WIDTH: {
|
||||
const { width } = action;
|
||||
|
||||
return {
|
||||
...state,
|
||||
width: {
|
||||
current: width,
|
||||
userSet: width
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
SINGLE_COLUMN_BREAKPOINT,
|
||||
TWO_COLUMN_BREAKPOINT
|
||||
} from './constants';
|
||||
import { isFilmstripResizable } from './functions.web';
|
||||
import './subscriber.any';
|
||||
|
||||
|
||||
|
@ -36,6 +37,7 @@ StateListenerRegistry.register(
|
|||
},
|
||||
/* listener */ (currentState, store) => {
|
||||
const state = store.getState();
|
||||
const resizableFilmstrip = isFilmstripResizable(state);
|
||||
|
||||
if (shouldDisplayTileView(state)) {
|
||||
const gridDimensions = getTileViewGridDimensions(state);
|
||||
|
@ -45,6 +47,9 @@ StateListenerRegistry.register(
|
|||
store.dispatch(setTileViewDimensions(gridDimensions));
|
||||
}
|
||||
}
|
||||
if (resizableFilmstrip) {
|
||||
store.dispatch(setVerticalViewDimensions());
|
||||
}
|
||||
}, {
|
||||
deepEquals: true
|
||||
});
|
||||
|
@ -170,3 +175,12 @@ StateListenerRegistry.register(
|
|||
store.dispatch(setTileViewDimensions(gridDimensions));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Listens for changes in the filmstrip width to determine the size of the tiles.
|
||||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/filmstrip'].width?.current,
|
||||
/* listener */(_, store) => {
|
||||
store.dispatch(setVerticalViewDimensions());
|
||||
});
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import VideoLayout from '../../../../modules/UI/videolayout/VideoLayout';
|
||||
import { Watermarks } from '../../base/react';
|
||||
import { connect } from '../../base/redux';
|
||||
import { setColorAlpha } from '../../base/util';
|
||||
import { FILMSTRIP_BREAKPOINT, isFilmstripResizable } from '../../filmstrip';
|
||||
import { SharedVideo } from '../../shared-video/components/web';
|
||||
import { Captions } from '../../subtitles/';
|
||||
import { setTileView } from '../../video-layout/actions';
|
||||
|
@ -21,12 +23,12 @@ type Props = {
|
|||
/**
|
||||
* The user selected background color.
|
||||
*/
|
||||
_customBackgroundColor: string,
|
||||
_customBackgroundColor: string,
|
||||
|
||||
/**
|
||||
* The user selected background image url.
|
||||
*/
|
||||
_customBackgroundImageUrl: string,
|
||||
_customBackgroundImageUrl: string,
|
||||
|
||||
/**
|
||||
* Prop that indicates whether the chat is open.
|
||||
|
@ -39,6 +41,21 @@ type Props = {
|
|||
*/
|
||||
_noAutoPlayVideo: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip is resizable.
|
||||
*/
|
||||
_resizableFilmstrip: boolean,
|
||||
|
||||
/**
|
||||
* The width of the vertical filmstrip (user resized).
|
||||
*/
|
||||
_verticalFilmstripWidth: ?number,
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip is visible.
|
||||
*/
|
||||
_visibleFilmstrip: boolean,
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
|
@ -54,6 +71,10 @@ type Props = {
|
|||
class LargeVideo extends Component<Props> {
|
||||
_tappedTimeout: ?TimeoutID;
|
||||
|
||||
_containerRef: Object;
|
||||
|
||||
_wrapperRef: Object;
|
||||
|
||||
/**
|
||||
* Constructor of the component.
|
||||
*
|
||||
|
@ -62,8 +83,25 @@ class LargeVideo extends Component<Props> {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._containerRef = React.createRef();
|
||||
this._wrapperRef = React.createRef();
|
||||
|
||||
this._clearTapTimeout = this._clearTapTimeout.bind(this);
|
||||
this._onDoubleTap = this._onDoubleTap.bind(this);
|
||||
this._updateLayout = this._updateLayout.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@code Component#componentDidUpdate}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const { _visibleFilmstrip } = this.props;
|
||||
|
||||
if (prevProps._visibleFilmstrip !== _visibleFilmstrip) {
|
||||
this._updateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,6 +122,7 @@ class LargeVideo extends Component<Props> {
|
|||
<div
|
||||
className = { className }
|
||||
id = 'largeVideoContainer'
|
||||
ref = { this._containerRef }
|
||||
style = { style }>
|
||||
<SharedVideo />
|
||||
<div id = 'etherpad' />
|
||||
|
@ -112,6 +151,7 @@ class LargeVideo extends Component<Props> {
|
|||
<div
|
||||
id = 'largeVideoWrapper'
|
||||
onTouchEnd = { this._onDoubleTap }
|
||||
ref = { this._wrapperRef }
|
||||
role = 'figure' >
|
||||
<video
|
||||
autoPlay = { !_noAutoPlayVideo }
|
||||
|
@ -126,6 +166,32 @@ class LargeVideo extends Component<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
_updateLayout: () => void;
|
||||
|
||||
/**
|
||||
* Refreshes the video layout to determine the dimensions of the stage view.
|
||||
* If the filmstrip is toggled it adds CSS transition classes and removes them
|
||||
* when the transition is done.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_updateLayout() {
|
||||
const { _verticalFilmstripWidth, _resizableFilmstrip } = this.props;
|
||||
|
||||
if (_resizableFilmstrip && _verticalFilmstripWidth >= FILMSTRIP_BREAKPOINT) {
|
||||
this._containerRef.current.classList.add('transition');
|
||||
this._wrapperRef.current.classList.add('transition');
|
||||
VideoLayout.refreshLayout();
|
||||
|
||||
setTimeout(() => {
|
||||
this._containerRef.current && this._containerRef.current.classList.remove('transition');
|
||||
this._wrapperRef.current && this._wrapperRef.current.classList.remove('transition');
|
||||
}, 1000);
|
||||
} else {
|
||||
VideoLayout.refreshLayout();
|
||||
}
|
||||
}
|
||||
|
||||
_clearTapTimeout: () => void;
|
||||
|
||||
/**
|
||||
|
@ -147,7 +213,12 @@ class LargeVideo extends Component<Props> {
|
|||
*/
|
||||
_getCustomSyles() {
|
||||
const styles = {};
|
||||
const { _customBackgroundColor, _customBackgroundImageUrl } = this.props;
|
||||
const {
|
||||
_customBackgroundColor,
|
||||
_customBackgroundImageUrl,
|
||||
_verticalFilmstripWidth,
|
||||
_visibleFilmstrip
|
||||
} = this.props;
|
||||
|
||||
styles.backgroundColor = _customBackgroundColor || interfaceConfig.DEFAULT_BACKGROUND;
|
||||
|
||||
|
@ -162,6 +233,10 @@ class LargeVideo extends Component<Props> {
|
|||
styles.backgroundSize = 'cover';
|
||||
}
|
||||
|
||||
if (_visibleFilmstrip && _verticalFilmstripWidth >= FILMSTRIP_BREAKPOINT) {
|
||||
styles.width = `calc(100% - ${_verticalFilmstripWidth || 0}px)`;
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
|
@ -199,13 +274,17 @@ function _mapStateToProps(state) {
|
|||
const testingConfig = state['features/base/config'].testing;
|
||||
const { backgroundColor, backgroundImageUrl } = state['features/dynamic-branding'];
|
||||
const { isOpen: isChatOpen } = state['features/chat'];
|
||||
const { width: verticalFilmstripWidth, visible } = state['features/filmstrip'];
|
||||
|
||||
return {
|
||||
_backgroundAlpha: state['features/base/config'].backgroundAlpha,
|
||||
_customBackgroundColor: backgroundColor,
|
||||
_customBackgroundImageUrl: backgroundImageUrl,
|
||||
_isChatOpen: isChatOpen,
|
||||
_noAutoPlayVideo: testingConfig?.noAutoPlayVideo
|
||||
_noAutoPlayVideo: testingConfig?.noAutoPlayVideo,
|
||||
_resizableFilmstrip: isFilmstripResizable(state),
|
||||
_verticalFilmstripWidth: verticalFilmstripWidth.current,
|
||||
_visibleFilmstrip: visible
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -59,9 +59,10 @@ export function getCurrentLayout(state: Object) {
|
|||
* returned will be between 1 and 7, inclusive.
|
||||
*
|
||||
* @param {Object} state - The redux store state.
|
||||
* @param {number} width - Custom width to use for calculation.
|
||||
* @returns {number}
|
||||
*/
|
||||
export function getMaxColumnCount(state: Object) {
|
||||
export function getMaxColumnCount(state: Object, width: ?number) {
|
||||
const configuredMax = (typeof interfaceConfig === 'undefined'
|
||||
? DEFAULT_MAX_COLUMNS
|
||||
: interfaceConfig.TILE_VIEW_MAX_COLUMNS) || DEFAULT_MAX_COLUMNS;
|
||||
|
@ -69,20 +70,21 @@ export function getMaxColumnCount(state: Object) {
|
|||
|
||||
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 && clientWidth < ASPECT_RATIO_BREAKPOINT) {
|
||||
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 (clientWidth < SINGLE_COLUMN_BREAKPOINT) {
|
||||
if (widthToUse < SINGLE_COLUMN_BREAKPOINT) {
|
||||
return Math.min(1, Math.max(configuredMax, 1));
|
||||
}
|
||||
|
||||
// Enforce two column view below breakpoint.
|
||||
if (clientWidth < TWO_COLUMN_BREAKPOINT) {
|
||||
if (widthToUse < TWO_COLUMN_BREAKPOINT) {
|
||||
return Math.min(2, Math.max(configuredMax, 1));
|
||||
}
|
||||
}
|
||||
|
@ -96,11 +98,12 @@ export function getMaxColumnCount(state: Object) {
|
|||
* which rows will be added but no more columns.
|
||||
*
|
||||
* @param {Object} state - The redux store state.
|
||||
* @param {number} width - Custom width to use for calculation.
|
||||
* @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) {
|
||||
const maxColumns = getMaxColumnCount(state);
|
||||
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.
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
} from '../base/participants';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
import { TRACK_ADDED, TRACK_REMOVED, TRACK_STOPPED } from '../base/tracks';
|
||||
import { SET_FILMSTRIP_VISIBLE } from '../filmstrip';
|
||||
import { PARTICIPANTS_PANE_CLOSE, PARTICIPANTS_PANE_OPEN } from '../participants-pane/actionTypes.js';
|
||||
|
||||
import './middleware.any';
|
||||
|
@ -54,7 +53,6 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
|
||||
case PARTICIPANTS_PANE_CLOSE:
|
||||
case PARTICIPANTS_PANE_OPEN:
|
||||
case SET_FILMSTRIP_VISIBLE:
|
||||
VideoLayout.resizeVideoArea();
|
||||
break;
|
||||
|
||||
|
|
Loading…
Reference in New Issue