From e23cc424f840ce22417139e0ac4877fd9ce890f6 Mon Sep 17 00:00:00 2001 From: MassiveBox Date: Wed, 9 Apr 2025 22:53:40 +0200 Subject: [PATCH] Code quality improvements --- src/editor.ts | 172 +++++++++++++++++++++++++++++++++++++++++++++++ src/editorTab.ts | 126 ---------------------------------- src/index.ts | 11 ++- 3 files changed, 176 insertions(+), 133 deletions(-) create mode 100644 src/editor.ts delete mode 100644 src/editorTab.ts diff --git a/src/editor.ts b/src/editor.ts new file mode 100644 index 0000000..c744a2f --- /dev/null +++ b/src/editor.ts @@ -0,0 +1,172 @@ +import {MaterialIconProvider} from "@js-draw/material-icons"; +import {getFile, saveFile, uploadAsset} from "@/file"; +import {DATA_PATH, JSON_MIME, SVG_MIME, TOOLBAR_PATH} from "@/const"; +import {IDsToAssetPath} from "@/helper"; +import Editor, {BaseWidget, EditorEventType} from "js-draw"; +import {Dialog, Plugin, openTab, getFrontend} from "siyuan"; +import {replaceSyncID} from "@/protyle"; +import {removeFile} from "@/api"; + +export class PluginEditor { + + private readonly element: HTMLElement; + private readonly editor: Editor; + + private readonly fileID: string; + private syncID: string; + private readonly initialSyncID: string; + + getElement(): HTMLElement { return this.element; } + getEditor(): Editor { return this.editor; } + getFileID(): string { return this.fileID; } + getSyncID(): string { return this.syncID; } + getInitialSyncID(): string { return this.initialSyncID; } + + constructor(fileID: string, initialSyncID: string) { + + this.element = document.createElement("div"); + this.element.style.height = '100%'; + this.editor = new Editor(this.element, { + iconProvider: new MaterialIconProvider(), + }); + + this.fileID = fileID; + this.initialSyncID = initialSyncID; + this.syncID = initialSyncID; + + this.genToolbar() + + // restore drawing + getFile(DATA_PATH +IDsToAssetPath(fileID, initialSyncID)).then(svg => { + if(svg != null) { + this.editor.loadFromSVG(svg); + } + }); + + this.editor.dispatch(this.editor.setBackgroundStyle({ autoresize: true }), false); + this.editor.getRootElement().style.height = '100%'; + + } + + private async genToolbar() { + + const toolbar = this.editor.addToolbar(); + + // restore toolbar state + const toolbarState = await getFile(TOOLBAR_PATH); + if (toolbarState != null) { + toolbar.deserializeState(toolbarState); + } + + // save button + const saveButton = toolbar.addSaveButton(async () => { + await this.saveCallback(saveButton); + }); + + // save toolbar config on tool change (toolbar state is not saved in SVGs!) + this.editor.notifier.on(EditorEventType.ToolUpdated, () => { + saveFile(TOOLBAR_PATH, JSON_MIME, toolbar.serializeState()); + }); + + } + + private async saveCallback(saveButton: BaseWidget) { + + const svgElem = this.editor.toSVG(); + let newSyncID: string; + const oldSyncID = this.syncID; + + try { + newSyncID = (await uploadAsset(this.fileID, SVG_MIME, svgElem.outerHTML)).syncID; + if(newSyncID != oldSyncID) { + const changed = await replaceSyncID(this.fileID, oldSyncID, newSyncID); + if(!changed) { + alert( + "Error replacing old sync ID with new one! You may need to manually replace the file path." + + "\nTry saving the drawing again. This is a bug, please open an issue as soon as you can." + + "\nIf your document doesn't show the drawing, you can recover it from the SiYuan workspace directory." + ); + return; + } + await removeFile(DATA_PATH + IDsToAssetPath(this.fileID, oldSyncID)); + } + saveButton.setDisabled(true); + setTimeout(() => { // @todo improve save button feedback + saveButton.setDisabled(false); + }, 500); + } catch (error) { + alert("Error saving drawing! Enter developer mode to find the error, and a copy of the current status."); + console.error(error); + console.log("Couldn't save SVG: ", svgElem.outerHTML) + return; + } + + this.syncID = newSyncID; + + } + +} + +export class EditorManager { + + private editor: PluginEditor + + constructor(editor: PluginEditor) { + this.editor = editor; + } + + static registerTab(p: Plugin) { + p.addTab({ + 'type': "whiteboard", + init() { + const fileID = this.data.fileID; + const initialSyncID = this.data.initialSyncID; + if (fileID == null || initialSyncID == null) { + alert("File or Sync ID and path missing - couldn't open file.") + return; + } + const editor = new PluginEditor(fileID, initialSyncID); + this.element.appendChild(editor.getElement()); + } + }); + } + + toTab(p: Plugin) { + for(const tab of p.getOpenedTab()["whiteboard"]) { + if(tab.data.fileID == this.editor.getFileID()) { + alert("File is already open in another editor tab!"); + return; + } + } + openTab({ + app: p.app, + custom: { + title: 'Drawing', + icon: 'iconDraw', + id: "siyuan-jsdraw-pluginwhiteboard", + data: { + fileID: this.editor.getFileID(), + initialSyncID: this.editor.getInitialSyncID() + } + } + }); + } + + toDialog() { + const dialog = new Dialog({ + width: "100vw", + height: "100vh", + content: `
`, + }); + dialog.element.querySelector("#DrawingPanel").appendChild(this.editor.getElement()); + } + + open(p: Plugin) { + if(getFrontend() != "mobile") { + this.toTab(p); + } else { + this.toDialog(); + } + } + +} \ No newline at end of file diff --git a/src/editorTab.ts b/src/editorTab.ts deleted file mode 100644 index 001320d..0000000 --- a/src/editorTab.ts +++ /dev/null @@ -1,126 +0,0 @@ -import {Dialog, getFrontend, ITabModel, openTab, Plugin} from "siyuan" -import Editor, {BaseWidget, EditorEventType} from "js-draw"; -import { MaterialIconProvider } from '@js-draw/material-icons'; -import 'js-draw/styles'; -import {getFile, saveFile, uploadAsset} from "@/file"; -import {DATA_PATH, JSON_MIME, SVG_MIME, TOOLBAR_PATH} from "@/const"; -import {replaceSyncID} from "@/protyle"; -import {IDsToAssetPath} from "@/helper"; -import {removeFile} from "@/api"; - -export function openEditorTab(p: Plugin, fileID: string, initialSyncID: string) { - if(getFrontend() == "mobile") { - const dialog = new Dialog({ - width: "100vw", - height: "100vh", - content: `
`, - }); - createEditor(dialog.element.querySelector("#DrawingPanel"), fileID, initialSyncID); - return; - } - for(const tab of p.getOpenedTab()["whiteboard"]) { - if(tab.data.fileID == fileID) { - alert("File is already open in another editor tab!"); - return; - } - } - openTab({ - app: p.app, - custom: { - title: 'Drawing', - icon: 'iconDraw', - id: "siyuan-jsdraw-pluginwhiteboard", - data: { - fileID: fileID, - initialSyncID: initialSyncID - } - } - }); -} - -async function saveCallback(editor: Editor, fileID: string, oldSyncID: string, saveButton: BaseWidget): Promise { - - const svgElem = editor.toSVG(); - let newSyncID; - - try { - newSyncID = (await uploadAsset(fileID, SVG_MIME, svgElem.outerHTML)).syncID; - if(newSyncID != oldSyncID) { - const changed = await replaceSyncID(fileID, oldSyncID, newSyncID); - if(!changed) { - alert( - "Error replacing old sync ID with new one! You may need to manually replace the file path." + - "\nTry saving the drawing again. This is a bug, please open an issue as soon as you can." + - "\nIf your document doesn't show the drawing, you can recover it from the SiYuan workspace directory." - ); - return oldSyncID; - } - await removeFile(DATA_PATH + IDsToAssetPath(fileID, oldSyncID)); - } - saveButton.setDisabled(true); - setTimeout(() => { // @todo improve save button feedback - saveButton.setDisabled(false); - }, 500); - } catch (error) { - alert("Error saving drawing! Enter developer mode to find the error, and a copy of the current status."); - console.error(error); - console.log("Couldn't save SVG: ", svgElem.outerHTML) - return oldSyncID; - } - - return newSyncID - -} - -export function createEditor(element: HTMLElement, fileID: string, initialSyncID: string) { - - const editor = new Editor(element, { - iconProvider: new MaterialIconProvider(), - }); - - const toolbar = editor.addToolbar(); - - // restore toolbar state - getFile(TOOLBAR_PATH).then(toolbarState => { - if(toolbarState!= null) { - toolbar.deserializeState(toolbarState) - } - }); - // restore drawing - getFile(DATA_PATH +IDsToAssetPath(fileID, initialSyncID)).then(svg => { - if(svg != null) { - editor.loadFromSVG(svg); - } - }); - - let syncID = initialSyncID; - // save logic - const saveButton = toolbar.addSaveButton(() => { - saveCallback(editor, fileID, syncID, saveButton).then( - newSyncID => { - syncID = newSyncID - } - ) - }); - - // save toolbar config on tool change (toolbar state is not saved in SVGs!) - editor.notifier.on(EditorEventType.ToolUpdated, () => { - saveFile(TOOLBAR_PATH, JSON_MIME, toolbar.serializeState()); - }); - - editor.dispatch(editor.setBackgroundStyle({ autoresize: true }), false); - editor.getRootElement().style.height = '100%'; - -} - -export function editorTabInit(tab: ITabModel) { - - const fileID = tab.data.fileID; - const initialSyncID = tab.data.initialSyncID; - if (fileID == null || initialSyncID == null) { - alert("File or Sync ID and path missing - couldn't open file.") - return; - } - createEditor(tab.element, fileID, initialSyncID); - -} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index adf4b94..16886a8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,18 +6,15 @@ import { findImgSrc, imgSrcToIDs, generateTimeString, generateRandomString } from "@/helper"; -import {editorTabInit, openEditorTab} from "@/editorTab"; import {migrate} from "@/migration"; +import {EditorManager, PluginEditor} from "@/editor"; export default class DrawJSPlugin extends Plugin { onload() { loadIcons(this); - this.addTab({ - 'type': "whiteboard", - init() { editorTabInit(this) } - }); + EditorManager.registerTab(this); migrate() this.protyleSlash = [{ @@ -28,7 +25,7 @@ export default class DrawJSPlugin extends Plugin { const fileID = generateRandomString(); const syncID = generateTimeString() + '-' + generateRandomString(); protyle.insert(getMarkdownBlock(fileID, syncID), true, false); - openEditorTab(this, fileID, syncID); + new EditorManager(new PluginEditor(fileID, syncID)).open(this) } }]; @@ -39,7 +36,7 @@ export default class DrawJSPlugin extends Plugin { icon: "iconDraw", label: "Edit with js-draw", click: () => { - openEditorTab(this, ids.fileID, ids.syncID); + new EditorManager(new PluginEditor(ids.fileID, ids.syncID)).open(this) } }) })