2022-04-05 22:49:13 +08:00
|
|
|
import * as React from "react";
|
2022-04-26 21:55:05 +08:00
|
|
|
import _ from "lodash";
|
2022-04-05 22:49:13 +08:00
|
|
|
import Cursors from "./cursors/container";
|
|
|
|
import { TldrawApp, Tldraw } from "@tldraw/tldraw";
|
2022-04-26 21:55:05 +08:00
|
|
|
import {
|
|
|
|
ColorStyle,
|
|
|
|
DashStyle,
|
|
|
|
SizeStyle,
|
|
|
|
TDDocument,
|
|
|
|
TDShapeType,
|
|
|
|
} from "@tldraw/tldraw";
|
2022-04-11 00:31:12 +08:00
|
|
|
import { Renderer } from "@tldraw/core";
|
|
|
|
|
|
|
|
function usePrevious(value) {
|
|
|
|
const ref = React.useRef();
|
|
|
|
React.useEffect(() => {
|
|
|
|
ref.current = value;
|
|
|
|
}, [value]);
|
|
|
|
return ref.current;
|
|
|
|
}
|
2022-04-05 22:49:13 +08:00
|
|
|
|
|
|
|
const findRemoved = (A, B) => {
|
|
|
|
return A.filter((a) => {
|
|
|
|
return !B.includes(a);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
export default function Whiteboard(props) {
|
|
|
|
const {
|
|
|
|
isPresenter,
|
|
|
|
removeShape,
|
|
|
|
initDefaultPages,
|
|
|
|
meetingId,
|
|
|
|
persistShape,
|
|
|
|
persistAsset,
|
|
|
|
shapes,
|
|
|
|
assets,
|
|
|
|
currentUser,
|
|
|
|
publishCursorUpdate,
|
2022-04-26 21:55:05 +08:00
|
|
|
curPres,
|
|
|
|
curSlide,
|
|
|
|
changeCurrentSlide,
|
2022-04-05 22:49:13 +08:00
|
|
|
} = props;
|
2022-04-26 21:55:05 +08:00
|
|
|
console.log('curPres : ', curPres)
|
|
|
|
const { pages, pageStates } = initDefaultPages(curPres?.pages.length || 1);
|
2022-04-05 22:49:13 +08:00
|
|
|
const rDocument = React.useRef({
|
|
|
|
name: "test",
|
|
|
|
version: TldrawApp.version,
|
|
|
|
id: `WB-${meetingId}`,
|
|
|
|
pages,
|
|
|
|
pageStates,
|
|
|
|
bindings: {},
|
|
|
|
assets,
|
|
|
|
});
|
|
|
|
const [doc, setDoc] = React.useState(rDocument.current);
|
2022-04-26 21:55:05 +08:00
|
|
|
const [curPage, setCurPage] = React.useState({ id: "1" });
|
2022-04-11 00:31:12 +08:00
|
|
|
const [ass, setAss] = React.useState(assets);
|
2022-04-26 21:55:05 +08:00
|
|
|
const [command, setCommand] = React.useState("");
|
|
|
|
const [selectedIds, setSelectedIds] = React.useState([]);
|
2022-04-11 00:31:12 +08:00
|
|
|
const [tldrawAPI, setTLDrawAPI] = React.useState(null);
|
|
|
|
const prevShapes = usePrevious(shapes);
|
2022-04-26 21:55:05 +08:00
|
|
|
const prevPage = usePrevious(curPage);
|
2022-04-11 00:31:12 +08:00
|
|
|
|
2022-04-05 22:49:13 +08:00
|
|
|
const handleChange = React.useCallback((state) => {
|
|
|
|
rDocument.current = state.document;
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
React.useMemo(() => {
|
|
|
|
const currentDoc = rDocument.current;
|
|
|
|
const propShapes = Object.entries(shapes || {})?.map(([k, v]) => v.id);
|
2022-04-11 00:31:12 +08:00
|
|
|
|
2022-04-26 21:55:05 +08:00
|
|
|
if (!curPage && tldrawAPI) {
|
|
|
|
tldrawAPI.getPage();
|
2022-04-05 22:49:13 +08:00
|
|
|
}
|
2022-04-11 00:31:12 +08:00
|
|
|
|
2022-04-05 22:49:13 +08:00
|
|
|
const next = { ...currentDoc };
|
|
|
|
|
|
|
|
next.assets = { ...assets };
|
|
|
|
|
2022-04-26 21:55:05 +08:00
|
|
|
const pShapes = Object.entries(shapes || {})?.map(([k, v]) => v.id);
|
|
|
|
shapes?.forEach((s) => {
|
|
|
|
try {
|
|
|
|
Object.keys(next.pages[s.parentId].shapes).map((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 (curPres?.pages.length) {
|
|
|
|
curPres.pages.map((p, i) => {
|
|
|
|
next.assets[`slide-background-asset-${i}`] = {
|
|
|
|
id: `slide-background-asset-${i}`,
|
|
|
|
size: [2560 / 3.5, 1440 / 3.5],
|
|
|
|
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}`,
|
|
|
|
childIndex: 1,
|
|
|
|
point: [50, 60],
|
|
|
|
size: [2560 / 3.5, 1440 / 3.5],
|
|
|
|
style: {
|
|
|
|
dash: DashStyle.Draw,
|
|
|
|
size: SizeStyle.Medium,
|
|
|
|
color: ColorStyle.Blue,
|
2022-04-05 22:49:13 +08:00
|
|
|
},
|
|
|
|
};
|
2022-04-26 21:55:05 +08:00
|
|
|
} catch (err) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// setDoc(next);
|
|
|
|
});
|
|
|
|
}
|
2022-04-05 22:49:13 +08:00
|
|
|
|
|
|
|
rDocument.current = next;
|
|
|
|
|
2022-04-26 21:55:05 +08:00
|
|
|
const pageID = tldrawAPI?.getPage()?.id;
|
|
|
|
if (next.pageStates[pageID]?.selectedIds.length > 0) {
|
2022-04-05 22:49:13 +08:00
|
|
|
// if a selected id is not in the list of shapes remove it from list
|
2022-04-26 21:55:05 +08:00
|
|
|
next.pageStates[pageID]?.selectedIds.map((k) => {
|
|
|
|
if (!next.pages[pageID].shapes[k]) {
|
|
|
|
next.pageStates[pageID].selectedIds =
|
|
|
|
next.pageStates[pageID].selectedIds.filter(
|
|
|
|
(id) => id !== k
|
|
|
|
);
|
2022-04-05 22:49:13 +08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
setDoc(next);
|
2022-04-26 21:55:05 +08:00
|
|
|
//
|
|
|
|
if (
|
|
|
|
tldrawAPI &&
|
|
|
|
!_.isEqual(shapes, prevShapes) &&
|
|
|
|
!_.isEqual(assets, ass)
|
|
|
|
) {
|
2022-04-11 00:31:12 +08:00
|
|
|
setAss(assets);
|
2022-04-26 21:55:05 +08:00
|
|
|
tldrawAPI?.replacePageContent(next?.pages[pageID]?.shapes, {}, assets);
|
2022-04-11 00:31:12 +08:00
|
|
|
}
|
|
|
|
|
2022-04-26 21:55:05 +08:00
|
|
|
if (tldrawAPI && !_.isEqual(shapes, prevShapes) && !_.isEqual(assets, ass)) {
|
|
|
|
tldrawAPI?.replacePageContent(next?.pages[pageID]?.shapes, {}, assets);
|
|
|
|
}
|
|
|
|
|
|
|
|
// setDoc(next);
|
|
|
|
|
|
|
|
}, [assets, shapes, curPres, tldrawAPI]);
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
isPresenter && curPage && changeCurrentSlide(curPage?.id);
|
|
|
|
}, [curPage]);
|
2022-04-11 00:31:12 +08:00
|
|
|
|
2022-04-26 21:55:05 +08:00
|
|
|
React.useEffect(() => {
|
|
|
|
tldrawAPI &&
|
|
|
|
!isPresenter &&
|
|
|
|
curSlide?.activeSlide &&
|
|
|
|
tldrawAPI.changePage(curSlide?.activeSlide);
|
|
|
|
}, [curSlide]);
|
2022-04-05 22:49:13 +08:00
|
|
|
|
|
|
|
return (
|
2022-04-26 21:55:05 +08:00
|
|
|
<>
|
|
|
|
<Cursors
|
|
|
|
tldrawAPI={tldrawAPI}
|
|
|
|
currentUser={currentUser}
|
|
|
|
publishCursorUpdate={publishCursorUpdate}
|
|
|
|
>
|
|
|
|
<Tldraw
|
|
|
|
document={doc}
|
|
|
|
disableAssets={false}
|
|
|
|
onChangePage={(app, s, b, a) => {
|
|
|
|
setCurPage(app.getPage());
|
|
|
|
}}
|
|
|
|
onCommand={(e, s, g) => {
|
2022-04-05 22:49:13 +08:00
|
|
|
|
2022-04-26 21:55:05 +08:00
|
|
|
console.log('ON COMMAND ', s)
|
|
|
|
if (s?.includes("session:complete:EraseSession")) {
|
|
|
|
const propShapes = Object.entries(shapes || {})?.map(
|
|
|
|
([k, v]) => v.id
|
|
|
|
);
|
|
|
|
const localShapes = Object.entries(e.getShapes())?.map(
|
|
|
|
([k, v]) => v.id
|
|
|
|
);
|
|
|
|
const removedShapes = findRemoved(propShapes, localShapes);
|
|
|
|
if (removedShapes && removedShapes.length > 0) {
|
|
|
|
removedShapes.forEach((s) => removeShape(s));
|
|
|
|
}
|
|
|
|
}
|
2022-04-05 22:49:13 +08:00
|
|
|
|
2022-04-26 21:55:05 +08:00
|
|
|
if (s === "delete") {
|
|
|
|
if (e.getShapes()) {
|
|
|
|
const validIDS = e.getShapes().map(s => s.id);
|
|
|
|
const remove = [];
|
|
|
|
Object.entries(shapes).map(([k, v]) => {
|
|
|
|
if (v.parentId === `${tldrawAPI?.getPage()?.id}` && !validIDS.includes(v?.id)) {
|
|
|
|
remove.push(v);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
console.log('REMOVE', remove)
|
|
|
|
remove?.forEach((s) => removeShape(s.id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
onMount={(app) => {
|
|
|
|
setTLDrawAPI(app);
|
|
|
|
}}
|
|
|
|
onChange={handleChange}
|
|
|
|
onPersist={(e) => {
|
|
|
|
///////////// handle assets /////////////////////////
|
|
|
|
e?.assets?.forEach((a) => {
|
|
|
|
persistAsset(a);
|
|
|
|
});
|
|
|
|
|
|
|
|
///////////// handle shapes /////////////////////////
|
|
|
|
if (e?.selectedIds.length < 1) {
|
|
|
|
Object.entries(e.getShapes())?.forEach(([k, v]) => {
|
|
|
|
if (v.type === "draw") persistShape(v);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
Object.entries(e.getShapes())?.forEach(([k, v]) => {
|
|
|
|
persistShape(v);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
showPages={false || isPresenter}
|
|
|
|
showUI={true || isPresenter}
|
|
|
|
showMenu={false}
|
|
|
|
showMultiplayerMenu={false}
|
|
|
|
// readOnly={!isPresenter}
|
|
|
|
/>
|
|
|
|
</Cursors>
|
|
|
|
</>
|
2022-04-05 22:49:13 +08:00
|
|
|
);
|
|
|
|
}
|