bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/layout/layout-manager.jsx

566 lines
16 KiB
React
Raw Normal View History

import React, { Component, Fragment } from 'react';
import Storage from '/imports/ui/services/storage/session';
import { Session } from 'meteor/session';
2020-06-09 11:09:46 +08:00
import { withLayoutConsumer } from '/imports/ui/components/layout/context';
import { isVideoBroadcasting } from '/imports/ui/components/screenshare/service';
2020-07-07 03:11:37 +08:00
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;
2020-04-23 22:07:44 +08:00
const WEBCAMSAREA_MIN_PERCENT = 0.2;
const WEBCAMSAREA_MAX_PERCENT = 0.8;
2020-06-16 09:22:28 +08:00
// const PRESENTATIONAREA_MIN_PERCENT = 0.2;
2020-06-05 07:01:17 +08:00
const PRESENTATIONAREA_MIN_WIDTH = 385; // Value based on presentation toolbar
2020-06-16 09:22:28 +08:00
// const PRESENTATIONAREA_MAX_PERCENT = 0.8;
2020-04-23 22:07:44 +08:00
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);
2020-04-23 22:07:44 +08:00
this.calculatesLayout = this.calculatesLayout.bind(this);
}
componentDidMount() {
this.setLayoutSizes();
window.addEventListener('resize', _.throttle(() => this.setLayoutSizes(), 200));
window.addEventListener('panelChanged', () => {
2020-07-06 20:58:50 +08:00
this.setLayoutSizes(true);
});
2020-06-05 07:01:17 +08:00
window.addEventListener('autoArrangeChanged', () => {
setTimeout(() => this.setLayoutSizes(false, true), 200);
2020-06-05 07:01:17 +08:00
});
window.addEventListener('slideChanged', () => {
setTimeout(() => this.setLayoutSizes(), 200);
});
window.addEventListener('togglePresentationHide', () => {
setTimeout(() => this.setLayoutSizes(), 200);
});
window.addEventListener('webcamAreaResize', () => {
this.setLayoutSizes();
});
window.addEventListener('webcamPlacementChange', () => {
2020-07-06 20:58:50 +08:00
this.setLayoutSizes(false, false, true);
});
}
2020-04-23 22:07:44 +08:00
componentDidUpdate(prevProps) {
2020-06-09 11:09:46 +08:00
const { layoutContextState } = this.props;
const { layoutContextState: prevLayoutContextState } = prevProps;
2020-05-01 06:18:03 +08:00
const {
2020-06-09 11:09:46 +08:00
numUsersVideo,
2020-05-01 06:18:03 +08:00
} = layoutContextState;
2020-04-23 22:07:44 +08:00
const {
2020-06-09 11:09:46 +08:00
numUsersVideo: prevNumUsersVideo,
2020-04-23 22:07:44 +08:00
} = prevLayoutContextState;
2020-06-09 11:09:46 +08:00
if (numUsersVideo !== prevNumUsersVideo) {
2020-06-05 07:01:17 +08:00
setTimeout(() => this.setLayoutSizes(), 500);
}
2020-04-23 22:07:44 +08:00
}
setLayoutSizes(panelChanged = false, autoarrangeChanged = false, placementChanged = false) {
const { layoutContextDispatch, layoutContextState } = this.props;
const { autoArrangeLayout } = layoutContextState;
if (autoarrangeChanged && !autoArrangeLayout && !placementChanged) return;
2020-04-23 22:07:44 +08:00
const layoutSizes = this.calculatesLayout(panelChanged);
layoutContextDispatch(
{
type: 'setWindowSize',
value: {
width: windowWidth(),
height: windowHeight(),
},
},
);
2020-04-23 22:07:44 +08:00
layoutContextDispatch(
{
type: 'setMediaBounds',
value: {
width: layoutSizes.mediaBounds.width,
height: layoutSizes.mediaBounds.height,
top: layoutSizes.mediaBounds.top,
left: layoutSizes.mediaBounds.left,
},
},
);
layoutContextDispatch(
{
type: 'setUserListSize',
value: {
2020-04-23 22:07:44 +08:00
width: layoutSizes.userListSize.width,
},
},
);
layoutContextDispatch(
{
type: 'setChatSize',
value: {
2020-04-23 22:07:44 +08:00
width: layoutSizes.chatSize.width,
},
},
);
layoutContextDispatch(
{
type: 'setBreakoutRoomSize',
value: {
width: layoutSizes.breakoutRoomSize.width,
},
},
);
2020-06-16 23:20:24 +08:00
layoutContextDispatch(
{
type: 'setWebcamsAreaSize',
2020-06-16 23:20:24 +08:00
value: {
width: layoutSizes.webcamsAreaSize.width,
height: layoutSizes.webcamsAreaSize.height,
},
},
);
layoutContextDispatch(
{
type: 'setPresentationAreaSize',
value: {
2020-04-23 22:07:44 +08:00
width: layoutSizes.presentationAreaSize.width,
height: layoutSizes.presentationAreaSize.height,
},
},
);
const newLayoutData = {
windowSize: {
width: windowWidth(),
height: windowHeight(),
},
2020-04-23 22:07:44 +08:00
mediaBounds: {
width: layoutSizes.mediaBounds.width,
height: layoutSizes.mediaBounds.height,
top: layoutSizes.mediaBounds.top,
left: layoutSizes.mediaBounds.left,
},
userListSize: {
2020-04-23 22:07:44 +08:00
width: layoutSizes.userListSize.width,
},
chatSize: {
2020-04-23 22:07:44 +08:00
width: layoutSizes.chatSize.width,
},
breakoutRoomSize: {
width: layoutSizes.breakoutRoomSize.width,
},
webcamsAreaSize: {
2020-04-23 22:07:44 +08:00
width: layoutSizes.webcamsAreaSize.width,
height: layoutSizes.webcamsAreaSize.height,
},
presentationAreaSize: {
2020-04-23 22:07:44 +08:00
width: layoutSizes.presentationAreaSize.width,
height: layoutSizes.presentationAreaSize.height,
},
};
Storage.setItem('layoutData', newLayoutData);
2020-06-05 07:01:17 +08:00
window.dispatchEvent(new Event('layoutSizesSets'));
}
2020-06-16 09:22:28 +08:00
defineWebcamPlacement(mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight) {
const { layoutContextDispatch, layoutContextState } = this.props;
const { autoArrangeLayout } = layoutContextState;
const isScreenShare = isVideoBroadcasting();
2020-06-16 09:22:28 +08:00
if (!autoArrangeLayout) return;
if (isScreenShare) {
layoutContextDispatch(
{
type: 'setWebcamsPlacement',
value: 'top',
},
);
2020-06-21 10:53:55 +08:00
Storage.setItem('webcamsPlacement', 'top');
return;
}
if ((mediaAreaWidth - presentationWidth) > (mediaAreaHeight - presentationHeight)) {
layoutContextDispatch(
{
type: 'setWebcamsPlacement',
value: 'left',
},
);
2020-06-21 10:53:55 +08:00
Storage.setItem('webcamsPlacement', 'left');
} else {
layoutContextDispatch(
{
type: 'setWebcamsPlacement',
value: 'top',
},
);
2020-06-21 10:53:55 +08:00
Storage.setItem('webcamsPlacement', 'top');
2020-06-16 09:22:28 +08:00
}
}
calculatesPanelsSize(panelChanged) {
2020-05-01 06:18:03 +08:00
const { layoutContextState } = this.props;
2020-04-23 22:07:44 +08:00
const {
userListSize: userListSizeContext,
chatSize: chatSizeContext,
breakoutRoomSize: breakoutRoomSizeContext,
} = layoutContextState;
const openPanel = Session.get('openPanel');
const storageLData = storageLayoutData();
2020-04-23 22:07:44 +08:00
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,
};
}
switch (openPanel) {
case 'userlist': {
newChatSize = {
width: 0,
};
newBreakoutRoomSize = {
width: 0,
};
break;
}
case 'chat': {
newBreakoutRoomSize = {
width: 0,
};
break;
}
case 'breakoutroom': {
newChatSize = {
width: 0,
};
break;
}
case '': {
newUserListSize = {
width: 0,
};
newChatSize = {
width: 0,
};
newBreakoutRoomSize = {
width: 0,
};
break;
}
default: {
throw new Error('Unexpected openPanel value');
}
}
2020-05-01 06:18:03 +08:00
return {
newUserListSize,
newChatSize,
newBreakoutRoomSize,
2020-04-23 22:07:44 +08:00
};
2020-05-01 06:18:03 +08:00
}
calculatesWebcamsAreaSize(
mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight,
2020-05-01 06:18:03 +08:00
) {
const {
layoutContextState,
} = this.props;
2020-06-09 11:09:46 +08:00
const { webcamsPlacement, numUsersVideo } = layoutContextState;
2020-04-23 22:07:44 +08:00
2020-06-05 07:01:17 +08:00
const autoArrangeLayout = Storage.getItem('autoArrangeLayout');
2020-05-01 06:18:03 +08:00
const webcamsAreaUserSetsHeight = Storage.getItem('webcamsAreaUserSetsHeight');
const webcamsAreaUserSetsWidth = Storage.getItem('webcamsAreaUserSetsWidth');
2020-06-16 09:22:28 +08:00
let webcamsAreaWidth;
let webcamsAreaHeight;
2020-05-01 06:18:03 +08:00
2020-06-09 11:09:46 +08:00
if (numUsersVideo < 1) {
2020-05-01 06:18:03 +08:00
return {
2020-06-16 09:22:28 +08:00
webcamsAreaWidth: 0,
webcamsAreaHeight: 0,
2020-05-01 06:18:03 +08:00
};
}
2020-06-16 09:22:28 +08:00
if (autoArrangeLayout) {
2020-05-01 06:18:03 +08:00
if (webcamsPlacement === 'left' || webcamsPlacement === 'right') {
webcamsAreaWidth = (mediaAreaWidth - presentationWidth)
< (mediaAreaWidth * WEBCAMSAREA_MIN_PERCENT)
? mediaAreaWidth * WEBCAMSAREA_MIN_PERCENT
: mediaAreaWidth - presentationWidth;
2020-06-16 09:22:28 +08:00
webcamsAreaHeight = mediaAreaHeight;
2020-04-23 22:07:44 +08:00
} else {
2020-06-16 09:22:28 +08:00
webcamsAreaWidth = mediaAreaWidth;
webcamsAreaHeight = (mediaAreaHeight - presentationHeight)
< (mediaAreaHeight * WEBCAMSAREA_MIN_PERCENT)
? mediaAreaHeight * WEBCAMSAREA_MIN_PERCENT
: mediaAreaHeight - presentationHeight;
2020-06-05 07:01:17 +08:00
}
2020-06-16 09:22:28 +08:00
} 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,
);
2020-06-05 07:01:17 +08:00
}
if ((webcamsPlacement === 'left' || webcamsPlacement === 'right') && (mediaAreaWidth - webcamsAreaWidth) < PRESENTATIONAREA_MIN_WIDTH) {
2020-06-16 09:22:28 +08:00
webcamsAreaWidth = mediaAreaWidth - PRESENTATIONAREA_MIN_WIDTH;
2020-05-01 06:18:03 +08:00
}
return {
2020-06-16 09:22:28 +08:00
webcamsAreaWidth,
webcamsAreaHeight,
2020-05-01 06:18:03 +08:00
};
}
2020-04-23 22:07:44 +08:00
2020-05-01 06:18:03 +08:00
calculatesPresentationAreaSize(
2020-06-16 09:22:28 +08:00
mediaAreaWidth, mediaAreaHeight, webcamAreaWidth, webcamAreaHeight,
2020-05-01 06:18:03 +08:00
) {
const {
layoutContextState,
} = this.props;
2020-06-09 11:09:46 +08:00
const {
webcamsPlacement,
2020-06-09 11:09:46 +08:00
numUsersVideo,
} = layoutContextState;
2020-05-01 06:18:03 +08:00
2020-06-09 11:09:46 +08:00
if (numUsersVideo < 1) {
2020-05-01 06:18:03 +08:00
return {
2020-06-16 09:22:28 +08:00
presentationAreaWidth: mediaAreaWidth,
presentationAreaHeight: mediaAreaHeight - 20,
2020-05-01 06:18:03 +08:00
};
}
let presentationAreaWidth;
let presentationAreaHeight;
if (webcamsPlacement === 'left' || webcamsPlacement === 'right') {
presentationAreaWidth = mediaAreaWidth - webcamAreaWidth - 20;
presentationAreaHeight = mediaAreaHeight - 20;
} else {
presentationAreaWidth = mediaAreaWidth;
presentationAreaHeight = mediaAreaHeight - webcamAreaHeight - 30;
}
2020-05-08 05:27:00 +08:00
2020-05-01 06:18:03 +08:00
return {
2020-06-16 09:22:28 +08:00
presentationAreaWidth,
presentationAreaHeight,
2020-05-01 06:18:03 +08:00
};
}
calculatesLayout(panelChanged = false) {
2020-05-01 06:18:03 +08:00
const {
layoutContextState,
} = this.props;
2020-06-16 09:22:28 +08:00
const {
presentationIsFullscreen,
presentationSlideSize,
} = layoutContextState;
2020-05-01 06:18:03 +08:00
2020-06-16 09:22:28 +08:00
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;
}
2020-05-01 06:18:03 +08:00
const mediaAreaHeight = windowHeight() - (NAVBAR_HEIGHT + ACTIONSBAR_HEIGHT) - 10;
const mediaAreaWidth = windowWidth() - (firstPanel.width + secondPanel.width);
2020-05-01 06:18:03 +08:00
const newMediaBounds = {
width: mediaAreaWidth,
height: mediaAreaHeight,
top: NAVBAR_HEIGHT,
left: firstPanel.width + secondPanel.width,
2020-05-01 06:18:03 +08:00
};
const { presentationWidth, presentationHeight } = LayoutManager.calculatesPresentationSize(
2020-06-16 09:22:28 +08:00
mediaAreaWidth, mediaAreaHeight, presentationSlideWidth, presentationSlideHeight,
);
this.defineWebcamPlacement(
mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight,
);
const { webcamsAreaWidth, webcamsAreaHeight } = this.calculatesWebcamsAreaSize(
mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight,
);
2020-06-16 09:22:28 +08:00
const newWebcamsAreaSize = {
2020-06-16 09:22:28 +08:00
width: webcamsAreaWidth,
height: webcamsAreaHeight,
};
let newPresentationAreaSize;
let newScreenShareAreaSize;
2020-07-06 22:35:11 +08:00
const { presentationAreaWidth, presentationAreaHeight } = this.calculatesPresentationAreaSize(
mediaAreaWidth, mediaAreaHeight, webcamsAreaWidth, webcamsAreaHeight,
);
if (!presentationIsFullscreen) {
newPresentationAreaSize = {
width: presentationAreaWidth || 0,
height: presentationAreaHeight || 0,
};
} else {
2020-07-06 22:35:11 +08:00
newPresentationAreaSize = {
width: windowWidth(),
height: windowHeight(),
};
}
return {
2020-04-23 22:07:44 +08:00
mediaBounds: newMediaBounds,
userListSize: newUserListSize,
chatSize: newChatSize,
breakoutRoomSize: newBreakoutRoomSize,
webcamsAreaSize: newWebcamsAreaSize,
presentationAreaSize: newPresentationAreaSize,
screenShareAreaSize: newScreenShareAreaSize,
};
}
render() {
return <Fragment />;
}
}
2020-06-09 11:09:46 +08:00
export default withLayoutConsumer(LayoutManager);
export {
USERLIST_MIN_WIDTH,
USERLIST_MAX_WIDTH,
CHAT_MIN_WIDTH,
CHAT_MAX_WIDTH,
NAVBAR_HEIGHT,
ACTIONSBAR_HEIGHT,
2020-04-23 22:07:44 +08:00
WEBCAMSAREA_MIN_PERCENT,
WEBCAMSAREA_MAX_PERCENT,
2020-06-05 07:01:17 +08:00
PRESENTATIONAREA_MIN_WIDTH,
};