refactor: push layout

This commit is contained in:
Joao Victor 2022-09-02 15:18:17 -03:00
parent f9bbef759c
commit 84f9eeebc4
3 changed files with 358 additions and 230 deletions

View File

@ -30,12 +30,11 @@ import PresentationAreaContainer from '../presentation/presentation-area/contain
import ScreenshareContainer from '../screenshare/container';
import ExternalVideoContainer from '../external-video-player/container';
import Styled from './styles';
import { LAYOUT_TYPE, DEVICE_TYPE, ACTIONS, SMALL_VIEWPORT_BREAKPOINT } from '../layout/enums';
import { DEVICE_TYPE, ACTIONS, SMALL_VIEWPORT_BREAKPOINT } from '../layout/enums';
import {
isMobile, isTablet, isTabletPortrait, isTabletLandscape, isDesktop,
} from '../layout/utils';
import LayoutEngine from '../layout/layout-manager/layoutEngine';
import getFromUserSettings from '/imports/ui/services/users-settings';
import NavBarContainer from '../nav-bar/container';
import SidebarNavigationContainer from '../sidebar-navigation/container';
import SidebarContentContainer from '../sidebar-content/container';
@ -46,24 +45,16 @@ import Settings from '/imports/ui/services/settings';
import { registerTitleView } from '/imports/utils/dom-utils';
import Notifications from '../notifications/container';
import GlobalStyles from '/imports/ui/stylesheets/styled-components/globalStyles';
import MediaService from '/imports/ui/components/media/service';
import ActionsBarContainer from '../actions-bar/container';
import { updateSettings } from '/imports/ui/components/settings/service';
import PushLayoutEngine from '../layout/push-layout/pushLayoutEngine';
const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
const APP_CONFIG = Meteor.settings.public.app;
const DESKTOP_FONT_SIZE = APP_CONFIG.desktopFontSize;
const MOBILE_FONT_SIZE = APP_CONFIG.mobileFontSize;
const OVERRIDE_LOCALE = APP_CONFIG.defaultSettings.application.overrideLocale;
const HIDE_PRESENTATION = Meteor.settings.public.layout.hidePresentation;
const LAYOUT_CONFIG = Meteor.settings.public.layout;
const equalDouble = (n1, n2) => {
const precision = 0.01;
return Math.abs(n1 - n2) <= precision;
};
const intlMessages = defineMessages({
userListLabel: {
id: 'app.userList.label',
@ -159,18 +150,7 @@ class App extends Component {
intl,
validIOSVersion,
layoutContextDispatch,
meetingLayout,
settingsLayout,
cameraWidth,
cameraHeight,
layoutPresOpen,
layoutCamPosition,
layoutFocusedCam,
layoutRate,
horizontalPosition,
isRTL,
shouldShowScreenshare,
shouldShowExternalVideo,
} = this.props;
const { browserName } = browserInfo;
const { osName } = deviceInfo;
@ -193,57 +173,6 @@ class App extends Component {
value: parseInt(fontSize.slice(0, -2), 10),
});
const userLayout = LAYOUT_TYPE[getFromUserSettings('bbb_change_layout', false)];
Settings.application.selectedLayout = settingsLayout
|| userLayout
|| meetingLayout;
let selectedLayout = Settings.application.selectedLayout;
if (isMobile()) {
selectedLayout = selectedLayout === 'custom' ? 'smart' : selectedLayout;
Settings.application.selectedLayout = selectedLayout;
}
Settings.save();
const initialPresentation = !getFromUserSettings('bbb_hide_presentation', HIDE_PRESENTATION || !layoutPresOpen) || shouldShowScreenshare || shouldShowExternalVideo;
MediaService.setPresentationIsOpen(layoutContextDispatch, initialPresentation);
if (selectedLayout === 'custom') {
setTimeout(() => {
layoutContextDispatch({
type: ACTIONS.SET_FOCUSED_CAMERA_ID,
value: layoutFocusedCam,
});
layoutContextDispatch({
type: ACTIONS.SET_CAMERA_DOCK_POSITION,
value: layoutCamPosition,
});
if (!equalDouble(layoutRate, 0)) {
let w, h;
if (horizontalPosition) {
w = window.innerWidth * layoutRate;
h = cameraHeight;
} else {
w = cameraWidth;
h = window.innerHeight * layoutRate;
}
layoutContextDispatch({
type: ACTIONS.SET_CAMERA_DOCK_SIZE,
value: {
width: w,
height: h,
browserWidth: window.innerWidth,
browserHeight: window.innerHeight,
}
});
}
}, 0);
}
const body = document.getElementsByTagName('body')[0];
if (browserName) {
@ -286,146 +215,11 @@ class App extends Component {
intl,
mountModal,
deviceType,
meetingLayout,
meetingLayoutUpdatedAt,
presentationIsOpen,
focusedCamera,
cameraPosition,
presentationVideoRate,
cameraWidth,
cameraHeight,
cameraIsResizing,
isPresenter,
isModerator,
layoutPresOpen,
layoutIsResizing,
layoutCamPosition,
layoutFocusedCam,
layoutRate,
horizontalPosition,
selectedLayout, // layout name
pushLayout, // is layout pushed
pushLayoutMeeting,
layoutContextDispatch,
mountRandomUserModal,
setPushLayout,
setMeetingLayout,
} = this.props;
this.renderDarkMode();
const meetingLayoutDidChange = meetingLayout !== prevProps.meetingLayout;
const pushLayoutMeetingDidChange = pushLayoutMeeting !== prevProps.pushLayoutMeeting;
const shouldSwitchLayout = isPresenter
? meetingLayoutDidChange
: (meetingLayoutDidChange || pushLayoutMeetingDidChange) && pushLayoutMeeting;
if (shouldSwitchLayout) {
let contextLayout = meetingLayout;
if (isMobile()) {
contextLayout = meetingLayout === 'custom' ? 'smart' : meetingLayout;
}
layoutContextDispatch({
type: ACTIONS.SET_LAYOUT_TYPE,
value: contextLayout,
});
updateSettings({
application: {
...Settings.application,
selectedLayout: contextLayout,
},
});
}
if (pushLayoutMeetingDidChange) {
updateSettings({
application: {
...Settings.application,
pushLayout: pushLayoutMeeting,
},
});
}
if (meetingLayout === "custom" && !isPresenter) {
if (layoutFocusedCam !== prevProps.layoutFocusedCam
|| meetingLayoutUpdatedAt !== prevProps.meetingLayoutUpdatedAt) {
layoutContextDispatch({
type: ACTIONS.SET_FOCUSED_CAMERA_ID,
value: layoutFocusedCam,
});
}
if (layoutCamPosition !== prevProps.layoutCamPosition
|| meetingLayoutUpdatedAt !== prevProps.meetingLayoutUpdatedAt) {
layoutContextDispatch({
type: ACTIONS.SET_CAMERA_DOCK_POSITION,
value: layoutCamPosition,
});
}
if (!equalDouble(layoutRate, prevProps.layoutRate)
|| meetingLayoutUpdatedAt !== prevProps.meetingLayoutUpdatedAt) {
let w, h;
if (horizontalPosition) {
w = window.innerWidth * layoutRate;
h = cameraHeight;
} else {
w = cameraWidth;
h = window.innerHeight * layoutRate;
}
if (layoutIsResizing !== prevProps.layoutIsResizing) {
layoutContextDispatch({
type: ACTIONS.SET_CAMERA_DOCK_IS_RESIZING,
value: layoutIsResizing,
});
}
layoutContextDispatch({
type: ACTIONS.SET_CAMERA_DOCK_SIZE,
value: {
width: w,
height: h,
browserWidth: window.innerWidth,
browserHeight: window.innerHeight,
}
});
}
if (layoutPresOpen !== prevProps.layoutPresOpen
|| meetingLayoutUpdatedAt !== prevProps.meetingLayoutUpdatedAt) {
layoutContextDispatch({
type: ACTIONS.SET_PRESENTATION_IS_OPEN,
value: layoutPresOpen,
});
}
}
const layoutChanged = presentationIsOpen !== prevProps.presentationIsOpen
|| selectedLayout !== prevProps.selectedLayout
|| cameraIsResizing !== prevProps.cameraIsResizing
|| cameraPosition !== prevProps.cameraPosition
|| focusedCamera !== prevProps.focusedCamera
|| !equalDouble(presentationVideoRate, prevProps.presentationVideoRate);
if ((pushLayout && layoutChanged) // change layout sizes / states
|| (pushLayout !== prevProps.pushLayout) // push layout once after presenter toggles / special case where we set pushLayout to false in all viewers
) {
if (isPresenter) {
setMeetingLayout();
} else if (isModerator) {
setPushLayout();
}
}
if (mountRandomUserModal) mountModal(<RandomUserSelectContainer />);
if (prevProps.currentUserEmoji.status !== currentUserEmoji.status) {
@ -623,6 +417,68 @@ class App extends Component {
: DarkReader.disable();
}
mountPushLayoutEngine() {
const {
cameraWidth,
cameraHeight,
cameraIsResizing,
cameraPosition,
focusedCamera,
horizontalPosition,
isLayoutMeetingResizing,
isPresenter,
isModerator,
layoutContextDispatch,
meetingLayout,
meetingLayoutCameraPosition,
meetingLayoutFocusedCamera,
meetingLayoutVideoRate,
meetingPresentationIsOpen,
meetingLayoutUpdatedAt,
presentationIsOpen,
presentationVideoRate,
pushLayout,
pushLayoutMeeting,
selectedLayout,
setPushLayout,
setMeetingLayout,
shouldShowScreenshare,
shouldShowExternalVideo,
} = this.props;
return (
<PushLayoutEngine
{...{
cameraWidth,
cameraHeight,
cameraIsResizing,
cameraPosition,
focusedCamera,
horizontalPosition,
isLayoutMeetingResizing,
isPresenter,
isModerator,
layoutContextDispatch,
meetingLayout,
meetingLayoutCameraPosition,
meetingLayoutFocusedCamera,
meetingLayoutVideoRate,
meetingPresentationIsOpen,
meetingLayoutUpdatedAt,
presentationIsOpen,
presentationVideoRate,
pushLayout,
pushLayoutMeeting,
selectedLayout,
setPushLayout,
setMeetingLayout,
shouldShowScreenshare,
shouldShowExternalVideo,
}}
/>
);
}
render() {
const {
customStyle,
@ -641,6 +497,7 @@ class App extends Component {
<>
<Notifications />
<LayoutEngine layoutType={selectedLayout} />
{this.mountPushLayoutEngine()}
<GlobalStyles />
<Styled.Layout
id="layout"

View File

@ -61,8 +61,6 @@ const AppContainer = (props) => {
const {
actionsbar,
meetingLayout,
meetingLayoutUpdatedAt,
selectedLayout,
pushLayout,
pushLayoutMeeting,
@ -72,11 +70,13 @@ const AppContainer = (props) => {
isPresenter,
randomlySelectedUser,
isModalOpen,
presentationIsOpen: layoutPresOpen,
isResizing: layoutIsResizing,
cameraPosition: layoutCamPosition,
focusedCamera: layoutFocusedCam,
presentationVideoRate: layoutRate,
meetingLayout,
meetingLayoutUpdatedAt,
meetingPresentationIsOpen,
isLayoutMeetingResizing,
meetingLayoutCameraPosition,
meetingLayoutFocusedCamera,
meetingLayoutVideoRate,
...otherProps
} = props;
@ -87,7 +87,6 @@ const AppContainer = (props) => {
const cameraDock = layoutSelectOutput((i) => i.cameraDock);
const cameraDockInput = layoutSelectInput((i) => i.cameraDock);
const presentation = layoutSelectInput((i) => i.presentation);
const layoutType = layoutSelect((i) => i.layoutType);
const deviceType = layoutSelect((i) => i.deviceType);
const layoutContextDispatch = layoutDispatch();
@ -154,11 +153,11 @@ const AppContainer = (props) => {
cameraWidth: cameraDock.width,
cameraHeight: cameraDock.height,
cameraIsResizing: cameraDockInput.isResizing,
layoutPresOpen,
layoutIsResizing,
layoutCamPosition,
layoutFocusedCam,
layoutRate,
meetingPresentationIsOpen,
isLayoutMeetingResizing,
meetingLayoutCameraPosition,
meetingLayoutFocusedCamera,
meetingLayoutVideoRate,
horizontalPosition,
deviceType,
layoutContextDispatch,
@ -222,8 +221,17 @@ export default injectIntl(withModalMounter(withTracker(({ intl, baseControls })
randomlySelectedUser,
} = currentMeeting;
const meetingLayout = LayoutMeetings.findOne({ meetingId: Auth.meetingID }) || {};
const { layout, pushLayout: pushLayoutMeeting, layoutUpdatedAt, presentationIsOpen, isResizing, cameraPosition, focusedCamera, presentationVideoRate } = meetingLayout;
const meetingLayoutObj = LayoutMeetings.findOne({ meetingId: Auth.meetingID }) || {};
const {
layout: meetingLayout,
pushLayout: pushLayoutMeeting,
layoutUpdatedAt: meetingLayoutUpdatedAt,
presentationIsOpen: meetingPresentationIsOpen,
isResizing: isMeetingLayoutResizing,
cameraPosition: meetingLayoutCameraPosition,
focusedCamera: meetingLayoutFocusedCamera,
presentationVideoRate: meetingLayoutVideoRate,
} = meetingLayoutObj;
if (currentUser && !currentUser.approved) {
baseControls.updateLoadingState(intl.formatMessage(intlMessages.waitingApprovalMessage));
@ -267,13 +275,13 @@ export default injectIntl(withModalMounter(withTracker(({ intl, baseControls })
currentUserId: currentUser?.userId,
isPresenter,
isModerator: currentUser?.role === ROLE_MODERATOR,
meetingLayout: layout,
meetingLayoutUpdatedAt: layoutUpdatedAt,
presentationIsOpen,
isResizing,
cameraPosition,
focusedCamera,
presentationVideoRate,
meetingLayout,
meetingLayoutUpdatedAt,
meetingPresentationIsOpen,
isMeetingLayoutResizing,
meetingLayoutCameraPosition,
meetingLayoutFocusedCamera,
meetingLayoutVideoRate,
selectedLayout,
pushLayout,
pushLayoutMeeting,

View File

@ -0,0 +1,263 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Meteor } from 'meteor/meteor';
import getFromUserSettings from '/imports/ui/services/users-settings';
import Settings from '/imports/ui/services/settings';
import MediaService from '/imports/ui/components/media/service';
import { LAYOUT_TYPE, ACTIONS } from '../enums';
import { isMobile } from '../utils';
import { updateSettings } from '/imports/ui/components/settings/service';
const HIDE_PRESENTATION = Meteor.settings.public.layout.hidePresentation;
const equalDouble = (n1, n2) => {
const precision = 0.01;
return Math.abs(n1 - n2) <= precision;
};
const propTypes = {
cameraWidth: PropTypes.number,
cameraHeight: PropTypes.number,
cameraIsResizing: PropTypes.bool,
cameraPosition: PropTypes.string,
focusedCamera: PropTypes.string,
horizontalPosition: PropTypes.string,
isLayoutMeetingResizing: PropTypes.bool,
isPresenter: PropTypes.bool,
isModerator: PropTypes.bool,
layoutContextDispatch: PropTypes.func,
meetingLayout: PropTypes.string,
meetingLayoutCameraPosition: PropTypes.string,
meetingLayoutFocusedCamera: PropTypes.string,
meetingLayoutVideoRate: PropTypes.number,
meetingPresentationIsOpen: PropTypes.bool,
meetingLayoutUpdatedAt: PropTypes.number,
presentationIsOpen: PropTypes.bool,
presentationVideoRate: PropTypes.number,
pushLayout: PropTypes.bool,
pushLayoutMeeting: PropTypes.bool,
selectedLayout: PropTypes.string,
setPushLayout: PropTypes.func,
setMeetingLayout: PropTypes.func,
shouldShowScreenshare: PropTypes.bool,
shouldShowExternalVideo: PropTypes.bool,
};
class PushLayoutEngine extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
const {
cameraWidth,
cameraHeight,
horizontalPosition,
layoutContextDispatch,
meetingLayout,
meetingLayoutCameraPosition,
meetingLayoutFocusedCamera,
meetingLayoutVideoRate,
meetingPresentationIsOpen,
shouldShowScreenshare,
shouldShowExternalVideo,
} = this.props;
const userLayout = LAYOUT_TYPE[getFromUserSettings('bbb_change_layout', false)];
Settings.application.selectedLayout = userLayout || meetingLayout;
let selectedLayout = Settings.application.selectedLayout;
if (isMobile()) {
selectedLayout = selectedLayout === 'custom' ? 'smart' : selectedLayout;
Settings.application.selectedLayout = selectedLayout;
}
Settings.save();
const initialPresentation = !getFromUserSettings('bbb_hide_presentation', HIDE_PRESENTATION || !meetingPresentationIsOpen) || shouldShowScreenshare || shouldShowExternalVideo;
MediaService.setPresentationIsOpen(layoutContextDispatch, initialPresentation);
if (selectedLayout === 'custom') {
setTimeout(() => {
layoutContextDispatch({
type: ACTIONS.SET_FOCUSED_CAMERA_ID,
value: meetingLayoutFocusedCamera,
});
layoutContextDispatch({
type: ACTIONS.SET_CAMERA_DOCK_POSITION,
value: meetingLayoutCameraPosition,
});
if (!equalDouble(meetingLayoutVideoRate, 0)) {
let w, h;
if (horizontalPosition) {
w = window.innerWidth * meetingLayoutVideoRate;
h = cameraHeight;
} else {
w = cameraWidth;
h = window.innerHeight * meetingLayoutVideoRate;
}
layoutContextDispatch({
type: ACTIONS.SET_CAMERA_DOCK_SIZE,
value: {
width: w,
height: h,
browserWidth: window.innerWidth,
browserHeight: window.innerHeight,
}
});
}
}, 0);
}
}
componentDidUpdate(prevProps) {
const {
cameraWidth,
cameraHeight,
cameraIsResizing,
cameraPosition,
focusedCamera,
horizontalPosition,
isLayoutMeetingResizing,
isModerator,
isPresenter,
layoutContextDispatch,
meetingLayout,
meetingLayoutUpdatedAt,
meetingPresentationIsOpen,
meetingLayoutCameraPosition,
meetingLayoutFocusedCamera,
meetingLayoutVideoRate,
presentationIsOpen,
presentationVideoRate,
pushLayout,
pushLayoutMeeting,
selectedLayout,
setMeetingLayout,
setPushLayout,
} = this.props;
const meetingLayoutDidChange = meetingLayout !== prevProps.meetingLayout;
const pushLayoutMeetingDidChange = pushLayoutMeeting !== prevProps.pushLayoutMeeting;
const shouldSwitchLayout = isPresenter
? meetingLayoutDidChange
: (meetingLayoutDidChange || pushLayoutMeetingDidChange) && pushLayoutMeeting;
if (shouldSwitchLayout) {
let contextLayout = meetingLayout;
if (isMobile()) {
contextLayout = meetingLayout === 'custom' ? 'smart' : meetingLayout;
}
layoutContextDispatch({
type: ACTIONS.SET_LAYOUT_TYPE,
value: contextLayout,
});
updateSettings({
application: {
...Settings.application,
selectedLayout: contextLayout,
},
});
}
if (pushLayoutMeetingDidChange) {
updateSettings({
application: {
...Settings.application,
pushLayout: pushLayoutMeeting,
},
});
}
if (meetingLayout === "custom" && !isPresenter) {
if (meetingLayoutFocusedCamera !== prevProps.meetingLayoutFocusedCamera
|| meetingLayoutUpdatedAt !== prevProps.meetingLayoutUpdatedAt) {
layoutContextDispatch({
type: ACTIONS.SET_FOCUSED_CAMERA_ID,
value: meetingLayoutFocusedCamera,
});
}
if (meetingLayoutCameraPosition !== prevProps.meetingLayoutCameraPosition
|| meetingLayoutUpdatedAt !== prevProps.meetingLayoutUpdatedAt) {
layoutContextDispatch({
type: ACTIONS.SET_CAMERA_DOCK_POSITION,
value: meetingLayoutCameraPosition,
});
}
if (!equalDouble(meetingLayoutVideoRate, prevProps.meetingLayoutVideoRate)
|| meetingLayoutUpdatedAt !== prevProps.meetingLayoutUpdatedAt) {
let w, h;
if (horizontalPosition) {
w = window.innerWidth * meetingLayoutVideoRate;
h = cameraHeight;
} else {
w = cameraWidth;
h = window.innerHeight * meetingLayoutVideoRate;
}
if (isLayoutMeetingResizing !== prevProps.isLayoutMeetingResizing) {
layoutContextDispatch({
type: ACTIONS.SET_CAMERA_DOCK_IS_RESIZING,
value: isLayoutMeetingResizing,
});
}
layoutContextDispatch({
type: ACTIONS.SET_CAMERA_DOCK_SIZE,
value: {
width: w,
height: h,
browserWidth: window.innerWidth,
browserHeight: window.innerHeight,
}
});
}
if (meetingPresentationIsOpen !== prevProps.meetingPresentationIsOpen
|| meetingLayoutUpdatedAt !== prevProps.meetingLayoutUpdatedAt) {
layoutContextDispatch({
type: ACTIONS.SET_PRESENTATION_IS_OPEN,
value: meetingPresentationIsOpen,
});
}
}
const layoutChanged = presentationIsOpen !== prevProps.presentationIsOpen
|| selectedLayout !== prevProps.selectedLayout
|| cameraIsResizing !== prevProps.cameraIsResizing
|| cameraPosition !== prevProps.cameraPosition
|| focusedCamera !== prevProps.focusedCamera
|| !equalDouble(presentationVideoRate, prevProps.presentationVideoRate);
if ((pushLayout && layoutChanged) // change layout sizes / states
|| (pushLayout !== prevProps.pushLayout) // push layout once after presenter toggles / special case where we set pushLayout to false in all viewers
) {
if (isPresenter) {
setMeetingLayout();
} else if (isModerator) {
setPushLayout();
}
}
}
render() {
return null;
}
};
PushLayoutEngine.propTypes = propTypes;
export default PushLayoutEngine;