diff --git a/apps/client/src/widgets/type_widgets/canvas.ts b/apps/client/src/widgets/type_widgets/canvas.ts index 427c1697d..9a0d6e34f 100644 --- a/apps/client/src/widgets/type_widgets/canvas.ts +++ b/apps/client/src/widgets/type_widgets/canvas.ts @@ -1,9 +1,8 @@ import TypeWidget from "./type_widget.js"; -import utils from "../../services/utils.js"; import server from "../../services/server.js"; import type FNote from "../../entities/fnote.js"; import options from "../../services/options.js"; -import type { AppState, BinaryFileData, ExcalidrawImperativeAPI, LibraryItem, SceneData } from "@excalidraw/excalidraw/types"; +import type { AppState, BinaryFileData, LibraryItem } from "@excalidraw/excalidraw/types"; import type { ExcalidrawElement, Theme } from "@excalidraw/excalidraw/element/types"; import type Canvas from "./canvas_el.js"; @@ -109,8 +108,6 @@ export default class ExcalidrawTypeWidget extends TypeWidget { private librarycache: LibraryItem[]; private attachmentMetadata: AttachmentMetadata[]; private themeStyle!: Theme; - private excalidrawApi!: ExcalidrawImperativeAPI; - private excalidrawWrapperRef!: React.RefObject; private $render!: JQuery; private reactHandlers!: JQuery; @@ -209,11 +206,15 @@ export default class ExcalidrawTypeWidget extends TypeWidget { * called to populate the widget container with the note content */ async doRefresh(note: FNote) { + if (!this.canvasInstance) { + await this.#init(); + } + // see if the note changed, since we do not get a new class for a new note const noteChanged = this.currentNoteId !== note.noteId; if (noteChanged) { // reset the scene to omit unnecessary onchange handler - this.canvasInstance?.resetSceneVersion(); + this.canvasInstance.resetSceneVersion(); } this.currentNoteId = note.noteId; @@ -221,10 +222,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget { const blob = await note.getBlob(); // before we load content into excalidraw, make sure excalidraw has loaded - while (!this.canvasInstance?.excalidrawApi) { - console.log("excalidrawApi not yet loaded, sleep 200ms..."); - await utils.sleep(200); - } + await this.canvasInstance.waitForApiToBecomeAvailable(); /** * new and empty note - make sure that canvas is empty. @@ -233,15 +231,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget { * newly instantiated? */ if (!blob?.content?.trim()) { - const sceneData: SceneData = { - elements: [], - appState: { - theme: this.themeStyle - } - }; - - // TODO: Props mismatch. - this.canvasInstance.excalidrawApi.updateScene(sceneData as any); + this.canvasInstance.resetScene(this.themeStyle); } else if (blob.content) { let content: CanvasContent; @@ -285,7 +275,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget { const metadata = results.map((result) => result.metadata); // Update the library and save to independent variables - this.canvasInstance.excalidrawApi.updateLibrary({ libraryItems, merge: false }); + this.canvasInstance.updateLibrary(libraryItems); // save state of library to compare it to the new state later. this.librarycache = libraryItems; @@ -313,12 +303,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget { // this.libraryChanged is unset in dataSaved() // there's no separate method to get library items, so have to abuse this one - const libraryItems = await this.canvasInstance.excalidrawApi.updateLibrary({ - libraryItems() { - return []; - }, - merge: true - }); + const libraryItems = await this.canvasInstance.getLibraryItems(); // excalidraw saves the library as a own state. the items are saved to libraryItems. then we compare the library right now with a libraryitemcache. The cache is filled when we first load the Library into the note. //We need the cache to delete old attachments later in the server. diff --git a/apps/client/src/widgets/type_widgets/canvas_el.ts b/apps/client/src/widgets/type_widgets/canvas_el.ts index a6e611ac4..80d7c5fb8 100644 --- a/apps/client/src/widgets/type_widgets/canvas_el.ts +++ b/apps/client/src/widgets/type_widgets/canvas_el.ts @@ -1,19 +1,19 @@ import "@excalidraw/excalidraw/index.css"; import { Excalidraw, getSceneVersion, exportToSvg } from "@excalidraw/excalidraw"; -import { createElement, createRef, Fragment, RefObject, render, useEffect, useState } from "preact/compat"; -import { AppState, BinaryFileData, ExcalidrawImperativeAPI, ExcalidrawProps, SceneData } from "@excalidraw/excalidraw/types"; -import type { ComponentType, VNode } from "preact"; +import { createElement, render } from "preact/compat"; +import { AppState, BinaryFileData, ExcalidrawImperativeAPI, ExcalidrawProps, LibraryItem, SceneData } from "@excalidraw/excalidraw/types"; +import type { ComponentType } from "preact"; +import utils from "../../services/utils"; +import { Theme } from "@excalidraw/excalidraw/element/types"; -/** -1 indicates that it is fresh. excalidraw scene version is always >0 */ +/** Indicates that it is fresh. excalidraw scene version is always >0 */ const SCENE_VERSION_INITIAL = -1; -/** -2 indicates error */ -const SCENE_VERSION_ERROR = -2; export default class Canvas { private currentSceneVersion: number; private opts: ExcalidrawProps; - excalidrawApi!: ExcalidrawImperativeAPI; + private excalidrawApi!: ExcalidrawImperativeAPI; constructor(opts: ExcalidrawProps) { this.opts = opts; @@ -29,6 +29,13 @@ export default class Canvas { }), targetEl); } + async waitForApiToBecomeAvailable() { + while (!this.excalidrawApi) { + console.log("excalidrawApi not yet loaded, sleep 200ms..."); + await utils.sleep(200); + } + } + private createCanvasElement(opts: ExcalidrawProps) { return createElement("div", { className: "excalidraw-wrapper", }, createElement(Excalidraw as ComponentType, opts) @@ -68,6 +75,15 @@ export default class Canvas { return this.currentSceneVersion === SCENE_VERSION_INITIAL; } + resetScene(theme: Theme) { + this.excalidrawApi.updateScene({ + elements: [], + appState: { + theme + } + }); + } + loadData(content: any, theme: any) { const { elements, files } = content; const appState: Partial = content.appState ?? {}; @@ -143,4 +159,17 @@ export default class Canvas { } } + async getLibraryItems() { + return this.excalidrawApi.updateLibrary({ + libraryItems() { + return []; + }, + merge: true + }); + } + + async updateLibrary(libraryItems: LibraryItem[]) { + this.excalidrawApi.updateLibrary({ libraryItems, merge: false }); + } + }