diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx
index 2238d77940..2d52b94a0b 100755
--- a/bigbluebutton-html5/imports/startup/client/base.jsx
+++ b/bigbluebutton-html5/imports/startup/client/base.jsx
@@ -11,12 +11,12 @@ import AudioManager from '/imports/ui/services/audio-manager';
import logger from '/imports/startup/client/logger';
import Users from '/imports/api/users';
import { Session } from 'meteor/session';
+import { FormattedMessage } from 'react-intl';
import IntlStartup from './intl';
import Meetings, { RecordMeetings } from '../../api/meetings';
import AppService from '/imports/ui/components/app/service';
import Breakouts from '/imports/api/breakouts';
import AudioService from '/imports/ui/components/audio/service';
-import { FormattedMessage } from 'react-intl';
import { notify } from '/imports/ui/services/notification';
const BREAKOUT_END_NOTIFY_DELAY = 50;
diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx
index 0e801cf28c..b6775edcc5 100755
--- a/bigbluebutton-html5/imports/ui/components/app/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx
@@ -21,6 +21,7 @@ import LockNotifier from '/imports/ui/components/lock-viewers/notify/container';
import PingPongContainer from '/imports/ui/components/ping-pong/container';
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';
const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
@@ -103,6 +104,7 @@ class App extends Component {
this.handleWindowResize = throttle(this.handleWindowResize).bind(this);
this.shouldAriaHide = this.shouldAriaHide.bind(this);
+ this.renderMedia = withDraggableContext(this.renderMedia.bind(this));
}
componentDidMount() {
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 82118c3b01..edb1b405e9 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,16 +1,15 @@
-import React, { Component, Fragment } from 'react';
+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';
-import { withDraggableContext } from './context';
+import { withDraggableConsumer } from './context';
import VideoProviderContainer from '/imports/ui/components/video-provider/container';
import { styles } from '../styles.scss';
import Storage from '../../../services/storage/session';
-const { webcamsDefaultPlacement } = Meteor.settings.public.layout;
const BROWSER_ISMOBILE = isMobile || isIPad13;
const propTypes = {
@@ -32,7 +31,7 @@ const defaultProps = {
};
const dispatchResizeEvent = () => window.dispatchEvent(new Event('resize'));
-class WebcamDraggable extends Component {
+class WebcamDraggable extends PureComponent {
constructor(props) {
super(props);
@@ -58,17 +57,34 @@ class WebcamDraggable extends Component {
}
componentDidUpdate(prevProps) {
- const { swapLayout, webcamDraggableState } = this.props;
- const { placement } = webcamDraggableState;
+ const { swapLayout, webcamDraggableState, webcamDraggableDispatch } = this.props;
+ const {
+ placement: statePlacement,
+ orientation,
+ lastPlacementLandscape,
+ lastPlacementPortrait,
+ } = webcamDraggableState;
const { webcamDraggableState: prevWebcamDraggableState } = prevProps;
- const { placement: prevPlacement } = prevWebcamDraggableState;
+ const { placement: prevPlacement, orientation: prevOrientation } = prevWebcamDraggableState;
if (prevProps.swapLayout !== swapLayout) {
setTimeout(() => this.forceUpdate(), 500);
}
- if (prevPlacement !== placement) {
+ if (prevPlacement !== statePlacement) {
setTimeout(() => this.forceUpdate(), 200);
setTimeout(() => window.dispatchEvent(new Event('resize')), 500);
}
+
+ 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() {
@@ -227,12 +243,16 @@ class WebcamDraggable extends Component {
if (targetClassname) {
if (targetClassname.includes('Top')) {
webcamDraggableDispatch({ type: 'setplacementToTop' });
+ webcamDraggableDispatch({ type: 'setLastPlacementLandscapeToTop' });
} else if (targetClassname.includes('Right')) {
webcamDraggableDispatch({ type: 'setplacementToRight' });
+ webcamDraggableDispatch({ type: 'setLastPlacementPortraitToRight' });
} else if (targetClassname.includes('Bottom')) {
webcamDraggableDispatch({ type: 'setplacementToBottom' });
+ webcamDraggableDispatch({ type: 'setLastPlacementLandscapeToBottom' });
} else if (targetClassname.includes('Left')) {
webcamDraggableDispatch({ type: 'setplacementToLeft' });
+ webcamDraggableDispatch({ type: 'setLastPlacementPortraitToLeft' });
}
}
webcamDraggableDispatch({ type: 'dragEnd' });
@@ -261,12 +281,12 @@ class WebcamDraggable extends Component {
videoListSize,
optimalGrid,
} = webcamDraggableState;
- let placement = Storage.getItem('webcamPlacement');
+
+ const placement = Storage.getItem('webcamPlacement');
+
const lastPosition = Storage.getItem('webcamLastPosition') || { x: 0, y: 0 };
+
let position = lastPosition;
- if (!placement) {
- placement = webcamsDefaultPlacement;
- }
if (dragging) {
position = webcamDraggableState.tempPosition;
@@ -537,4 +557,4 @@ class WebcamDraggable extends Component {
WebcamDraggable.propTypes = propTypes;
WebcamDraggable.defaultProps = defaultProps;
-export default withDraggableContext(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 c17a3104e0..155886dcaf 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,10 +1,15 @@
import React, { createContext, useReducer, useEffect } from 'react';
import Storage from '../../../services/storage/session';
+const { webcamsDefaultPlacement } = Meteor.settings.public.layout;
+
export const WebcamDraggableContext = createContext();
const initialState = {
- placement: 'top',
+ placement: webcamsDefaultPlacement,
+ lastPlacementLandscape: 'top',
+ lastPlacementPortrait: 'left',
+ orientation: null,
mediaSize: {
width: 0,
height: 0,
@@ -58,12 +63,48 @@ const reducer = (state, action) => {
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,
@@ -165,13 +206,22 @@ const ContextConsumer = Component => props => (
const ContextProvider = (props) => {
const [webcamDraggableState, webcamDraggableDispatch] = useReducer(reducer, initialState);
- const { placement, lastPosition } = webcamDraggableState;
+ const {
+ placement,
+ lastPosition,
+ lastPlacementLandscape,
+ lastPlacementPortrait,
+ } = webcamDraggableState;
const { children } = props;
useEffect(() => {
Storage.setItem('webcamPlacement', placement);
+ Storage.setItem('webcamLastPlacementLandscape', lastPlacementLandscape);
+ Storage.setItem('webcamlastPlacementPortrait', lastPlacementPortrait);
Storage.setItem('webcamLastPosition', lastPosition);
}, [
placement,
+ lastPlacementLandscape,
+ lastPlacementPortrait,
lastPosition,
]);
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
index 0b6b1a6f68..d736d3b4ef 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
@@ -15,6 +15,7 @@ import PresentationCloseButton from './presentation-close-button/component';
import DownloadPresentationButton from './download-presentation-button/component';
import FullscreenService from '../fullscreen-button/service';
import FullscreenButtonContainer from '../fullscreen-button/container';
+import { withDraggableContext, withDraggableConsumer } from '../media/webcam-draggable-overlay/context';
const intlMessages = defineMessages({
presentationLabel: {
@@ -81,10 +82,25 @@ class PresentationArea extends PureComponent {
window.addEventListener('resize', this.onResize);
this.getInitialPresentationSizes();
this.refPresentationContainer.addEventListener('fullscreenchange', this.onFullscreenChange);
+
+ const { slidePosition, webcamDraggableDispatch } = this.props;
+ const { width: currWidth, height: currHeight } = slidePosition;
+ if (currWidth > currHeight || currWidth === currHeight) {
+ webcamDraggableDispatch({ type: 'setOrientationToLandscape' });
+ }
+ if (currHeight > currWidth) {
+ webcamDraggableDispatch({ type: 'setOrientationToPortrait' });
+ }
}
componentDidUpdate(prevProps) {
- const { currentPresentation, notify, intl } = this.props;
+ const {
+ currentPresentation,
+ notify,
+ intl,
+ slidePosition,
+ webcamDraggableDispatch,
+ } = this.props;
if (prevProps.currentPresentation.name !== currentPresentation.name) {
notify(
@@ -93,6 +109,18 @@ class PresentationArea extends PureComponent {
'presentation',
);
}
+
+ const { width: prevWidth, height: prevHeight } = prevProps.slidePosition;
+ const { width: currWidth, height: currHeight } = slidePosition;
+
+ if (prevWidth !== currWidth || prevHeight !== currHeight) {
+ if (currWidth > currHeight || currWidth === currHeight) {
+ webcamDraggableDispatch({ type: 'setOrientationToLandscape' });
+ }
+ if (currHeight > currWidth) {
+ webcamDraggableDispatch({ type: 'setOrientationToPortrait' });
+ }
+ }
}
componentWillUnmount() {
@@ -654,7 +682,7 @@ class PresentationArea extends PureComponent {
}
}
-export default injectIntl(PresentationArea);
+export default injectIntl(withDraggableConsumer(PresentationArea));
PresentationArea.propTypes = {
intl: intlShape.isRequired,