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