Merge branch 'v2.6.x-release' into tldraw-with-annotation-export
This commit is contained in:
commit
745034da12
@ -108,17 +108,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
#TD-Tools > div {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#TD-Styles + div {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
document.addEventListener('gesturestart', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
</script>
|
||||
<script src="compatibility/adapter.js?v=VERSION" language="javascript"></script>
|
||||
<script src="compatibility/sip.js?v=VERSION" language="javascript"></script>
|
||||
<script src="compatibility/kurento-utils.js?v=VERSION" language="javascript"></script>
|
||||
<script src="compatibility/tflite-simd.js?v=VERSION" language="javascript"></script>
|
||||
|
@ -31,6 +31,9 @@ import ChatAdapter from '/imports/ui/components/components-data/chat-context/ada
|
||||
import UsersAdapter from '/imports/ui/components/components-data/users-context/adapter';
|
||||
import GroupChatAdapter from '/imports/ui/components/components-data/group-chat-context/adapter';
|
||||
import { liveDataEventBrokerInitializer } from '/imports/ui/services/LiveDataEventBroker/LiveDataEventBroker';
|
||||
// The adapter import is "unused" as far as static code is concerned, but it
|
||||
// needs to here to override global prototypes. So: don't remove it - prlanzarin 25 Apr 2022
|
||||
import adapter from 'webrtc-adapter';
|
||||
|
||||
import collectionMirrorInitializer from './collection-mirror-initializer';
|
||||
|
||||
|
@ -30,11 +30,9 @@ const process = () => {
|
||||
};
|
||||
|
||||
export default function handleWhiteboardSend({ header, body }, meetingId) {
|
||||
//console.log("!!!!!!!!!! handleWhiteboardSend!!!!!!!!!!: ");
|
||||
const userId = header.userId;
|
||||
const whiteboardId = body.whiteboardId;
|
||||
const annotations = body.annotations;
|
||||
//console.log(annotations);
|
||||
|
||||
check(userId, String);
|
||||
check(whiteboardId, String);
|
||||
@ -45,13 +43,11 @@ export default function handleWhiteboardSend({ header, body }, meetingId) {
|
||||
}
|
||||
|
||||
annotations.map(annotation => {
|
||||
annotationsQueue[meetingId].push({ meetingId, whiteboardId, userId, annotation });
|
||||
addAnnotation(meetingId, whiteboardId, userId, annotation);
|
||||
annotationsQueue[meetingId].push({ meetingId, whiteboardId, userId: annotation.userId, annotation });
|
||||
addAnnotation(meetingId, whiteboardId, annotation.userId, annotation);
|
||||
})
|
||||
if (queueMetrics) {
|
||||
Metrics.setAnnotationQueueLength(meetingId, annotationsQueue[meetingId].length);
|
||||
}
|
||||
if (!annotationsRecieverIsRunning) process();
|
||||
|
||||
//return addAnnotation(meetingId, whiteboardId, userId, annotation);
|
||||
}
|
||||
|
@ -22,13 +22,6 @@ export default function deleteAnnotations(annotations, whiteboardId) {
|
||||
annotationsIds: annotations,
|
||||
};
|
||||
|
||||
console.log('$$$$$$$$$$$ shapes to remove $$$$$$$$$$$$$$$$$$$$$$')
|
||||
console.log('$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$')
|
||||
console.log(annotations)
|
||||
console.log('$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$')
|
||||
// shape.meetingId = meetingId;
|
||||
//deleteShape(shape);
|
||||
|
||||
return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
|
||||
} catch (err) {
|
||||
Logger.error(`Exception while invoking method deleteAnnotation ${err.stack}`);
|
||||
|
@ -23,9 +23,6 @@ export default function sendAnnotationHelper(annotations, meetingId, requesterUs
|
||||
annotations: whiteboardAnnotations,
|
||||
};
|
||||
|
||||
//console.log("AEAFAEWFEWFWE")
|
||||
//console.log(annotations)
|
||||
|
||||
RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
|
||||
});
|
||||
|
||||
|
@ -5,7 +5,6 @@ import transferUser from './methods/transferUser';
|
||||
import toggleLockSettings from './methods/toggleLockSettings';
|
||||
import toggleWebcamsOnlyForModerator from './methods/toggleWebcamsOnlyForModerator';
|
||||
import clearRandomlySelectedUser from './methods/clearRandomlySelectedUser';
|
||||
import changeCurrentSlide from './methods/changeCurrentSlide';
|
||||
import changeLayout from './methods/changeLayout';
|
||||
|
||||
Meteor.methods({
|
||||
@ -16,5 +15,4 @@ Meteor.methods({
|
||||
toggleWebcamsOnlyForModerator,
|
||||
clearRandomlySelectedUser,
|
||||
changeLayout,
|
||||
changeCurrentSlide,
|
||||
});
|
||||
|
@ -1,31 +0,0 @@
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import { extractCredentials } from '/imports/api/common/server/helpers';
|
||||
import { check } from 'meteor/check';
|
||||
import setCurrentSlide from '../modifiers/setCurrentSlide';
|
||||
|
||||
export default function changeCurrentSlide(slideNum) {
|
||||
const REDIS_CONFIG = Meteor.settings.private.redis;
|
||||
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
|
||||
const EVENT_NAME = 'BroadcastLayoutMsg';
|
||||
|
||||
|
||||
console.log('$$$$$$$$$$$$$$$$$$$')
|
||||
console.log(slideNum)
|
||||
console.log('$$$$$$$$$$$$$$$$$$$')
|
||||
|
||||
try {
|
||||
const { meetingId, requesterUserId } = extractCredentials(this.userId);
|
||||
setCurrentSlide(meetingId, slideNum);
|
||||
// check(meetingId, String);
|
||||
// check(requesterUserId, String);
|
||||
|
||||
// const payload = {
|
||||
// layout,
|
||||
// };
|
||||
|
||||
// RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
|
||||
} catch (err) {
|
||||
// Logger.error(`Exception while invoking method changeLayout ${err.stack}`);
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function setCurrentSlide(meetingId, slideNum) {
|
||||
try {
|
||||
const selector = {
|
||||
meetingId,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
activeSlide: slideNum,
|
||||
},
|
||||
};
|
||||
|
||||
const activeUpdate = Meetings.update(selector, modifier);
|
||||
|
||||
if (activeUpdate) {
|
||||
Logger.info(`Meeting active slide changed for meeting=${meetingId}`);
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.error(`Exception while invoking method setCurrentSlide ${err.stack}`);
|
||||
}
|
||||
}
|
@ -1,12 +1,3 @@
|
||||
// import { Meteor } from 'meteor/meteor';
|
||||
// import switchSlide from './methods/switchSlide';
|
||||
// import zoomSlide from './methods/zoomSlide';
|
||||
|
||||
// Meteor.methods({
|
||||
// switchSlide,
|
||||
// zoomSlide,
|
||||
// });
|
||||
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import switchSlide from './methods/switchSlide';
|
||||
import zoomSlide from './methods/zoomSlide';
|
||||
@ -16,4 +7,4 @@ Meteor.methods({
|
||||
switchSlide,
|
||||
zoomSlide,
|
||||
persistAsset,
|
||||
});
|
||||
});
|
||||
|
@ -101,17 +101,16 @@ export default function addSlide(meetingId, podId, presentationId, slide) {
|
||||
const slideData = {
|
||||
width,
|
||||
height,
|
||||
x: xCamera,
|
||||
y: yCamera,
|
||||
xCamera,
|
||||
yCamera,
|
||||
zoom,
|
||||
};
|
||||
const slidePosition = calculateSlideData(slideData);
|
||||
//const slidePosition = calculateSlideData(slideData);
|
||||
|
||||
addSlidePositions(meetingId, podId, presentationId, slideId, slideData);
|
||||
}
|
||||
|
||||
try {
|
||||
console.log("modifier!!! ", modifier )
|
||||
const { insertedId, numberAffected } = Slides.upsert(selector, modifier);
|
||||
|
||||
requestWhiteboardHistory(meetingId, slideId);
|
||||
|
@ -18,8 +18,8 @@ export default function addSlidePositions(
|
||||
check(slidePosition, {
|
||||
width: Number,
|
||||
height: Number,
|
||||
x: Number,
|
||||
y: Number,
|
||||
xCamera: Number,
|
||||
yCamera: Number,
|
||||
zoom: Number,
|
||||
});
|
||||
|
||||
|
@ -931,7 +931,8 @@ class Presentation extends PureComponent {
|
||||
slidePosition={slidePosition}
|
||||
getSvgRef={this.getSvgRef}
|
||||
setTldrawAPI={this.setTldrawAPI}
|
||||
curPageId={this.state.tldrawAPI?.getPage()?.id}
|
||||
curPageId={currentSlide?.num.toString()}
|
||||
svgUri={currentSlide?.svgUri}
|
||||
/>
|
||||
{isFullscreen && <PollingContainer />}
|
||||
{this.renderPresentationToolbar()}
|
||||
|
@ -1,78 +1,74 @@
|
||||
import React, { PureComponent } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { defineMessages, injectIntl } from "react-intl";
|
||||
import deviceInfo from "/imports/utils/deviceInfo";
|
||||
import injectWbResizeEvent from "/imports/ui/components/presentation/resize-wrapper/component";
|
||||
import {
|
||||
HUNDRED_PERCENT,
|
||||
MAX_PERCENT,
|
||||
STEP,
|
||||
} from "/imports/utils/slideCalcUtils";
|
||||
import Styled from "./styles";
|
||||
import ZoomTool from "./zoom-tool/component";
|
||||
import TooltipContainer from "/imports/ui/components/common/tooltip/container";
|
||||
import KEY_CODES from "/imports/utils/keyCodes";
|
||||
import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import deviceInfo from '/imports/utils/deviceInfo';
|
||||
import injectWbResizeEvent from '/imports/ui/components/presentation/resize-wrapper/component';
|
||||
import { HUNDRED_PERCENT, MAX_PERCENT, STEP } from '/imports/utils/slideCalcUtils';
|
||||
import Styled from './styles';
|
||||
import ZoomTool from './zoom-tool/component';
|
||||
import TooltipContainer from '/imports/ui/components/common/tooltip/container';
|
||||
import KEY_CODES from '/imports/utils/keyCodes';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
previousSlideLabel: {
|
||||
id: "app.presentation.presentationToolbar.prevSlideLabel",
|
||||
description: "Previous slide button label",
|
||||
id: 'app.presentation.presentationToolbar.prevSlideLabel',
|
||||
description: 'Previous slide button label',
|
||||
},
|
||||
previousSlideDesc: {
|
||||
id: "app.presentation.presentationToolbar.prevSlideDesc",
|
||||
description: "Aria description for when switching to previous slide",
|
||||
id: 'app.presentation.presentationToolbar.prevSlideDesc',
|
||||
description: 'Aria description for when switching to previous slide',
|
||||
},
|
||||
nextSlideLabel: {
|
||||
id: "app.presentation.presentationToolbar.nextSlideLabel",
|
||||
description: "Next slide button label",
|
||||
id: 'app.presentation.presentationToolbar.nextSlideLabel',
|
||||
description: 'Next slide button label',
|
||||
},
|
||||
nextSlideDesc: {
|
||||
id: "app.presentation.presentationToolbar.nextSlideDesc",
|
||||
description: "Aria description for when switching to next slide",
|
||||
id: 'app.presentation.presentationToolbar.nextSlideDesc',
|
||||
description: 'Aria description for when switching to next slide',
|
||||
},
|
||||
noNextSlideDesc: {
|
||||
id: "app.presentation.presentationToolbar.noNextSlideDesc",
|
||||
description: "",
|
||||
id: 'app.presentation.presentationToolbar.noNextSlideDesc',
|
||||
description: '',
|
||||
},
|
||||
noPrevSlideDesc: {
|
||||
id: "app.presentation.presentationToolbar.noPrevSlideDesc",
|
||||
description: "",
|
||||
id: 'app.presentation.presentationToolbar.noPrevSlideDesc',
|
||||
description: '',
|
||||
},
|
||||
skipSlideLabel: {
|
||||
id: "app.presentation.presentationToolbar.skipSlideLabel",
|
||||
description: "Aria label for when switching to a specific slide",
|
||||
id: 'app.presentation.presentationToolbar.skipSlideLabel',
|
||||
description: 'Aria label for when switching to a specific slide',
|
||||
},
|
||||
skipSlideDesc: {
|
||||
id: "app.presentation.presentationToolbar.skipSlideDesc",
|
||||
description: "Aria description for when switching to a specific slide",
|
||||
id: 'app.presentation.presentationToolbar.skipSlideDesc',
|
||||
description: 'Aria description for when switching to a specific slide',
|
||||
},
|
||||
goToSlide: {
|
||||
id: "app.presentation.presentationToolbar.goToSlide",
|
||||
description: "button for slide select",
|
||||
id: 'app.presentation.presentationToolbar.goToSlide',
|
||||
description: 'button for slide select',
|
||||
},
|
||||
selectLabel: {
|
||||
id: "app.presentation.presentationToolbar.selectLabel",
|
||||
description: "slide select label",
|
||||
id: 'app.presentation.presentationToolbar.selectLabel',
|
||||
description: 'slide select label',
|
||||
},
|
||||
fitToWidth: {
|
||||
id: "app.presentation.presentationToolbar.fitToWidth",
|
||||
description: "button for fit to width",
|
||||
id: 'app.presentation.presentationToolbar.fitToWidth',
|
||||
description: 'button for fit to width',
|
||||
},
|
||||
fitToWidthDesc: {
|
||||
id: "app.presentation.presentationToolbar.fitWidthDesc",
|
||||
description: "Aria description to display the whole width of the slide",
|
||||
id: 'app.presentation.presentationToolbar.fitWidthDesc',
|
||||
description: 'Aria description to display the whole width of the slide',
|
||||
},
|
||||
fitToPage: {
|
||||
id: "app.presentation.presentationToolbar.fitToPage",
|
||||
description: "button label for fit to width",
|
||||
id: 'app.presentation.presentationToolbar.fitToPage',
|
||||
description: 'button label for fit to width',
|
||||
},
|
||||
fitToPageDesc: {
|
||||
id: "app.presentation.presentationToolbar.fitScreenDesc",
|
||||
description: "Aria description to display the whole slide",
|
||||
id: 'app.presentation.presentationToolbar.fitScreenDesc',
|
||||
description: 'Aria description to display the whole slide',
|
||||
},
|
||||
presentationLabel: {
|
||||
id: "app.presentationUploder.title",
|
||||
description: "presentation area element label",
|
||||
id: 'app.presentationUploder.title',
|
||||
description: 'presentation area element label',
|
||||
},
|
||||
});
|
||||
|
||||
@ -80,10 +76,6 @@ class PresentationToolbar extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
curPageId: 1,
|
||||
};
|
||||
|
||||
this.handleSkipToSlideChange = this.handleSkipToSlideChange.bind(this);
|
||||
this.change = this.change.bind(this);
|
||||
this.renderAriaDescs = this.renderAriaDescs.bind(this);
|
||||
@ -94,16 +86,16 @@ class PresentationToolbar extends PureComponent {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener("keydown", this.switchSlide);
|
||||
document.addEventListener('keydown', this.switchSlide);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener("keydown", this.switchSlide);
|
||||
document.removeEventListener('keydown', this.switchSlide);
|
||||
}
|
||||
|
||||
switchSlide(event) {
|
||||
const { target, which } = event;
|
||||
const isBody = target.nodeName === "BODY";
|
||||
const isBody = target.nodeName === 'BODY';
|
||||
|
||||
if (isBody) {
|
||||
switch (which) {
|
||||
@ -124,26 +116,34 @@ class PresentationToolbar extends PureComponent {
|
||||
}
|
||||
|
||||
handleSkipToSlideChange(event) {
|
||||
const { skipToSlide, podId } = this.props;
|
||||
const {
|
||||
skipToSlide,
|
||||
podId,
|
||||
} = this.props;
|
||||
const requestedSlideNum = Number.parseInt(event.target.value, 10);
|
||||
|
||||
if (event) event.currentTarget.blur();
|
||||
// skipToSlide(requestedSlideNum, podId);
|
||||
this.props?.tldrawAPI?.changePage(requestedSlideNum);
|
||||
this.setState({
|
||||
curPageId: parseInt(this.props?.tldrawAPI?.getPage()?.id),
|
||||
});
|
||||
skipToSlide(requestedSlideNum, podId);
|
||||
}
|
||||
|
||||
nextSlideHandler(event) {
|
||||
const { nextSlide, currentSlideNum, numberOfSlides, podId } = this.props;
|
||||
const {
|
||||
nextSlide,
|
||||
currentSlideNum,
|
||||
numberOfSlides,
|
||||
podId,
|
||||
} = this.props;
|
||||
|
||||
if (event) event.currentTarget.blur();
|
||||
nextSlide(currentSlideNum, numberOfSlides, podId);
|
||||
}
|
||||
|
||||
previousSlideHandler(event) {
|
||||
const { previousSlide, currentSlideNum, podId } = this.props;
|
||||
const {
|
||||
previousSlide,
|
||||
currentSlideNum,
|
||||
podId,
|
||||
} = this.props;
|
||||
|
||||
if (event) event.currentTarget.blur();
|
||||
previousSlide(currentSlideNum, podId);
|
||||
@ -160,13 +160,13 @@ class PresentationToolbar extends PureComponent {
|
||||
} = this.props;
|
||||
|
||||
handleToggleFullScreen(fullscreenRef);
|
||||
const newElement = isFullscreen ? "" : fullscreenElementId;
|
||||
const newElement = isFullscreen ? '' : fullscreenElementId;
|
||||
|
||||
layoutContextDispatch({
|
||||
type: fullscreenAction,
|
||||
value: {
|
||||
element: newElement,
|
||||
group: "",
|
||||
group: '',
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -211,11 +211,15 @@ class PresentationToolbar extends PureComponent {
|
||||
const { intl } = this.props;
|
||||
const optionList = [];
|
||||
for (let i = 1; i <= numberOfSlides; i += 1) {
|
||||
optionList.push(
|
||||
<option value={i} key={i}>
|
||||
{intl.formatMessage(intlMessages.goToSlide, { 0: i })}
|
||||
</option>
|
||||
);
|
||||
optionList.push((
|
||||
<option
|
||||
value={i}
|
||||
key={i}
|
||||
>
|
||||
{
|
||||
intl.formatMessage(intlMessages.goToSlide, { 0: i })
|
||||
}
|
||||
</option>));
|
||||
}
|
||||
|
||||
return optionList;
|
||||
@ -241,47 +245,42 @@ class PresentationToolbar extends PureComponent {
|
||||
|
||||
const { isMobile } = deviceInfo;
|
||||
|
||||
const startOfSlides = parseInt(this.state.curPageId) === 1;
|
||||
const endOfSlides = parseInt(this.state.curPageId) === numberOfSlides;
|
||||
const startOfSlides = !(currentSlideNum > 1);
|
||||
const endOfSlides = !(currentSlideNum < numberOfSlides);
|
||||
|
||||
const prevSlideAriaLabel = startOfSlides
|
||||
? intl.formatMessage(intlMessages.previousSlideLabel)
|
||||
: `${intl.formatMessage(intlMessages.previousSlideLabel)} (${
|
||||
parseInt(this.props?.tldrawAPI?.getPage()?.id) <= 1
|
||||
? ""
|
||||
: parseInt(this.props?.tldrawAPI?.getPage()?.id) - 1
|
||||
})`;
|
||||
: `${intl.formatMessage(intlMessages.previousSlideLabel)} (${currentSlideNum <= 1 ? '' : (currentSlideNum - 1)})`;
|
||||
|
||||
const nextSlideAriaLabel = endOfSlides
|
||||
? intl.formatMessage(intlMessages.nextSlideLabel)
|
||||
: `${intl.formatMessage(intlMessages.nextSlideLabel)} (${
|
||||
parseInt(this.props?.tldrawAPI?.getPage()?.id) >= 1
|
||||
? parseInt(this.props?.tldrawAPI?.getPage()?.id) + 1
|
||||
: ""
|
||||
})`;
|
||||
: `${intl.formatMessage(intlMessages.nextSlideLabel)} (${currentSlideNum >= 1 ? (currentSlideNum + 1) : ''})`;
|
||||
|
||||
return (
|
||||
<Styled.PresentationToolbarWrapper
|
||||
id="presentationToolbarWrapper"
|
||||
style={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
style={
|
||||
{
|
||||
width: toolbarWidth,
|
||||
}
|
||||
}>
|
||||
{this.renderAriaDescs()}
|
||||
{
|
||||
<div>
|
||||
{isPollingEnabled ? (
|
||||
<Styled.QuickPollButton
|
||||
{...{
|
||||
currentSlidHasContent,
|
||||
intl,
|
||||
amIPresenter,
|
||||
parseCurrentSlideContent,
|
||||
startPoll,
|
||||
currentSlide,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{isPollingEnabled
|
||||
? (
|
||||
<Styled.QuickPollButton
|
||||
{...{
|
||||
currentSlidHasContent,
|
||||
intl,
|
||||
amIPresenter,
|
||||
parseCurrentSlideContent,
|
||||
startPoll,
|
||||
currentSlide,
|
||||
}}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
@ -289,29 +288,18 @@ class PresentationToolbar extends PureComponent {
|
||||
<Styled.PrevSlideButton
|
||||
role="button"
|
||||
aria-label={prevSlideAriaLabel}
|
||||
aria-describedby={
|
||||
startOfSlides ? "noPrevSlideDesc" : "prevSlideDesc"
|
||||
}
|
||||
aria-describedby={startOfSlides ? 'noPrevSlideDesc' : 'prevSlideDesc'}
|
||||
disabled={startOfSlides || !isMeteorConnected}
|
||||
color="default"
|
||||
icon="left_arrow"
|
||||
size="md"
|
||||
onClick={() => {
|
||||
this.props?.tldrawAPI?.changePage(
|
||||
parseInt(this.props?.tldrawAPI?.getPage()?.id) - 1
|
||||
);
|
||||
this.setState({
|
||||
curPageId: parseInt(this.props?.tldrawAPI?.getPage()?.id),
|
||||
});
|
||||
}}
|
||||
onClick={this.previousSlideHandler}
|
||||
label={intl.formatMessage(intlMessages.previousSlideLabel)}
|
||||
hideLabel
|
||||
data-test="prevSlide"
|
||||
/>
|
||||
|
||||
<TooltipContainer
|
||||
title={intl.formatMessage(intlMessages.selectLabel)}
|
||||
>
|
||||
<TooltipContainer title={intl.formatMessage(intlMessages.selectLabel)}>
|
||||
<Styled.SkipSlideSelect
|
||||
id="skipSlide"
|
||||
aria-label={intl.formatMessage(intlMessages.skipSlideLabel)}
|
||||
@ -319,7 +307,7 @@ class PresentationToolbar extends PureComponent {
|
||||
aria-live="polite"
|
||||
aria-relevant="all"
|
||||
disabled={!isMeteorConnected}
|
||||
value={this.state.curPageId}
|
||||
value={currentSlideNum}
|
||||
onChange={this.handleSkipToSlideChange}
|
||||
data-test="skipSlide"
|
||||
>
|
||||
@ -329,21 +317,12 @@ class PresentationToolbar extends PureComponent {
|
||||
<Styled.NextSlideButton
|
||||
role="button"
|
||||
aria-label={nextSlideAriaLabel}
|
||||
aria-describedby={
|
||||
endOfSlides ? "noNextSlideDesc" : "nextSlideDesc"
|
||||
}
|
||||
aria-describedby={endOfSlides ? 'noNextSlideDesc' : 'nextSlideDesc'}
|
||||
disabled={endOfSlides || !isMeteorConnected}
|
||||
color="default"
|
||||
icon="right_arrow"
|
||||
size="md"
|
||||
onClick={() => {
|
||||
this.props?.tldrawAPI?.changePage(
|
||||
parseInt(this.props?.tldrawAPI?.getPage()?.id) + 1
|
||||
);
|
||||
this.setState({
|
||||
curPageId: parseInt(this.props?.tldrawAPI?.getPage()?.id),
|
||||
});
|
||||
}}
|
||||
onClick={this.nextSlideHandler}
|
||||
label={intl.formatMessage(intlMessages.nextSlideLabel)}
|
||||
hideLabel
|
||||
data-test="nextSlide"
|
||||
@ -352,12 +331,16 @@ class PresentationToolbar extends PureComponent {
|
||||
}
|
||||
{
|
||||
<Styled.PresentationZoomControls>
|
||||
{!isMobile ? (
|
||||
<TooltipContainer>
|
||||
{
|
||||
!isMobile
|
||||
? (
|
||||
<TooltipContainer>
|
||||
<ZoomTool
|
||||
zoomValue={
|
||||
//this.props?.tldrawAPI?.pageStates[currentSlideNum.toString()]?.camera?.zoom
|
||||
this.props?.tldrawAPI?.getPageState()?.camera?.zoom
|
||||
}
|
||||
currentSlideNum={currentSlideNum}
|
||||
change={this.change}
|
||||
minBound={0.1}
|
||||
maxBound={5}
|
||||
@ -365,19 +348,16 @@ class PresentationToolbar extends PureComponent {
|
||||
isMeteorConnected={isMeteorConnected}
|
||||
tldrawAPI={this.props?.tldrawAPI}
|
||||
/>
|
||||
</TooltipContainer>
|
||||
) : null}
|
||||
</TooltipContainer>
|
||||
)
|
||||
: null
|
||||
}
|
||||
<Styled.FitToWidthButton
|
||||
role="button"
|
||||
aria-describedby={fitToWidth ? "fitPageDesc" : "fitWidthDesc"}
|
||||
aria-label={
|
||||
fitToWidth
|
||||
? `${intl.formatMessage(
|
||||
intlMessages.presentationLabel
|
||||
)} ${intl.formatMessage(intlMessages.fitToPage)}`
|
||||
: `${intl.formatMessage(
|
||||
intlMessages.presentationLabel
|
||||
)} ${intl.formatMessage(intlMessages.fitToWidth)}`
|
||||
aria-describedby={fitToWidth ? 'fitPageDesc' : 'fitWidthDesc'}
|
||||
aria-label={fitToWidth
|
||||
? `${intl.formatMessage(intlMessages.presentationLabel)} ${intl.formatMessage(intlMessages.fitToPage)}`
|
||||
: `${intl.formatMessage(intlMessages.presentationLabel)} ${intl.formatMessage(intlMessages.fitToWidth)}`
|
||||
}
|
||||
color="default"
|
||||
disabled={!isMeteorConnected}
|
||||
|
@ -190,7 +190,7 @@ class ZoomTool extends PureComponent {
|
||||
aria-describedby="resetZoomDescription"
|
||||
disabled={(stateZoomValue === minBound) || !isMeteorConnected}
|
||||
color="default"
|
||||
customIcon={`${this.props?.tldrawAPI?.getPageState()?.camera?.zoom * 100}%`}
|
||||
customIcon={`${parseInt(this.props?.tldrawAPI?.getPageState()?.camera?.zoom * 100)}%`}
|
||||
size="md"
|
||||
onClick={() => tldrawAPI?.zoomTo(1)}
|
||||
label={intl.formatMessage(intlMessages.resetZoomLabel)}
|
||||
|
@ -32,7 +32,7 @@ class UserContent extends PureComponent {
|
||||
return (
|
||||
<Styled.Content data-test="userListContent">
|
||||
{isChatEnabled() ? <UserMessagesContainer /> : null}
|
||||
{/* {currentUser.role === ROLE_MODERATOR ? <UserCaptionsContainer /> : null} */}
|
||||
{currentUser.role === ROLE_MODERATOR ? <UserCaptionsContainer /> : null}
|
||||
<UserNotesContainer />
|
||||
{showWaitingRoom && currentUser.role === ROLE_MODERATOR
|
||||
? (
|
||||
|
@ -38,13 +38,13 @@ export default function Whiteboard(props) {
|
||||
assets,
|
||||
currentUser,
|
||||
curPres,
|
||||
curSlide,
|
||||
changeCurrentSlide,
|
||||
whiteboardId,
|
||||
podId,
|
||||
zoomSlide,
|
||||
skipToSlide,
|
||||
slidePosition,
|
||||
curPageId,
|
||||
svgUri,
|
||||
} = props;
|
||||
|
||||
const { pages, pageStates } = initDefaultPages(curPres?.pages.length || 1);
|
||||
@ -57,138 +57,96 @@ export default function Whiteboard(props) {
|
||||
bindings: {},
|
||||
assets,
|
||||
});
|
||||
const [doc, setDoc] = React.useState(rDocument.current);
|
||||
const [curPage, setCurPage] = React.useState({ id: "1" });
|
||||
//const [doc, setDoc] = React.useState(rDocument.current);
|
||||
const [_assets, setAssets] = React.useState(assets);
|
||||
const [command, setCommand] = React.useState("");
|
||||
const [wbAccess, setWBAccess] = React.useState(props?.hasMultiUserAccess(props.whiteboardId, props.currentUser.userId));
|
||||
const [selectedIds, setSelectedIds] = React.useState([]);
|
||||
const [tldrawAPI, setTLDrawAPI] = React.useState(null);
|
||||
const prevShapes = usePrevious(shapes);
|
||||
const prevPage = usePrevious(curPage);
|
||||
const prevSlidePosition = usePrevious(slidePosition);
|
||||
const prevPageId = usePrevious(curPageId);
|
||||
|
||||
const handleChange = React.useCallback((state, reason) => {
|
||||
rDocument.current = state.document;
|
||||
}, []);
|
||||
|
||||
React.useMemo(() => {
|
||||
const doc = React.useMemo(() => {
|
||||
const currentDoc = rDocument.current;
|
||||
const propShapes = Object.entries(shapes.filter(s => s.parentId === tldrawAPI?.getPage()?.id) || {})?.map(([k, v]) => v.id);
|
||||
|
||||
if (tldrawAPI) {
|
||||
tldrawAPI?.getPage()?.id && tldrawAPI.changePage(tldrawAPI?.getPage()?.id);
|
||||
let next = { ...currentDoc };
|
||||
|
||||
let pageBindings = null;
|
||||
let history = null;
|
||||
let stack = null;
|
||||
let changed = false;
|
||||
|
||||
if (next.pageStates[curPageId] && !_.isEqual(prevShapes, shapes)) {
|
||||
// mergeDocument loses bindings and history, save it
|
||||
pageBindings = tldrawAPI?.getPage(curPageId)?.bindings;
|
||||
history = tldrawAPI?.history
|
||||
stack = tldrawAPI?.stack
|
||||
|
||||
next.pages[curPageId].shapes = shapes;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
const next = { ...currentDoc };
|
||||
if (next.pages[curPageId] && !next.pages[curPageId].shapes["slide-background-shape"]) {
|
||||
next.assets[`slide-background-asset-${curPageId}`] = {
|
||||
id: `slide-background-asset-${curPageId}`,
|
||||
size: [slidePosition?.width || 0, slidePosition?.height || 0],
|
||||
src: svgUri,
|
||||
type: "image",
|
||||
};
|
||||
|
||||
next.assets = { ...assets };
|
||||
next.pages[curPageId].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,
|
||||
},
|
||||
};
|
||||
|
||||
const pShapes = Object.entries(shapes || {})?.map(([k, v]) => v.id);
|
||||
shapes.filter(s => s.parentId === tldrawAPI?.getPage()?.id)?.forEach((s) => {
|
||||
try {
|
||||
Object.keys(next.pages[s.parentId].shapes).forEach((k) => {
|
||||
if (!pShapes.includes(k) && s.parentId === tldrawAPI?.getPage()?.id) {
|
||||
delete next.pages[s.parentId].shapes[k];
|
||||
}
|
||||
});
|
||||
|
||||
next.pages[s.parentId] = {
|
||||
...next.pages[s.parentId],
|
||||
shapes: {
|
||||
...next.pages[s.parentId].shapes,
|
||||
[s.id]: { ...s },
|
||||
},
|
||||
};
|
||||
|
||||
} catch (err) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
tldrawAPI?.mergeDocument(next);
|
||||
if (tldrawAPI && history) tldrawAPI.history = history;
|
||||
if (tldrawAPI && stack) tldrawAPI.stack = stack;
|
||||
if (pageBindings && Object.keys(pageBindings).length !== 0) {
|
||||
currentDoc.pages[curPageId].bindings = pageBindings;
|
||||
}
|
||||
});
|
||||
|
||||
if (curPres?.pages.length) {
|
||||
curPres.pages.map((p, i) => {
|
||||
next.assets[`slide-background-asset-${i}`] = {
|
||||
id: `slide-background-asset-${i}`,
|
||||
size: [slidePosition?.width || 0, slidePosition?.height || 0],
|
||||
src: curPres?.pages[i]?.svgUri,
|
||||
type: "image",
|
||||
};
|
||||
|
||||
try {
|
||||
next.pages[i + 1]["shapes"]["slide-background-shape"] = {
|
||||
assetId: `slide-background-asset-${i}`,
|
||||
childIndex: 1,
|
||||
id: "slide-background-shape",
|
||||
name: "Image",
|
||||
type: TDShapeType.Image,
|
||||
parentId: `${i + 1}`,
|
||||
point: [0, 0],
|
||||
isLocked: true,
|
||||
size: [slidePosition?.width || 0, slidePosition?.height || 0],
|
||||
style: {
|
||||
dash: DashStyle.Draw,
|
||||
size: SizeStyle.Medium,
|
||||
color: ColorStyle.Blue,
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
logger.error({
|
||||
logCode: 'whiteboard_set_slide_background_error',
|
||||
extraInfo: { error: err },
|
||||
}, 'Error on adding background slide image');
|
||||
}
|
||||
return p;
|
||||
// setDoc(next);
|
||||
});
|
||||
}
|
||||
|
||||
rDocument.current = next;
|
||||
|
||||
const pageID = tldrawAPI?.getPage()?.id;
|
||||
if (next.pageStates[pageID]?.selectedIds.length > 0) {
|
||||
// if a selected id is not in the list of shapes remove it from list
|
||||
next.pageStates[pageID]?.selectedIds.map((k) => {
|
||||
if (!next.pages[pageID].shapes[k]) {
|
||||
next.pageStates[pageID].selectedIds =
|
||||
next.pageStates[pageID].selectedIds.filter(
|
||||
(id) => id !== k
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (next.pageStates[pageID] && !isPresenter && !_.isEqual(slidePosition, prevSlidePosition)) {
|
||||
next.pageStates[pageID].camera.point = [slidePosition.xCamera, slidePosition.yCamera]
|
||||
next.pageStates[pageID].camera.zoom = slidePosition.zoom
|
||||
}
|
||||
|
||||
setDoc(next);
|
||||
|
||||
if (
|
||||
tldrawAPI &&
|
||||
!_.isEqual(shapes, prevShapes) &&
|
||||
!_.isEqual(assets, _assets)
|
||||
) {
|
||||
setAssets(assets);
|
||||
tldrawAPI?.replacePageContent(next?.pages[pageID]?.shapes, {}, assets);
|
||||
}
|
||||
|
||||
if (tldrawAPI && !_.isEqual(shapes, prevShapes) && !_.isEqual(assets, _assets)) {
|
||||
tldrawAPI?.replacePageContent(next?.pages[pageID]?.shapes, {}, assets);
|
||||
}
|
||||
}, [assets, shapes, curPres, tldrawAPI, curPageId]);
|
||||
return currentDoc;
|
||||
}, [assets, shapes, tldrawAPI, curPageId, slidePosition]);
|
||||
|
||||
// change tldraw page when presentation page changes
|
||||
React.useEffect(() => {
|
||||
isPresenter && curPage && changeCurrentSlide(curPage?.id);
|
||||
}, [curPage]);
|
||||
const previousPageZoom = tldrawAPI?.getPageState()?.camera?.zoom;
|
||||
tldrawAPI &&
|
||||
curPageId &&
|
||||
tldrawAPI.changePage(curPageId)
|
||||
//change zoom of the new page to follow the previous one
|
||||
previousPageZoom &&
|
||||
tldrawAPI.zoomTo(previousPageZoom)
|
||||
}, [curPageId]);
|
||||
|
||||
// change tldraw camera when slidePosition changes
|
||||
React.useEffect(() => {
|
||||
tldrawAPI &&
|
||||
!isPresenter &&
|
||||
curSlide?.activeSlide &&
|
||||
tldrawAPI.changePage(curSlide?.activeSlide);
|
||||
}, [curSlide]);
|
||||
!isPresenter &&
|
||||
curPageId &&
|
||||
slidePosition &&
|
||||
tldrawAPI?.setCamera([slidePosition.xCamera, slidePosition.yCamera], slidePosition.zoom);
|
||||
}, [curPageId, slidePosition]);
|
||||
|
||||
const hasWBAccess = props?.hasMultiUserAccess(props.whiteboardId, props.currentUser.userId);
|
||||
|
||||
@ -202,47 +160,58 @@ export default function Whiteboard(props) {
|
||||
<Tldraw
|
||||
document={doc}
|
||||
disableAssets={false}
|
||||
onChangePage={(app, s, b, a) => {
|
||||
setCurPage(app.getPage());
|
||||
}}
|
||||
onMount={(app) => {
|
||||
setTLDrawAPI(app);
|
||||
props.setTldrawAPI(app);
|
||||
curPageId && app.changePage(curPageId);
|
||||
curPageId && app.setCamera([slidePosition.xCamera, slidePosition.yCamera], slidePosition.zoom)
|
||||
}}
|
||||
onChange={handleChange}
|
||||
//onChange={handleChange}
|
||||
onPersist={(e) => {
|
||||
///////////// handle assets /////////////////////////
|
||||
e?.assets?.forEach((a) => {
|
||||
persistAsset(a);
|
||||
//persistAsset(a);
|
||||
});
|
||||
}}
|
||||
showPages={false}
|
||||
showZoom={false}
|
||||
showUI={isPresenter || hasWBAccess}
|
||||
showMenu={false}
|
||||
showUI={curPres ? (isPresenter || hasWBAccess) : true}
|
||||
showMenu={curPres ? false : true}
|
||||
showMultiplayerMenu={false}
|
||||
readOnly={!isPresenter && !hasWBAccess}
|
||||
onUndo={s => {
|
||||
s?.selectedIds?.map(id => {
|
||||
persistShape(s.getShape(id), whiteboardId);
|
||||
onUndo={(e, s) => {
|
||||
e?.selectedIds?.map(id => {
|
||||
persistShape(e.getShape(id), whiteboardId);
|
||||
})
|
||||
const pageShapes = e.state.document.pages[e.getPage()?.id]?.shapes;
|
||||
let shapesIdsToRemove = findRemoved(Object.keys(shapes), Object.keys(pageShapes))
|
||||
removeShapes(shapesIdsToRemove, whiteboardId)
|
||||
}}
|
||||
|
||||
onRedo={(e, s) => {
|
||||
e?.selectedIds?.map(id => {
|
||||
persistShape(e.getShape(id), whiteboardId);
|
||||
});
|
||||
const pageShapes = e.state.document.pages[e.getPage()?.id]?.shapes;
|
||||
let shapesIdsToRemove = findRemoved(Object.keys(shapes), Object.keys(pageShapes))
|
||||
removeShapes(shapesIdsToRemove, whiteboardId)
|
||||
let shapeIdsToReAdd = findRemoved(Object.keys(pageShapes), Object.keys(shapes))
|
||||
shapeIdsToReAdd.forEach(id => {
|
||||
persistShape(pageShapes[id], whiteboardId);
|
||||
})
|
||||
}}
|
||||
|
||||
onRedo={s => {
|
||||
s?.selectedIds?.map(id => {
|
||||
persistShape(s.getShape(id), whiteboardId);
|
||||
});
|
||||
}}
|
||||
|
||||
onChangePage={(app, s, b, a) => {
|
||||
if (curPage?.id !== app.getPage()?.id) setCurPage(app.getPage());
|
||||
if (app.getPage()?.id !== curPageId) {
|
||||
skipToSlide(Number.parseInt(app.getPage()?.id), podId)
|
||||
}
|
||||
}}
|
||||
onCommand={(e, s, g) => {
|
||||
if (s.includes("session:complete:DrawSession")) {
|
||||
Object.entries(rDocument?.current?.pages[e.getPage()?.id]?.shapes)
|
||||
Object.entries(e.state.document.pages[e.getPage()?.id]?.shapes)
|
||||
.filter(([k, s]) => s?.type === 'draw')
|
||||
.forEach(([k, s]) => {
|
||||
if (!e.prevShapes[k] || !k.includes('slide-background')) {
|
||||
if (!e.prevShapes[k] && !k.includes('slide-background')) {
|
||||
persistShape(s, whiteboardId);
|
||||
}
|
||||
});
|
||||
@ -262,39 +231,44 @@ export default function Whiteboard(props) {
|
||||
//remove shapes on origin page
|
||||
removeShapes(e.selectedIds, whiteboardId);
|
||||
//persist shapes for destination page
|
||||
const newWhiteboardId = curPres.pages.find(page => page.num === Number.parseInt(e.getPage()?.id)).id;
|
||||
movedShapes.forEach(s => {
|
||||
persistShape(s, whiteboardId);
|
||||
persistShape(s, newWhiteboardId);
|
||||
});
|
||||
}
|
||||
|
||||
if (s?.includes("session:complete:TransformSingleSession")
|
||||
|| s?.includes("session:complete:TranslateSession")
|
||||
|| s?.includes("updated_shapes")
|
||||
|| s?.includes("session:complete:RotateSession")) {
|
||||
const conditions = [
|
||||
"session:complete:TransformSingleSession", "session:complete:TranslateSession",
|
||||
"session:complete:TranslateSession", "session:complete:RotateSession",
|
||||
"session:complete:HandleSession", "updated_shapes", "duplicate",
|
||||
"stretch", "align", "move", "create", "flip", "toggle", "group",
|
||||
]
|
||||
if (conditions.some(el => s?.includes(el))) {
|
||||
e.selectedIds.forEach(id => {
|
||||
persistShape(e.getShape(id), whiteboardId);
|
||||
//checks to find any bindings assosiated with the selected shapes.
|
||||
//If any, they need to be updated as well.
|
||||
const pageBindings = rDocument?.current?.pages[e.getPage()?.id]?.bindings;
|
||||
const pageBindings = e.state.document.pages[e.getPage()?.id]?.bindings;
|
||||
const boundShapes = [];
|
||||
if (pageBindings) {
|
||||
Object.entries(pageBindings).map(([k,b]) => {
|
||||
if (b.toId.includes(id), whiteboardId) {
|
||||
boundShapes.push(rDocument?.current?.pages[e.getPage()?.id]?.shapes[b.fromId])
|
||||
if (b.toId.includes(id)) {
|
||||
boundShapes.push(e.state.document.pages[e.getPage()?.id]?.shapes[b.fromId])
|
||||
}
|
||||
})
|
||||
}
|
||||
//persist shape(s) that was updated by the client and any shapes bound to it.
|
||||
boundShapes.forEach(bs => persistShape(bs, whiteboardId))
|
||||
|
||||
const children = e.getShape(id).children
|
||||
//also persist children of the selected shape (grouped shapes)
|
||||
children && children.forEach(c => persistShape(e.getShape(c), whiteboardId))
|
||||
});
|
||||
}
|
||||
|
||||
if (s?.includes("session:complete:EraseSession") || s?.includes("delete")) {
|
||||
let shapesIdsToRemove = []
|
||||
shapes.forEach(s => {
|
||||
const ids = e.shapes.map(ss => ss.id);
|
||||
if (!ids.includes(s.id)) shapesIdsToRemove.push(s.id);
|
||||
});
|
||||
const pageShapes = e.state.document.pages[e.getPage()?.id]?.shapes;
|
||||
let shapesIdsToRemove = findRemoved(Object.keys(shapes), Object.keys(pageShapes))
|
||||
removeShapes(shapesIdsToRemove, whiteboardId)
|
||||
}
|
||||
}}
|
||||
|
@ -18,7 +18,6 @@ export default withTracker(({ whiteboardId }) => {
|
||||
const shapes = Service.getShapes(whiteboardId);
|
||||
const assets = Service.getAssets();
|
||||
const curPres = Service.getCurrentPres();
|
||||
const curSlide = Service.getCurSlide();
|
||||
|
||||
return {
|
||||
initDefaultPages: Service.initDefaultPages,
|
||||
@ -27,11 +26,11 @@ export default withTracker(({ whiteboardId }) => {
|
||||
isMultiUserActive: Service.isMultiUserActive,
|
||||
hasMultiUserAccess: Service.hasMultiUserAccess,
|
||||
changeCurrentSlide: Service.changeCurrentSlide,
|
||||
curSlide,
|
||||
shapes: shapes,
|
||||
assets: assets,
|
||||
curPres,
|
||||
removeShapes: Service.removeShapes,
|
||||
zoomSlide: PresentationToolbarService.zoomSlide,
|
||||
skipToSlide: PresentationToolbarService.skipToSlide,
|
||||
};
|
||||
})(WhiteboardContainer);
|
||||
|
@ -1,12 +1,10 @@
|
||||
import Cursor from '/imports/ui/components/cursor/service';
|
||||
import Cursor, { publishCursorUpdate } from '/imports/ui/components/cursor/service';
|
||||
import Users from '/imports/api/users';
|
||||
import { publishCursorUpdate } from '/imports/ui/components/cursor/service';
|
||||
|
||||
const getCurrentCursors = (whiteboardId) => {
|
||||
const selector = { whiteboardId };
|
||||
const filter = {};
|
||||
const cursors = Cursor.find(selector, filter).fetch();
|
||||
//console.log('CURSORS!!!!!!!!! : ',cursors);
|
||||
return cursors.map(cursor => {
|
||||
const { userId } = cursor;
|
||||
const user = Users.findOne({ userId }, { fields: { name: 1, presenter: 1, userId: 1, role: 1 } });
|
||||
|
@ -1,6 +1,5 @@
|
||||
import Users from '/imports/api/users';
|
||||
import Captions from "/imports/api/captions";
|
||||
import Meetings from "/imports/api/meetings";
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import WhiteboardMultiUser from '/imports/api/whiteboard-multi-user';
|
||||
import addAnnotationQuery from '/imports/api/annotations/addAnnotation';
|
||||
@ -8,7 +7,6 @@ import { Slides } from '/imports/api/slides';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import PresentationService from '/imports/ui/components/presentation/service';
|
||||
import logger from '/imports/startup/client/logger';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
const Annotations = new Mongo.Collection(null);
|
||||
|
||||
@ -17,7 +15,6 @@ const ANNOTATION_CONFIG = Meteor.settings.public.whiteboard.annotations;
|
||||
const DRAW_START = ANNOTATION_CONFIG.status.start;
|
||||
const DRAW_UPDATE = ANNOTATION_CONFIG.status.update;
|
||||
const DRAW_END = ANNOTATION_CONFIG.status.end;
|
||||
const ANNOTATION_TYPE_PENCIL = "pencil";
|
||||
|
||||
let annotationsStreamListener = null;
|
||||
|
||||
@ -30,63 +27,6 @@ function clearFakeAnnotations() {
|
||||
Annotations.remove({ id: /-fake/g });
|
||||
}
|
||||
|
||||
function handleAddedLiveSyncPreviewAnnotation({
|
||||
meetingId, whiteboardId, userId, annotation,
|
||||
}) {
|
||||
const isOwn = Auth.meetingID === meetingId && Auth.userID === userId;
|
||||
const query = addAnnotationQuery(meetingId, whiteboardId, userId, annotation);
|
||||
|
||||
if (!isOwn) {
|
||||
Annotations.upsert(query.selector, query.modifier);
|
||||
return;
|
||||
}
|
||||
|
||||
const fakeAnnotation = Annotations.findOne({ id: `${annotation.id}-fake` });
|
||||
let fakePoints;
|
||||
|
||||
if (fakeAnnotation) {
|
||||
fakePoints = fakeAnnotation.annotationInfo.points;
|
||||
const { points: lastPoints } = annotation.annotationInfo;
|
||||
|
||||
if (annotation.annotationType !== 'pencil') {
|
||||
Annotations.update(fakeAnnotation._id, {
|
||||
$set: {
|
||||
position: annotation.position,
|
||||
'annotationInfo.color': isEqual(fakePoints, lastPoints) || annotation.status === DRAW_END
|
||||
? annotation.annotationInfo.color : fakeAnnotation.annotationInfo.color,
|
||||
},
|
||||
$inc: { version: 1 }, // TODO: Remove all this version stuff
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Annotations.upsert(query.selector, query.modifier, (err) => {
|
||||
if (err) {
|
||||
logger.error({
|
||||
logCode: 'whiteboard_annotation_upsert_error',
|
||||
extraInfo: { error: err },
|
||||
}, 'Error on adding an annotation');
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove fake annotation for pencil on draw end
|
||||
if (annotation.status === DRAW_END) {
|
||||
Annotations.remove({ id: `${annotation.id}-fake` });
|
||||
return;
|
||||
}
|
||||
|
||||
if (annotation.status === DRAW_START) {
|
||||
Annotations.update(fakeAnnotation._id, {
|
||||
$set: {
|
||||
position: annotation.position - 1,
|
||||
},
|
||||
$inc: { version: 1 }, // TODO: Remove all this version stuff
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleAddedAnnotation({
|
||||
meetingId,
|
||||
whiteboardId,
|
||||
@ -156,20 +96,8 @@ export function initAnnotationsStreamListener() {
|
||||
|
||||
annotationsStreamListener.on("removed", handleRemovedAnnotation);
|
||||
|
||||
// <<<<<<< HEAD
|
||||
// annotationsStreamListener.on('added', ({ annotations }) => {
|
||||
// annotations.forEach((annotation) => {
|
||||
// const tool = annotation.annotation.annotationType;
|
||||
// if (tool === ANNOTATION_TYPE_TEXT) {
|
||||
// handleAddedLiveSyncPreviewAnnotation(annotation);
|
||||
// } else {
|
||||
// handleAddedAnnotation(annotation);
|
||||
// }
|
||||
// });
|
||||
// =======
|
||||
annotationsStreamListener.on("added", ({ annotations }) => {
|
||||
annotations.forEach((annotation) => handleAddedAnnotation(annotation));
|
||||
// >>>>>>> embed Tldraw into BBB client
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -364,7 +292,7 @@ const persistShape = (shape, whiteboardId) => {
|
||||
id: shape.id,
|
||||
annotationInfo: shape,
|
||||
wbId: whiteboardId,
|
||||
userId: Auth.userID,
|
||||
userId: shape.userId ? shape.userId : Auth.userID,
|
||||
};
|
||||
|
||||
sendAnnotation(annotation);
|
||||
@ -384,11 +312,16 @@ const getShapes = (whiteboardId) => {
|
||||
whiteboardId,
|
||||
},
|
||||
{
|
||||
fields: { annotationInfo: 1, },
|
||||
fields: { annotationInfo: 1, userId: 1, },
|
||||
},
|
||||
).fetch();
|
||||
|
||||
let result = annotations.map(a => a.annotationInfo);
|
||||
let result = {};
|
||||
|
||||
annotations.forEach((annotation) => {
|
||||
annotation.annotationInfo.userId = annotation.userId;
|
||||
result[annotation.annotationInfo.id] = annotation.annotationInfo;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
@ -397,11 +330,6 @@ const getCurrentPres = () => {
|
||||
return PresentationService.getCurrentPresentation(podId);
|
||||
}
|
||||
|
||||
const getCurSlide = () => {
|
||||
let m = Meetings.findOne({ meetingId: Auth.meetingID });
|
||||
return m;
|
||||
}
|
||||
|
||||
const getAssets = () => {
|
||||
// temporary storage for assets
|
||||
let a = Captions.find().fetch().filter(s => s.src);
|
||||
|
13
bigbluebutton-html5/package-lock.json
generated
13
bigbluebutton-html5/package-lock.json
generated
@ -6325,6 +6325,11 @@
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"sdp": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sdp/-/sdp-3.0.3.tgz",
|
||||
"integrity": "sha512-8EkfckS+XZQaPLyChu4ey7PghrdcraCVNpJe2Gfdi2ON1ylQ7OasuKX+b37R9slnRChwIAiQgt+oj8xXGD8x+A=="
|
||||
},
|
||||
"sdp-transform": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.7.0.tgz",
|
||||
@ -7019,6 +7024,14 @@
|
||||
"resolved": "https://registry.npmjs.org/wasm-check/-/wasm-check-2.0.3.tgz",
|
||||
"integrity": "sha512-UbZqpDMO4TZskoVKDH3B9NqY+yJllDJX8I9lUU4nuQjBGeU57jCjjgCslP3r8xiE+yf5GTIfeGvznvubgCdbhw=="
|
||||
},
|
||||
"webrtc-adapter": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-8.1.1.tgz",
|
||||
"integrity": "sha512-1yXevP7TeZGmklEXkvQVrZp3fOSJlLeXNGCA7NovQokxgP3/e2T3EVGL0eKU87S9vKppWjvRWqnJeSANEspOBg==",
|
||||
"requires": {
|
||||
"sdp": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
@ -88,6 +88,7 @@
|
||||
"tippy.js": "^5.1.3",
|
||||
"use-context-selector": "^1.3.7",
|
||||
"wasm-check": "^2.0.3",
|
||||
"webrtc-adapter": "^8.1.1",
|
||||
"winston": "^3.7.2",
|
||||
"yaml": "^1.7.2"
|
||||
},
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user