2024-08-29 23:12:09 +08:00
|
|
|
import { useEffect, useRef, useState } from 'react';
|
2024-08-28 20:59:22 +08:00
|
|
|
import { layoutDispatch, layoutSelect, layoutSelectInput } from './context';
|
2024-08-29 23:12:09 +08:00
|
|
|
import {
|
|
|
|
ACTIONS, DEVICE_TYPE, LAYOUT_TYPE, PANELS,
|
|
|
|
} from './enums';
|
2024-08-28 20:59:22 +08:00
|
|
|
import {
|
|
|
|
isMobile, isTablet, isTabletPortrait, isTabletLandscape, isDesktop,
|
|
|
|
} from './utils';
|
|
|
|
import { getSettingsSingletonInstance } from '/imports/ui/services/settings';
|
|
|
|
import { Input, Layout } from './layoutTypes';
|
|
|
|
import { throttle } from '/imports/utils/throttle';
|
|
|
|
import { SETTINGS } from '/imports/ui/services/settings/enums';
|
|
|
|
import useSettings from '/imports/ui/services/settings/hooks/useSettings';
|
|
|
|
import getFromUserSettings from '/imports/ui/services/users-settings';
|
2024-08-29 23:12:09 +08:00
|
|
|
import useMeeting from '/imports/ui/core/hooks/useMeeting';
|
|
|
|
import MediaService from '/imports/ui/components/media/service';
|
2024-08-30 02:08:24 +08:00
|
|
|
import { useVideoStreams, useVideoStreamsCount } from '/imports/ui/components/video-provider/hooks';
|
|
|
|
import { useIsChatEnabled, useIsPresentationEnabled, useIsScreenSharingEnabled } from '/imports/ui/services/features';
|
|
|
|
import useUserChangedLocalSettings from '/imports/ui/services/settings/hooks/useUserChangedLocalSettings';
|
|
|
|
import Session from '/imports/ui/services/storage/in-memory';
|
|
|
|
import deviceInfo from '/imports/utils/deviceInfo';
|
2024-08-28 20:59:22 +08:00
|
|
|
|
|
|
|
const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
|
|
|
|
|
|
|
|
const LayoutObserver: React.FC = () => {
|
2024-08-29 23:12:09 +08:00
|
|
|
const layoutType = useRef<string | null>(null);
|
2024-08-30 02:08:24 +08:00
|
|
|
const checkedUserSettings = useRef(false);
|
2024-08-28 20:59:22 +08:00
|
|
|
const layoutContextDispatch = layoutDispatch();
|
|
|
|
const deviceType = layoutSelect((i: Layout) => i.deviceType);
|
|
|
|
const cameraDockInput = layoutSelectInput((i: Input) => i.cameraDock);
|
|
|
|
const sidebarContentInput = layoutSelectInput((i: Input) => i.sidebarContent);
|
|
|
|
const presentationInput = layoutSelectInput((i: Input) => i.presentation);
|
2024-08-29 23:12:09 +08:00
|
|
|
const sharedNotesInput = layoutSelectInput((i: Input) => i.sharedNotes);
|
2024-08-30 02:08:24 +08:00
|
|
|
const sidebarContent = layoutSelectInput((i: Input) => i.sidebarContent);
|
|
|
|
const { sidebarContentPanel } = sidebarContent;
|
|
|
|
|
2024-08-28 20:59:22 +08:00
|
|
|
const [, setEnableResize] = useState(!window.matchMedia(MOBILE_MEDIA).matches);
|
|
|
|
const { selectedLayout } = useSettings(SETTINGS.APPLICATION) as { selectedLayout: string };
|
2024-08-29 23:12:09 +08:00
|
|
|
const {
|
|
|
|
data: currentMeeting,
|
|
|
|
} = useMeeting((m) => ({
|
|
|
|
layout: m.layout,
|
|
|
|
componentsFlags: m.componentsFlags,
|
|
|
|
}));
|
|
|
|
|
|
|
|
const isThereWebcam = useVideoStreamsCount() > 0;
|
2024-08-30 02:08:24 +08:00
|
|
|
const { streams: videoStream } = useVideoStreams();
|
2024-08-29 23:12:09 +08:00
|
|
|
const isScreenSharingEnabled = useIsScreenSharingEnabled();
|
|
|
|
const isPresentationEnabled = useIsPresentationEnabled();
|
2024-08-30 02:08:24 +08:00
|
|
|
const isChatEnabled = useIsChatEnabled();
|
|
|
|
|
|
|
|
const setLocalSettings = useUserChangedLocalSettings();
|
2024-08-28 20:59:22 +08:00
|
|
|
|
|
|
|
const { numCameras } = cameraDockInput;
|
|
|
|
const { isOpen: sidebarContentIsOpen } = sidebarContentInput;
|
|
|
|
const { isOpen: presentationIsOpen } = presentationInput;
|
|
|
|
|
2024-08-29 23:12:09 +08:00
|
|
|
const { currentLayoutType } = currentMeeting?.layout || {};
|
|
|
|
const meetingLayout = currentLayoutType && LAYOUT_TYPE[currentLayoutType as keyof typeof LAYOUT_TYPE];
|
|
|
|
const isSharingVideo = currentMeeting?.componentsFlags?.hasExternalVideo;
|
|
|
|
|
2024-08-28 20:59:22 +08:00
|
|
|
const setDeviceType = () => {
|
|
|
|
let newDeviceType = null;
|
|
|
|
if (isMobile()) newDeviceType = DEVICE_TYPE.MOBILE;
|
|
|
|
if (isTablet()) newDeviceType = DEVICE_TYPE.TABLET;
|
|
|
|
if (isTabletPortrait()) newDeviceType = DEVICE_TYPE.TABLET_PORTRAIT;
|
|
|
|
if (isTabletLandscape()) newDeviceType = DEVICE_TYPE.TABLET_LANDSCAPE;
|
|
|
|
if (isDesktop()) newDeviceType = DEVICE_TYPE.DESKTOP;
|
|
|
|
|
|
|
|
if (newDeviceType !== deviceType) {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_DEVICE_TYPE,
|
|
|
|
value: newDeviceType,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const throttledDeviceType = throttle(
|
|
|
|
() => setDeviceType(),
|
|
|
|
50, { trailing: true, leading: true },
|
|
|
|
);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
const Settings = getSettingsSingletonInstance();
|
|
|
|
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_IS_RTL,
|
|
|
|
value: document.documentElement.getAttribute('dir') === 'rtl',
|
|
|
|
});
|
|
|
|
|
|
|
|
const APP_CONFIG = window.meetingClientSettings.public.app;
|
|
|
|
const DESKTOP_FONT_SIZE = APP_CONFIG.desktopFontSize;
|
|
|
|
const MOBILE_FONT_SIZE = APP_CONFIG.mobileFontSize;
|
|
|
|
const fontSize = isMobile() ? MOBILE_FONT_SIZE : DESKTOP_FONT_SIZE;
|
|
|
|
document.getElementsByTagName('html')[0].style.fontSize = fontSize;
|
|
|
|
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_FONT_SIZE,
|
|
|
|
value: parseInt(fontSize.slice(0, -2), 10),
|
|
|
|
});
|
|
|
|
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_HAS_ACTIONBAR,
|
|
|
|
value: !getFromUserSettings('bbb_hide_actions_bar', false),
|
|
|
|
});
|
|
|
|
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_HAS_NAVBAR,
|
|
|
|
value: !getFromUserSettings('bbb_hide_nav_bar', false),
|
|
|
|
});
|
|
|
|
|
|
|
|
window.addEventListener('localeChanged', () => {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_IS_RTL,
|
|
|
|
value: Settings.application.isRTL,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
const handleWindowResize = throttle(() => {
|
|
|
|
setEnableResize((enableResize) => {
|
|
|
|
const shouldEnableResize = !window.matchMedia(MOBILE_MEDIA).matches;
|
|
|
|
if (enableResize === shouldEnableResize) return enableResize;
|
|
|
|
return shouldEnableResize;
|
|
|
|
});
|
|
|
|
throttledDeviceType();
|
|
|
|
});
|
|
|
|
|
|
|
|
handleWindowResize();
|
|
|
|
window.addEventListener('resize', handleWindowResize, false);
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
window.removeEventListener('resize', handleWindowResize, false);
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
const CHAT_CONFIG = window.meetingClientSettings.public.chat;
|
|
|
|
const PUBLIC_CHAT_ID = CHAT_CONFIG.public_group_id;
|
|
|
|
|
|
|
|
if (
|
|
|
|
selectedLayout?.toLowerCase?.()?.includes?.('focus')
|
|
|
|
&& !sidebarContentIsOpen
|
|
|
|
&& deviceType !== DEVICE_TYPE.MOBILE
|
|
|
|
&& numCameras > 0
|
|
|
|
&& presentationIsOpen
|
|
|
|
) {
|
|
|
|
setTimeout(() => {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
|
|
|
|
value: true,
|
|
|
|
});
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_ID_CHAT_OPEN,
|
|
|
|
value: PUBLIC_CHAT_ID,
|
|
|
|
});
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
|
|
|
|
value: PANELS.CHAT,
|
|
|
|
});
|
|
|
|
}, 0);
|
|
|
|
}
|
|
|
|
}, [selectedLayout]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
throttledDeviceType();
|
|
|
|
}, [deviceType]);
|
|
|
|
|
2024-08-29 23:12:09 +08:00
|
|
|
useEffect(() => {
|
|
|
|
if (
|
|
|
|
layoutContextDispatch
|
|
|
|
&& (typeof meetingLayout !== 'undefined')
|
|
|
|
&& (layoutType.current !== meetingLayout)
|
|
|
|
&& sharedNotesInput?.isPinned
|
|
|
|
) {
|
|
|
|
layoutType.current = meetingLayout;
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_PRESENTATION_IS_OPEN,
|
|
|
|
value: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}, [meetingLayout, layoutContextDispatch, layoutType]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_LAYOUT_TYPE,
|
|
|
|
value: selectedLayout,
|
|
|
|
});
|
|
|
|
}, [selectedLayout]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
MediaService.buildLayoutWhenPresentationAreaIsDisabled(
|
|
|
|
layoutContextDispatch,
|
|
|
|
isSharingVideo,
|
|
|
|
sharedNotesInput?.isPinned,
|
|
|
|
isThereWebcam,
|
|
|
|
isScreenSharingEnabled,
|
|
|
|
isPresentationEnabled,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2024-08-30 02:08:24 +08:00
|
|
|
useEffect(() => {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_NUM_CAMERAS,
|
|
|
|
value: videoStream.length,
|
|
|
|
});
|
|
|
|
}, [videoStream.length]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (Session.equals('layoutReady', true) && (sidebarContentPanel === PANELS.NONE)) {
|
|
|
|
if (!checkedUserSettings.current) {
|
|
|
|
const Settings = getSettingsSingletonInstance();
|
|
|
|
Settings.save(setLocalSettings);
|
|
|
|
|
|
|
|
if (getFromUserSettings('bbb_show_participants_on_login', window.meetingClientSettings.public.layout.showParticipantsOnLogin) && !deviceInfo.isPhone) {
|
|
|
|
if (isChatEnabled && getFromUserSettings('bbb_show_public_chat_on_login', !window.meetingClientSettings.public.chat.startClosed)) {
|
|
|
|
const PUBLIC_CHAT_ID = window.meetingClientSettings.public.chat.public_group_id;
|
|
|
|
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_NAVIGATION_IS_OPEN,
|
|
|
|
value: true,
|
|
|
|
});
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
|
|
|
|
value: true,
|
|
|
|
});
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
|
|
|
|
value: PANELS.CHAT,
|
|
|
|
});
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_ID_CHAT_OPEN,
|
|
|
|
value: PUBLIC_CHAT_ID,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_NAVIGATION_IS_OPEN,
|
|
|
|
value: true,
|
|
|
|
});
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
|
|
|
|
value: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_NAVIGATION_IS_OPEN,
|
|
|
|
value: false,
|
|
|
|
});
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
|
|
|
|
value: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
checkedUserSettings.current = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-08-28 20:59:22 +08:00
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
|
|
|
export default LayoutObserver;
|