@ -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'
} ,
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 . 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
}
} ,
type State = {
'.horizontal-filmstrip &.hidden' : {
bottom : '-50px' ,
/ * *
* Whether or not the mouse is pressed .
* /
isMouseDown : boolean ,
'&:hover' : {
backgroundColor : 'transparent'
}
} ,
/ * *
* Initial mouse position on drag handle mouse down .
* /
mousePosition : ? number ,
'&.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' / >
}
< / d i v >
< / d i v >
) }
{
this . _renderRemoteParticipants ( )
}
< / d i v >
< / > ) ;
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' / >
}
< / d i v >
className = { clsx ( 'dragHandleContainer' ,
classes . dragHandleContainer ,
isMouseDown && 'visible' )
}
onMouseDown = { this . _onDragHandleMouseDown } >
< div className = { clsx ( classes . dragHandle , 'dragHandle' ) } / >
< / d i v >
) }
{
this . _renderRemoteParticipants ( )
}
< / d i v >
{ filmstrip }
< / d i v >
: filmstrip
}
< AudioTracksContainer / >
< / d i v >
) ;
}
_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
} ;
}