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:
germanocaumo 2022-05-18 19:35:42 +00:00
parent e9208cad16
commit 19d4caec99
4 changed files with 208 additions and 271 deletions

View File

@ -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()}

View File

@ -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}

View File

@ -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)
}
}}

View File

@ -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);