From e2db60e662d9a7e3c8619249ed9a90309daa55b6 Mon Sep 17 00:00:00 2001 From: Vitor Mateus <vitormateusalmeida@gmail.com> Date: Thu, 23 Apr 2020 11:07:44 -0300 Subject: [PATCH] WIP Resizable works fine --- .../imports/startup/client/base.jsx | 5 + .../imports/ui/components/layout/context.jsx | 68 ++- .../ui/components/layout/layout-manager.jsx | 202 ++++++--- .../imports/ui/components/media/component.jsx | 32 +- .../imports/ui/components/media/container.jsx | 5 +- .../webcam-draggable-overlay/component.jsx | 400 ++++++++---------- .../webcam-draggable-overlay/context.jsx | 95 ----- .../ui/components/nav-bar/component.jsx | 2 - .../ui/components/presentation/component.jsx | 84 ++-- .../components/video-provider/component.jsx | 69 +-- .../video-provider/video-list/component.jsx | 6 + 11 files changed, 487 insertions(+), 481 deletions(-) diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx index 54995be0b6..62bcb2ddaf 100755 --- a/bigbluebutton-html5/imports/startup/client/base.jsx +++ b/bigbluebutton-html5/imports/startup/client/base.jsx @@ -21,6 +21,7 @@ import getFromUserSettings from '/imports/ui/services/users-settings'; import LayoutManager from '/imports/ui/components/layout/layout-manager'; import { withLayoutContext } from '/imports/ui/components/layout/context'; import IntlStartup from '/imports/startup/client/intl'; +import VideoService from '/imports/ui/components/video-provider/service'; const CHAT_CONFIG = Meteor.settings.public.chat; const CHAT_ENABLED = CHAT_CONFIG.enabled; @@ -215,6 +216,7 @@ class Base extends Component { locale, layoutContextState, layoutContextDispatch, + usersVideo, } = this.props; const { updateLoadingState } = this; @@ -226,6 +228,7 @@ class Base extends Component { <LayoutManager layoutContextState={layoutContextState} layoutContextDispatch={layoutContextDispatch} + usersVideo={usersVideo} /> { (!meetingExisted && !meetingExist && Auth.loggedIn) @@ -389,6 +392,7 @@ const BaseContainer = withTracker(() => { } const codeError = Session.get('codeError'); + const usersVideo = VideoService.getVideoStreams(); return { approved, @@ -405,6 +409,7 @@ const BaseContainer = withTracker(() => { subscriptionsReady: Session.get('subscriptionsReady'), loggedIn, codeError, + usersVideo, }; })(withLayoutContext(Base)); diff --git a/bigbluebutton-html5/imports/ui/components/layout/context.jsx b/bigbluebutton-html5/imports/ui/components/layout/context.jsx index 56eef52c34..fe07caca4a 100644 --- a/bigbluebutton-html5/imports/ui/components/layout/context.jsx +++ b/bigbluebutton-html5/imports/ui/components/layout/context.jsx @@ -1,12 +1,22 @@ -import React, { createContext, useReducer } from 'react'; +import React, { createContext, useReducer, useEffect } from 'react'; +import Storage from '/imports/ui/services/storage/session'; + +const { webcamsDefaultPlacement } = Meteor.settings.public.layout; export const LayoutContext = createContext(); const initialState = { + autoArrangeLayout: true, windowSize: { width: 0, height: 0, }, + mediaBounds: { + width: 0, + height: 0, + top: 0, + left: 0, + }, userListSize: { width: 0, }, @@ -17,15 +27,27 @@ const initialState = { width: 0, height: 0, }, + percentWebcamsAreaUserSets: { + width: 0, + height: 0, + }, + webcamsPlacement: webcamsDefaultPlacement || 'top', presentationAreaSize: { width: 0, height: 0, }, presentationIsFullscreen: null, + presentationOrientation: null, }; const reducer = (state, action) => { switch (action.type) { + case 'setAutoArrangeLayout': { + return { + ...state, + autoArrangeLayout: action.value, + }; + } case 'setWindowSize': { return { ...state, @@ -35,6 +57,17 @@ const reducer = (state, action) => { }, }; } + case 'setMediaBounds': { + return { + ...state, + mediaBounds: { + width: action.value.width, + height: action.value.height, + top: action.value.top, + left: action.value.left, + }, + }; + } case 'setUserListSize': { return { ...state, @@ -51,6 +84,13 @@ const reducer = (state, action) => { }, }; } + case 'setWebcamsPlacement': { + // webcamsPlacement: ('top' | 'right' | 'bottom' | 'left') string + return { + ...state, + webcamsPlacement: action.value, + }; + } case 'setWebcamsAreaSize': { return { ...state, @@ -60,6 +100,15 @@ const reducer = (state, action) => { }, }; } + case 'setPercentWebcamsAreaUserSets': { + return { + ...state, + percentWebcamsAreaUserSets: { + width: action.value.width, + height: action.value.height, + }, + }; + } case 'setPresentationAreaSize': { return { ...state, @@ -70,11 +119,19 @@ const reducer = (state, action) => { }; } case 'setPresentationFullscreen': { + // presentationIsFullscreen: (true | false) boolean return { ...state, presentationIsFullscreen: action.value, }; } + case 'setPresentationOrientation': { + // presentationOrientation: ('portrait' | 'landscape') string + return { + ...state, + presentationOrientation: action.value, + }; + } default: { throw new Error('Unexpected action'); } @@ -83,8 +140,17 @@ const reducer = (state, action) => { const ContextProvider = (props) => { const [layoutContextState, layoutContextDispatch] = useReducer(reducer, initialState); + const { webcamsPlacement, autoArrangeLayout } = layoutContextState; const { children } = props; + useEffect(() => { + Storage.setItem('webcamsPlacement', webcamsPlacement); + Storage.setItem('autoArrangeLayout', autoArrangeLayout); + }, [ + webcamsPlacement, + autoArrangeLayout, + ]); + return ( <LayoutContext.Provider value={{ layoutContextState, diff --git a/bigbluebutton-html5/imports/ui/components/layout/layout-manager.jsx b/bigbluebutton-html5/imports/ui/components/layout/layout-manager.jsx index 36c625b374..0094791e8c 100644 --- a/bigbluebutton-html5/imports/ui/components/layout/layout-manager.jsx +++ b/bigbluebutton-html5/imports/ui/components/layout/layout-manager.jsx @@ -16,6 +16,16 @@ const CHAT_MAX_WIDTH = 335; const NAVBAR_HEIGHT = 85; const ACTIONSBAR_HEIGHT = 42; +const WEBCAMSAREA_MIN_PERCENT = 0.2; +const WEBCAMSAREA_MAX_PERCENT = 0.8; + +// TODO function calPercentScreenOfUserSets +// Salvar porcentagem de uso do media setado pelo usuário +// Auto arange default true +// Não pegar só proporção e sim calcular área disponÃvel +// Primeiro calculo pega orientação e medidas originais da apresentação +// Segundo calculo olha para os valores e verifica se tem overflow e recalcula + const storageLayoutData = () => Storage.getItem('layoutData'); class LayoutManager extends Component { @@ -23,7 +33,7 @@ class LayoutManager extends Component { super(props); this.setLayoutSizes = this.setLayoutSizes.bind(this); - this.calculaLayout = this.calculaLayout.bind(this); + this.calculatesLayout = this.calculatesLayout.bind(this); } componentDidMount() { @@ -42,57 +52,31 @@ class LayoutManager extends Component { }); } - setLayoutSizes(userlistChanged = false, chatChanged = false) { - const { layoutContextDispatch } = this.props; - - let userListWidth; - let chatWidth; - let webcamsAreaWidth; - let presentationAreaWidth; - let webcamsAreaHeight; - let storageWindowWidth; - let presentationAreaHeight; + componentDidUpdate(prevProps) { + const { usersVideo, layoutContextState } = this.props; + const { usersVideo: prevUsersVideo, layoutContextState: prevLayoutContextState } = prevProps; + const { webcamsPlacement, webcamsAreaSize } = layoutContextState; + const { + webcamsPlacement: prevWebcamsPlacement, + webcamsAreaSize: prevWebcamsAreaSize, + } = prevLayoutContextState; - const layoutSizes = this.calculaLayout(userlistChanged, chatChanged); - // Get storage data and set in size variables - const storageLData = storageLayoutData(); - if (storageLData) { - storageWindowWidth = storageLData.windowSize.width; - userListWidth = storageLData.userListSize.width; - chatWidth = storageLData.chatSize.width; - webcamsAreaWidth = storageLData.webcamsAreaSize.width; - webcamsAreaHeight = storageLData.webcamsAreaSize.height; - presentationAreaWidth = storageLData.presentationAreaSize.width; - presentationAreaHeight = storageLData.presentationAreaSize.height; + if (usersVideo.length !== prevUsersVideo.length + || webcamsPlacement !== prevWebcamsPlacement) { + this.setLayoutSizes(); } - // If storage data does not exist or window size is changed or layout is changed - // (userlist or chat list size changed) - // Get size from calc function - if (!userListWidth - || windowWidth() !== storageWindowWidth - || userlistChanged - || chatChanged) { - userListWidth = layoutSizes.userListSize.width; - } - if (!chatWidth - || windowWidth() !== storageWindowWidth - || chatChanged - || userlistChanged) { - chatWidth = layoutSizes.chatSize.width; - } - if (!storageLayoutData() || windowWidth() !== storageWindowWidth) { - webcamsAreaWidth = layoutSizes.webcamsAreaSize.width; - webcamsAreaHeight = layoutSizes.webcamsAreaSize.height; - } - if (!storageLayoutData() - || windowWidth() !== storageWindowWidth - || layoutSizes.presentationAreaSize.width !== storageLayoutData().presentationAreaSize.width - || userlistChanged - || chatChanged) { - presentationAreaWidth = layoutSizes.presentationAreaSize.width; - presentationAreaHeight = layoutSizes.presentationAreaSize.height; + if (webcamsAreaSize.width !== prevWebcamsAreaSize.width + || webcamsAreaSize.height !== prevWebcamsAreaSize.height) { + this.setLayoutSizes(true, true, webcamsAreaSize); } + } + + setLayoutSizes(userlistChanged = false, chatChanged = false, webcamsAreaSize = null) { + const { layoutContextDispatch } = this.props; + + const layoutSizes = this + .calculatesLayout(userlistChanged, chatChanged, webcamsAreaSize); layoutContextDispatch( { @@ -103,11 +87,22 @@ class LayoutManager extends Component { }, }, ); + layoutContextDispatch( + { + type: 'setMediaBounds', + value: { + width: layoutSizes.mediaBounds.width, + height: layoutSizes.mediaBounds.height, + top: layoutSizes.mediaBounds.top, + left: layoutSizes.mediaBounds.left, + }, + }, + ); layoutContextDispatch( { type: 'setUserListSize', value: { - width: userListWidth, + width: layoutSizes.userListSize.width, }, }, ); @@ -115,7 +110,7 @@ class LayoutManager extends Component { { type: 'setChatSize', value: { - width: chatWidth, + width: layoutSizes.chatSize.width, }, }, ); @@ -123,8 +118,8 @@ class LayoutManager extends Component { { type: 'setWebcamsAreaSize', value: { - width: webcamsAreaWidth, - height: webcamsAreaHeight, + width: layoutSizes.webcamsAreaSize.width, + height: layoutSizes.webcamsAreaSize.height, }, }, ); @@ -132,8 +127,8 @@ class LayoutManager extends Component { { type: 'setPresentationAreaSize', value: { - width: presentationAreaWidth, - height: presentationAreaHeight, + width: layoutSizes.presentationAreaSize.width, + height: layoutSizes.presentationAreaSize.height, }, }, ); @@ -143,35 +138,49 @@ class LayoutManager extends Component { width: windowWidth(), height: windowHeight(), }, + mediaBounds: { + width: layoutSizes.mediaBounds.width, + height: layoutSizes.mediaBounds.height, + top: layoutSizes.mediaBounds.top, + left: layoutSizes.mediaBounds.left, + }, userListSize: { - width: userListWidth, + width: layoutSizes.userListSize.width, }, chatSize: { - width: chatWidth, + width: layoutSizes.chatSize.width, }, webcamsAreaSize: { - width: webcamsAreaWidth, - height: webcamsAreaHeight, + width: layoutSizes.webcamsAreaSize.width, + height: layoutSizes.webcamsAreaSize.height, }, presentationAreaSize: { - width: presentationAreaWidth, - height: presentationAreaHeight, + width: layoutSizes.presentationAreaSize.width, + height: layoutSizes.presentationAreaSize.height, }, }; Storage.setItem('layoutData', newLayoutData); } - calculaLayout(userlistChanged = false, chatChanged = false) { - const { layoutContextState } = this.props; + calculatesLayout(userlistChanged = false, chatChanged = false) { const { + layoutContextState, + usersVideo, + } = this.props; + const { + webcamsAreaSize, userListSize: userListSizeContext, chatSize: chatSizeContext, presentationIsFullscreen, + presentationOrientation, + webcamsPlacement, } = layoutContextState; const openPanel = Session.get('openPanel'); const storageLData = storageLayoutData(); + const autoArrangeLayout = Storage.getItem('autoArrangeLayout'); + let storageUserListWidth; let storageChatWidth; if (storageLData) { @@ -220,15 +229,75 @@ class LayoutManager extends Component { }; } + const fullAreaHeight = windowHeight() - (NAVBAR_HEIGHT + ACTIONSBAR_HEIGHT); + const fullAreaWidth = windowWidth() - (newUserListSize.width + newChatSize.width); + + + const newMediaBounds = { + width: fullAreaWidth, + height: fullAreaHeight, + top: NAVBAR_HEIGHT, + left: newUserListSize.width + newChatSize.width, + }; + console.log('webcamsAreaSize', webcamsAreaSize); + console.log('autoArrangeLayout', autoArrangeLayout); + + + let newWebcamAreaHeight; + let newWebcamAreaWidth; + let newPresentationAreaHeight; + let newPresentationAreaWidth; + if (usersVideo.length > 0) { + if (presentationOrientation === 'portrait' + || webcamsPlacement === 'left' + || webcamsPlacement === 'right' + ) { + newWebcamAreaWidth = min( + max( + webcamsAreaSize && autoArrangeLayout === false + ? webcamsAreaSize.width + : fullAreaWidth * WEBCAMSAREA_MIN_PERCENT, + fullAreaWidth * WEBCAMSAREA_MIN_PERCENT, + ), + fullAreaWidth * WEBCAMSAREA_MAX_PERCENT, + ); + newWebcamAreaHeight = fullAreaHeight; + + newPresentationAreaWidth = fullAreaWidth - newWebcamAreaWidth - 30; // 30 is margins + newPresentationAreaHeight = fullAreaHeight - 20; // 20 is margins + } else { + newWebcamAreaWidth = fullAreaWidth; + newWebcamAreaHeight = min( + max( + webcamsAreaSize && autoArrangeLayout === false + ? webcamsAreaSize.height + : fullAreaHeight * WEBCAMSAREA_MIN_PERCENT, + fullAreaHeight * WEBCAMSAREA_MIN_PERCENT, + ), + fullAreaHeight * WEBCAMSAREA_MAX_PERCENT, + ); + + newPresentationAreaWidth = fullAreaWidth; + newPresentationAreaHeight = fullAreaHeight - newWebcamAreaHeight - 30; // 30 is margins + } + } else { + newWebcamAreaWidth = 0; + newWebcamAreaHeight = 0; + + newPresentationAreaWidth = fullAreaWidth; + newPresentationAreaHeight = fullAreaHeight - 20; // 20 is margins + } + const newWebcamsAreaSize = { - width: windowWidth() - (newUserListSize.width + newChatSize.width), + width: newWebcamAreaWidth, + height: newWebcamAreaHeight, }; let newPresentationAreaSize; if (!presentationIsFullscreen) { newPresentationAreaSize = { - width: windowWidth() - (newUserListSize.width + newChatSize.width), - height: windowHeight() - (NAVBAR_HEIGHT + ACTIONSBAR_HEIGHT), + width: newPresentationAreaWidth, + height: newPresentationAreaHeight, }; } else { newPresentationAreaSize = { @@ -238,6 +307,7 @@ class LayoutManager extends Component { } return { + mediaBounds: newMediaBounds, userListSize: newUserListSize, chatSize: newChatSize, webcamsAreaSize: newWebcamsAreaSize, @@ -258,4 +328,6 @@ export { CHAT_MAX_WIDTH, NAVBAR_HEIGHT, ACTIONSBAR_HEIGHT, + WEBCAMSAREA_MIN_PERCENT, + WEBCAMSAREA_MAX_PERCENT, }; diff --git a/bigbluebutton-html5/imports/ui/components/media/component.jsx b/bigbluebutton-html5/imports/ui/components/media/component.jsx index 36c17f52fe..9f7c18c054 100644 --- a/bigbluebutton-html5/imports/ui/components/media/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/media/component.jsx @@ -16,7 +16,7 @@ const propTypes = { swapLayout: PropTypes.bool, disableVideo: PropTypes.bool, audioModalIsOpen: PropTypes.bool, - webcamDraggableState: PropTypes.instanceOf(Object).isRequired, + layoutContextState: PropTypes.instanceOf(Object).isRequired, }; const defaultProps = { @@ -43,12 +43,12 @@ export default class Media extends Component { children, audioModalIsOpen, usersVideo, - webcamDraggableState, + layoutContextState, } = this.props; - const { placement } = webcamDraggableState; - const placementStorage = Storage.getItem('webcamPlacement'); - const webcamPlacement = placement || placementStorage; + const { webcamsPlacement: placement } = layoutContextState; + const placementStorage = Storage.getItem('webcamsPlacement'); + const webcamsPlacement = placement || placementStorage; const contentClassName = cx({ [styles.content]: true, @@ -57,13 +57,13 @@ export default class Media extends Component { const overlayClassName = cx({ [styles.overlay]: true, [styles.hideOverlay]: hideOverlay, - [styles.floatingOverlay]: (webcamPlacement === 'floating'), + [styles.floatingOverlay]: (webcamsPlacement === 'floating'), }); const containerClassName = cx({ [styles.container]: true, - [styles.containerV]: webcamPlacement === 'top' || webcamPlacement === 'bottom' || webcamPlacement === 'floating', - [styles.containerH]: webcamPlacement === 'left' || webcamPlacement === 'right', + [styles.containerV]: webcamsPlacement === 'top' || webcamsPlacement === 'bottom' || webcamsPlacement === 'floating', + [styles.containerH]: webcamsPlacement === 'left' || webcamsPlacement === 'right', }); return ( @@ -77,24 +77,24 @@ export default class Media extends Component { style={{ maxHeight: usersVideo.length > 0 && ( - webcamPlacement !== 'left' - || webcamPlacement !== 'right' + webcamsPlacement !== 'left' + || webcamsPlacement !== 'right' ) && ( - webcamPlacement === 'top' - || webcamPlacement === 'bottom' + webcamsPlacement === 'top' + || webcamsPlacement === 'bottom' ) ? '80%' : '100%', minHeight: BROWSER_ISMOBILE && window.innerWidth > window.innerHeight ? '50%' : '20%', maxWidth: usersVideo.length > 0 && ( - webcamPlacement !== 'top' - || webcamPlacement !== 'bottom' + webcamsPlacement !== 'top' + || webcamsPlacement !== 'bottom' ) && ( - webcamPlacement === 'left' - || webcamPlacement === 'right' + webcamsPlacement === 'left' + || webcamsPlacement === 'right' ) ? '80%' : '100%', diff --git a/bigbluebutton-html5/imports/ui/components/media/container.jsx b/bigbluebutton-html5/imports/ui/components/media/container.jsx index 4efa85853f..bd0091cd16 100755 --- a/bigbluebutton-html5/imports/ui/components/media/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/media/container.jsx @@ -16,6 +16,7 @@ import DefaultContent from '../presentation/default-content/component'; import ExternalVideoContainer from '../external-video-player/container'; import Storage from '../../services/storage/session'; import { withDraggableConsumer } from './webcam-draggable-overlay/context'; +import { withLayoutConsumer } from '/imports/ui/components/layout/context'; const LAYOUT_CONFIG = Meteor.settings.public.layout; const KURENTO_CONFIG = Meteor.settings.public.kurento; @@ -103,7 +104,7 @@ class MediaContainer extends Component { } } -export default withDraggableConsumer(withModalMounter(withTracker(() => { +export default withLayoutConsumer(withModalMounter(withTracker(() => { const { dataSaving } = Settings; const { viewParticipantsWebcams, viewScreenshare } = dataSaving; const hidePresentation = getFromUserSettings('bbb_hide_presentation', LAYOUT_CONFIG.hidePresentation); @@ -150,7 +151,7 @@ export default withDraggableConsumer(withModalMounter(withTracker(() => { ); } - data.webcamPlacement = Storage.getItem('webcamPlacement'); + data.webcamsPlacement = Storage.getItem('webcamsPlacement'); MediaContainer.propTypes = propTypes; return data; diff --git a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx index 40f82c4ee6..e5a9fe8f63 100644 --- a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx @@ -1,7 +1,6 @@ import React, { PureComponent, Fragment } from 'react'; import Draggable from 'react-draggable'; import cx from 'classnames'; -import _ from 'lodash'; import PropTypes from 'prop-types'; import Resizable from 're-resizable'; import { isMobile, isIPad13 } from 'react-device-detect'; @@ -9,6 +8,8 @@ import { withDraggableConsumer } from './context'; import VideoProviderContainer from '/imports/ui/components/video-provider/container'; import { styles } from '../styles.scss'; import Storage from '../../../services/storage/session'; +import { withLayoutConsumer } from '/imports/ui/components/layout/context'; +import { WEBCAMSAREA_MIN_PERCENT } from '/imports/ui/components/layout/layout-manager'; const BROWSER_ISMOBILE = isMobile || isIPad13; @@ -20,7 +21,6 @@ const propTypes = { webcamDraggableState: PropTypes.objectOf(Object).isRequired, webcamDraggableDispatch: PropTypes.func.isRequired, refMediaContainer: PropTypes.shape({ current: PropTypes.instanceOf(Element) }), - usersVideoLenght: PropTypes.number.isRequired, }; const defaultProps = { @@ -30,70 +30,110 @@ const defaultProps = { audioModalIsOpen: false, refMediaContainer: null, }; -const dispatchResizeEvent = () => window.dispatchEvent(new Event('resize')); class WebcamDraggable extends PureComponent { constructor(props) { super(props); + const { layoutContextState } = props; + const { webcamsPlacement, mediaBounds } = layoutContextState; + this.state = { + webcamsAreaResizable: { + width: webcamsPlacement === 'top' || webcamsPlacement === 'bottom' ? mediaBounds.width : mediaBounds.width * WEBCAMSAREA_MIN_PERCENT, + height: webcamsPlacement === 'left' || webcamsPlacement === 'right' ? mediaBounds.height : mediaBounds.height * WEBCAMSAREA_MIN_PERCENT, + }, + resizing: false, + }; + this.handleWebcamDragStart = this.handleWebcamDragStart.bind(this); this.handleWebcamDragStop = this.handleWebcamDragStop.bind(this); this.onFullscreenChange = this.onFullscreenChange.bind(this); - this.debouncedOnResize = _.debounce(this.onWindowResize.bind(this), 500); this.onResizeStop = this.onResizeStop.bind(this); this.onResizeStart = this.onResizeStart.bind(this); - this.setPlacementPercent = this.setPlacementPercent.bind(this); - this.recalculateAreaSize = this.recalculateAreaSize.bind(this); - - this.state = { - resizing: false, - placementPercent: 0, - }; } componentDidMount() { - dispatchResizeEvent(); - window.addEventListener('resize', this.debouncedOnResize); + const { webcamDraggableState } = this.props; + const { + lastPlacementLandscape, + lastPlacementPortrait, + } = webcamDraggableState; + const { layoutContextState, layoutContextDispatch } = this.props; + const { presentationOrientation } = layoutContextState; document.addEventListener('fullscreenchange', this.onFullscreenChange); window.addEventListener('orientationchange', () => setTimeout(this.recalculateAreaSize, 500)); + + if (presentationOrientation === 'landscape') { + layoutContextDispatch({ + type: 'setWebcamsPlacement', + value: !lastPlacementLandscape ? 'top' : lastPlacementLandscape, + }); + } + if (presentationOrientation === 'portrait') { + layoutContextDispatch({ + type: 'setWebcamsPlacement', + value: !lastPlacementPortrait ? 'left' : lastPlacementPortrait, + }); + } } componentDidUpdate(prevProps) { + // const { swapLayout } = this.props; + + // Webcam Draggable Context + const { webcamDraggableState } = this.props; const { - swapLayout, - webcamDraggableState, - webcamDraggableDispatch, - usersVideoLenght, - } = this.props; - const { - placement: statePlacement, - orientation, lastPlacementLandscape, lastPlacementPortrait, } = webcamDraggableState; - const { webcamDraggableState: prevWebcamDraggableState } = prevProps; - const { placement: prevPlacement, orientation: prevOrientation } = prevWebcamDraggableState; - if (prevProps.swapLayout !== swapLayout) { - setTimeout(() => this.forceUpdate(), 500); - } - if (prevPlacement !== statePlacement) { - setTimeout(() => this.forceUpdate(), 200); - setTimeout(() => window.dispatchEvent(new Event('resize')), 500); - } - if (prevProps.usersVideoLenght !== usersVideoLenght) { - dispatchResizeEvent(); + // Layout Context + const { layoutContextState, layoutContextDispatch } = this.props; + const { layoutContextState: prevLayoutContextState } = prevProps; + const { + webcamsAreaSize, + presentationOrientation, + } = layoutContextState; + const { + webcamsAreaSize: prevWebcamsAreaSize, + presentationOrientation: prevPresentationOrientation, + } = prevLayoutContextState; + + + if (webcamsAreaSize.width !== prevWebcamsAreaSize.width + || webcamsAreaSize.height !== prevWebcamsAreaSize.height) { + this.setWebcamsAreaResizable(webcamsAreaSize.width, webcamsAreaSize.height); } - if (prevOrientation !== orientation) { - const storagePlacement = Storage.getItem('webcamPlacement'); - if ((prevOrientation == null || prevOrientation === 'portrait') && orientation === 'landscape') { - if (storagePlacement !== lastPlacementLandscape && lastPlacementLandscape === 'top') webcamDraggableDispatch({ type: 'setplacementToTop' }); - if (storagePlacement !== lastPlacementLandscape && lastPlacementLandscape === 'bottom') webcamDraggableDispatch({ type: 'setplacementToBottom' }); + if (prevPresentationOrientation !== presentationOrientation) { + const storagePlacement = Storage.getItem('webcamsPlacement'); + if ((prevPresentationOrientation == null || prevPresentationOrientation === 'portrait') && presentationOrientation === 'landscape') { + if (storagePlacement !== lastPlacementLandscape && lastPlacementLandscape === 'top') { + layoutContextDispatch({ + type: 'setWebcamsPlacement', + value: 'top', + }); + } + if (storagePlacement !== lastPlacementLandscape && lastPlacementLandscape === 'bottom') { + layoutContextDispatch({ + type: 'setWebcamsPlacement', + value: 'bottom', + }); + } } - if ((prevOrientation == null || prevOrientation === 'landscape') && orientation === 'portrait') { - if (storagePlacement !== lastPlacementPortrait && lastPlacementPortrait === 'left') webcamDraggableDispatch({ type: 'setplacementToLeft' }); - if (storagePlacement !== lastPlacementPortrait && lastPlacementPortrait === 'right') webcamDraggableDispatch({ type: 'setplacementToRight' }); + if ((prevPresentationOrientation == null || prevPresentationOrientation === 'landscape') && presentationOrientation === 'portrait') { + if (storagePlacement !== lastPlacementPortrait && lastPlacementPortrait === 'left') { + layoutContextDispatch({ + type: 'setWebcamsPlacement', + value: 'left', + }); + } + if (storagePlacement !== lastPlacementPortrait && lastPlacementPortrait === 'right') { + layoutContextDispatch({ + type: 'setWebcamsPlacement', + value: 'right', + }); + } } } } @@ -101,7 +141,6 @@ class WebcamDraggable extends PureComponent { componentWillUnmount() { window.removeEventListener('resize', this.debouncedOnResize); document.removeEventListener('fullscreenchange', this.onFullscreenChange); - dispatchResizeEvent(); } onFullscreenChange() { @@ -112,93 +151,48 @@ class WebcamDraggable extends PureComponent { this.setState({ resizing: true }); } - onWindowResize() { - const { webcamDraggableState, webcamDraggableDispatch } = this.props; - const { mediaSize } = webcamDraggableState; - const { width: stateWidth, height: stateHeight } = mediaSize; - const { width, height } = this.getMediaBounds(); - - if (stateWidth !== width || stateHeight !== height) { - webcamDraggableDispatch( - { - type: 'setMediaSize', - value: { - width, - height, - }, - }, - ); - setTimeout(() => window.dispatchEvent(new Event('resize')), 300); - } - } + onResizeStop(resizableWidth, resizableHeight) { + const { webcamsAreaResizable } = this.state; + const { layoutContextState, layoutContextDispatch } = this.props; + const { webcamsPlacement, webcamsAreaSize } = layoutContextState; - onResize() { - this.setPlacementPercent(); - } + layoutContextDispatch( + { + type: 'setAutoArrangeLayout', + value: false, + }, + ); + + const newWebcamsAreaResizable = { + width: Math.trunc(webcamsAreaResizable.width) + resizableWidth, + height: Math.trunc(webcamsAreaResizable.height) + resizableHeight, + }; - onResizeStop() { - const { webcamDraggableState, webcamDraggableDispatch } = this.props; - const { optimalGrid } = webcamDraggableState; - if (optimalGrid) { - webcamDraggableDispatch( - { - type: 'setVideoListSize', - value: { - width: optimalGrid.width, - height: optimalGrid.height, - }, + layoutContextDispatch( + { + type: 'setWebcamsAreaSize', + value: { + width: webcamsPlacement === 'top' || webcamsPlacement === 'bottom' ? webcamsAreaSize.width : newWebcamsAreaResizable.width, + height: webcamsPlacement === 'left' || webcamsPlacement === 'right' ? webcamsAreaSize.height : newWebcamsAreaResizable.height, }, - ); - } - this.setPlacementPercent(); - window.dispatchEvent(new Event('resize')); - setTimeout(() => this.setState({ resizing: false }), 500); - } + }, + ); - setPlacementPercent() { - const { webcamDraggableState } = this.props; - const { optimalGrid, placement } = webcamDraggableState; - if (placement === 'top' || placement === 'bottom') { - const mediaSelection = document.querySelector('section[class^=media]'); - const mediaHeight = mediaSelection ? mediaSelection.offsetHeight : 0; - this.setState({ placementPercent: (optimalGrid.height * 100) / mediaHeight }); - } - if (placement === 'left' || placement === 'right') { - const mediaSelection = document.querySelector('section[class^=media]'); - const mediaWidth = mediaSelection ? mediaSelection.offsetWidth : 0; - this.setState({ placementPercent: (optimalGrid.width * 100) / mediaWidth }); - } - } + this.setState({ + webcamsAreaResizable: { + width: webcamsPlacement === 'top' || webcamsPlacement === 'bottom' ? webcamsAreaSize.width : newWebcamsAreaResizable.width, + height: webcamsPlacement === 'left' || webcamsPlacement === 'right' ? webcamsAreaSize.height : newWebcamsAreaResizable.height, + }, + }); - getMediaBounds() { - const { refMediaContainer, webcamDraggableState, webcamDraggableDispatch } = this.props; - const { mediaSize: mediaState } = webcamDraggableState; - const { current: mediaContainer } = refMediaContainer; - if (mediaContainer) { - const mediaContainerRect = mediaContainer.getBoundingClientRect(); - const { - top, left, width: newWidth, height: newHeight, - } = mediaContainerRect; - if ((mediaState.width === 0 || mediaState.height === 0) && (newWidth > 0 && newHeight > 0)) { - webcamDraggableDispatch( - { - type: 'setMediaSize', - value: { - newWidth, - newHeight, - }, - }, - ); - } + setTimeout(() => this.setState({ resizing: false }), 500); + window.dispatchEvent(new Event('webcamAreaResize')); + } - return { - top, - left, - width: newWidth, - height: newHeight, - }; - } - return false; + setWebcamsAreaResizable(width, height) { + this.setState({ + webcamsAreaResizable: { width, height }, + }); } getWebcamsListBounds() { @@ -210,22 +204,20 @@ class WebcamDraggable extends PureComponent { top, left, width, height, } = videoListRefRect; return { - top, // 10 = margin - left, // 10 = margin - width, // 20 = margin - height, // 20 = margin + top, + left, + width, + height, }; } return false; } - recalculateAreaSize() { - this.onResizeStart(); - this.onResizeStop(); - } - calculatePosition() { - const { top: mediaTop, left: mediaLeft } = this.getMediaBounds(); + const { layoutContextState } = this.props; + const { mediaBounds } = layoutContextState; + + const { top: mediaTop, left: mediaLeft } = mediaBounds; const { top: webcamsListTop, left: webcamsListLeft } = this.getWebcamsListBounds(); const x = webcamsListLeft - mediaLeft; const y = webcamsListTop - mediaTop; @@ -251,52 +243,57 @@ class WebcamDraggable extends PureComponent { } handleWebcamDragStop(e) { - const { webcamDraggableDispatch } = this.props; + const { webcamDraggableDispatch, layoutContextDispatch } = this.props; const targetClassname = JSON.stringify(e.target.className); if (targetClassname) { if (targetClassname.includes('Top')) { - webcamDraggableDispatch({ type: 'setplacementToTop' }); - webcamDraggableDispatch({ type: 'setLastPlacementLandscapeToTop' }); + layoutContextDispatch({ + type: 'setWebcamsPlacement', + value: 'top', + }); } else if (targetClassname.includes('Right')) { - webcamDraggableDispatch({ type: 'setplacementToRight' }); - webcamDraggableDispatch({ type: 'setLastPlacementPortraitToRight' }); + layoutContextDispatch({ + type: 'setWebcamsPlacement', + value: 'right', + }); } else if (targetClassname.includes('Bottom')) { - webcamDraggableDispatch({ type: 'setplacementToBottom' }); - webcamDraggableDispatch({ type: 'setLastPlacementLandscapeToBottom' }); + layoutContextDispatch({ + type: 'setWebcamsPlacement', + value: 'bottom', + }); } else if (targetClassname.includes('Left')) { - webcamDraggableDispatch({ type: 'setplacementToLeft' }); - webcamDraggableDispatch({ type: 'setLastPlacementPortraitToLeft' }); + layoutContextDispatch({ + type: 'setWebcamsPlacement', + value: 'left', + }); } } webcamDraggableDispatch({ type: 'dragEnd' }); - window.dispatchEvent(new Event('resize')); - setTimeout(this.recalculateAreaSize, 500); + window.dispatchEvent(new Event('webcamAreaResize')); } render() { const { + layoutContextState, webcamDraggableState, swapLayout, hideOverlay, disableVideo, audioModalIsOpen, - refMediaContainer, } = this.props; - const { - resizing, - placementPercent, - } = this.state; + const { resizing, webcamsAreaResizable } = this.state; + + const { mediaBounds } = layoutContextState; const { dragging, isCameraFullscreen, - videoListSize, optimalGrid, } = webcamDraggableState; - const placement = Storage.getItem('webcamPlacement'); + const webcamsPlacement = Storage.getItem('webcamsPlacement'); const lastPosition = Storage.getItem('webcamLastPosition') || { x: 0, y: 0 }; @@ -323,7 +320,7 @@ class WebcamDraggable extends PureComponent { const { width: mediaWidth, height: mediaHeight, - } = this.getMediaBounds(); + } = mediaBounds; const { width: webcamsWidth, @@ -346,57 +343,16 @@ class WebcamDraggable extends PureComponent { [styles.fullHeight]: swapLayout, }); - const { current: mediaContainer } = refMediaContainer; - let layout = 'vertical'; - if (mediaContainer) { - const classNameMediaContainer = mediaContainer.className; - if (classNameMediaContainer.includes('containerH')) { - layout = 'horizontal'; - } else { - layout = 'vertical'; - } - } - const overlayClassName = cx({ [styles.overlay]: true, [styles.hideOverlay]: hideOverlay, [styles.floatingOverlay]: dragging, [styles.autoWidth]: dragging, - [styles.fullWidth]: ( - ( - placement === 'top' - || placement === 'bottom' - ) - || swapLayout - ) - && !dragging, - [styles.fullHeight]: ( - ( - placement === 'left' - && placement === 'right' - ) - || swapLayout - ) - && !dragging, - [styles.overlayToTop]: placement === 'top' && !dragging, - [styles.overlayToRight]: placement === 'right' && !dragging, - [styles.overlayToBottom]: placement === 'bottom' && !dragging, - [styles.overlayToLeft]: placement === 'left' && !dragging, + [styles.overlayToTop]: webcamsPlacement === 'top' && !dragging, + [styles.overlayToRight]: webcamsPlacement === 'right' && !dragging, + [styles.overlayToBottom]: webcamsPlacement === 'bottom' && !dragging, + [styles.overlayToLeft]: webcamsPlacement === 'left' && !dragging, [styles.dragging]: dragging, - [styles.hide]: ( - ( - placement === 'left' - || placement === 'right' - ) - && layout === 'vertical' - ) - || ( - ( - placement === 'top' - || placement === 'bottom' - ) - && layout === 'horizontal' - ), }); const dropZoneTopClassName = cx({ @@ -443,35 +399,6 @@ class WebcamDraggable extends PureComponent { [styles.dropZoneBgRight]: true, }); - const mediaSelection = document.querySelector('section[class^=media]'); - const mHeight = mediaSelection ? mediaSelection.offsetHeight : 0; - const mWidth = mediaSelection ? mediaSelection.offsetWidth : 0; - - let resizeWidth; - let resizeHeight; - if (resizing && (placement === 'top' || placement === 'bottom') && !dragging) { - resizeWidth = '100%'; - resizeHeight = videoListSize.height; - } - if (!resizing && (placement === 'top' || placement === 'bottom') && !dragging) { - resizeWidth = '100%'; - resizeHeight = mHeight * (placementPercent / 100); - } - - if (resizing && (placement === 'left' || placement === 'right') && !dragging) { - resizeWidth = videoListSize.width; - resizeHeight = '100%'; - } - if (!resizing && (placement === 'left' || placement === 'right') && !dragging) { - resizeWidth = mWidth * (placementPercent / 100); - resizeHeight = '100%'; - } - - if (dragging) { - resizeHeight = optimalGrid.height; - resizeWidth = optimalGrid.width; - } - return ( <Fragment> <div @@ -499,26 +426,29 @@ class WebcamDraggable extends PureComponent { onStart={this.handleWebcamDragStart} onStop={this.handleWebcamDragStop} onMouseDown={e => e.preventDefault()} - disabled={swapLayout || isCameraFullscreen || BROWSER_ISMOBILE} + disabled={swapLayout || isCameraFullscreen || BROWSER_ISMOBILE || resizing} position={position} > <Resizable + minWidth={mediaBounds.width * WEBCAMSAREA_MIN_PERCENT} + minHeight={mediaBounds.height * WEBCAMSAREA_MIN_PERCENT} size={ { - height: resizeHeight, - width: resizeWidth, + height: dragging ? optimalGrid.height : webcamsAreaResizable.height, + width: dragging ? optimalGrid.width : webcamsAreaResizable.width, } } lockAspectRatio handleWrapperClass="resizeWrapper" onResizeStart={this.onResizeStart} - onResize={dispatchResizeEvent} - onResizeStop={this.onResizeStop} + onResizeStop={(e, direction, ref, d) => { + this.onResizeStop(d.width, d.height); + }} enable={{ - top: (placement === 'bottom') && !swapLayout, - bottom: (placement === 'top') && !swapLayout, - left: (placement === 'right') && !swapLayout, - right: (placement === 'left') && !swapLayout, + top: (webcamsPlacement === 'bottom') && !swapLayout, + bottom: (webcamsPlacement === 'top') && !swapLayout, + left: (webcamsPlacement === 'right') && !swapLayout, + right: (webcamsPlacement === 'left') && !swapLayout, topLeft: false, topRight: false, bottomLeft: false, @@ -572,4 +502,4 @@ class WebcamDraggable extends PureComponent { WebcamDraggable.propTypes = propTypes; WebcamDraggable.defaultProps = defaultProps; -export default withDraggableConsumer(WebcamDraggable); +export default withDraggableConsumer(withLayoutConsumer(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 index 34b0e8935b..f86b406f77 100644 --- a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx +++ b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx @@ -1,19 +1,9 @@ import React, { createContext, useReducer, useEffect } from 'react'; import Storage from '/imports/ui/services/storage/session'; -const { webcamsDefaultPlacement } = Meteor.settings.public.layout; - export const WebcamDraggableContext = createContext(); const initialState = { - placement: webcamsDefaultPlacement || 'top', - lastPlacementLandscape: 'top', - lastPlacementPortrait: 'left', - orientation: null, - mediaSize: { - width: 0, - height: 0, - }, videoListSize: { width: 0, height: 0, @@ -39,81 +29,6 @@ const initialState = { const reducer = (state, action) => { switch (action.type) { - case 'setplacementToTop': { - return { - ...state, - placement: 'top', - }; - } - case 'setplacementToRight': { - return { - ...state, - placement: 'right', - }; - } - case 'setplacementToBottom': { - return { - ...state, - placement: 'bottom', - }; - } - case 'setplacementToLeft': { - return { - ...state, - placement: 'left', - }; - } - case 'setLastPlacementPortraitToLeft': { - return { - ...state, - lastPlacementPortrait: 'left', - }; - } - case 'setLastPlacementPortraitToRight': { - return { - ...state, - lastPlacementPortrait: 'right', - }; - } - case 'setLastPlacementLandscapeToTop': { - return { - ...state, - lastPlacementLandscape: 'top', - }; - } - case 'setLastPlacementLandscapeToBottom': { - return { - ...state, - lastPlacementLandscape: 'bottom', - }; - } - case 'setplacementToFloating': { - return { - ...state, - placement: 'floating', - }; - } - case 'setOrientationToLandscape': { - return { - ...state, - orientation: 'landscape', - }; - } - case 'setOrientationToPortrait': { - return { - ...state, - orientation: 'portrait', - }; - } - case 'setMediaSize': { - return { - ...state, - mediaSize: { - width: action.value.width, - height: action.value.height, - }, - }; - } case 'setVideoListSize': { return { ...state, @@ -147,15 +62,6 @@ const reducer = (state, action) => { }, }; } - case 'setLastPosition': { - return { - ...state, - lastPosition: { - x: action.value.x, - y: action.value.y, - }, - }; - } case 'setVideoRef': { return { ...state, @@ -214,7 +120,6 @@ const ContextProvider = (props) => { } = webcamDraggableState; const { children } = props; useEffect(() => { - Storage.setItem('webcamPlacement', placement); Storage.setItem('webcamLastPlacementLandscape', lastPlacementLandscape); Storage.setItem('webcamlastPlacementPortrait', lastPlacementPortrait); Storage.setItem('webcamLastPosition', lastPosition); diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx index fa019c0176..1c316076af 100755 --- a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx @@ -12,8 +12,6 @@ import Button from '../button/component'; import RecordingIndicator from './recording-indicator/container'; import TalkingIndicatorContainer from '/imports/ui/components/nav-bar/talking-indicator/container'; import SettingsDropdownContainer from './settings-dropdown/container'; -import { NAVBAR_HEIGHT } from '/imports/ui/components/layout/layout-manager'; - const intlMessages = defineMessages({ toggleUserListLabel: { diff --git a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx index 996bccb883..e24d55afd0 100755 --- a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx @@ -84,18 +84,26 @@ class PresentationArea extends PureComponent { } componentDidMount() { - // adding an event listener to scale the whiteboard on 'resize' events sent by chat/userlist etc - // window.addEventListener('resize', this.onResize); this.getInitialPresentationSizes(); this.refPresentationContainer.addEventListener('fullscreenchange', this.onFullscreenChange); + window.addEventListener('resize', this.onResize, false); + window.addEventListener('webcamAreaResize', this.onResize, false); + window.addEventListener('userListResizeChanged', this.onResize, false); + window.addEventListener('chatResizeChanged', this.onResize, false); - const { slidePosition, webcamDraggableDispatch } = this.props; + const { slidePosition, layoutContextDispatch } = this.props; const { width: currWidth, height: currHeight } = slidePosition; if (currWidth > currHeight || currWidth === currHeight) { - webcamDraggableDispatch({ type: 'setOrientationToLandscape' }); + layoutContextDispatch({ + type: 'setPresentationOrientation', + value: 'landscape', + }); } if (currHeight > currWidth) { - webcamDraggableDispatch({ type: 'setOrientationToPortrait' }); + layoutContextDispatch({ + type: 'setPresentationOrientation', + value: 'portrait', + }); } } @@ -103,32 +111,40 @@ class PresentationArea extends PureComponent { const { currentPresentation, slidePosition, - webcamDraggableDispatch, layoutSwapped, currentSlide, publishedPoll, isViewer, toggleSwapLayout, restoreOnUpdate, + layoutContextDispatch, layoutContextState, } = this.props; + + const { presentationAreaWidth, presentationAreaHeight } = this.state; const { presentationAreaSize } = layoutContextState; const { layoutContextState: prevLayoutContextState } = prevProps; const { presentationAreaSize: prevPresentationAreaSize } = prevLayoutContextState; - if (presentationAreaSize !== prevPresentationAreaSize) { - this.handleResize(); - } + if (presentationAreaSize !== prevPresentationAreaSize + || presentationAreaSize.width !== presentationAreaWidth + || presentationAreaSize.height !== presentationAreaHeight) this.handleResize(); const { width: prevWidth, height: prevHeight } = prevProps.slidePosition; const { width: currWidth, height: currHeight } = slidePosition; if (prevWidth !== currWidth || prevHeight !== currHeight) { if (currWidth > currHeight || currWidth === currHeight) { - webcamDraggableDispatch({ type: 'setOrientationToLandscape' }); + layoutContextDispatch({ + type: 'setPresentationOrientation', + value: 'landscape', + }); } if (currHeight > currWidth) { - webcamDraggableDispatch({ type: 'setOrientationToPortrait' }); + layoutContextDispatch({ + type: 'setPresentationOrientation', + value: 'portrait', + }); } } @@ -157,7 +173,10 @@ class PresentationArea extends PureComponent { } componentWillUnmount() { - // window.removeEventListener('resize', this.onResize); + window.removeEventListener('resize', this.onResize, false); + window.removeEventListener('webcamAreaResize', this.onResize, false); + window.removeEventListener('userListResizeChanged', this.onResize, false); + window.removeEventListener('chatResizeChanged', this.onResize, false); this.refPresentationContainer.removeEventListener('fullscreenchange', this.onFullscreenChange); } @@ -168,7 +187,6 @@ class PresentationArea extends PureComponent { if (isFullscreen !== newIsFullscreen) { this.setState({ isFullscreen: newIsFullscreen }); layoutContextDispatch({ type: 'setPresentationFullscreen', value: newIsFullscreen }); - // window.dispatchEvent(new Event('resize')); } } @@ -189,29 +207,31 @@ class PresentationArea extends PureComponent { } getPresentationSizesAvailable() { - const { userIsPresenter, multiUser, layoutContextState } = this.props; - const { refPresentationArea, refWhiteboardArea } = this; + // const { userIsPresenter, multiUser } = this.props; + const { layoutContextState } = this.props; + // const { refPresentationArea, refWhiteboardArea } = this; const { presentationAreaSize } = layoutContextState; const presentationSizes = {}; - if (refPresentationArea && refWhiteboardArea) { - // By default presentation sizes are equal to the sizes of the refPresentationArea - // direct parent of the svg wrapper - let { clientWidth, clientHeight } = refPresentationArea; - - // if a user is a presenter - this means there is a whiteboard toolbar on the right - // and we have to get the width/height of the refWhiteboardArea - // (inner hidden div with absolute position) - if (userIsPresenter || multiUser) { - ({ clientWidth, clientHeight } = refWhiteboardArea); - } - - presentationSizes.presentationAreaHeight = presentationAreaSize.height - this.getToolbarHeight(); - presentationSizes.presentationAreaWidth = presentationAreaSize.width; - // presentationSizes.presentationAreaHeight = clientHeight - this.getToolbarHeight(); - // presentationSizes.presentationAreaWidth = clientWidth; - } + // if (refPresentationArea && refWhiteboardArea) { + // By default presentation sizes are equal to the sizes of the refPresentationArea + // direct parent of the svg wrapper + // let { clientWidth, clientHeight } = refPresentationArea; + + // if a user is a presenter - this means there is a whiteboard toolbar on the right + // and we have to get the width/height of the refWhiteboardArea + // (inner hidden div with absolute position) + // if (userIsPresenter || multiUser) { + // ({ clientWidth, clientHeight } = refWhiteboardArea); + // } + + presentationSizes.presentationAreaHeight = presentationAreaSize + .height - this.getToolbarHeight(); + presentationSizes.presentationAreaWidth = presentationAreaSize.width; + // presentationSizes.presentationAreaHeight = clientHeight - this.getToolbarHeight(); + // presentationSizes.presentationAreaWidth = clientWidth; + // } return presentationSizes; } diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx index 910f89894a..8ebb5fee31 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx @@ -77,6 +77,10 @@ const propTypes = { }; class VideoProvider extends Component { + static onBeforeUnload() { + VideoService.onBeforeUnload(); + } + constructor(props) { super(props); @@ -101,8 +105,6 @@ class VideoProvider extends Component { this.onWsClose = this.onWsClose.bind(this); this.onWsMessage = this.onWsMessage.bind(this); - this.onBeforeUnload = this.onBeforeUnload.bind(this); - this.updateStreams = this.updateStreams.bind(this); } @@ -115,7 +117,7 @@ class VideoProvider extends Component { this.ws.onmessage = this.onWsMessage; - window.addEventListener('beforeunload', this.onBeforeUnload); + window.addEventListener('beforeunload', VideoProvider.onBeforeUnload); } componentDidUpdate(prevProps) { @@ -134,7 +136,7 @@ class VideoProvider extends Component { window.removeEventListener('online', this.openWs); window.removeEventListener('offline', this.onWsClose); - window.removeEventListener('beforeunload', this.onBeforeUnload); + window.removeEventListener('beforeunload', VideoProvider.onBeforeUnload); VideoService.exitVideo(); @@ -205,17 +207,41 @@ class VideoProvider extends Component { this.setState({ socketOpen: true }); } - onBeforeUnload() { - VideoService.onBeforeUnload(); + setReconnectionTimeout(cameraId, isLocal) { + const peer = this.webRtcPeers[cameraId]; + const peerHasStarted = peer && peer.started === true; + const shouldSetReconnectionTimeout = !this.restartTimeout[cameraId] && !peerHasStarted; + + if (shouldSetReconnectionTimeout) { + const newReconnectTimer = this.restartTimer[cameraId] || CAMERA_SHARE_FAILED_WAIT_TIME; + this.restartTimer[cameraId] = newReconnectTimer; + + logger.info({ + logCode: 'video_provider_setup_reconnect', + extraInfo: { + cameraId, + reconnectTimer: newReconnectTimer, + }, + }, `Camera has a new reconnect timer of ${newReconnectTimer} ms for ${cameraId}`); + + this.restartTimeout[cameraId] = setTimeout( + this._getWebRTCStartTimeout(cameraId, isLocal), + this.restartTimer[cameraId], + ); + } } updateStreams(streams) { const streamsCameraIds = streams.map(s => s.cameraId); const streamsConnected = Object.keys(this.webRtcPeers); - const streamsToConnect = streamsCameraIds.filter(cameraId => !streamsConnected.includes(cameraId)); + const streamsToConnect = streamsCameraIds.filter( + cameraId => !streamsConnected.includes(cameraId), + ); - const streamsToDisconnect = streamsConnected.filter(cameraId => !streamsCameraIds.includes(cameraId)); + const streamsToDisconnect = streamsConnected.filter( + cameraId => !streamsCameraIds.includes(cameraId), + ); streamsToConnect.forEach((cameraId) => { const isLocal = VideoService.isLocalStream(cameraId); @@ -505,7 +531,8 @@ class VideoProvider extends Component { const peer = this.webRtcPeers[cameraId]; if (peer && peer.peerConnection) { const conn = peer.peerConnection; - conn.oniceconnectionstatechange = this._getOnIceConnectionStateChangeCallback(cameraId, isLocal); + conn.oniceconnectionstatechange = this + ._getOnIceConnectionStateChangeCallback(cameraId, isLocal); VideoService.monitor(conn); } } @@ -595,30 +622,6 @@ class VideoProvider extends Component { }, `Camera peer creation failed for ${cameraId} due to ${error.message}`); } - setReconnectionTimeout(cameraId, isLocal) { - const peer = this.webRtcPeers[cameraId]; - const peerHasStarted = peer && peer.started === true; - const shouldSetReconnectionTimeout = !this.restartTimeout[cameraId] && !peerHasStarted; - - if (shouldSetReconnectionTimeout) { - const newReconnectTimer = this.restartTimer[cameraId] || CAMERA_SHARE_FAILED_WAIT_TIME; - this.restartTimer[cameraId] = newReconnectTimer; - - logger.info({ - logCode: 'video_provider_setup_reconnect', - extraInfo: { - cameraId, - reconnectTimer: newReconnectTimer, - }, - }, `Camera has a new reconnect timer of ${newReconnectTimer} ms for ${cameraId}`); - - this.restartTimeout[cameraId] = setTimeout( - this._getWebRTCStartTimeout(cameraId, isLocal), - this.restartTimer[cameraId], - ); - } - } - _getOnIceCandidateCallback(cameraId, isLocal) { return (candidate) => { const peer = this.webRtcPeers[cameraId]; 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 bfe4b551bc..844c74b0bc 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx @@ -102,11 +102,17 @@ class VideoList extends Component { this.handleCanvasResize(); window.addEventListener('resize', this.handleCanvasResize, false); + window.addEventListener('webcamAreaResize', this.handleCanvasResize, false); + window.addEventListener('userListResizeChanged', this.handleCanvasResize, false); + window.addEventListener('chatResizeChanged', this.handleCanvasResize, false); window.addEventListener('videoPlayFailed', this.handlePlayElementFailed); } componentWillUnmount() { window.removeEventListener('resize', this.handleCanvasResize, false); + window.removeEventListener('webcamAreaResize', this.handleCanvasResize, false); + window.removeEventListener('userListResizeChanged', this.handleCanvasResize, false); + window.removeEventListener('chatResizeChanged', this.handleCanvasResize, false); window.removeEventListener('videoPlayFailed', this.handlePlayElementFailed); } -- GitLab