diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx index e82853475ada9a30d30444795599e116596f27a0..040ea81e531f9e111bab4e3b1b9cb4beb83e0e57 100755 --- a/bigbluebutton-html5/imports/startup/client/base.jsx +++ b/bigbluebutton-html5/imports/startup/client/base.jsx @@ -1,4 +1,4 @@ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import { withTracker } from 'meteor/react-meteor-data'; import PropTypes from 'prop-types'; import Auth from '/imports/ui/services/auth'; @@ -18,9 +18,6 @@ import AudioService from '/imports/ui/components/audio/service'; import { notify } from '/imports/ui/services/notification'; import deviceInfo from '/imports/utils/deviceInfo'; 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 VideoService from '/imports/ui/components/video-provider/service'; const CHAT_CONFIG = Meteor.settings.public.chat; const CHAT_ENABLED = CHAT_CONFIG.enabled; @@ -104,23 +101,12 @@ class Base extends Component { ejected, isMeteorConnected, subscriptionsReady, - layoutContextDispatch, - usersVideo, } = this.props; const { loading, meetingExisted, } = this.state; - if (usersVideo !== prevProps.usersVideo) { - layoutContextDispatch( - { - type: 'setUsersVideo', - value: usersVideo.length, - }, - ); - } - if (!prevProps.subscriptionsReady && subscriptionsReady) { logger.info({ logCode: 'startup_client_subscriptions_ready' }, 'Subscriptions are ready'); } @@ -225,14 +211,9 @@ class Base extends Component { const { meetingExisted } = this.state; return ( - <Fragment> - <LayoutManager /> - { - (!meetingExisted && !meetingExist && Auth.loggedIn) - ? <LoadingScreen /> - : this.renderByState() - } - </Fragment> + (!meetingExisted && !meetingExist && Auth.loggedIn) + ? <LoadingScreen /> + : this.renderByState() ); } } @@ -384,7 +365,6 @@ const BaseContainer = withTracker(() => { Session.set('openPanel', ''); } - const usersVideo = VideoService.getVideoStreams(); const codeError = Session.get('codeError'); return { @@ -401,9 +381,8 @@ const BaseContainer = withTracker(() => { meetingIsBreakout: AppService.meetingIsBreakout(), subscriptionsReady: Session.get('subscriptionsReady'), loggedIn, - usersVideo, codeError, }; -})(withLayoutContext(Base)); +})(Base); export default BaseContainer; diff --git a/bigbluebutton-html5/imports/startup/client/intl.jsx b/bigbluebutton-html5/imports/startup/client/intl.jsx index 55a16f072b655f587e655f77a1095740112f095b..3e139e57c74e1a80cd498828926f1889893de47e 100644 --- a/bigbluebutton-html5/imports/startup/client/intl.jsx +++ b/bigbluebutton-html5/imports/startup/client/intl.jsx @@ -115,18 +115,6 @@ const defaultProps = { }; class IntlStartup extends Component { - static saveLocale(localeName) { - Settings.application.locale = localeName; - if (RTL_LANGUAGES.includes(localeName.substring(0, 2))) { - document.body.parentNode.setAttribute('dir', 'rtl'); - Settings.application.isRTL = true; - } else { - document.body.parentNode.setAttribute('dir', 'ltr'); - Settings.application.isRTL = false; - } - Settings.save(); - } - constructor(props) { super(props); @@ -143,14 +131,14 @@ class IntlStartup extends Component { this.fetchLocalizedMessages = this.fetchLocalizedMessages.bind(this); } - componentDidMount() { + componentWillMount() { const { locale } = this.props; this.fetchLocalizedMessages(locale, true); } - componentDidUpdate() { + componentWillUpdate(nextProps) { const { fetching, normalizedLocale } = this.state; - const { locale } = this.props; + const { locale } = nextProps; if (!fetching && normalizedLocale @@ -174,23 +162,35 @@ class IntlStartup extends Component { .then(({ messages, normalizedLocale }) => { const dasherizedLocale = normalizedLocale.replace('_', '-'); this.setState({ messages, fetching: false, normalizedLocale: dasherizedLocale }, () => { - IntlStartup.saveLocale(dasherizedLocale); + this.saveLocale(dasherizedLocale); }); }) .catch(() => { this.setState({ fetching: false, normalizedLocale: null }, () => { - IntlStartup.saveLocale(DEFAULT_LANGUAGE); + this.saveLocale(DEFAULT_LANGUAGE); }); }); }); } + saveLocale(localeName) { + Settings.application.locale = localeName; + if (RTL_LANGUAGES.includes(localeName.substring(0, 2))) { + document.body.parentNode.setAttribute('dir', 'rtl'); + Settings.application.isRTL = true; + } else { + document.body.parentNode.setAttribute('dir', 'ltr'); + Settings.application.isRTL = false; + } + Settings.save(); + } + render() { const { fetching, normalizedLocale, messages } = this.state; const { children } = this.props; - const test = normalizedLocale; + return fetching ? <LoadingScreen /> : ( - <IntlProvider locale={test} messages={messages}> + <IntlProvider locale={normalizedLocale} messages={messages}> {children} </IntlProvider> ); diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx index 629402ecac0d348faaccab82d5c23c4ebe7b50bb..c89a30a828c511e4b3cbe7dd71302c731300d6e2 100755 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx @@ -91,9 +91,9 @@ class ActionsDropdown extends PureComponent { this.makePresentationItems = this.makePresentationItems.bind(this); } - componentDidUpdate(prevProps) { - const { amIPresenter: wasPresenter } = prevProps; - const { amIPresenter: isPresenter, mountModal } = this.props; + componentWillUpdate(nextProps) { + const { amIPresenter: isPresenter } = nextProps; + const { amIPresenter: wasPresenter, mountModal } = this.props; if (wasPresenter && !isPresenter) { mountModal(null); } diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx index a39cebecff08f47773d1a95caa1efa631a25f1df..cbb2f601bf9e0956fce298540ad1e1a447e0f429 100755 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx @@ -7,38 +7,8 @@ import AudioControlsContainer from '../audio/audio-controls/container'; import JoinVideoOptionsContainer from '../video-provider/video-button/container'; import CaptionsButtonContainer from '/imports/ui/components/actions-bar/captions/container'; import PresentationOptionsContainer from './presentation-options/component'; -import Button from '/imports/ui/components/button/component'; -import Storage from '/imports/ui/services/storage/session'; -import { ACTIONSBAR_HEIGHT } from '/imports/ui/components/layout/layout-manager'; -import { withLayoutConsumer } from '/imports/ui/components/layout/context'; class ActionsBar extends PureComponent { - constructor(props) { - super(props); - - this.autoArrangeToggle = this.autoArrangeToggle.bind(this); - } - - componentDidUpdate(prevProps) { - const { layoutContextState } = this.props; - const { layoutContextState: prevLayoutContextState } = prevProps; - const { autoArrangeLayout } = layoutContextState; - const { autoArrangeLayout: prevAutoArrangeLayout } = prevLayoutContextState; - if (autoArrangeLayout !== prevAutoArrangeLayout) this.forceUpdate(); - } - - autoArrangeToggle() { - const { layoutContextDispatch } = this.props; - const autoArrangeLayout = Storage.getItem('autoArrangeLayout'); - layoutContextDispatch( - { - type: 'setAutoArrangeLayout', - value: !autoArrangeLayout, - }, - ); - window.dispatchEvent(new Event('autoArrangeChanged')); - } - render() { const { amIPresenter, @@ -69,19 +39,13 @@ class ActionsBar extends PureComponent { } = this.props; const actionBarClasses = {}; - const autoArrangeLayout = Storage.getItem('autoArrangeLayout'); actionBarClasses[styles.centerWithActions] = amIPresenter; actionBarClasses[styles.center] = true; actionBarClasses[styles.mobileLayoutSwapped] = isLayoutSwapped && amIPresenter; return ( - <div - className={styles.actionsbar} - style={{ - height: ACTIONSBAR_HEIGHT, - }} - > + <div className={styles.actionsbar}> <div className={styles.left}> <ActionsDropdown {...{ amIPresenter, @@ -123,18 +87,6 @@ class ActionsBar extends PureComponent { screenshareDataSavingSetting, }} /> - <Button - className={cx(styles.button, autoArrangeLayout || styles.btn)} - icon={autoArrangeLayout ? 'lock' : 'unlock'} - color={autoArrangeLayout ? 'primary' : 'default'} - ghost={!autoArrangeLayout} - onClick={this.autoArrangeToggle} - label={autoArrangeLayout ? 'Disable Auto Arrange' : 'Enable Auto Arrange'} - aria-label="Auto Arrange test" - hideLabel - circle - size="lg" - /> </div> <div className={styles.right}> {isLayoutSwapped @@ -152,4 +104,4 @@ class ActionsBar extends PureComponent { } } -export default withLayoutConsumer(ActionsBar); +export default ActionsBar; diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx index a2772cb1687e292d97a3ba1ac13c8a27c8705498..b087b4447822f6e2719c4a18d9d7a6dd46b89e35 100755 --- a/bigbluebutton-html5/imports/ui/components/app/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx @@ -23,7 +23,6 @@ import MediaService from '/imports/ui/components/media/service'; import ManyWebcamsNotifier from '/imports/ui/components/video-provider/many-users-notify/container'; import { withDraggableContext } from '../media/webcam-draggable-overlay/context'; import { styles } from './styles'; -import { NAVBAR_HEIGHT } from '/imports/ui/components/layout/layout-manager'; const MOBILE_MEDIA = 'only screen and (max-width: 40em)'; const APP_CONFIG = Meteor.settings.public.app; @@ -230,12 +229,7 @@ class App extends Component { if (!navbar) return null; return ( - <header - className={styles.navbar} - style={{ - height: NAVBAR_HEIGHT, - }} - > + <header className={styles.navbar}> {navbar} </header> ); diff --git a/bigbluebutton-html5/imports/ui/components/app/styles.scss b/bigbluebutton-html5/imports/ui/components/app/styles.scss index 4e09d202f3bbf93ec53c4500946563b57940c284..0d03f0eba183c776db7a25b3910ff4078da5d792 100755 --- a/bigbluebutton-html5/imports/ui/components/app/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/app/styles.scss @@ -155,12 +155,12 @@ } @include mq($medium-up) { - // flex: 0 25vw; + flex: 0 25vw; order: 1; } @include mq($xlarge-up) { - // flex-basis: 20vw; + flex-basis: 20vw; } } @@ -172,10 +172,10 @@ .breakoutRoom { height: 100%; - // width: 20vw; + width: 20vw; background-color: var(--color-white); @include mq($small-only) { - // width: auto; + width: auto; height: auto; } } diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/service.js b/bigbluebutton-html5/imports/ui/components/breakout-room/service.js index 9fe60517c9b3cb73a8d9d9d132aa8e11b1b3ecfb..28d549d134b134eb3dd261ec8bf1c0b26746dfa1 100644 --- a/bigbluebutton-html5/imports/ui/components/breakout-room/service.js +++ b/bigbluebutton-html5/imports/ui/components/breakout-room/service.js @@ -27,10 +27,7 @@ const breakoutRoomUser = (breakoutId) => { return breakoutUser; }; -const closeBreakoutPanel = () => { - Session.set('openPanel', 'userlist'); - window.dispatchEvent(new Event('panelChanged')); -}; +const closeBreakoutPanel = () => Session.set('openPanel', 'userlist'); const endAllBreakouts = () => { makeCall('endAllBreakouts'); diff --git a/bigbluebutton-html5/imports/ui/components/chat/alert/push-alert/component.jsx b/bigbluebutton-html5/imports/ui/components/chat/alert/push-alert/component.jsx index db51df75ae5971c6d95a30f5664f06c16123dd21..31a3a02ed888311402c7373cb6d01f6d11c960a4 100755 --- a/bigbluebutton-html5/imports/ui/components/chat/alert/push-alert/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/chat/alert/push-alert/component.jsx @@ -1,5 +1,6 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; +import _ from 'lodash'; import injectNotify from '/imports/ui/components/toast/inject-notify/component'; import { Session } from 'meteor/session'; @@ -30,7 +31,6 @@ class ChatPushAlert extends PureComponent { onClick={() => { Session.set('openPanel', 'chat'); Session.set('idChatOpen', chat); - window.dispatchEvent(new Event('panelChanged')); }} onKeyPress={() => null} > diff --git a/bigbluebutton-html5/imports/ui/components/chat/component.jsx b/bigbluebutton-html5/imports/ui/components/chat/component.jsx index 44a2f0c6c66909301972225434f652e8e01bd940..e19380a741ebb8d2065d2ac6f9fa7ae76652a8f7 100755 --- a/bigbluebutton-html5/imports/ui/components/chat/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/chat/component.jsx @@ -60,7 +60,6 @@ const Chat = (props) => { onClick={() => { Session.set('idChatOpen', ''); Session.set('openPanel', 'userlist'); - window.dispatchEvent(new Event('panelChanged')); }} aria-label={intl.formatMessage(intlMessages.hideChatLabel, { 0: title })} accessKey={HIDE_CHAT_AK} @@ -82,7 +81,6 @@ const Chat = (props) => { actions.handleClosePrivateChat(chatID); Session.set('idChatOpen', ''); Session.set('openPanel', 'userlist'); - window.dispatchEvent(new Event('panelChanged')); }} aria-label={intl.formatMessage(intlMessages.closeChatLabel, { 0: title })} label={intl.formatMessage(intlMessages.closeChatLabel, { 0: title })} diff --git a/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx index db7be924cbf08ba2f5a3f45a758ebf005fd442ae..4183ae38cff6697594836c9583fea2feff0acafd 100644 --- a/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx @@ -72,6 +72,10 @@ class Dropdown extends Component { this.handleWindowClick = this.handleWindowClick.bind(this); } + componentWillUpdate(nextProps, nextState) { + return nextState.isOpen ? screenreaderTrap.trap(this.dropdown) : screenreaderTrap.untrap(); + } + componentDidUpdate(prevProps, prevState) { const { onShow, @@ -81,11 +85,7 @@ class Dropdown extends Component { const { isOpen } = this.state; - if (isOpen) { - screenreaderTrap.trap(this.dropdown); - } else { - screenreaderTrap.untrap(); - } + if (isOpen && !prevState.isOpen) { onShow(); } if (!isOpen && prevState.isOpen) { onHide(); } diff --git a/bigbluebutton-html5/imports/ui/components/layout/context.jsx b/bigbluebutton-html5/imports/ui/components/layout/context.jsx deleted file mode 100644 index 4a83c894f3d5725452d866a9a88020af95dd621d..0000000000000000000000000000000000000000 --- a/bigbluebutton-html5/imports/ui/components/layout/context.jsx +++ /dev/null @@ -1,290 +0,0 @@ -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, - webcamsAreaResizing: false, - numUsersVideo: null, - windowSize: { - width: 0, - height: 0, - }, - mediaBounds: { - width: 0, - height: 0, - top: 0, - left: 0, - }, - userListSize: { - width: 0, - }, - chatSize: { - width: 0, - }, - noteSize: { - width: 0, - }, - captionsSize: { - width: 0, - }, - pollSize: { - width: 0, - }, - waitingSize: { - width: 0, - }, - breakoutRoomSize: { - width: 0, - }, - webcamsAreaSize: { - width: 0, - height: 0, - }, - tempWebcamsAreaSize: { - width: 0, - height: 0, - }, - webcamsAreaUserSetsHeight: 0, - webcamsAreaUserSetsWidth: 0, - webcamsPlacement: webcamsDefaultPlacement || 'top', - presentationAreaSize: { - width: 0, - height: 0, - }, - presentationSlideSize: { - width: 0, - height: 0, - }, - presentationIsFullscreen: null, - presentationOrientation: null, -}; - -const reducer = (state, action) => { - switch (action.type) { - case 'setAutoArrangeLayout': { - return { - ...state, - autoArrangeLayout: action.value, - }; - } - case 'setWebcamsAreaResizing': { - return { - ...state, - webcamsAreaResizing: action.value, - }; - } - case 'setUsersVideo': { - return { - ...state, - numUsersVideo: action.value, - }; - } - case 'setWindowSize': { - return { - ...state, - windowSize: { - width: action.value.width, - height: action.value.height, - }, - }; - } - 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, - userListSize: { - width: action.value.width, - }, - }; - } - case 'setChatSize': { - return { - ...state, - chatSize: { - width: action.value.width, - }, - }; - } - case 'setNoteSize': { - return { - ...state, - noteSize: { - width: action.value.width, - }, - }; - } - case 'setCaptionsSize': { - return { - ...state, - captionsSize: { - width: action.value.width, - }, - }; - } - case 'setPollSize': { - return { - ...state, - pollSize: { - width: action.value.width, - }, - }; - } - case 'setWaitingUsersPanelSize': { - return { - ...state, - waitingSize: { - width: action.value.width, - }, - }; - } - case 'setBreakoutRoomSize': { - return { - ...state, - breakoutRoomSize: { - width: action.value.width, - }, - }; - } - case 'setWebcamsPlacement': { - // webcamsPlacement: ('top' | 'right' | 'bottom' | 'left') string - return { - ...state, - webcamsPlacement: action.value, - }; - } - case 'setWebcamsAreaSize': { - return { - ...state, - webcamsAreaSize: { - width: action.value.width, - height: action.value.height, - }, - }; - } - case 'setTempWebcamsAreaSize': { - return { - ...state, - tempWebcamsAreaSize: { - width: action.value.width, - height: action.value.height, - }, - }; - } - case 'setWebcamsAreaUserSetsHeight': { - return { - ...state, - webcamsAreaUserSetsHeight: action.value, - }; - } - case 'setWebcamsAreaUserSetsWidth': { - return { - ...state, - webcamsAreaUserSetsWidth: action.value, - }; - } - case 'setPresentationAreaSize': { - return { - ...state, - presentationAreaSize: { - width: action.value.width, - height: action.value.height, - }, - }; - } - case 'setPresentationSlideSize': { - return { - ...state, - presentationSlideSize: { - width: action.value.width, - height: action.value.height, - }, - }; - } - 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'); - } - } -}; - -const ContextProvider = (props) => { - const [layoutContextState, layoutContextDispatch] = useReducer(reducer, initialState); - const { - webcamsPlacement, - webcamsAreaUserSetsHeight, - webcamsAreaUserSetsWidth, - autoArrangeLayout, - } = layoutContextState; - const { children } = props; - - useEffect(() => { - Storage.setItem('webcamsPlacement', webcamsPlacement); - Storage.setItem('webcamsAreaUserSetsHeight', webcamsAreaUserSetsHeight); - Storage.setItem('webcamsAreaUserSetsWidth', webcamsAreaUserSetsWidth); - Storage.setItem('autoArrangeLayout', autoArrangeLayout); - }, [ - webcamsPlacement, - webcamsAreaUserSetsHeight, - webcamsAreaUserSetsWidth, - autoArrangeLayout, - ]); - - return ( - <LayoutContext.Provider value={{ - layoutContextState, - layoutContextDispatch, - ...props, - }} - > - {children} - </LayoutContext.Provider> - ); -}; - -const withProvider = Component => props => ( - <ContextProvider {...props}> - <Component /> - </ContextProvider> -); - -const ContextConsumer = Component => props => ( - <LayoutContext.Consumer> - {contexts => <Component {...props} {...contexts} />} - </LayoutContext.Consumer> -); - -const withLayoutConsumer = Component => ContextConsumer(Component); -const withLayoutContext = Component => withProvider(withLayoutConsumer(Component)); - -export { - withProvider, - withLayoutConsumer, - withLayoutContext, -}; diff --git a/bigbluebutton-html5/imports/ui/components/layout/layout-manager.jsx b/bigbluebutton-html5/imports/ui/components/layout/layout-manager.jsx deleted file mode 100644 index 5a27d6fe807e793b63b75851d021e3f325c78b20..0000000000000000000000000000000000000000 --- a/bigbluebutton-html5/imports/ui/components/layout/layout-manager.jsx +++ /dev/null @@ -1,547 +0,0 @@ -import React, { Component, Fragment } from 'react'; -import Storage from '/imports/ui/services/storage/session'; -import { Session } from 'meteor/session'; -import { withLayoutConsumer } from '/imports/ui/components/layout/context'; -import { isVideoBroadcasting } from '/imports/ui/components/screenshare/service'; -import _ from 'lodash'; - -const windowWidth = () => window.innerWidth; -const windowHeight = () => window.innerHeight; -const min = (value1, value2) => (value1 <= value2 ? value1 : value2); -const max = (value1, value2) => (value1 >= value2 ? value1 : value2); - -// values based on sass file -const USERLIST_MIN_WIDTH = 150; -const USERLIST_MAX_WIDTH = 240; -const CHAT_MIN_WIDTH = 150; -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; -// const PRESENTATIONAREA_MIN_PERCENT = 0.2; -const PRESENTATIONAREA_MIN_WIDTH = 385; // Value based on presentation toolbar -// const PRESENTATIONAREA_MAX_PERCENT = 0.8; - -const storageLayoutData = () => Storage.getItem('layoutData'); - -class LayoutManager extends Component { - static calculatesPresentationSize( - mediaAreaWidth, mediaAreaHeight, presentationSlideWidth, presentationSlideHeight, - ) { - let presentationWidth; - let presentationHeight; - if (presentationSlideWidth > presentationSlideHeight - || presentationSlideWidth === presentationSlideHeight) { - presentationWidth = mediaAreaWidth; - presentationHeight = (mediaAreaWidth * presentationSlideHeight) - / presentationSlideWidth; - // if overflow - if (presentationHeight > mediaAreaHeight) { - presentationWidth = (mediaAreaHeight * presentationWidth) / presentationHeight; - presentationHeight = mediaAreaHeight; - } - } - if (presentationSlideHeight > presentationSlideWidth) { - presentationWidth = (mediaAreaHeight * presentationSlideWidth) - / presentationSlideHeight; - presentationHeight = mediaAreaHeight; - // if overflow - if (presentationWidth > mediaAreaWidth) { - presentationHeight = (mediaAreaWidth * presentationWidth) / presentationHeight; - presentationWidth = mediaAreaWidth; - } - } - - return { - presentationWidth, - presentationHeight, - }; - } - - constructor(props) { - super(props); - - this.setLayoutSizes = this.setLayoutSizes.bind(this); - this.calculatesLayout = this.calculatesLayout.bind(this); - } - - componentDidMount() { - this.setLayoutSizes(); - window.addEventListener('resize', _.throttle(() => this.setLayoutSizes(), 200)); - - window.addEventListener('panelChanged', () => { - this.setLayoutSizes(true); - }); - - window.addEventListener('autoArrangeChanged', () => { - setTimeout(() => this.setLayoutSizes(false, true), 200); - }); - - window.addEventListener('slideChanged', () => { - setTimeout(() => this.setLayoutSizes(), 200); - }); - - window.addEventListener('togglePresentationHide', () => { - setTimeout(() => this.setLayoutSizes(), 200); - }); - - window.addEventListener('webcamAreaResize', () => { - this.setLayoutSizes(); - }); - - window.addEventListener('webcamPlacementChange', () => { - this.setLayoutSizes(false, false, true); - }); - } - - componentDidUpdate(prevProps) { - const { layoutContextState } = this.props; - const { layoutContextState: prevLayoutContextState } = prevProps; - const { - numUsersVideo, - } = layoutContextState; - const { - numUsersVideo: prevNumUsersVideo, - } = prevLayoutContextState; - - if (numUsersVideo !== prevNumUsersVideo) { - setTimeout(() => this.setLayoutSizes(), 500); - } - } - - setLayoutSizes(panelChanged = false, autoarrangeChanged = false, placementChanged = false) { - const { layoutContextDispatch, layoutContextState } = this.props; - const { autoArrangeLayout } = layoutContextState; - - if (autoarrangeChanged && !autoArrangeLayout && !placementChanged) return; - - const layoutSizes = this.calculatesLayout(panelChanged); - - layoutContextDispatch( - { - type: 'setWindowSize', - value: { - width: windowWidth(), - height: windowHeight(), - }, - }, - ); - 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: layoutSizes.userListSize.width, - }, - }, - ); - layoutContextDispatch( - { - type: 'setChatSize', - value: { - width: layoutSizes.chatSize.width, - }, - }, - ); - layoutContextDispatch( - { - type: 'setBreakoutRoomSize', - value: { - width: layoutSizes.breakoutRoomSize.width, - }, - }, - ); - layoutContextDispatch( - { - type: 'setWebcamsAreaSize', - value: { - width: layoutSizes.webcamsAreaSize.width, - height: layoutSizes.webcamsAreaSize.height, - }, - }, - ); - layoutContextDispatch( - { - type: 'setPresentationAreaSize', - value: { - width: layoutSizes.presentationAreaSize.width, - height: layoutSizes.presentationAreaSize.height, - }, - }, - ); - - const newLayoutData = { - windowSize: { - width: windowWidth(), - height: windowHeight(), - }, - mediaBounds: { - width: layoutSizes.mediaBounds.width, - height: layoutSizes.mediaBounds.height, - top: layoutSizes.mediaBounds.top, - left: layoutSizes.mediaBounds.left, - }, - userListSize: { - width: layoutSizes.userListSize.width, - }, - chatSize: { - width: layoutSizes.chatSize.width, - }, - breakoutRoomSize: { - width: layoutSizes.breakoutRoomSize.width, - }, - webcamsAreaSize: { - width: layoutSizes.webcamsAreaSize.width, - height: layoutSizes.webcamsAreaSize.height, - }, - presentationAreaSize: { - width: layoutSizes.presentationAreaSize.width, - height: layoutSizes.presentationAreaSize.height, - }, - }; - - Storage.setItem('layoutData', newLayoutData); - window.dispatchEvent(new Event('layoutSizesSets')); - } - - defineWebcamPlacement(mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight) { - const { layoutContextDispatch, layoutContextState } = this.props; - const { autoArrangeLayout } = layoutContextState; - const isScreenShare = isVideoBroadcasting(); - - if (!autoArrangeLayout) return; - - if (isScreenShare) { - layoutContextDispatch( - { - type: 'setWebcamsPlacement', - value: 'top', - }, - ); - Storage.setItem('webcamsPlacement', 'top'); - return; - } - - if ((mediaAreaWidth - presentationWidth) > (mediaAreaHeight - presentationHeight)) { - layoutContextDispatch( - { - type: 'setWebcamsPlacement', - value: 'left', - }, - ); - Storage.setItem('webcamsPlacement', 'left'); - } else { - layoutContextDispatch( - { - type: 'setWebcamsPlacement', - value: 'top', - }, - ); - Storage.setItem('webcamsPlacement', 'top'); - } - } - - calculatesPanelsSize(panelChanged) { - const { layoutContextState } = this.props; - const { - userListSize: userListSizeContext, - chatSize: chatSizeContext, - breakoutRoomSize: breakoutRoomSizeContext, - } = layoutContextState; - const openPanel = Session.get('openPanel'); - const storageLData = storageLayoutData(); - - let storageUserListWidth; - let storageChatWidth; - let storageBreakoutRoomWidth; - if (storageLData) { - storageUserListWidth = storageLData.userListSize.width; - storageChatWidth = storageLData.chatSize.width; - storageBreakoutRoomWidth = storageLData.breakoutRoomSize.width; - } - - let newUserListSize; - let newChatSize; - let newBreakoutRoomSize; - - if (panelChanged && userListSizeContext.width !== 0) { - newUserListSize = userListSizeContext; - } else if (!storageUserListWidth) { - newUserListSize = { - width: min(max((windowWidth() * 0.1), USERLIST_MIN_WIDTH), USERLIST_MAX_WIDTH), - }; - } else { - newUserListSize = { - width: storageUserListWidth, - }; - } - - if (panelChanged && chatSizeContext.width !== 0) { - newChatSize = chatSizeContext; - } else if (!storageChatWidth) { - newChatSize = { - width: min(max((windowWidth() * 0.2), CHAT_MIN_WIDTH), CHAT_MAX_WIDTH), - }; - } else { - newChatSize = { - width: storageChatWidth, - }; - } - - if (panelChanged && breakoutRoomSizeContext.width !== 0) { - newBreakoutRoomSize = breakoutRoomSizeContext; - } else if (!storageBreakoutRoomWidth) { - newBreakoutRoomSize = { - width: min(max((windowWidth() * 0.2), CHAT_MIN_WIDTH), CHAT_MAX_WIDTH), - }; - } else { - newBreakoutRoomSize = { - width: storageBreakoutRoomWidth, - }; - } - - if (openPanel === 'userlist') { - newChatSize = { - width: 0, - }; - newBreakoutRoomSize = { - width: 0, - }; - } - - if (openPanel === '') { - newUserListSize = { - width: 0, - }; - newChatSize = { - width: 0, - }; - newBreakoutRoomSize = { - width: 0, - }; - } - - return { - newUserListSize, - newChatSize, - newBreakoutRoomSize, - }; - } - - calculatesWebcamsAreaSize( - mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight, - ) { - const { - layoutContextState, - } = this.props; - const { webcamsPlacement, numUsersVideo } = layoutContextState; - - const autoArrangeLayout = Storage.getItem('autoArrangeLayout'); - const webcamsAreaUserSetsHeight = Storage.getItem('webcamsAreaUserSetsHeight'); - const webcamsAreaUserSetsWidth = Storage.getItem('webcamsAreaUserSetsWidth'); - - let webcamsAreaWidth; - let webcamsAreaHeight; - - if (numUsersVideo < 1) { - return { - webcamsAreaWidth: 0, - webcamsAreaHeight: 0, - }; - } - - if (autoArrangeLayout) { - if (webcamsPlacement === 'left' || webcamsPlacement === 'right') { - webcamsAreaWidth = (mediaAreaWidth - presentationWidth) - < (mediaAreaWidth * WEBCAMSAREA_MIN_PERCENT) - ? mediaAreaWidth * WEBCAMSAREA_MIN_PERCENT - : mediaAreaWidth - presentationWidth; - webcamsAreaHeight = mediaAreaHeight; - } else { - webcamsAreaWidth = mediaAreaWidth; - webcamsAreaHeight = (mediaAreaHeight - presentationHeight) - < (mediaAreaHeight * WEBCAMSAREA_MIN_PERCENT) - ? mediaAreaHeight * WEBCAMSAREA_MIN_PERCENT - : mediaAreaHeight - presentationHeight; - } - } else if (webcamsPlacement === 'left' || webcamsPlacement === 'right') { - webcamsAreaWidth = min( - max( - webcamsAreaUserSetsWidth - || mediaAreaWidth * WEBCAMSAREA_MIN_PERCENT, - mediaAreaWidth * WEBCAMSAREA_MIN_PERCENT, - ), - mediaAreaWidth * WEBCAMSAREA_MAX_PERCENT, - ); - webcamsAreaHeight = mediaAreaHeight; - } else { - webcamsAreaWidth = mediaAreaWidth; - webcamsAreaHeight = min( - max( - webcamsAreaUserSetsHeight - || mediaAreaHeight * WEBCAMSAREA_MIN_PERCENT, - mediaAreaHeight * WEBCAMSAREA_MIN_PERCENT, - ), - mediaAreaHeight * WEBCAMSAREA_MAX_PERCENT, - ); - } - - if ((webcamsPlacement === 'left' || webcamsPlacement === 'right') && (mediaAreaWidth - webcamsAreaWidth) < PRESENTATIONAREA_MIN_WIDTH) { - webcamsAreaWidth = mediaAreaWidth - PRESENTATIONAREA_MIN_WIDTH; - } - - return { - webcamsAreaWidth, - webcamsAreaHeight, - }; - } - - calculatesPresentationAreaSize( - mediaAreaWidth, mediaAreaHeight, webcamAreaWidth, webcamAreaHeight, - ) { - const { - layoutContextState, - } = this.props; - const { - webcamsPlacement, - numUsersVideo, - } = layoutContextState; - - if (numUsersVideo < 1) { - return { - presentationAreaWidth: mediaAreaWidth, - presentationAreaHeight: mediaAreaHeight - 20, - }; - } - - let presentationAreaWidth; - let presentationAreaHeight; - - if (webcamsPlacement === 'left' || webcamsPlacement === 'right') { - presentationAreaWidth = mediaAreaWidth - webcamAreaWidth - 20; - presentationAreaHeight = mediaAreaHeight - 20; - } else { - presentationAreaWidth = mediaAreaWidth; - presentationAreaHeight = mediaAreaHeight - webcamAreaHeight - 30; - } - - return { - presentationAreaWidth, - presentationAreaHeight, - }; - } - - calculatesLayout(panelChanged = false) { - const { - layoutContextState, - } = this.props; - const { - presentationIsFullscreen, - presentationSlideSize, - } = layoutContextState; - - const { - width: presentationSlideWidth, - height: presentationSlideHeight, - } = presentationSlideSize; - - const panelsSize = this.calculatesPanelsSize(panelChanged); - - const { - newUserListSize, - newChatSize, - newBreakoutRoomSize, - } = panelsSize; - - const firstPanel = newUserListSize; - let secondPanel = { - width: 0, - }; - if (newChatSize.width > 0) { - secondPanel = newChatSize; - } else if (newBreakoutRoomSize.width > 0) { - secondPanel = newBreakoutRoomSize; - } - - const mediaAreaHeight = windowHeight() - (NAVBAR_HEIGHT + ACTIONSBAR_HEIGHT) - 10; - const mediaAreaWidth = windowWidth() - (firstPanel.width + secondPanel.width); - const newMediaBounds = { - width: mediaAreaWidth, - height: mediaAreaHeight, - top: NAVBAR_HEIGHT, - left: firstPanel.width + secondPanel.width, - }; - - const { presentationWidth, presentationHeight } = LayoutManager.calculatesPresentationSize( - mediaAreaWidth, mediaAreaHeight, presentationSlideWidth, presentationSlideHeight, - ); - - this.defineWebcamPlacement( - mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight, - ); - - const { webcamsAreaWidth, webcamsAreaHeight } = this.calculatesWebcamsAreaSize( - mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight, - ); - - const newWebcamsAreaSize = { - width: webcamsAreaWidth, - height: webcamsAreaHeight, - }; - let newPresentationAreaSize; - let newScreenShareAreaSize; - const { presentationAreaWidth, presentationAreaHeight } = this.calculatesPresentationAreaSize( - mediaAreaWidth, mediaAreaHeight, webcamsAreaWidth, webcamsAreaHeight, - ); - if (!presentationIsFullscreen) { - newPresentationAreaSize = { - width: presentationAreaWidth || 0, - height: presentationAreaHeight || 0, - }; - } else { - newPresentationAreaSize = { - width: windowWidth(), - height: windowHeight(), - }; - } - - return { - mediaBounds: newMediaBounds, - userListSize: newUserListSize, - chatSize: newChatSize, - breakoutRoomSize: newBreakoutRoomSize, - webcamsAreaSize: newWebcamsAreaSize, - presentationAreaSize: newPresentationAreaSize, - screenShareAreaSize: newScreenShareAreaSize, - }; - } - - render() { - return <Fragment />; - } -} - -export default withLayoutConsumer(LayoutManager); -export { - USERLIST_MIN_WIDTH, - USERLIST_MAX_WIDTH, - CHAT_MIN_WIDTH, - CHAT_MAX_WIDTH, - NAVBAR_HEIGHT, - ACTIONSBAR_HEIGHT, - WEBCAMSAREA_MIN_PERCENT, - WEBCAMSAREA_MAX_PERCENT, - PRESENTATIONAREA_MIN_WIDTH, -}; diff --git a/bigbluebutton-html5/imports/ui/components/media/component.jsx b/bigbluebutton-html5/imports/ui/components/media/component.jsx index 9f7c18c054cb076a97eb2bd1b29468b05e865182..36c17f52feb71b662ac0f8d1cdd798e8a00d1694 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, - layoutContextState: PropTypes.instanceOf(Object).isRequired, + webcamDraggableState: PropTypes.instanceOf(Object).isRequired, }; const defaultProps = { @@ -43,12 +43,12 @@ export default class Media extends Component { children, audioModalIsOpen, usersVideo, - layoutContextState, + webcamDraggableState, } = this.props; - const { webcamsPlacement: placement } = layoutContextState; - const placementStorage = Storage.getItem('webcamsPlacement'); - const webcamsPlacement = placement || placementStorage; + const { placement } = webcamDraggableState; + const placementStorage = Storage.getItem('webcamPlacement'); + const webcamPlacement = 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]: (webcamsPlacement === 'floating'), + [styles.floatingOverlay]: (webcamPlacement === 'floating'), }); const containerClassName = cx({ [styles.container]: true, - [styles.containerV]: webcamsPlacement === 'top' || webcamsPlacement === 'bottom' || webcamsPlacement === 'floating', - [styles.containerH]: webcamsPlacement === 'left' || webcamsPlacement === 'right', + [styles.containerV]: webcamPlacement === 'top' || webcamPlacement === 'bottom' || webcamPlacement === 'floating', + [styles.containerH]: webcamPlacement === 'left' || webcamPlacement === 'right', }); return ( @@ -77,24 +77,24 @@ export default class Media extends Component { style={{ maxHeight: usersVideo.length > 0 && ( - webcamsPlacement !== 'left' - || webcamsPlacement !== 'right' + webcamPlacement !== 'left' + || webcamPlacement !== 'right' ) && ( - webcamsPlacement === 'top' - || webcamsPlacement === 'bottom' + webcamPlacement === 'top' + || webcamPlacement === 'bottom' ) ? '80%' : '100%', minHeight: BROWSER_ISMOBILE && window.innerWidth > window.innerHeight ? '50%' : '20%', maxWidth: usersVideo.length > 0 && ( - webcamsPlacement !== 'top' - || webcamsPlacement !== 'bottom' + webcamPlacement !== 'top' + || webcamPlacement !== 'bottom' ) && ( - webcamsPlacement === 'left' - || webcamsPlacement === 'right' + webcamPlacement === 'left' + || webcamPlacement === 'right' ) ? '80%' : '100%', diff --git a/bigbluebutton-html5/imports/ui/components/media/container.jsx b/bigbluebutton-html5/imports/ui/components/media/container.jsx index 0e6466180a14d6fbcdc8803bcfb60789dd52dcd4..4efa85853f0a7c769154664a565761f96cf7f4c7 100755 --- a/bigbluebutton-html5/imports/ui/components/media/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/media/container.jsx @@ -9,13 +9,13 @@ import VideoService from '/imports/ui/components/video-provider/service'; import getFromUserSettings from '/imports/ui/services/users-settings'; import { withModalMounter } from '/imports/ui/components/modal/service'; import Media from './component'; -import MediaService, { getSwapLayout, shouldEnableSwapLayout } from '/imports/ui/components/media/service'; +import MediaService, { getSwapLayout, shouldEnableSwapLayout } from './service'; import PresentationPodsContainer from '../presentation-pod/container'; import ScreenshareContainer from '../screenshare/container'; import DefaultContent from '../presentation/default-content/component'; import ExternalVideoContainer from '../external-video-player/container'; import Storage from '../../services/storage/session'; -import { withLayoutConsumer } from '/imports/ui/components/layout/context'; +import { withDraggableConsumer } from './webcam-draggable-overlay/context'; const LAYOUT_CONFIG = Meteor.settings.public.layout; const KURENTO_CONFIG = Meteor.settings.public.kurento; @@ -49,22 +49,19 @@ const intlMessages = defineMessages({ }); class MediaContainer extends Component { - componentDidMount() { + componentWillMount() { document.addEventListener('installChromeExtension', this.installChromeExtension.bind(this)); document.addEventListener('screenshareNotSupported', this.screenshareNotSupported.bind(this)); } - componentDidUpdate(prevProps) { + componentWillReceiveProps(nextProps) { const { isScreensharing, intl, } = this.props; - const { - isScreensharing: wasScreenSharing, - } = prevProps; - if (isScreensharing !== wasScreenSharing) { - if (isScreensharing) { + if (isScreensharing !== nextProps.isScreensharing) { + if (nextProps.isScreensharing) { notify(intl.formatMessage(intlMessages.screenshareStarted), 'info', 'desktop'); } else { notify(intl.formatMessage(intlMessages.screenshareEnded), 'info', 'desktop'); @@ -106,7 +103,7 @@ class MediaContainer extends Component { } } -export default withLayoutConsumer(withModalMounter(withTracker(() => { +export default withDraggableConsumer(withModalMounter(withTracker(() => { const { dataSaving } = Settings; const { viewParticipantsWebcams, viewScreenshare } = dataSaving; const hidePresentation = getFromUserSettings('bbb_hide_presentation', LAYOUT_CONFIG.hidePresentation); @@ -153,7 +150,7 @@ export default withLayoutConsumer(withModalMounter(withTracker(() => { ); } - data.webcamsPlacement = Storage.getItem('webcamsPlacement'); + data.webcamPlacement = Storage.getItem('webcamPlacement'); MediaContainer.propTypes = propTypes; return data; diff --git a/bigbluebutton-html5/imports/ui/components/media/service.js b/bigbluebutton-html5/imports/ui/components/media/service.js index 4a0670f0443b7daa0ad09d926ca4887fcf0714ed..9811280c05f5fa8669a9789b462962daa23a7818 100755 --- a/bigbluebutton-html5/imports/ui/components/media/service.js +++ b/bigbluebutton-html5/imports/ui/components/media/service.js @@ -4,6 +4,7 @@ import { getVideoUrl } from '/imports/ui/components/external-video-player/servic import Auth from '/imports/ui/services/auth'; import Users from '/imports/api/users'; import Settings from '/imports/ui/services/settings'; +import PollingService from '/imports/ui/components/polling/service'; import getFromUserSettings from '/imports/ui/services/users-settings'; const LAYOUT_CONFIG = Meteor.settings.public.layout; @@ -52,7 +53,6 @@ const setSwapLayout = () => { }; const toggleSwapLayout = () => { - window.dispatchEvent(new Event('togglePresentationHide')); swapLayout.value = !swapLayout.value; swapLayout.tracker.changed(); }; 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 a47575fcd678a49504151ef6deb05c5d0257c27b..40f82c4ee666aae39e75a241924f05665530a9e4 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,6 +1,7 @@ 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'; @@ -8,8 +9,6 @@ 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, PRESENTATIONAREA_MIN_WIDTH } from '/imports/ui/components/layout/layout-manager'; const BROWSER_ISMOBILE = isMobile || isIPad13; @@ -21,8 +20,7 @@ const propTypes = { webcamDraggableState: PropTypes.objectOf(Object).isRequired, webcamDraggableDispatch: PropTypes.func.isRequired, refMediaContainer: PropTypes.shape({ current: PropTypes.instanceOf(Element) }), - layoutContextState: PropTypes.objectOf(Object).isRequired, - layoutContextDispatch: PropTypes.func.isRequired, + usersVideoLenght: PropTypes.number.isRequired, }; const defaultProps = { @@ -32,38 +30,78 @@ 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, - hideWebcams: 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.handleLayoutSizesSets = this.handleLayoutSizesSets.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); document.addEventListener('fullscreenchange', this.onFullscreenChange); - window.addEventListener('layoutSizesSets', this.handleLayoutSizesSets); + window.addEventListener('orientationchange', () => setTimeout(this.recalculateAreaSize, 500)); + } + + componentDidUpdate(prevProps) { + 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(); + } + + 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 ((prevOrientation == null || prevOrientation === 'landscape') && orientation === 'portrait') { + if (storagePlacement !== lastPlacementPortrait && lastPlacementPortrait === 'left') webcamDraggableDispatch({ type: 'setplacementToLeft' }); + if (storagePlacement !== lastPlacementPortrait && lastPlacementPortrait === 'right') webcamDraggableDispatch({ type: 'setplacementToRight' }); + } + } } componentWillUnmount() { + window.removeEventListener('resize', this.debouncedOnResize); document.removeEventListener('fullscreenchange', this.onFullscreenChange); - window.removeEventListener('layoutSizesSets', this.handleLayoutSizesSets); + dispatchResizeEvent(); } onFullscreenChange() { @@ -71,115 +109,97 @@ class WebcamDraggable extends PureComponent { } onResizeStart() { - const { layoutContextDispatch } = this.props; - this.setState({ resizing: true }); - layoutContextDispatch( - { - type: 'setWebcamsAreaResizing', - value: true, - }, - ); } - onResizeHandle(resizableWidth, resizableHeight) { - const { webcamsAreaResizable } = this.state; - const { layoutContextState, layoutContextDispatch } = this.props; - const { webcamsPlacement, webcamsAreaSize } = layoutContextState; - - layoutContextDispatch( - { - type: 'setAutoArrangeLayout', - value: false, - }, - ); - - const newWebcamsAreaResizable = { - width: Math.trunc(webcamsAreaResizable.width) + resizableWidth, - height: Math.trunc(webcamsAreaResizable.height) + resizableHeight, - }; - - const newWidth = webcamsPlacement === 'top' || webcamsPlacement === 'bottom' ? webcamsAreaSize.width : newWebcamsAreaResizable.width; - const newHeight = webcamsPlacement === 'left' || webcamsPlacement === 'right' ? webcamsAreaSize.height : newWebcamsAreaResizable.height; + onWindowResize() { + const { webcamDraggableState, webcamDraggableDispatch } = this.props; + const { mediaSize } = webcamDraggableState; + const { width: stateWidth, height: stateHeight } = mediaSize; + const { width, height } = this.getMediaBounds(); - layoutContextDispatch( - { - type: 'setTempWebcamsAreaSize', - value: { - width: newWidth, - height: newHeight, - }, - }, - ); - - window.dispatchEvent(new Event('webcamAreaResize')); - } - - onResizeStop(resizableWidth, resizableHeight) { - const { webcamsAreaResizable } = this.state; - const { layoutContextState, layoutContextDispatch } = this.props; - const { webcamsPlacement, webcamsAreaSize } = layoutContextState; - - layoutContextDispatch( - { - type: 'setWebcamsAreaResizing', - value: false, - }, - ); - - const newWebcamsAreaResizable = { - width: Math.trunc(webcamsAreaResizable.width) + resizableWidth, - height: Math.trunc(webcamsAreaResizable.height) + resizableHeight, - }; - - if (webcamsPlacement === 'top' || webcamsPlacement === 'bottom') { - layoutContextDispatch( + if (stateWidth !== width || stateHeight !== height) { + webcamDraggableDispatch( { - type: 'setWebcamsAreaUserSetsHeight', - value: newWebcamsAreaResizable.height, + type: 'setMediaSize', + value: { + width, + height, + }, }, ); + setTimeout(() => window.dispatchEvent(new Event('resize')), 300); } + } + + onResize() { + this.setPlacementPercent(); + } - if (webcamsPlacement === 'right' || webcamsPlacement === 'left') { - layoutContextDispatch( + onResizeStop() { + const { webcamDraggableState, webcamDraggableDispatch } = this.props; + const { optimalGrid } = webcamDraggableState; + if (optimalGrid) { + webcamDraggableDispatch( { - type: 'setWebcamsAreaUserSetsWidth', - value: newWebcamsAreaResizable.width, + type: 'setVideoListSize', + value: { + width: optimalGrid.width, + height: optimalGrid.height, + }, }, ); } - - const newWidth = webcamsPlacement === 'top' || webcamsPlacement === 'bottom' - ? webcamsAreaSize.width - : newWebcamsAreaResizable.width; - const newHeight = webcamsPlacement === 'left' || webcamsPlacement === 'right' - ? webcamsAreaSize.height - : newWebcamsAreaResizable.height; - - layoutContextDispatch( - { - type: 'setWebcamsAreaSize', - value: { - width: newWidth, - height: newHeight, - }, - }, - ); - - this.setWebcamsAreaResizable(newWidth, newHeight); - + this.setPlacementPercent(); + window.dispatchEvent(new Event('resize')); setTimeout(() => this.setState({ resizing: false }), 500); - window.dispatchEvent(new Event('webcamAreaResize')); } - setWebcamsAreaResizable(width, height) { - this.setState({ - webcamsAreaResizable: { width, height }, - }); + 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 }); + } } - setHideWebcams(hideWebcams) { this.setState({ hideWebcams }); } + 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, + }, + }, + ); + } + + return { + top, + left, + width: newWidth, + height: newHeight, + }; + } + return false; + } getWebcamsListBounds() { const { webcamDraggableState } = this.props; @@ -190,20 +210,22 @@ class WebcamDraggable extends PureComponent { top, left, width, height, } = videoListRefRect; return { - top, - left, - width, - height, + top, // 10 = margin + left, // 10 = margin + width, // 20 = margin + height, // 20 = margin }; } return false; } - calculatePosition() { - const { layoutContextState } = this.props; - const { mediaBounds } = layoutContextState; + recalculateAreaSize() { + this.onResizeStart(); + this.onResizeStop(); + } - const { top: mediaTop, left: mediaLeft } = mediaBounds; + calculatePosition() { + const { top: mediaTop, left: mediaLeft } = this.getMediaBounds(); const { top: webcamsListTop, left: webcamsListLeft } = this.getWebcamsListBounds(); const x = webcamsListLeft - mediaLeft; const y = webcamsListTop - mediaTop; @@ -213,14 +235,6 @@ class WebcamDraggable extends PureComponent { }; } - handleLayoutSizesSets() { - const { layoutContextState } = this.props; - const { webcamsAreaSize } = layoutContextState; - - this.setWebcamsAreaResizable(webcamsAreaSize.width, webcamsAreaSize.height); - this.setHideWebcams(false); - } - handleWebcamDragStart() { const { webcamDraggableDispatch } = this.props; const { x, y } = this.calculatePosition(); @@ -237,69 +251,52 @@ class WebcamDraggable extends PureComponent { } handleWebcamDragStop(e) { - const { webcamDraggableDispatch, layoutContextDispatch } = this.props; + const { webcamDraggableDispatch } = this.props; const targetClassname = JSON.stringify(e.target.className); - this.setHideWebcams(true); - - layoutContextDispatch( - { - type: 'setAutoArrangeLayout', - value: false, - }, - ); - if (targetClassname) { if (targetClassname.includes('Top')) { - layoutContextDispatch({ - type: 'setWebcamsPlacement', - value: 'top', - }); + webcamDraggableDispatch({ type: 'setplacementToTop' }); + webcamDraggableDispatch({ type: 'setLastPlacementLandscapeToTop' }); } else if (targetClassname.includes('Right')) { - layoutContextDispatch({ - type: 'setWebcamsPlacement', - value: 'right', - }); + webcamDraggableDispatch({ type: 'setplacementToRight' }); + webcamDraggableDispatch({ type: 'setLastPlacementPortraitToRight' }); } else if (targetClassname.includes('Bottom')) { - layoutContextDispatch({ - type: 'setWebcamsPlacement', - value: 'bottom', - }); + webcamDraggableDispatch({ type: 'setplacementToBottom' }); + webcamDraggableDispatch({ type: 'setLastPlacementLandscapeToBottom' }); } else if (targetClassname.includes('Left')) { - layoutContextDispatch({ - type: 'setWebcamsPlacement', - value: 'left', - }); + webcamDraggableDispatch({ type: 'setplacementToLeft' }); + webcamDraggableDispatch({ type: 'setLastPlacementPortraitToLeft' }); } } webcamDraggableDispatch({ type: 'dragEnd' }); - window.dispatchEvent(new Event('webcamPlacementChange')); + window.dispatchEvent(new Event('resize')); + setTimeout(this.recalculateAreaSize, 500); } render() { const { - layoutContextState, webcamDraggableState, swapLayout, hideOverlay, disableVideo, audioModalIsOpen, + refMediaContainer, } = this.props; - const { resizing, webcamsAreaResizable, hideWebcams } = this.state; - const { - mediaBounds, - webcamsAreaSize, - } = layoutContextState; + resizing, + placementPercent, + } = this.state; const { dragging, isCameraFullscreen, + videoListSize, optimalGrid, } = webcamDraggableState; - const webcamsPlacement = Storage.getItem('webcamsPlacement'); + const placement = Storage.getItem('webcamPlacement'); const lastPosition = Storage.getItem('webcamLastPosition') || { x: 0, y: 0 }; @@ -326,7 +323,7 @@ class WebcamDraggable extends PureComponent { const { width: mediaWidth, height: mediaHeight, - } = mediaBounds; + } = this.getMediaBounds(); const { width: webcamsWidth, @@ -349,16 +346,57 @@ 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.overlayToTop]: webcamsPlacement === 'top' && !dragging, - [styles.overlayToRight]: webcamsPlacement === 'right' && !dragging, - [styles.overlayToBottom]: webcamsPlacement === 'bottom' && !dragging, - [styles.overlayToLeft]: webcamsPlacement === 'left' && !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.dragging]: dragging, + [styles.hide]: ( + ( + placement === 'left' + || placement === 'right' + ) + && layout === 'vertical' + ) + || ( + ( + placement === 'top' + || placement === 'bottom' + ) + && layout === 'horizontal' + ), }); const dropZoneTopClassName = cx({ @@ -405,17 +443,33 @@ class WebcamDraggable extends PureComponent { [styles.dropZoneBgRight]: true, }); - let sizeHeight; - let sizeWidth; + 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) { - sizeWidth = optimalGrid.width; - sizeHeight = optimalGrid.height; - } else if (resizing) { - sizeWidth = webcamsAreaResizable.width; - sizeHeight = webcamsAreaResizable.height; - } else { - sizeWidth = webcamsAreaSize.width; - sizeHeight = webcamsAreaSize.height; + resizeHeight = optimalGrid.height; + resizeWidth = optimalGrid.width; } return ( @@ -445,37 +499,26 @@ class WebcamDraggable extends PureComponent { onStart={this.handleWebcamDragStart} onStop={this.handleWebcamDragStop} onMouseDown={e => e.preventDefault()} - disabled={swapLayout || isCameraFullscreen || BROWSER_ISMOBILE || resizing} + disabled={swapLayout || isCameraFullscreen || BROWSER_ISMOBILE} position={position} > <Resizable - minWidth={mediaWidth * WEBCAMSAREA_MIN_PERCENT} - minHeight={mediaHeight * WEBCAMSAREA_MIN_PERCENT} - maxWidth={ - webcamsPlacement === 'left' || webcamsPlacement === 'right' - ? mediaWidth - PRESENTATIONAREA_MIN_WIDTH - : undefined - } size={ { - width: sizeWidth, - height: sizeHeight, + height: resizeHeight, + width: resizeWidth, } } - // lockAspectRatio + lockAspectRatio handleWrapperClass="resizeWrapper" onResizeStart={this.onResizeStart} - onResize={(e, direction, ref, d) => { - this.onResizeHandle(d.width, d.height); - }} - onResizeStop={(e, direction, ref, d) => { - this.onResizeStop(d.width, d.height); - }} + onResize={dispatchResizeEvent} + onResizeStop={this.onResizeStop} enable={{ - top: (webcamsPlacement === 'bottom') && !swapLayout, - bottom: (webcamsPlacement === 'top') && !swapLayout, - left: (webcamsPlacement === 'right') && !swapLayout, - right: (webcamsPlacement === 'left') && !swapLayout, + top: (placement === 'bottom') && !swapLayout, + bottom: (placement === 'top') && !swapLayout, + left: (placement === 'right') && !swapLayout, + right: (placement === 'left') && !swapLayout, topLeft: false, topRight: false, bottomLeft: false, @@ -484,12 +527,10 @@ class WebcamDraggable extends PureComponent { className={ !swapLayout ? overlayClassName - : contentClassName - } + : contentClassName} style={{ marginLeft: 0, marginRight: 0, - display: hideWebcams ? 'none' : undefined, }} > { @@ -531,4 +572,4 @@ class WebcamDraggable extends PureComponent { WebcamDraggable.propTypes = propTypes; WebcamDraggable.defaultProps = defaultProps; -export default withDraggableConsumer(withLayoutConsumer(WebcamDraggable)); +export default withDraggableConsumer(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 f86b406f7701f5250ebf8f463fb482c1bbdda712..a0981de896110c2c1fc8fc6a6ac613bf2a7c6a73 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,9 +1,19 @@ import React, { createContext, useReducer, useEffect } from 'react'; -import Storage from '/imports/ui/services/storage/session'; +import Storage from '../../../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, @@ -29,6 +39,81 @@ 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, @@ -62,6 +147,15 @@ const reducer = (state, action) => { }, }; } + case 'setLastPosition': { + return { + ...state, + lastPosition: { + x: action.value.x, + y: action.value.y, + }, + }; + } case 'setVideoRef': { return { ...state, @@ -120,6 +214,7 @@ 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 5acf94dcb45050cb205054522f92e77d0cd8d66e..0a23e1e2c2bd5aadb3661c68b5f9ae7ee66fabbd 100755 --- a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { Session } from 'meteor/session'; import cx from 'classnames'; @@ -8,11 +8,12 @@ import getFromUserSettings from '/imports/ui/services/users-settings'; import { defineMessages, injectIntl } from 'react-intl'; import Icon from '../icon/component'; import { styles } from './styles.scss'; -import Button from '/imports/ui/components/button/component'; +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'; + const intlMessages = defineMessages({ toggleUserListLabel: { id: 'app.navBar.userListToggleBtnLabel', @@ -40,7 +41,7 @@ const defaultProps = { shortcuts: '', }; -class NavBar extends Component { +class NavBar extends PureComponent { static handleToggleUserList() { Session.set( 'openPanel', @@ -49,8 +50,6 @@ class NavBar extends Component { : 'userlist', ); Session.set('idChatOpen', ''); - - window.dispatchEvent(new Event('panelChanged')); } componentDidMount() { @@ -90,9 +89,7 @@ class NavBar extends Component { ariaLabel += hasUnreadMessages ? (` ${intl.formatMessage(intlMessages.newMessages)}`) : ''; return ( - <div - className={styles.navbar} - > + <div className={styles.navbar}> <div className={styles.top}> <div className={styles.left}> {!isExpanded ? null diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss b/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss index 0040f6a63ae7eb6696ac4eb7b6a9798aa3685dae..b3e4205a4709f36587341f26c2b1991f16968a83 100755 --- a/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss @@ -11,6 +11,7 @@ .navbar { display: flex; flex-direction: column; + height:var(--mobile-nav-height); } .top, diff --git a/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx b/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx index a77d4b43ed0fa48fc4307f2966bd0f085390aad3..1c867fa59c7648afbbed209ac61ee123c22c53e6 100755 --- a/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import BreakoutRoomContainer from '/imports/ui/components/breakout-room/container'; import UserListContainer from '/imports/ui/components/user-list/container'; @@ -11,13 +11,6 @@ import { defineMessages, injectIntl } from 'react-intl'; import Resizable from 're-resizable'; import { styles } from '/imports/ui/components/app/styles'; import _ from 'lodash'; -import { withLayoutConsumer } from '/imports/ui/components/layout/context'; -import { - USERLIST_MIN_WIDTH, - USERLIST_MAX_WIDTH, - CHAT_MIN_WIDTH, - CHAT_MAX_WIDTH, -} from '/imports/ui/components/layout/layout-manager'; const intlMessages = defineMessages({ chatLabel: { @@ -50,8 +43,12 @@ const propTypes = { const DEFAULT_PANEL_WIDTH = 340; // Variables for resizing user-list. -const USERLIST_MIN_WIDTH_PX = USERLIST_MIN_WIDTH; -const USERLIST_MAX_WIDTH_PX = USERLIST_MAX_WIDTH; +const USERLIST_MIN_WIDTH_PX = 150; +const USERLIST_MAX_WIDTH_PX = 240; + +// Variables for resizing chat. +const CHAT_MIN_WIDTH = 150; +const CHAT_MAX_WIDTH = 350; // Variables for resizing poll. const POLL_MIN_WIDTH = 320; @@ -69,9 +66,11 @@ const CAPTIONS_MAX_WIDTH = 400; const WAITING_MIN_WIDTH = DEFAULT_PANEL_WIDTH; const WAITING_MAX_WIDTH = 800; -class PanelManager extends Component { - constructor(props) { - super(props); +const dispatchResizeEvent = () => window.dispatchEvent(new Event('resize')); + +class PanelManager extends PureComponent { + constructor() { + super(); this.padKey = _.uniqueId('resize-pad-'); this.userlistKey = _.uniqueId('userlist-'); @@ -82,232 +81,23 @@ class PanelManager extends Component { this.captionsKey = _.uniqueId('captions-'); this.waitingUsers = _.uniqueId('waitingUsers-'); - const { layoutContextState } = props; - const { userListSize, chatSize } = layoutContextState; - this.state = { - userlistWidth: userListSize.width, - chatWidth: chatSize.width, + chatWidth: DEFAULT_PANEL_WIDTH, + pollWidth: DEFAULT_PANEL_WIDTH, + userlistWidth: 180, noteWidth: DEFAULT_PANEL_WIDTH, captionsWidth: DEFAULT_PANEL_WIDTH, - pollWidth: DEFAULT_PANEL_WIDTH, waitingWidth: DEFAULT_PANEL_WIDTH, - breakoutRoomWidth: 0, }; - - this.setUserListWidth = this.setUserListWidth.bind(this); - } - - shouldComponentUpdate(prevProps) { - const { layoutContextState } = this.props; - const { layoutContextState: prevLayoutContextState } = prevProps; - const { - userListSize, - chatSize, - breakoutRoomSize, - } = layoutContextState; - const { - userListSize: prevUserListSize, - chatSize: prevChatSize, - breakoutRoomSize: prevBreakoutRoomSize, - } = prevLayoutContextState; - - if ((layoutContextState !== prevLayoutContextState) - && (userListSize.width === prevUserListSize.width - && chatSize.width === prevChatSize.width - && breakoutRoomSize.width === prevBreakoutRoomSize.width)) return false; - return true; } componentDidUpdate(prevProps) { - const { - userlistWidth, - chatWidth, - noteWidth, - captionsWidth, - pollWidth, - waitingWidth, - breakoutRoomWidth, - } = this.state; - const { layoutContextState } = this.props; - const { - userListSize, - chatSize, - noteSize, - captionsSize, - pollSize, - waitingSize, - breakoutRoomSize, - } = layoutContextState; - const { layoutContextState: oldLayoutContextState } = prevProps; - const { - userListSize: oldUserListSize, - chatSize: oldChatSize, - noteSize: oldNoteSize, - captionsSize: oldCaptionsSize, - pollSize: oldPollSize, - waitingSize: oldWaitingSize, - breakoutRoomSize: oldBreakoutRoomSize, - } = oldLayoutContextState; - - if (userListSize.width !== oldUserListSize.width && userListSize.width !== userlistWidth) { - this.setUserListWidth(userListSize.width); - } - if (chatSize.width !== oldChatSize.width && chatSize.width !== chatWidth) { - this.setChatWidth(chatSize.width); - } - if (noteSize.width !== oldNoteSize.width && noteSize.width !== noteWidth) { - this.setNoteWidth(noteSize.width); - } - if (captionsSize.width !== oldCaptionsSize.width && captionsSize.width !== captionsWidth) { - this.setCaptionsWidth(captionsSize.width); - } - if (pollSize.width !== oldPollSize.width && pollSize.width !== pollWidth) { - this.setPollWidth(pollSize.width); - } - if (waitingSize.width !== oldWaitingSize.width && waitingSize.width !== waitingWidth) { - this.setWaitingWidth(waitingSize.width); - } - if (breakoutRoomSize.width !== oldBreakoutRoomSize.width - && breakoutRoomSize.width !== breakoutRoomWidth) { - this.setBreakoutRoomWidth(breakoutRoomSize.width); - } - } - - setUserListWidth(userlistWidth) { - this.setState({ userlistWidth }); - } - - setChatWidth(chatWidth) { - this.setState({ chatWidth }); - } - - setNoteWidth(noteWidth) { - this.setState({ noteWidth }); - } - - setCaptionsWidth(captionsWidth) { - this.setState({ captionsWidth }); - } - - setPollWidth(pollWidth) { - this.setState({ pollWidth }); - } - - setWaitingWidth(waitingWidth) { - this.setState({ waitingWidth }); - } - - setBreakoutRoomWidth(breakoutRoomWidth) { - this.setState({ breakoutRoomWidth }); - } - - userListResizeStop(addvalue) { - const { userlistWidth } = this.state; - const { layoutContextDispatch } = this.props; - - this.setUserListWidth(userlistWidth + addvalue); - - layoutContextDispatch( - { - type: 'setUserListSize', - value: { - width: userlistWidth + addvalue, - }, - }, - ); + const { openPanel } = this.props; + const { openPanel: oldOpenPanel } = prevProps; - window.dispatchEvent(new Event('panelChanged')); - } - - chatResizeStop(addvalue) { - const { chatWidth } = this.state; - const { layoutContextDispatch } = this.props; - - this.setChatWidth(chatWidth + addvalue); - - layoutContextDispatch( - { - type: 'setChatSize', - value: { - width: chatWidth + addvalue, - }, - }, - ); - - window.dispatchEvent(new Event('panelChanged')); - } - - noteResizeStop(addvalue) { - const { noteWidth } = this.state; - const { layoutContextDispatch } = this.props; - - this.setNoteWidth(noteWidth + addvalue); - - layoutContextDispatch( - { - type: 'setNoteSize', - value: { - width: noteWidth + addvalue, - }, - }, - ); - - window.dispatchEvent(new Event('panelChanged')); - } - - captionsResizeStop(addvalue) { - const { captionsWidth } = this.state; - const { layoutContextDispatch } = this.props; - - this.setCaptionsWidth(captionsWidth + addvalue); - - layoutContextDispatch( - { - type: 'setCaptionsSize', - value: { - width: captionsWidth + addvalue, - }, - }, - ); - - window.dispatchEvent(new Event('panelChanged')); - } - - pollResizeStop(addvalue) { - const { pollWidth } = this.state; - const { layoutContextDispatch } = this.props; - - this.setPollWidth(pollWidth + addvalue); - - layoutContextDispatch( - { - type: 'setPollSize', - value: { - width: pollWidth + addvalue, - }, - }, - ); - - window.dispatchEvent(new Event('panelChanged')); - } - - waitingResizeStop(addvalue) { - const { waitingWidth } = this.state; - const { layoutContextDispatch } = this.props; - - this.setWaitingWidth(waitingWidth + addvalue); - - layoutContextDispatch( - { - type: 'setWaitingUsersPanelSize', - value: { - width: waitingWidth + addvalue, - }, - }, - ); - - window.dispatchEvent(new Event('panelChanged')); + if (openPanel !== oldOpenPanel) { + window.dispatchEvent(new Event('resize')); + } } renderUserList() { @@ -355,8 +145,11 @@ class PanelManager extends Component { enable={resizableEnableOptions} key={this.userlistKey} size={{ width: userlistWidth }} + onResize={dispatchResizeEvent} onResizeStop={(e, direction, ref, d) => { - this.userListResizeStop(d.width); + this.setState({ + userlistWidth: userlistWidth + d.width, + }); }} > {this.renderUserList()} @@ -401,8 +194,11 @@ class PanelManager extends Component { enable={resizableEnableOptions} key={this.chatKey} size={{ width: chatWidth }} + onResize={dispatchResizeEvent} onResizeStop={(e, direction, ref, d) => { - this.chatResizeStop(d.width); + this.setState({ + chatWidth: chatWidth + d.width, + }); }} > {this.renderChat()} @@ -447,8 +243,11 @@ class PanelManager extends Component { enable={resizableEnableOptions} key={this.noteKey} size={{ width: noteWidth }} + onResize={dispatchResizeEvent} onResizeStop={(e, direction, ref, d) => { - this.noteResizeStop(d.width); + this.setState({ + noteWidth: noteWidth + d.width, + }); }} > {this.renderNote()} @@ -493,8 +292,11 @@ class PanelManager extends Component { enable={resizableEnableOptions} key={this.captionsKey} size={{ width: captionsWidth }} + onResize={dispatchResizeEvent} onResizeStop={(e, direction, ref, d) => { - this.captionsResizeStop(captionsWidth + d.width); + this.setState({ + captionsWidth: captionsWidth + d.width, + }); }} > {this.renderCaptions()} @@ -539,8 +341,11 @@ class PanelManager extends Component { enable={resizableEnableOptions} key={this.waitingUsers} size={{ width: waitingWidth }} + onResize={dispatchResizeEvent} onResizeStop={(e, direction, ref, d) => { - this.waitingResizeStop(waitingWidth + d.width); + this.setState({ + waitingWidth: waitingWidth + d.width, + }); }} > {this.renderWaitingUsersPanel()} @@ -549,15 +354,8 @@ class PanelManager extends Component { } renderBreakoutRoom() { - const { breakoutRoomWidth } = this.state; return ( - <div - className={styles.breakoutRoom} - key={this.breakoutroomKey} - style={{ - width: breakoutRoomWidth, - }} - > + <div className={styles.breakoutRoom} key={this.breakoutroomKey}> <BreakoutRoomContainer /> </div> ); @@ -595,8 +393,10 @@ class PanelManager extends Component { key={this.pollKey} size={{ width: pollWidth }} onResizeStop={(e, direction, ref, d) => { - // window.dispatchEvent(new Event('resize')); - this.pollResizeStop(pollWidth + d.width); + window.dispatchEvent(new Event('resize')); + this.setState({ + pollWidth: pollWidth + d.width, + }); }} > {this.renderPoll()} @@ -608,7 +408,6 @@ class PanelManager extends Component { const { enableResize, openPanel } = this.props; if (openPanel === '') return null; const panels = []; - if (enableResize) { panels.push( this.renderUserListResizable(), @@ -670,6 +469,6 @@ class PanelManager extends Component { } } -export default injectIntl(withLayoutConsumer(PanelManager)); +export default injectIntl(PanelManager); PanelManager.propTypes = propTypes; diff --git a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx index aa07839e398c8a194f55466c4bd5979e76f5a5f5..adf8f4e09b09004bc5cc9359c3ffe263abdc90fd 100755 --- a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx @@ -4,6 +4,7 @@ import WhiteboardOverlayContainer from '/imports/ui/components/whiteboard/whiteb import WhiteboardToolbarContainer from '/imports/ui/components/whiteboard/whiteboard-toolbar/container'; import { HUNDRED_PERCENT, MAX_PERCENT } from '/imports/utils/slideCalcUtils'; import { defineMessages, injectIntl, intlShape } from 'react-intl'; +import { toast } from 'react-toastify'; import PresentationToolbarContainer from './presentation-toolbar/container'; import CursorWrapperContainer from './cursor/cursor-wrapper-container/container'; import AnnotationGroupContainer from '../whiteboard/annotation-group/container'; @@ -16,7 +17,6 @@ import DownloadPresentationButton from './download-presentation-button/component import FullscreenService from '../fullscreen-button/service'; import FullscreenButtonContainer from '../fullscreen-button/container'; import { withDraggableConsumer } from '../media/webcam-draggable-overlay/context'; -import { withLayoutConsumer } from '/imports/ui/components/layout/context'; import Icon from '/imports/ui/components/icon/component'; const intlMessages = defineMessages({ @@ -54,10 +54,6 @@ class PresentationArea extends PureComponent { this.panAndZoomChanger = this.panAndZoomChanger.bind(this); this.fitToWidthHandler = this.fitToWidthHandler.bind(this); this.onFullscreenChange = this.onFullscreenChange.bind(this); - this.getPresentationSizesAvailable = this.getPresentationSizesAvailable.bind(this); - this.handleResize = this.handleResize.bind(this); - - this.onResize = () => setTimeout(this.handleResize.bind(this), 0); this.renderCurrentPresentationToast = this.renderCurrentPresentationToast.bind(this); } @@ -87,101 +83,59 @@ 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('layoutSizesSets', this.onResize, false); - window.addEventListener('webcamAreaResize', this.handleResize, false); - const { slidePosition, layoutContextDispatch } = this.props; + const { slidePosition, webcamDraggableDispatch } = this.props; const { width: currWidth, height: currHeight } = slidePosition; - - layoutContextDispatch({ - type: 'setPresentationSlideSize', - value: { - width: currWidth, - height: currHeight, - }, - }); - if (currWidth > currHeight || currWidth === currHeight) { - layoutContextDispatch({ - type: 'setPresentationOrientation', - value: 'landscape', - }); + webcamDraggableDispatch({ type: 'setOrientationToLandscape' }); } if (currHeight > currWidth) { - layoutContextDispatch({ - type: 'setPresentationOrientation', - value: 'portrait', - }); + webcamDraggableDispatch({ type: 'setOrientationToPortrait' }); } } componentDidUpdate(prevProps) { const { currentPresentation, - notify, - intl, slidePosition, - layoutContextDispatch, - layoutContextState, + webcamDraggableDispatch, layoutSwapped, currentSlide, publishedPoll, isViewer, toggleSwapLayout, restoreOnUpdate, - currentPresentationId, } = this.props; - const { numUsersVideo } = layoutContextState; - const { layoutContextState: prevLayoutContextState } = prevProps; - const { - numUsersVideo: prevNumUsersVideo, - } = prevLayoutContextState; - - if (numUsersVideo !== prevNumUsersVideo) { - this.onResize(); - } - if (prevProps.slidePosition.id !== slidePosition.id) { - window.dispatchEvent(new Event('slideChanged')); - } const { width: prevWidth, height: prevHeight } = prevProps.slidePosition; const { width: currWidth, height: currHeight } = slidePosition; if (prevWidth !== currWidth || prevHeight !== currHeight) { - layoutContextDispatch({ - type: 'setPresentationSlideSize', - value: { - width: currWidth, - height: currHeight, - }, - }); if (currWidth > currHeight || currWidth === currHeight) { - layoutContextDispatch({ - type: 'setPresentationOrientation', - value: 'landscape', - }); + webcamDraggableDispatch({ type: 'setOrientationToLandscape' }); } if (currHeight > currWidth) { - layoutContextDispatch({ - type: 'setPresentationOrientation', - value: 'portrait', - }); + webcamDraggableDispatch({ type: 'setOrientationToPortrait' }); } } - const presentationChanged = currentPresentationId !== currentPresentation._id; + if (prevProps.currentPresentation.name !== currentPresentation.name) { + if (this.currentPresentationToastId) { + return toast.update(this.currentPresentationToastId, { + render: this.renderCurrentPresentationToast(), + }); + } - if (presentationChanged) { - Session.set('currentPresentationId', currentPresentation._id); - notify( - `${intl.formatMessage(intlMessages.changeNotification)} ${currentPresentation.name}`, - 'info', - 'presentation', - ); + this.currentPresentationToastId = toast(this.renderCurrentPresentationToast(), { + onClose: () => { this.currentPresentationToastId = null; }, + autoClose: true, + }); } + if (layoutSwapped && restoreOnUpdate && isViewer && currentSlide) { const slideChanged = currentSlide.id !== prevProps.currentSlide.id; const positionChanged = slidePosition.viewBoxHeight !== prevProps.slidePosition.viewBoxHeight @@ -194,18 +148,16 @@ class PresentationArea extends PureComponent { } componentWillUnmount() { - window.removeEventListener('resize', this.onResize, false); - window.removeEventListener('layoutSizesSets', this.onResize, false); + window.removeEventListener('resize', this.onResize); this.refPresentationContainer.removeEventListener('fullscreenchange', this.onFullscreenChange); } onFullscreenChange() { - const { layoutContextDispatch } = this.props; const { isFullscreen } = this.state; const newIsFullscreen = FullscreenService.isFullScreen(this.refPresentationContainer); if (isFullscreen !== newIsFullscreen) { this.setState({ isFullscreen: newIsFullscreen }); - layoutContextDispatch({ type: 'setPresentationFullscreen', value: newIsFullscreen }); + window.dispatchEvent(new Event('resize')); } } @@ -226,25 +178,25 @@ class PresentationArea extends PureComponent { } getPresentationSizesAvailable() { - const { layoutContextState } = this.props; - const { - presentationAreaSize, - webcamsAreaResizing, - mediaBounds, - tempWebcamsAreaSize, - webcamsPlacement, - } = layoutContextState; - const presentationSizes = { - presentationAreaWidth: 0, - presentationAreaHeight: 0, - }; + const { userIsPresenter, multiUser } = this.props; + const { refPresentationArea, refWhiteboardArea } = this; + 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.presentationAreaWidth = webcamsAreaResizing && (webcamsPlacement === 'left' || webcamsPlacement === 'right') - ? mediaBounds.width - tempWebcamsAreaSize.width - : presentationAreaSize.width; - presentationSizes.presentationAreaHeight = webcamsAreaResizing && (webcamsPlacement === 'top' || webcamsPlacement === 'bottom') - ? mediaBounds.height - tempWebcamsAreaSize.height - (this.getToolbarHeight() || 0) - 30 - : presentationAreaSize.height - (this.getToolbarHeight() || 0); + presentationSizes.presentationAreaHeight = clientHeight - this.getToolbarHeight(); + presentationSizes.presentationAreaWidth = clientWidth; + } return presentationSizes; } @@ -693,8 +645,8 @@ class PresentationArea extends PureComponent { const { showSlide, - // fitToWidth, - // presentationAreaWidth, + fitToWidth, + presentationAreaWidth, localPosition, } = this.state; @@ -725,7 +677,16 @@ class PresentationArea extends PureComponent { let toolbarWidth = 0; if (this.refWhiteboardArea) { - toolbarWidth = svgWidth; + if (svgWidth === presentationAreaWidth + || presentationAreaWidth <= 400 + || fitToWidth === true) { + toolbarWidth = '100%'; + } else if (svgWidth <= 400 + && presentationAreaWidth > 400) { + toolbarWidth = '400px'; + } else { + toolbarWidth = svgWidth; + } } return ( @@ -775,7 +736,7 @@ class PresentationArea extends PureComponent { } } -export default injectIntl(withDraggableConsumer(withLayoutConsumer(PresentationArea))); +export default injectIntl(withDraggableConsumer(PresentationArea)); PresentationArea.propTypes = { intl: intlShape.isRequired, diff --git a/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx index 61d1b86e2b955ef5ea84081826903fe3515fd71b..85a74fee86897c799d7c29d24f804f48933dcdc6 100755 --- a/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx @@ -38,7 +38,7 @@ export default class Cursor extends Component { return obj; } - static getScaledSizes(props, state) { + static getScaledSizes(props) { // TODO: This might need to change for the use case of fit-to-width portrait // slides in non-presenter view. Some elements are still shrinking. const scaleFactor = props.widthRatio / props.physicalWidthRatio; @@ -58,25 +58,14 @@ export default class Cursor extends Component { yOffset: props.cursorLabelBox.yOffset * scaleFactor, // making width and height a little bit larger than the size of the text // received from BBox, so that the text didn't touch the border - width: (state.labelBoxWidth + 3) * scaleFactor, - height: (state.labelBoxHeight + 3) * scaleFactor, + width: (props.labelBoxWidth + 3) * scaleFactor, + height: (props.labelBoxHeight + 3) * scaleFactor, strokeWidth: props.cursorLabelBox.labelBoxStrokeWidth * scaleFactor, }, }; } - constructor(props) { - super(props); - this.state = { - scaledSizes: null, - labelBoxWidth: 0, - labelBoxHeight: 0, - }; - - this.setLabelBoxDimensions = this.setLabelBoxDimensions.bind(this); - } - - componentDidMount() { + componentWillMount() { const { cursorX, cursorY, @@ -86,9 +75,8 @@ export default class Cursor extends Component { isMultiUser, } = this.props; - this.setState({ - scaledSizes: Cursor.getScaledSizes(this.props, this.state), - }); + // setting the initial cursor info + this.scaledSizes = Cursor.getScaledSizes(this.props); this.cursorCoordinate = Cursor.getCursorCoordinates( cursorX, cursorY, @@ -99,83 +87,66 @@ export default class Cursor extends Component { const { fill, displayLabel } = Cursor.getFillAndLabel(presenter, isMultiUser); this.fill = fill; this.displayLabel = displayLabel; - // we need to find the BBox of the text, so that we could set a proper border box arount it } - componentDidUpdate(prevProps, prevState) { - const { - scaledSizes, - } = this.state; - if (!prevState.scaledSizes && scaledSizes) { - this.calculateCursorLabelBoxDimensions(); - } + componentDidMount() { + // we need to find the BBox of the text, so that we could set a proper border box arount it + this.calculateCursorLabelBoxDimensions(); + } + componentWillReceiveProps(nextProps) { const { presenter, isMultiUser, widthRatio, physicalWidthRatio, + labelBoxWidth, + labelBoxHeight, cursorX, cursorY, - slideWidth, - slideHeight, } = this.props; - const { - labelBoxWidth, - labelBoxHeight, - } = this.state; - - const { - labelBoxWidth: prevLabelBoxWidth, - labelBoxHeight: prevLabelBoxHeight, - } = prevState; - if (presenter !== prevProps.presenter || isMultiUser !== prevProps.isMultiUser) { + if (presenter !== nextProps.presenter || isMultiUser !== nextProps.isMultiUser) { const { fill, displayLabel } = Cursor.getFillAndLabel( - presenter, - isMultiUser, + nextProps.presenter, + nextProps.isMultiUser, ); this.displayLabel = displayLabel; this.fill = fill; } - if ((widthRatio !== prevProps.widthRatio - || physicalWidthRatio !== prevProps.physicalWidthRatio) - || (labelBoxWidth !== prevLabelBoxWidth - || labelBoxHeight !== prevLabelBoxHeight)) { - this.setState({ - scaledSizes: Cursor.getScaledSizes(this.props, this.state), - }); + if ((widthRatio !== nextProps.widthRatio + || physicalWidthRatio !== nextProps.physicalWidthRatio) + || (labelBoxWidth !== nextProps.labelBoxWidth + || labelBoxHeight !== nextProps.labelBoxHeight)) { + this.scaledSizes = Cursor.getScaledSizes(nextProps); } - if (cursorX !== prevProps.cursorX || cursorY !== prevProps.cursorY) { + if (cursorX !== nextProps.cursorX || cursorY !== nextProps.cursorY) { const cursorCoordinate = Cursor.getCursorCoordinates( - cursorX, - cursorY, - slideWidth, - slideHeight, + nextProps.cursorX, + nextProps.cursorY, + nextProps.slideWidth, + nextProps.slideHeight, ); this.cursorCoordinate = cursorCoordinate; } } - setLabelBoxDimensions(labelBoxWidth, labelBoxHeight) { - this.setState({ - labelBoxWidth, - labelBoxHeight, - }); - } - // this function retrieves the text node, measures its BBox and sets the size for the outer box calculateCursorLabelBoxDimensions() { + const { + setLabelBoxDimensions, + } = this.props; + let labelBoxWidth = 0; let labelBoxHeight = 0; - if (this.cursorLabelRef) { const { width, height } = this.cursorLabelRef.getBBox(); const { widthRatio, physicalWidthRatio, cursorLabelBox } = this.props; labelBoxWidth = Cursor.invertScale(width, widthRatio, physicalWidthRatio); labelBoxHeight = Cursor.invertScale(height, widthRatio, physicalWidthRatio); + // if the width of the text node is bigger than the maxSize - set the width to maxWidth if (labelBoxWidth > cursorLabelBox.maxWidth) { labelBoxWidth = cursorLabelBox.maxWidth; @@ -183,30 +154,26 @@ export default class Cursor extends Component { } // updating labelBoxWidth and labelBoxHeight in the container, which then passes it down here - this.setLabelBoxDimensions(labelBoxWidth, labelBoxHeight); + setLabelBoxDimensions(labelBoxWidth, labelBoxHeight); } render() { - const { - scaledSizes, - } = this.state; const { cursorId, userName, isRTL, } = this.props; - + const { cursorCoordinate, fill, } = this; - - if (!scaledSizes) return null; + const { cursorLabelBox, cursorLabelText, finalRadius, - } = scaledSizes; + } = this.scaledSizes; const { x, @@ -325,6 +292,17 @@ Cursor.propTypes = { fontSize: PropTypes.number.isRequired, }), + // Defines the width of the label box + labelBoxWidth: PropTypes.number.isRequired, + // Defines the height of the label box + labelBoxHeight: PropTypes.number.isRequired, + + // Defines the function, which sets the state for the label box and passes it back down + // we need it, since we need to render the text first -> measure its dimensions -> + // set proper width and height of the border box -> pass it down -> + // catch in the 'componentWillReceiveProps' -> apply new values + setLabelBoxDimensions: PropTypes.func.isRequired, + // Defines the direction the client text should be displayed isRTL: PropTypes.bool.isRequired, }; diff --git a/bigbluebutton-html5/imports/ui/components/presentation/cursor/container.jsx b/bigbluebutton-html5/imports/ui/components/presentation/cursor/container.jsx index 1279973447f7ad6169fe73dad688d5c407e8376d..4938b194f66975db5376fa0b28fbb4e1a32a973a 100644 --- a/bigbluebutton-html5/imports/ui/components/presentation/cursor/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/cursor/container.jsx @@ -6,16 +6,33 @@ import Cursor from './component'; class CursorContainer extends Component { + constructor() { + super(); + this.state = { + labelBoxWidth: 0, + labelBoxHeight: 0, + }; + this.setLabelBoxDimensions = this.setLabelBoxDimensions.bind(this); + } + setLabelBoxDimensions(labelBoxWidth, labelBoxHeight) { + this.setState({ + labelBoxWidth, + labelBoxHeight, + }); + } render() { const { cursorX, cursorY } = this.props; + const { labelBoxWidth, labelBoxHeight } = this.state; if (cursorX > 0 && cursorY > 0) { return ( <Cursor cursorX={cursorX} cursorY={cursorY} + labelBoxWidth={labelBoxWidth} + labelBoxHeight={labelBoxHeight} setLabelBoxDimensions={this.setLabelBoxDimensions} {...this.props} /> diff --git a/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx b/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx index e733b16d05a57b5c49655db2a35aaeb10730a7fc..5d7753857a8e750ff9e3c10b11a7b43ebd3e38f1 100755 --- a/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx @@ -48,11 +48,11 @@ class ScreenshareComponent extends React.Component { window.addEventListener('screensharePlayFailed', this.handlePlayElementFailed); } - componentDidUpdate(prevProps) { + componentWillReceiveProps(nextProps) { const { isPresenter, unshareScreen, } = this.props; - if (prevProps.isPresenter && !isPresenter) { + if (isPresenter && !nextProps.isPresenter) { unshareScreen(); } } @@ -176,10 +176,7 @@ class ScreenshareComponent extends React.Component { <video id="screenshareVideo" key="screenshareVideo" - style={{ - maxHeight: '100%', - width: '100%', - }} + style={{ maxHeight: '100%', width: '100%' }} playsInline onLoadedData={this.onVideoLoad} ref={(ref) => { this.videoTag = ref; }} diff --git a/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx index 0f993dd5bfe4447e8d8be6a869fb00a02d726671..6420327c623cf7eed982f22a47b8b131acaf6850 100644 --- a/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx @@ -56,7 +56,6 @@ const handleClickToggleChat = (id) => { } else { Session.set('idChatOpen', ''); } - window.dispatchEvent(new Event('panelChanged')); }; const ChatListItem = (props) => { diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/breakout-room/component.jsx index ef853eea3a020a5df7e1b9fe892db46481903b51..2371966dcebd2ea19ec83c19668894aafb260a61 100644 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/breakout-room/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/breakout-room/component.jsx @@ -18,7 +18,6 @@ const toggleBreakoutPanel = () => { ? 'userlist' : 'breakoutroom', ); - window.dispatchEvent(new Event('panelChanged')); }; const BreakoutRoomItem = ({ diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx index 03dbddaf7df1ff5887af3af2e6f4eac535678de1..58117844d04d4df62380694285a2de82b1be3689 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx @@ -160,8 +160,6 @@ class UserDropdown extends PureComponent { showNestedOptions: false, }; - this.title = _.uniqueId('dropdown-title-'); - this.seperator = _.uniqueId('action-separator-'); this.audio = new Audio(`${Meteor.settings.public.app.cdn + Meteor.settings.public.app.basename}/resources/sounds/bbb-handRaise.mp3`); this.handleScroll = this.handleScroll.bind(this); @@ -173,6 +171,11 @@ class UserDropdown extends PureComponent { this.makeDropdownItem = this.makeDropdownItem.bind(this); } + componentWillMount() { + this.title = _.uniqueId('dropdown-title-'); + this.seperator = _.uniqueId('action-separator-'); + } + componentDidUpdate() { this.checkDropdownDirection(); } diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx index 8ebb5fee311a23b26a8d205645a719f49d82ab6f..910f89894aeedad10b7c019a322d9685cce3c13f 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx @@ -77,10 +77,6 @@ const propTypes = { }; class VideoProvider extends Component { - static onBeforeUnload() { - VideoService.onBeforeUnload(); - } - constructor(props) { super(props); @@ -105,6 +101,8 @@ 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); } @@ -117,7 +115,7 @@ class VideoProvider extends Component { this.ws.onmessage = this.onWsMessage; - window.addEventListener('beforeunload', VideoProvider.onBeforeUnload); + window.addEventListener('beforeunload', this.onBeforeUnload); } componentDidUpdate(prevProps) { @@ -136,7 +134,7 @@ class VideoProvider extends Component { window.removeEventListener('online', this.openWs); window.removeEventListener('offline', this.onWsClose); - window.removeEventListener('beforeunload', VideoProvider.onBeforeUnload); + window.removeEventListener('beforeunload', this.onBeforeUnload); VideoService.exitVideo(); @@ -207,41 +205,17 @@ class VideoProvider extends Component { this.setState({ socketOpen: true }); } - 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], - ); - } + onBeforeUnload() { + VideoService.onBeforeUnload(); } 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); @@ -531,8 +505,7 @@ 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); } } @@ -622,6 +595,30 @@ 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/container.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx index 214027c24aa8c72088035cb2ccefe490efeb5f68..6e3e491010fe05d77f308e49b388d1085f32c2bb 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx @@ -2,7 +2,6 @@ import React from 'react'; import { withTracker } from 'meteor/react-meteor-data'; import VideoProvider from './component'; import VideoService from './service'; -import { withLayoutContext } from '/imports/ui/components/layout/context'; const VideoProviderContainer = ({ children, ...props }) => { const { streams } = props; @@ -13,4 +12,4 @@ export default withTracker(props => ({ swapLayout: props.swapLayout, streams: VideoService.getVideoStreams(), isUserLocked: VideoService.isUserLocked(), -}))(withLayoutContext(VideoProviderContainer)); +}))(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 210bfec8f9af83e1f0c03d0a960e46f3713e4c1c..bfe4b551bc8d658fd1605b061c08e6699594c0d1 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,13 +102,11 @@ class VideoList extends Component { this.handleCanvasResize(); window.addEventListener('resize', this.handleCanvasResize, false); - window.addEventListener('layoutSizesSets', this.handleCanvasResize, false); window.addEventListener('videoPlayFailed', this.handlePlayElementFailed); } componentWillUnmount() { window.removeEventListener('resize', this.handleCanvasResize, false); - window.removeEventListener('layoutSizesSets', this.handleCanvasResize, false); window.removeEventListener('videoPlayFailed', this.handlePlayElementFailed); } diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx index 7dbbf745e9bc634c322bce2e0833ad6120f096cb..3a808503eae96addbe86d87827d5220f57649657 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx @@ -89,26 +89,16 @@ export default class PencilDrawComponent extends Component { return version !== nextProps.version; } - componentDidUpdate(prevProps) { - const { annotation: prevAnnotation } = prevProps; - const { points: prevPoints } = prevAnnotation; - const { annotation, slideWidth, slideHeight } = this.props; + componentWillUpdate(nextProps) { + const { annotation: nextAnnotation, slideWidth, slideHeight } = nextProps; + const { points: nextPoints } = nextAnnotation; + const { annotation } = this.props; const { points } = annotation; - if (prevPoints.length !== points.length) { - this.path = this.getCoordinates(annotation, slideWidth, slideHeight); + if (nextPoints.length !== points.length) { + this.path = this.getCoordinates(nextAnnotation, slideWidth, slideHeight); } } - // componentWillUpdate(nextProps) { - // const { annotation: nextAnnotation, slideWidth, slideHeight } = nextProps; - // const { points: nextPoints } = nextAnnotation; - // const { annotation } = this.props; - // const { points } = annotation; - // if (nextPoints.length !== points.length) { - // this.path = this.getCoordinates(nextAnnotation, slideWidth, slideHeight); - // } - // } - getCoordinates(annotation, slideWidth, slideHeight) { if ((!annotation || annotation.points.length === 0) || (annotation.status === 'DRAW_END' && !annotation.commands)) { diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx index ca4c93824e19caf227fed1c965bf558dc3312818..c6382af1ef3f2d93f758617505d43f7055d784fd 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx @@ -9,11 +9,10 @@ class PollDrawComponent extends Component { super(props); this.state = { - // We did it because it was calculated in the componentWillMount - calculated: false, // flag indicating whether we need to continue calculating the sizes or display the annotation prepareToDisplay: true, - // outer (white) rectangle's coordinates and sizes (calculated in componentDidMount) + + // outer (white) rectangle's coordinates and sizes (calculated in componentWillMount) outerRect: { x: 0, y: 0, @@ -51,12 +50,131 @@ class PollDrawComponent extends Component { lineToMeasure: [], fontSizeDirection: 1, }; + } + + componentWillMount() { + // in this part we retrieve the props and perform initial calculations for the state + // calculating only the parts which have to be done just once and don't require + // rendering / rerendering the text objects + + const { annotation } = this.props; + const { points, result } = annotation; + const { slideWidth, slideHeight, intl } = this.props; + + // x1 and y1 - coordinates of the top left corner of the annotation + // initial width and height are the width and height of the annotation + // all the points are given as percentages of the slide + const x1 = points[0]; + const y1 = points[1]; + const initialWidth = points[2]; + const initialHeight = points[3]; + + // calculating the data for the outer rectangle + // 0.001 is needed to accomodate bottom and right borders of the annotation + const x = (x1 / 100) * slideWidth; + const y = (y1 / 100) * slideHeight; + const width = ((initialWidth - 0.001) / 100) * slideWidth; + const height = ((initialHeight - 0.001) / 100) * slideHeight; + + let votesTotal = 0; + let maxNumVotes = 0; + const textArray = []; + + // counting the total number of votes, finding the biggest number of votes + result.reduce((previousValue, currentValue) => { + votesTotal = previousValue + currentValue.numVotes; + if (maxNumVotes < currentValue.numVotes) { + maxNumVotes = currentValue.numVotes; + } + + return votesTotal; + }, 0); + + // filling the textArray with data to display + // adding value of the iterator to each line needed to create unique + // keys while rendering at the end + const arrayLength = result.length; + for (let i = 0; i < arrayLength; i += 1) { + const _tempArray = []; + const _result = result[i]; + let isDefaultPoll; + switch (_result.key.toLowerCase()) { + case 'true': + case 'false': + case 'yes': + case 'no': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + isDefaultPoll = true; + break; + default: + isDefaultPoll = false; + break; + } + + if (isDefaultPoll) { + _result.key = intl.formatMessage({ id: `app.poll.answer.${_result.key.toLowerCase()}` }); + } + + _tempArray.push(_result.key, `${_result.numVotes}`); + if (votesTotal === 0) { + _tempArray.push('0%'); + _tempArray.push(i); + } else { + const percResult = (_result.numVotes / votesTotal) * 100; + _tempArray.push(`${Math.round(percResult)}%`); + _tempArray.push(i); + } + + textArray.push(_tempArray); + } + + // calculating the data for the inner rectangle + const innerWidth = width * 0.95; + const innerHeight = height - (width * 0.05); + const innerX = x + (width * 0.025); + const innerY = y + (width * 0.025); + const thickness = (width - innerWidth) / 10; + + // calculating the maximum possible width and height of the each line + // 25% of the height goes to the padding + const maxLineWidth = innerWidth / 3; + const maxLineHeight = (innerHeight * 0.75) / textArray.length; - this.pollInitialCalculation = this.pollInitialCalculation.bind(this); + const lineToMeasure = textArray[0]; + const { pollAnswerIds } = PollService; + const messageIndex = lineToMeasure[0].toLowerCase(); + if (pollAnswerIds[messageIndex]) { + lineToMeasure[0] = intl.formatMessage(pollAnswerIds[messageIndex]); + } + + // saving all the initial calculations in the state + this.setState({ + outerRect: { + x, + y, + width, + height, + }, + innerRect: { + x: innerX, + y: innerY, + width: innerWidth, + height: innerHeight, + }, + thickness, + maxNumVotes, + textArray, + maxLineWidth, + maxLineHeight, + lineToMeasure, + }); } componentDidMount() { - this.pollInitialCalculation(); this.checkSizes(); } @@ -82,11 +200,9 @@ class PollDrawComponent extends Component { fontSizeDirection, calcFontSize, textArray, - calculated, } = this.state; const { annotation } = this.props; - if (!calculated) return null; // increment the font size by 2 to prevent Maximum update depth exceeded const fontSizeIncrement = 2; @@ -186,129 +302,6 @@ class PollDrawComponent extends Component { }); } - pollInitialCalculation() { - // in this part we retrieve the props and perform initial calculations for the state - // calculating only the parts which have to be done just once and don't require - // rendering / rerendering the text objects - - // if (!state.initialState) return; - const { annotation } = this.props; - const { points, result } = annotation; - const { slideWidth, slideHeight, intl } = this.props; - - // x1 and y1 - coordinates of the top left corner of the annotation - // initial width and height are the width and height of the annotation - // all the points are given as percentages of the slide - const x1 = points[0]; - const y1 = points[1]; - const initialWidth = points[2]; - const initialHeight = points[3]; - - // calculating the data for the outer rectangle - // 0.001 is needed to accomodate bottom and right borders of the annotation - const x = (x1 / 100) * slideWidth; - const y = (y1 / 100) * slideHeight; - const width = ((initialWidth - 0.001) / 100) * slideWidth; - const height = ((initialHeight - 0.001) / 100) * slideHeight; - - let votesTotal = 0; - let maxNumVotes = 0; - const textArray = []; - - // counting the total number of votes, finding the biggest number of votes - result.reduce((previousValue, currentValue) => { - votesTotal = previousValue + currentValue.numVotes; - if (maxNumVotes < currentValue.numVotes) { - maxNumVotes = currentValue.numVotes; - } - - return votesTotal; - }, 0); - - // filling the textArray with data to display - // adding value of the iterator to each line needed to create unique - // keys while rendering at the end - const arrayLength = result.length; - for (let i = 0; i < arrayLength; i += 1) { - const _tempArray = []; - const _result = result[i]; - let isDefaultPoll; - switch (_result.key.toLowerCase()) { - case 'true': - case 'false': - case 'yes': - case 'no': - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - isDefaultPoll = true; - break; - default: - isDefaultPoll = false; - break; - } - - if (isDefaultPoll) { - _result.key = intl.formatMessage({ id: `app.poll.answer.${_result.key.toLowerCase()}` }); - } - - _tempArray.push(_result.key, `${_result.numVotes}`); - if (votesTotal === 0) { - _tempArray.push('0%'); - _tempArray.push(i); - } else { - const percResult = (_result.numVotes / votesTotal) * 100; - _tempArray.push(`${Math.round(percResult)}%`); - _tempArray.push(i); - } - - textArray.push(_tempArray); - } - - // calculating the data for the inner rectangle - const innerWidth = width * 0.95; - const innerHeight = height - (width * 0.05); - const innerX = x + (width * 0.025); - const innerY = y + (width * 0.025); - const thickness = (width - innerWidth) / 10; - - // calculating the maximum possible width and height of the each line - // 25% of the height goes to the padding - const maxLineWidth = innerWidth / 3; - const maxLineHeight = (innerHeight * 0.75) / textArray.length; - - const lineToMeasure = textArray[0]; - const { pollAnswerIds } = PollService; - const messageIndex = lineToMeasure[0].toLowerCase(); - if (pollAnswerIds[messageIndex]) { - lineToMeasure[0] = intl.formatMessage(pollAnswerIds[messageIndex]); - } - - this.setState({ - outerRect: { - x, - y, - width, - height, - }, - innerRect: { - x: innerX, - y: innerY, - width: innerWidth, - height: innerHeight, - }, - thickness, - maxNumVotes, - textArray, - maxLineWidth, - maxLineHeight, - lineToMeasure, - calculated: true, - }); - } - renderPoll() { const { backgroundColor, diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx index f0dfc7e7416bb821c9f667594a1271d9e6dc4a8f..056d81ce9772d52b506feffbb74d36af8f7f0eff 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx @@ -65,6 +65,17 @@ export default class TextDrawListener extends Component { } + // If the activeId suddenly became empty - this means the shape was deleted + // While the user was drawing it. So we are resetting the state. + componentWillReceiveProps(nextProps) { + const { drawSettings } = this.props; + const nextDrawsettings = nextProps.drawSettings; + + if (drawSettings.textShapeActiveId !== '' && nextDrawsettings.textShapeActiveId === '') { + this.resetState(); + } + } + componentDidUpdate(prevProps) { const { drawSettings, @@ -74,12 +85,6 @@ export default class TextDrawListener extends Component { const prevDrawsettings = prevProps.drawSettings; const prevTextShapeValue = prevProps.drawSettings.textShapeValue; - // If the activeId suddenly became empty - this means the shape was deleted - // While the user was drawing it. So we are resetting the state. - if (prevDrawsettings.textShapeActiveId !== '' && drawSettings.textShapeActiveId === '') { - this.resetState(); - } - // Updating the component in cases when: // Either color / font-size or text value has changed // and excluding the case when the textShapeActiveId changed to '' diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-menu-item/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-menu-item/component.jsx index 443321242e5543979d0ab57b8ad8fd111ada851a..db141bbfada4b3ae731975884a1810b590b4a34e 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-menu-item/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-menu-item/component.jsx @@ -11,13 +11,18 @@ export default class ToolbarMenuItem extends Component { // a flag to keep track of whether the menu item was actually clicked this.clicked = false; - this.uniqueRef = _.uniqueId('toolbar-menu-item'); + this.handleTouchStart = this.handleTouchStart.bind(this); this.handleOnMouseUp = this.handleOnMouseUp.bind(this); this.handleOnMouseDown = this.handleOnMouseDown.bind(this); this.setRef = this.setRef.bind(this); } + // generating a unique ref string for the toolbar-item + componentWillMount() { + this.uniqueRef = _.uniqueId('toolbar-menu-item'); + } + componentDidMount() { // adding and removing touchstart events can be done via standard React way // by passing onTouchStart={this.funcName} once they stop triggering mousedown events diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-submenu-item/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-submenu-item/component.jsx index bc04ac5025b8bc92e052a6f03ed05485575c4715..a54768658132512abee61a671539739257e5fcb4 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-submenu-item/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-submenu-item/component.jsx @@ -7,12 +7,17 @@ import { styles } from '../styles'; export default class ToolbarSubmenuItem extends Component { constructor() { super(); - this.uniqueRef = _.uniqueId('toolbar-submenu-item'); + this.handleTouchStart = this.handleTouchStart.bind(this); this.handleOnMouseUp = this.handleOnMouseUp.bind(this); this.setRef = this.setRef.bind(this); } + // generating a unique ref string for the toolbar-item + componentWillMount() { + this.uniqueRef = _.uniqueId('toolbar-submenu-item'); + } + componentDidMount() { // adding and removing touchstart events can be done via standard React way // by passing onTouchStart={this.funcName} once they stop triggering mousedown events