refactor tldraw doc state to not crash on fast updates +
change slide going and controlled by akka messages undo is working locally
This commit is contained in:
parent
e9208cad16
commit
19d4caec99
@ -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,44 @@ 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) : ''})`;
|
||||
|
||||
console.log("aeeeeeeeeeeeee: ", this.props?.tldrawAPI);
|
||||
|
||||
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 +290,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 +309,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 +319,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 +333,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 +350,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}
|
||||
|
@ -43,10 +43,14 @@ export default function Whiteboard(props) {
|
||||
whiteboardId,
|
||||
podId,
|
||||
zoomSlide,
|
||||
skipToSlide,
|
||||
slidePosition,
|
||||
curPageId,
|
||||
svgUri,
|
||||
} = props;
|
||||
|
||||
if (!curPres || !curPageId) return null;
|
||||
|
||||
const { pages, pageStates } = initDefaultPages(curPres?.pages.length || 1);
|
||||
const rDocument = React.useRef({
|
||||
name: "test",
|
||||
@ -57,7 +61,7 @@ export default function Whiteboard(props) {
|
||||
bindings: {},
|
||||
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 [command, setCommand] = React.useState("");
|
||||
@ -67,128 +71,76 @@ export default function Whiteboard(props) {
|
||||
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 changed = false;
|
||||
|
||||
if (next.pageStates[curPageId] && !_.isEqual(prevShapes, shapes)) {
|
||||
// mergeDocument loses bindings and history, save it
|
||||
pageBindings = tldrawAPI?.getPage(curPageId)?.bindings;
|
||||
history = tldrawAPI?.history
|
||||
|
||||
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];
|
||||
}
|
||||
});
|
||||
changed = true;
|
||||
}
|
||||
|
||||
next.pages[s.parentId] = {
|
||||
...next.pages[s.parentId],
|
||||
shapes: {
|
||||
...next.pages[s.parentId].shapes,
|
||||
[s.id]: { ...s },
|
||||
},
|
||||
};
|
||||
|
||||
} catch (err) {
|
||||
if (!isPresenter && !_.isEqual(slidePosition, prevSlidePosition)) {
|
||||
tldrawAPI?.setCamera([slidePosition.xCamera, slidePosition.yCamera], slidePosition.zoom);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
tldrawAPI?.mergeDocument(next);
|
||||
if (tldrawAPI && history) tldrawAPI.history = history;
|
||||
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]);
|
||||
|
||||
React.useEffect(() => {
|
||||
isPresenter && curPage && changeCurrentSlide(curPage?.id);
|
||||
}, [curPage]);
|
||||
|
||||
return currentDoc;
|
||||
}, [assets, shapes, curPres, tldrawAPI, curPageId, slidePosition]);
|
||||
|
||||
React.useEffect(() => {
|
||||
//console.log("changing slide!! ", curPageId, tldrawAPI)
|
||||
tldrawAPI &&
|
||||
!isPresenter &&
|
||||
curSlide?.activeSlide &&
|
||||
tldrawAPI.changePage(curSlide?.activeSlide);
|
||||
}, [curSlide]);
|
||||
curPageId &&
|
||||
tldrawAPI.changePage(curPageId);
|
||||
}, [curPageId]);
|
||||
|
||||
const hasWBAccess = props?.hasMultiUserAccess(props.whiteboardId, props.currentUser.userId);
|
||||
|
||||
@ -202,18 +154,17 @@ 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}
|
||||
@ -222,27 +173,32 @@ export default function Whiteboard(props) {
|
||||
showMenu={false}
|
||||
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={s => {
|
||||
s?.selectedIds?.map(id => {
|
||||
onRedo={(e, s) => {
|
||||
e?.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,25 +218,27 @@ 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")) {
|
||||
|| s?.includes("session:complete:RotateSession")
|
||||
|| s?.includes("session:complete:HandleSession")) {
|
||||
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])
|
||||
boundShapes.push(e.state.document.pages[e.getPage()?.id]?.shapes[b.fromId])
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -290,11 +248,8 @@ export default function Whiteboard(props) {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}}
|
||||
|
@ -388,7 +388,11 @@ const getShapes = (whiteboardId) => {
|
||||
},
|
||||
).fetch();
|
||||
|
||||
let result = annotations.map(a => a.annotationInfo);
|
||||
let result = {};
|
||||
|
||||
annotations.forEach((annotation) => {
|
||||
result[annotation.annotationInfo.id] = annotation.annotationInfo;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
@ -397,11 +401,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);
|
||||
|
Loading…
Reference in New Issue
Block a user