bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/whiteboard/container.jsx

226 lines
7.2 KiB
React
Raw Normal View History

2023-09-28 04:42:47 +08:00
import React, { useContext } from 'react';
2023-09-28 10:15:33 +08:00
import { useQuery, useSubscription } from '@apollo/client';
2023-09-28 10:26:30 +08:00
import {
ColorStyle, DashStyle, SizeStyle, TDShapeType,
} from '@tldraw/tldraw';
2023-09-28 04:42:47 +08:00
import {
CURRENT_PRESENTATION_PAGE_SUBSCRIPTION,
2023-09-28 10:15:33 +08:00
CURRENT_PAGE_ANNOTATIONS_QUERY,
CURRENT_PAGE_ANNOTATIONS_STREAM,
2023-10-12 04:17:10 +08:00
CURRENT_PAGE_WRITERS_SUBSCRIPTION,
2023-09-28 04:42:47 +08:00
} from './queries';
import {
initDefaultPages,
persistShape,
removeShapes,
changeCurrentSlide,
notifyNotAllowedChange,
notifyShapeNumberExceeded,
toggleToolsAnimations,
formatAnnotations,
} from './service';
import PresentationToolbarService from '../presentation/presentation-toolbar/service';
import SettingsService from '/imports/ui/services/settings';
import { UsersContext } from '../components-data/users-context/context';
import Auth from '/imports/ui/services/auth';
import {
layoutSelect,
layoutDispatch,
} from '/imports/ui/components/layout/context';
import FullscreenService from '/imports/ui/components/common/fullscreen-button/service';
import deviceInfo from '/imports/utils/deviceInfo';
import Whiteboard from './component';
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
const WHITEBOARD_CONFIG = Meteor.settings.public.whiteboard;
2023-09-28 10:15:33 +08:00
let annotations = [];
let lastUpdatedAt = null;
2023-09-28 04:42:47 +08:00
const WhiteboardContainer = (props) => {
const {
intl,
slidePosition,
svgUri,
} = props;
const { data: presentationPageData } = useSubscription(CURRENT_PRESENTATION_PAGE_SUBSCRIPTION);
const { pres_page_curr: presentationPageArray } = (presentationPageData || {});
const currentPresentationPage = presentationPageArray && presentationPageArray[0];
const curPageId = currentPresentationPage?.num;
const presentationId = currentPresentationPage?.presentationId;
2023-10-12 04:17:10 +08:00
const { data: whiteboardWritersData } = useSubscription(CURRENT_PAGE_WRITERS_SUBSCRIPTION);
const whiteboardWriters = whiteboardWritersData?.pres_page_writers || [];
const hasWBAccess = whiteboardWriters?.some((writer) => writer.userId === Auth.userID);
2023-10-13 00:30:39 +08:00
const isMultiUserActive = whiteboardWriters?.length > 0;
2023-09-28 04:42:47 +08:00
const {
loading: annotationsLoading,
data: annotationsData,
2023-09-28 10:15:33 +08:00
} = useQuery(CURRENT_PAGE_ANNOTATIONS_QUERY);
2023-09-28 10:26:30 +08:00
const { pres_annotation_curr: history } = (annotationsData || []);
2023-09-28 10:15:33 +08:00
const lastHistoryTime = history?.[0]?.lastUpdatedAt || null;
if (!lastUpdatedAt) {
2023-09-28 10:26:30 +08:00
if (lastHistoryTime) {
2023-09-28 10:15:33 +08:00
if (new Date(lastUpdatedAt).getTime() < new Date(lastHistoryTime).getTime()) {
lastUpdatedAt = lastHistoryTime;
}
2023-09-28 10:26:30 +08:00
} else {
2023-09-28 10:15:33 +08:00
const newLastUpdatedAt = new Date();
lastUpdatedAt = newLastUpdatedAt.toISOString();
}
}
const { data: streamData } = useSubscription(
CURRENT_PAGE_ANNOTATIONS_STREAM,
{
variables: { lastUpdatedAt },
2023-09-28 10:26:30 +08:00
},
2023-09-28 10:15:33 +08:00
);
const { pres_annotation_curr_stream: streamDataItem } = (streamData || []);
2023-09-28 04:42:47 +08:00
2023-09-28 10:15:33 +08:00
if (streamDataItem) {
if (new Date(lastUpdatedAt).getTime() < new Date(streamDataItem[0].lastUpdatedAt).getTime()) {
2023-10-17 21:49:55 +08:00
if (streamDataItem[0].annotationInfo === '') {
// remove shape
annotations = annotations.filter(
(annotation) => annotation.annotationId !== streamDataItem[0].annotationId,
);
} else {
// add shape
annotations = annotations.concat(streamDataItem);
}
2023-09-28 10:15:33 +08:00
lastUpdatedAt = streamDataItem[0].lastUpdatedAt;
}
}
2023-09-28 04:42:47 +08:00
let shapes = {};
2023-09-28 10:15:33 +08:00
if (!annotationsLoading && history) {
2023-09-28 10:26:30 +08:00
const pageAnnotations = history
.concat(annotations)
.filter((annotation) => annotation.pageId === currentPresentationPage?.pageId);
2023-09-28 10:15:33 +08:00
shapes = formatAnnotations(pageAnnotations, intl, curPageId);
2023-09-28 04:42:47 +08:00
}
const { isIphone } = deviceInfo;
const assets = {};
assets[`slide-background-asset-${curPageId}`] = {
id: `slide-background-asset-${curPageId}`,
size: [slidePosition?.width || 0, slidePosition?.height || 0],
src: svgUri,
type: 'image',
};
const usingUsersContext = useContext(UsersContext);
const isRTL = layoutSelect((i) => i.isRTL);
const width = layoutSelect((i) => i?.output?.presentation?.width);
const height = layoutSelect((i) => i?.output?.presentation?.height);
const sidebarNavigationWidth = layoutSelect(
2023-09-28 10:26:30 +08:00
(i) => i?.output?.sidebarNavigation?.width,
2023-09-28 04:42:47 +08:00
);
const { users } = usingUsersContext;
const currentUser = users[Auth.meetingID][Auth.userID];
const isPresenter = currentUser.presenter;
const isModerator = currentUser.role === ROLE_MODERATOR;
const { maxStickyNoteLength, maxNumberOfAnnotations } = WHITEBOARD_CONFIG;
const fontFamily = WHITEBOARD_CONFIG.styles.text.family;
2023-09-28 10:26:30 +08:00
const handleToggleFullScreen = (ref) => FullscreenService.toggleFullScreen(ref);
2023-09-28 04:42:47 +08:00
const layoutContextDispatch = layoutDispatch();
shapes['slide-background-shape'] = {
assetId: `slide-background-asset-${curPageId}`,
childIndex: -1,
id: 'slide-background-shape',
name: 'Image',
type: TDShapeType.Image,
parentId: `${curPageId}`,
point: [0, 0],
isLocked: true,
size: [slidePosition?.width || 0, slidePosition?.height || 0],
style: {
dash: DashStyle.Draw,
size: SizeStyle.Medium,
color: ColorStyle.Blue,
},
};
if (!presentationId || !currentPresentationPage) {
return null;
}
const hasShapeAccess = (id) => {
const owner = shapes[id]?.userId;
const isBackgroundShape = id?.includes('slide-background');
const isPollsResult = shapes[id]?.name?.includes('poll-result');
2023-09-28 10:26:30 +08:00
const hasAccess = (!isBackgroundShape && !isPollsResult)
|| (isPresenter
&& ((owner && owner === currentUser?.userId)
|| !owner
|| isPresenter
|| isModerator));
2023-09-28 04:42:47 +08:00
return hasAccess;
};
// set shapes as locked for those who aren't allowed to edit it
Object.entries(shapes).forEach(([shapeId, shape]) => {
if (!shape.isLocked && !hasShapeAccess(shapeId) && !shape.name?.includes('poll-result')) {
const modShape = shape;
modShape.isLocked = true;
}
});
2023-09-28 10:15:33 +08:00
2023-09-28 10:26:30 +08:00
return (
<Whiteboard
{...{
isPresenter,
isModerator,
currentUser,
isRTL,
width,
height,
maxStickyNoteLength,
maxNumberOfAnnotations,
fontFamily,
hasShapeAccess,
handleToggleFullScreen,
sidebarNavigationWidth,
layoutContextDispatch,
initDefaultPages,
persistShape,
isMultiUserActive,
changeCurrentSlide,
shapes,
assets,
removeShapes,
zoomSlide: PresentationToolbarService.zoomSlide,
skipToSlide: PresentationToolbarService.skipToSlide,
nextSlide: PresentationToolbarService.nextSlide,
previousSlide: PresentationToolbarService.previousSlide,
2023-10-18 00:35:48 +08:00
numberOfSlides: currentPresentationPage?.totalPages,
2023-09-28 10:26:30 +08:00
notifyNotAllowedChange,
notifyShapeNumberExceeded,
whiteboardToolbarAutoHide:
2023-09-28 04:42:47 +08:00
SettingsService?.application?.whiteboardToolbarAutoHide,
2023-09-28 10:26:30 +08:00
animations: SettingsService?.application?.animations,
toggleToolsAnimations,
isIphone,
currentPresentationPage,
2023-10-18 00:35:48 +08:00
numberOfPages: currentPresentationPage?.totalPages,
2023-09-28 10:26:30 +08:00
presentationId,
2023-10-12 04:17:10 +08:00
hasWBAccess,
2023-10-13 00:30:39 +08:00
whiteboardWriters,
2023-09-28 10:26:30 +08:00
}}
{...props}
meetingId={Auth.meetingID}
/>
);
};
2023-09-28 04:42:47 +08:00
export default WhiteboardContainer;