From 68ec6411aaeb09701c83b3ba940bc2c95a6c3cf2 Mon Sep 17 00:00:00 2001 From: Vitor Mateus Date: Tue, 9 Jul 2019 20:11:48 -0300 Subject: [PATCH] Webcam draggable refactoring --- .../imports/ui/components/app/component.jsx | 3 + .../imports/ui/components/media/component.jsx | 10 +- .../imports/ui/components/media/styles.scss | 85 ++- .../webcam-draggable-overlay/component.jsx | 673 +++++------------- .../webcam-draggable-overlay/context.jsx | 178 +++++ .../components/video-provider/component.jsx | 7 - .../components/video-provider/container.jsx | 2 +- .../video-provider/video-list/component.jsx | 18 +- .../video-provider/video-list/styles.scss | 13 +- .../video-list/video-list-item/component.jsx | 19 +- 10 files changed, 466 insertions(+), 542 deletions(-) create mode 100644 bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx index 1480cff609..04fe4c34e6 100755 --- a/bigbluebutton-html5/imports/ui/components/app/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx @@ -266,6 +266,9 @@ class App extends Component { className={styles.media} aria-label={intl.formatMessage(intlMessages.mediaLabel)} aria-hidden={this.shouldAriaHide()} + style={{ + overflow: 'hidden', + }} > {media} {this.renderCaptions()} diff --git a/bigbluebutton-html5/imports/ui/components/media/component.jsx b/bigbluebutton-html5/imports/ui/components/media/component.jsx index f6b431ab25..2a69970124 100644 --- a/bigbluebutton-html5/imports/ui/components/media/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/media/component.jsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; -import WebcamDraggableOverlay from './webcam-draggable-overlay/component'; +import WebcamDraggable from './webcam-draggable-overlay/component'; import { styles } from './styles'; @@ -70,6 +70,9 @@ export default class Media extends Component { id="container" className={cx(styles.container)} ref={this.refContainer} + style={{ + overflow: 'hidden', + }} >
{children}
- { - document.addEventListener(event, this.handleFullscreenChange); - }); - - // Ensures that the event will be called before the resize - document.addEventListener('webcamFullscreenButtonChange', this.fullscreenButtonChange); + window.addEventListener('resize', _.debounce(this.onResize.bind(this), 500)); } componentDidUpdate(prevProps) { - const { swapLayout, usersVideo, mediaContainer } = this.props; - const { lastPosition } = this.state; - const { y } = lastPosition; - const userLength = usersVideo.length; - const prevUserLength = prevProps.usersVideo.length; + const { swapLayout } = this.props; + if (prevProps.swapLayout === true && swapLayout === false) { + setTimeout(() => this.forceUpdate(), 500); + } + } - if (prevProps.mediaContainer && mediaContainer) { - const mediaContainerRect = mediaContainer.getBoundingClientRect(); - const { - left: mediaLeft, - top: mediaTop, - } = mediaContainerRect; - const prevMediaContainerRect = prevProps.mediaContainer.getBoundingClientRect(); - const { - left: prevMediaLeft, - top: prevMediaTop, - } = prevMediaContainerRect; + onResize() { + const { webcamDraggableState, webcamDraggableDispatch } = this.props; + const { mediaSize } = webcamDraggableState; + const { width: stateWidth, height: stateHeight } = mediaSize; + const { width, height } = this.getMediaBounds(); - if (mediaLeft !== prevMediaLeft || mediaTop !== prevMediaTop) { - this.shouldUpdatePosition = false; - } else if (this.shouldUpdatePosition === false) { - this.shouldUpdatePosition = true; - } - } - - if (prevProps.swapLayout && !swapLayout && userLength === 1) { - this.shouldUpdatePosition = false; - } - if (prevProps.swapLayout && !swapLayout && userLength > 1) { - this.setLastPosition(0, y); - } - if (prevUserLength === 1 && userLength > 1) { - this.setResetPosition(true); - this.setDropOnTop(true); - } - if (prevUserLength !== userLength) { - WebcamDraggableOverlay.waitFor( - () => WebcamDraggableOverlay.getVideoCountBySelector() === userLength, - this.updateWebcamPositionByResize, + if (stateWidth !== width || stateHeight !== height) { + webcamDraggableDispatch( + { + type: 'setMediaSize', + value: { + width, + height, + }, + }, ); } } - componentWillUnmount() { - fullscreenChangedEvents.forEach((event) => { - document.removeEventListener(event, this.handleFullscreenChange); - }); - - document.removeEventListener('webcamFullscreenButtonChange', this.fullscreenButtonChange); - document.removeEventListener('videoFocusChange', this.eventVideoFocusChangeListener); - } - - setIsFullScreen(isFullScreen) { - this.setState({ isFullScreen }); - } - - setResetPosition(resetPosition) { - this.setState({ resetPosition }); - } - - setLastPosition(x, y) { - this.setState({ - lastPosition: { - x, - y, - }, - }); - } - - setDropOnTop(dropOnTop) { - this.setState({ dropOnTop }); - } - - setInitialReferencePoint() { - const { refMediaContainer, usersVideo } = this.props; + getMediaBounds() { + const { refMediaContainer, webcamDraggableState, webcamDraggableDispatch } = this.props; + const { mediaSize: mediaState } = webcamDraggableState; const { current: mediaContainer } = refMediaContainer; - const userLength = usersVideo.length; - - const webcamBySelector = WebcamDraggableOverlay.getWebcamGridBySelector(); - - if (webcamBySelector && mediaContainer && this.shouldUpdatePosition) { - const webcamBySelectorRect = webcamBySelector.getBoundingClientRect(); - const { - width: webcamWidth, - height: webcamHeight, - } = webcamBySelectorRect; - + if (mediaContainer) { const mediaContainerRect = mediaContainer.getBoundingClientRect(); const { - width: mediaWidth, - height: mediaHeight, + top, left, width, height, } = mediaContainerRect; - const x = mediaWidth - ((webcamWidth + 10) * userLength); // 10 is margin - const y = mediaHeight - ((webcamHeight + 10)); // 10 is margin + if (mediaState.width === 0 || mediaState.height === 0) { + webcamDraggableDispatch( + { + type: 'setMediaSize', + value: { + width, + height, + }, + }, + ); + } - if (x === 0 && y === 0) return false; - - this.setState({ - initialRectPosition: { - x, - y, - }, - }); - return true; + return { + top, + left, + width, + height, + }; } return false; } - setLastWebcamPosition() { - const { refMediaContainer, usersVideo, floatingOverlay } = this.props; - const { current: mediaContainer } = refMediaContainer; - const { - initialRectPosition, - dragging, - dropOnTop, - dropOnBottom, - } = this.state; - const userLength = usersVideo.length; - - const { x: initX, y: initY } = initialRectPosition; - const webcamBySelector = WebcamDraggableOverlay.getWebcamGridBySelector(); - - if (webcamBySelector && mediaContainer && this.shouldUpdatePosition) { - const webcamBySelectorRect = webcamBySelector.getBoundingClientRect(); + getWebcamsListBounds() { + const { webcamDraggableState, singleWebcam } = this.props; + const { videoListRef } = webcamDraggableState; + if (videoListRef) { + const videoListRefRect = videoListRef.getBoundingClientRect(); const { - left: webcamLeft, - top: webcamTop, - } = webcamBySelectorRect; - - const mediaContainerRect = mediaContainer.getBoundingClientRect(); - const { - left: mediaLeft, - top: mediaTop, - } = mediaContainerRect; - - const webcamXByMedia = webcamLeft - mediaLeft; - const webcamYByMedia = webcamTop - mediaTop; - - let x = -(initX - webcamXByMedia); - x = floatingOverlay ? -((initX - webcamXByMedia) + 10) : x; - x = userLength > 1 ? 0 : x; - - x = !dragging && webcamXByMedia < 0 ? -initX : x; - - let y = -(initY - webcamYByMedia); - y = webcamYByMedia < 0 ? -initY : y; - - y = userLength > 1 && dropOnTop ? -initY : y; - y = userLength > 1 && dropOnBottom ? 0 : y; - - y = y < -initY ? -initY : y; - y = y > 0 ? 0 : y; - - this.setLastPosition(x, y); + top, left, width, height, + } = videoListRefRect; + return { + top: top - 10, // 10 = margin + left: left - (singleWebcam ? 10 : 0), // 10 = margin + width: width + (singleWebcam ? 20 : 0), // 20 = margin + height: height + 20, // 20 = margin + }; } + return false; } - setisMinWidth(isMinWidth) { - this.setState({ isMinWidth }); + calculatePosition() { + const { top: mediaTop, left: mediaLeft } = this.getMediaBounds(); + const { top: webcamsListTop, left: webcamsListLeft } = this.getWebcamsListBounds(); + const x = webcamsListLeft - mediaLeft; + const y = webcamsListTop - mediaTop; + return { + x, + y, + }; } - videoMounted() { - this.setResetPosition(true); - WebcamDraggableOverlay.waitFor(this.setInitialReferencePoint, this.setLastWebcamPosition); - this.setState({ isVideoLoaded: true }); - } + async handleWebcamDragStart() { + const { webcamDraggableDispatch, singleWebcam } = this.props; + const { x, y } = await this.calculatePosition(); - fullscreenButtonChange() { - this.setIsFullScreen(true); - } + webcamDraggableDispatch({ type: 'dragStart' }); - updateWebcamPositionByResize() { - const { - isVideoLoaded, - isMinWidth, - } = this.state; - - if (isVideoLoaded) { - this.setInitialReferencePoint(); - this.setLastWebcamPosition(); - } - - if (window.innerWidth < 641) { - this.setisMinWidth(true); - this.setState({ dropOnTop: true }); - this.setResetPosition(true); - } else if (isMinWidth) { - this.setisMinWidth(false); - } - } - - eventVideoFocusChangeListener() { - setTimeout(() => { - this.setInitialReferencePoint(); - this.setLastWebcamPosition(); - }, 500); - } - - handleFullscreenChange() { - if (document.fullscreenElement - || document.webkitFullscreenElement - || document.mozFullScreenElement - || document.msFullscreenElement) { - window.removeEventListener('resize', this.eventResizeListener); - this.setIsFullScreen(true); - } else { - this.setIsFullScreen(false); - window.addEventListener('resize', this.eventResizeListener); - } - } - - handleWebcamDragStart() { - const { floatingOverlay } = this.props; - const { - dragging, - showDropZones, - dropOnTop, - dropOnBottom, - resetPosition, - } = this.state; - - if (!floatingOverlay && dropOnTop) WebcamDraggableOverlay.getOverlayBySelector().style.top = 0; - - if (!dragging) this.setState({ dragging: true }); - if (dropOnTop) this.setState({ dropOnTop: false }); - if (dropOnBottom) this.setState({ dropOnBottom: false }); - if (!showDropZones) this.setState({ showDropZones: true }); - - if (resetPosition) this.setState({ resetPosition: false }); + webcamDraggableDispatch( + { + type: 'setTempPosition', + value: { + x: singleWebcam ? x : 0, + y, + }, + }, + ); } handleWebcamDragStop(e, position) { - const { - dragging, - showDropZones, - } = this.state; - + const { webcamDraggableDispatch, singleWebcam } = this.props; + const targetClassname = e.target.className; const { x, y } = position; - if (dragging) this.setState({ dragging: false }); - if (showDropZones) this.setState({ showDropZones: false }); - - this.setLastPosition(x, y); - window.dispatchEvent(new Event('resize')); - } - - dropZoneTopEnterHandler() { - const { - showBgDropZoneTop, - } = this.state; - - if (!showBgDropZoneTop) this.setState({ showBgDropZoneTop: true }); - } - - dropZoneBottomEnterHandler() { - const { - showBgDropZoneBottom, - } = this.state; - - if (!showBgDropZoneBottom) this.setState({ showBgDropZoneBottom: true }); - } - - dropZoneTopLeaveHandler() { - const { - showBgDropZoneTop, - } = this.state; - - if (showBgDropZoneTop) this.setState({ showBgDropZoneTop: false }); - } - - dropZoneBottomLeaveHandler() { - const { - showBgDropZoneBottom, - } = this.state; - - if (showBgDropZoneBottom) this.setState({ showBgDropZoneBottom: false }); - } - - dropZoneTopMouseUpHandler() { - const { dropOnTop } = this.state; - if (!dropOnTop) { - this.setState({ - dropOnTop: true, - dropOnBottom: false, - resetPosition: true, - }); + if (targetClassname.includes('Top')) { + webcamDraggableDispatch({ type: 'setplacementToTop' }); + } else if (targetClassname.includes('Bottom')) { + webcamDraggableDispatch({ type: 'setplacementToBottom' }); + } else if (singleWebcam) { + webcamDraggableDispatch( + { + type: 'setLastPosition', + value: { + x, + y, + }, + }, + ); + webcamDraggableDispatch({ type: 'setplacementToFloating' }); } + webcamDraggableDispatch({ type: 'dragEnd' }); window.dispatchEvent(new Event('resize')); - setTimeout(() => this.setLastWebcamPosition(), 500); - } - - dropZoneBottomMouseUpHandler() { - const { dropOnBottom } = this.state; - if (!dropOnBottom) { - this.setState({ - dropOnTop: false, - dropOnBottom: true, - resetPosition: true, - }); - } - window.dispatchEvent(new Event('resize')); - setTimeout(() => this.setLastWebcamPosition(), 500); } render() { const { + webcamDraggableState, + singleWebcam, swapLayout, - floatingOverlay, hideOverlay, disableVideo, audioModalIsOpen, - refMediaContainer, - usersVideo, } = this.props; - const userLength = usersVideo.length; - - const { current: mediaContainer } = refMediaContainer; - - let mediaContainerRect; - let mediaHeight; - if (mediaContainer) { - mediaContainerRect = mediaContainer.getBoundingClientRect(); - const { - height, - } = mediaContainerRect; - mediaHeight = height; + const { dragging, isFullscreen } = webcamDraggableState; + let placement = Storage.getItem('webcamPlacement'); + const lastPosition = Storage.getItem('webcamLastPosition') || { x: 0, y: 0 }; + let position = lastPosition; + if (!placement) { + placement = webcamsDefaultPlacement; } + if (dragging) { + position = webcamDraggableState.tempPosition; + } else if (!dragging && placement === 'floating' && singleWebcam) { + position = webcamDraggableState.lastPosition; + } else { + position = { + x: 0, + y: 0, + }; + } + + if (swapLayout || isFullscreen || BROWSER_ISMOBILE) { + position = { + x: 0, + y: 0, + }; + } const { - dragging, - showDropZones, - showBgDropZoneTop, - showBgDropZoneBottom, - dropOnTop, - dropOnBottom, - initialPosition, - lastPosition, - resetPosition, - isFullScreen, - isMinWidth, - } = this.state; + width: mediaWidth, + height: mediaHeight, + } = this.getMediaBounds(); + + const { + width: webcamsWidth, + height: webcamsHeight, + } = this.getWebcamsListBounds(); + + const isOverflowWidth = (lastPosition.x + webcamsWidth) > mediaWidth; + const isOverflowHeight = (lastPosition.y + webcamsHeight) > mediaHeight; + + position = { + x: isOverflowWidth + && !dragging && !swapLayout && singleWebcam && placement === 'floating' ? mediaWidth - webcamsWidth : position.x, + y: isOverflowHeight + && !dragging && !swapLayout && singleWebcam && placement === 'floating' ? mediaHeight - (webcamsHeight + 1) : position.y, + }; const contentClassName = cx({ [styles.content]: true, @@ -490,62 +214,50 @@ export default class WebcamDraggableOverlay extends Component { const overlayClassName = cx({ [styles.overlay]: true, - [styles.overlayRelative]: (dropOnTop || dropOnBottom), - [styles.overlayAbsoluteMult]: (!dropOnTop && !dropOnBottom) && userLength > 1, [styles.hideOverlay]: hideOverlay, - [styles.floatingOverlay]: floatingOverlay && (!dropOnTop && !dropOnBottom), - [styles.overlayToTop]: dropOnTop, - [styles.overlayToBottom]: dropOnBottom, + [styles.floatingOverlay]: (singleWebcam && placement === 'floating') || dragging, + [styles.fit]: singleWebcam && (placement === 'floating' || dragging), + [styles.full]: (singleWebcam && (placement === 'top' || placement === 'bottom') + && !dragging) + || !singleWebcam, + [styles.overlayToTop]: (placement === 'floating' && !singleWebcam) + || (placement === 'top' && !dragging), + [styles.overlayToBottom]: placement === 'bottom' && !dragging, [styles.dragging]: dragging, }); const dropZoneTopClassName = cx({ [styles.dropZoneTop]: true, - [styles.show]: showDropZones, - [styles.hide]: !showDropZones, + [styles.show]: dragging, + [styles.hide]: !dragging, + [styles.cursorGrabbing]: dragging, }); const dropZoneBottomClassName = cx({ [styles.dropZoneBottom]: true, - [styles.show]: showDropZones, - [styles.hide]: !showDropZones, + [styles.show]: dragging, + [styles.hide]: !dragging, + [styles.cursorGrabbing]: dragging, }); const dropZoneBgTopClassName = cx({ - [styles.dropZoneBg]: true, - [styles.top]: true, - [styles.show]: showBgDropZoneTop, - [styles.hide]: !showBgDropZoneTop, + [styles.dropZoneBgTop]: true, }); const dropZoneBgBottomClassName = cx({ - [styles.dropZoneBg]: true, - [styles.bottom]: true, - [styles.show]: showBgDropZoneBottom, - [styles.hide]: !showBgDropZoneBottom, + [styles.dropZoneBgBottom]: true, }); - const cursor = () => { - if ((!swapLayout || !isFullScreen || !BROWSER_ISMOBILE || !isMinWidth) && !dragging) return 'grab'; - if (dragging) return 'grabbing'; - return 'default'; - }; - return (
1 ? '50%' : '20%' }} - /> -
1 ? '50%' : '20%' }} - /> + style={{ height: !singleWebcam ? '50%' : '20%' }} + > +
+
e.preventDefault()} - disabled={swapLayout || isFullScreen || BROWSER_ISMOBILE || isMinWidth} - position={resetPosition || swapLayout ? initialPosition : lastPosition} + disabled={swapLayout || isFullscreen || BROWSER_ISMOBILE} + position={position} >
- { - !disableVideo && !audioModalIsOpen - ? ( - - ) : null} + {!disableVideo && !audioModalIsOpen ? ( + + ) : null}
1 ? '50%' : '20%' }} - /> -
1 ? '50%' : '20%' }} - /> + style={{ height: !singleWebcam ? '50%' : '20%' }} + > +
+
); } } -WebcamDraggableOverlay.propTypes = propTypes; -WebcamDraggableOverlay.defaultProps = defaultProps; +export default withDraggableContext(WebcamDraggable); diff --git a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx new file mode 100644 index 0000000000..de962e2af3 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx @@ -0,0 +1,178 @@ +import React, { createContext, useReducer, useEffect } from 'react'; +import Storage from '../../../services/storage/session'; + +export const WebcamDraggableContext = createContext(); + +const initialState = { + placement: 'top', + mediaSize: { + width: 0, + height: 0, + }, + initialRef: { + x: 0, + y: 0, + }, + tempPosition: { + x: 0, + y: 0, + }, + lastPosition: { + x: 0, + y: 0, + }, + dragging: false, + videoRef: null, + videoListRef: null, + isFullscreen: false, +}; + +const reducer = (state, action) => { + switch (action.type) { + case 'setplacementToTop': { + return { + ...state, + placement: 'top', + }; + } + case 'setplacementToBottom': { + return { + ...state, + placement: 'bottom', + }; + } + case 'setplacementToFloating': { + return { + ...state, + placement: 'floating', + }; + } + case 'setMediaSize': { + return { + ...state, + mediaSize: { + width: action.value.width, + height: action.value.height, + }, + }; + } + case 'setWebcamRef': { + return { + ...state, + webcamRef: action.value, + }; + } + case 'setInitialRef': { + return { + ...state, + initialRef: { + x: action.value.x, + y: action.value.y, + }, + }; + } + case 'setTempPosition': { + return { + ...state, + tempPosition: { + x: action.value.x, + y: action.value.y, + }, + }; + } + case 'setLastPosition': { + return { + ...state, + lastPosition: { + x: action.value.x, + y: action.value.y, + }, + }; + } + case 'setVideoRef': { + return { + ...state, + videoRef: action.value, + }; + } + case 'setVideoListRef': { + return { + ...state, + videoListRef: action.value, + }; + } + case 'dragStart': { + return { + ...state, + dragging: true, + }; + } + case 'dragEnd': { + return { + ...state, + dragging: false, + }; + } + case 'onFullscreen': { + return { + ...state, + isFullscreen: true, + }; + } + case 'offFullscreen': { + return { + ...state, + isFullscreen: false, + }; + } + default: { + throw new Error('Unexpected action'); + } + } +}; + +const ContextConsumer = Component => props => ( + + {contexts => } + +); + +const ContextProvider = (props) => { + const [webcamDraggableState, webcamDraggableDispatch] = useReducer(reducer, initialState); + const { placement, lastPosition } = webcamDraggableState; + const { children } = props; + useEffect(() => { + Storage.setItem('webcamPlacement', placement); + Storage.setItem('webcamLastPosition', lastPosition); + }, [ + placement, + lastPosition, + ]); + + return ( + + {children} + + ); +}; + +const withProvider = Component => props => ( + + + +); + +const withConsumer = Component => ContextConsumer(Component); + +const withDraggableContext = Component => withProvider(withConsumer(Component)); + +export { + withProvider, + withConsumer, + withDraggableContext, +}; diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx index 6206696881..bc4ffb595e 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx @@ -161,9 +161,6 @@ class VideoProvider extends Component { } componentDidMount() { - const { onMount } = this.props; - onMount(); - this.checkIceConnectivity(); document.addEventListener('joinVideo', this.shareWebcam); // TODO find a better way to do this document.addEventListener('exitVideo', this.unshareWebcam); @@ -1017,14 +1014,10 @@ class VideoProvider extends Component { const { users, enableVideoStats, - cursor, - swapLayout, mediaHeight, } = this.props; return ( { export default withTracker(props => ({ cursor: props.cursor, swapLayout: props.swapLayout, + mediaHeight: props.mediaHeight, meetingId: VideoService.meetingId(), users: VideoService.getAllUsersVideo(), userId: VideoService.userId(), @@ -19,5 +20,4 @@ export default withTracker(props => ({ userName: VideoService.userName(), enableVideoStats: getFromUserSettings('enableVideoStats', Meteor.settings.public.kurento.enableVideoStats), voiceBridge: VideoService.voiceBridge(), - onMount: props.onMount, }))(VideoProviderContainer); diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx index e0c23e436d..9718f1476a 100644 --- a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx @@ -5,6 +5,7 @@ import cx from 'classnames'; import _ from 'lodash'; import { styles } from './styles'; import VideoListItem from './video-list-item/component'; +import { withConsumer } from '../../media/webcam-draggable-overlay/context'; const propTypes = { users: PropTypes.arrayOf(PropTypes.object).isRequired, @@ -77,6 +78,14 @@ class VideoList extends Component { } componentDidMount() { + const { webcamDraggableDispatch } = this.props; + webcamDraggableDispatch( + { + type: 'setVideoListRef', + value: this.grid, + }, + ); + this.handleCanvasResize(); window.addEventListener('resize', this.handleCanvasResize, false); } @@ -93,6 +102,7 @@ class VideoList extends Component { } const { focusedId } = this.state; const { width: canvasWidth, height: canvasHeight } = this.canvas.getBoundingClientRect(); + const gridGutter = parseInt(window.getComputedStyle(this.grid) .getPropertyValue('grid-row-gap'), 10); const hasFocusedItem = numItems > 2 && focusedId; @@ -142,8 +152,6 @@ class VideoList extends Component { getStats, stopGettingStats, enableVideoStats, - cursor, - swapLayout, } = this.props; const { focusedId } = this.state; @@ -167,9 +175,6 @@ class VideoList extends Component { [styles.videoListItem]: true, [styles.focused]: focusedId === user.id && users.length > 2, })} - style={{ - cursor, - }} > getStats(user.id, videoRef, callback)} stopGettingStats={() => stopGettingStats(user.id)} enableVideoStats={enableVideoStats} - swapLayout={swapLayout} />
); @@ -231,4 +235,4 @@ class VideoList extends Component { VideoList.propTypes = propTypes; -export default injectIntl(VideoList); +export default injectIntl(withConsumer(VideoList)); diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/styles.scss b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/styles.scss index 88edd08c26..57849ba1c9 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/styles.scss @@ -39,6 +39,9 @@ .videoListItem { display: flex; + overflow: hidden; + width: 100%; + height: 100%; &.focused { grid-column: 1 / span 2; @@ -50,7 +53,6 @@ position: relative; display: flex; min-width: 100%; - height: 100%; border-radius: 5px; &::after { @@ -83,6 +85,14 @@ border-radius: 5px; } +.cursorGrab{ + cursor: grab; +} + +.cursorGrabbing{ + cursor: grabbing; +} + @keyframes spin { from { transform: rotate(0deg); @@ -129,6 +139,7 @@ .media { @extend %media-area; + background-color: var(--color-gray); } .info { diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx index 5a67965a6d..fc7b9ae6ad 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx @@ -16,6 +16,7 @@ import logger from '/imports/startup/client/logger'; import VideoListItemStats from './video-list-item-stats/component'; import FullscreenButtonContainer from '../../fullscreen-button/container'; import { styles } from '../styles'; +import { withConsumer } from '../../../media/webcam-draggable-overlay/context'; const intlMessages = defineMessages({ connectionStatsLabel: { @@ -40,7 +41,15 @@ class VideoListItem extends Component { } componentDidMount() { - const { onMount } = this.props; + const { onMount, webcamDraggableDispatch } = this.props; + + webcamDraggableDispatch( + { + type: 'setVideoRef', + value: this.videoTag, + }, + ); + onMount(this.videoTag); this.videoTag.addEventListener('loadeddata', () => this.setVideoIsReady()); @@ -130,7 +139,9 @@ class VideoListItem extends Component { render() { const { showStats, stats, videoIsReady } = this.state; const { - user, numOfUsers, + user, + numOfUsers, + webcamDraggableState, } = this.props; const availableActions = this.getAvailableActions(); const enableVideoMenu = Meteor.settings.public.kurento.enableVideoMenu || false; @@ -149,6 +160,8 @@ class VideoListItem extends Component { muted className={cx({ [styles.media]: true, + [styles.cursorGrab]: !webcamDraggableState.dragging, + [styles.cursorGrabbing]: webcamDraggableState.dragging, })} ref={(ref) => { this.videoTag = ref; }} autoPlay @@ -198,7 +211,7 @@ class VideoListItem extends Component { } } -export default injectIntl(VideoListItem); +export default injectIntl(withConsumer(VideoListItem)); VideoListItem.defaultProps = { numOfUsers: 0,