Improve error handling and code structure
This commit is contained in:
parent
f35342a791
commit
8d4779b8fe
5 changed files with 116 additions and 67 deletions
|
@ -2,6 +2,9 @@
|
||||||
"insertDrawing": "Insert Drawing",
|
"insertDrawing": "Insert Drawing",
|
||||||
"editDrawing": "Edit with js-draw",
|
"editDrawing": "Edit with js-draw",
|
||||||
"errNoFileID": "File ID missing - couldn't open file.",
|
"errNoFileID": "File ID missing - couldn't open file.",
|
||||||
|
"errSyncIDNotFound": "Couldn't find SyncID in document for drawing, make sure you're trying to edit a drawing that is included in at least a note.",
|
||||||
|
"errCreateUnknown": "Unknown error while creating editor, please try again.",
|
||||||
|
"errInvalidBackgroundColor": "Invalid background color! Please enter an HEX color, like #000000 (black) or #FFFFFF (white). The old background color will be used.",
|
||||||
"drawing": "Drawing",
|
"drawing": "Drawing",
|
||||||
"settings": {
|
"settings": {
|
||||||
"name": "js-draw Plugin Settings",
|
"name": "js-draw Plugin Settings",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {PluginFile} from "@/file";
|
import {PluginFile} from "@/file";
|
||||||
import {CONFIG_FILENAME, JSON_MIME, STORAGE_PATH} from "@/const";
|
import {CONFIG_FILENAME, JSON_MIME, STORAGE_PATH} from "@/const";
|
||||||
import {Plugin} from "siyuan";
|
import {Plugin, showMessage} from "siyuan";
|
||||||
import {SettingUtils} from "@/libs/setting-utils";
|
import {SettingUtils} from "@/libs/setting-utils";
|
||||||
import {validateColor} from "@/helper";
|
import {validateColor} from "@/helper";
|
||||||
|
|
||||||
|
@ -61,10 +61,6 @@ export class PluginConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config: Options) {
|
setConfig(config: Options) {
|
||||||
if(!validateColor(config.background)) {
|
|
||||||
alert("Invalid background color! Please enter an HEX color, like #000000 (black) or #FFFFFF (white)");
|
|
||||||
config.background = this.options.background;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.options = config;
|
this.options = config;
|
||||||
}
|
}
|
||||||
|
@ -83,12 +79,13 @@ export class PluginConfigViewer {
|
||||||
this.populateSettingMenu();
|
this.populateSettingMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
populateSettingMenu() {
|
async configSaveCallback(data) {
|
||||||
|
|
||||||
this.settingUtils = new SettingUtils({
|
if(!validateColor(data.background)) {
|
||||||
plugin: this.plugin,
|
showMessage(this.plugin.i18n.errInvalidBackgroundColor, 0, 'error');
|
||||||
name: this.plugin.i18n.settings.name,
|
data.background = this.config.options.background;
|
||||||
callback: async (data) => {
|
this.settingUtils.set('background', data.background);
|
||||||
|
}
|
||||||
this.config.setConfig({
|
this.config.setConfig({
|
||||||
grid: data.grid,
|
grid: data.grid,
|
||||||
background: data.background,
|
background: data.background,
|
||||||
|
@ -96,6 +93,16 @@ export class PluginConfigViewer {
|
||||||
analytics: data.analytics,
|
analytics: data.analytics,
|
||||||
});
|
});
|
||||||
await this.config.save();
|
await this.config.save();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
populateSettingMenu() {
|
||||||
|
|
||||||
|
this.settingUtils = new SettingUtils({
|
||||||
|
plugin: this.plugin,
|
||||||
|
name: this.plugin.i18n.settings.name,
|
||||||
|
callback: async (data) => {
|
||||||
|
await this.configSaveCallback(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,12 @@ import {MaterialIconProvider} from "@js-draw/material-icons";
|
||||||
import {PluginAsset, PluginFile} from "@/file";
|
import {PluginAsset, PluginFile} from "@/file";
|
||||||
import {JSON_MIME, STORAGE_PATH, SVG_MIME, TOOLBAR_FILENAME} from "@/const";
|
import {JSON_MIME, STORAGE_PATH, SVG_MIME, TOOLBAR_FILENAME} from "@/const";
|
||||||
import Editor, {BackgroundComponentBackgroundType, BaseWidget, Color4, EditorEventType} from "js-draw";
|
import Editor, {BackgroundComponentBackgroundType, BaseWidget, Color4, EditorEventType} from "js-draw";
|
||||||
import {Dialog, getFrontend, openTab, Plugin} from "siyuan";
|
import {Dialog, getFrontend, openTab, Plugin, showMessage} from "siyuan";
|
||||||
import {findSyncIDInProtyle, replaceSyncID} from "@/protyle";
|
import {findSyncIDInProtyle, replaceSyncID} from "@/protyle";
|
||||||
import DrawJSPlugin from "@/index";
|
import DrawJSPlugin from "@/index";
|
||||||
import {DefaultEditorOptions} from "@/config";
|
import {DefaultEditorOptions} from "@/config";
|
||||||
import 'js-draw/styles';
|
import 'js-draw/styles';
|
||||||
|
import {SyncIDNotFoundError, UnchangedProtyleError} from "@/errors";
|
||||||
|
|
||||||
export class PluginEditor {
|
export class PluginEditor {
|
||||||
|
|
||||||
|
@ -23,8 +24,9 @@ export class PluginEditor {
|
||||||
getEditor(): Editor { return this.editor; }
|
getEditor(): Editor { return this.editor; }
|
||||||
getFileID(): string { return this.fileID; }
|
getFileID(): string { return this.fileID; }
|
||||||
getSyncID(): string { return this.syncID; }
|
getSyncID(): string { return this.syncID; }
|
||||||
|
setSyncID(syncID: string) { this.syncID = syncID; }
|
||||||
|
|
||||||
constructor(fileID: string, defaultEditorOptions: DefaultEditorOptions) {
|
private constructor(fileID: string) {
|
||||||
|
|
||||||
this.fileID = fileID;
|
this.fileID = fileID;
|
||||||
|
|
||||||
|
@ -34,25 +36,31 @@ export class PluginEditor {
|
||||||
iconProvider: new MaterialIconProvider(),
|
iconProvider: new MaterialIconProvider(),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.genToolbar().then(() => {
|
|
||||||
this.editor.dispatch(this.editor.setBackgroundStyle({ autoresize: true }), false);
|
this.editor.dispatch(this.editor.setBackgroundStyle({ autoresize: true }), false);
|
||||||
this.editor.getRootElement().style.height = '100%';
|
this.editor.getRootElement().style.height = '100%';
|
||||||
});
|
|
||||||
|
|
||||||
findSyncIDInProtyle(this.fileID).then(async (syncID) => {
|
|
||||||
|
|
||||||
if(syncID == null) {
|
|
||||||
alert(
|
|
||||||
"Couldn't find SyncID in protyle for this file.\n" +
|
|
||||||
"Make sure the drawing you're trying to edit exists in a note.\n" +
|
|
||||||
"Close this editor tab now, and try to open the editor again."
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.syncID = syncID;
|
static async create(fileID: string, defaultEditorOptions: DefaultEditorOptions): Promise<PluginEditor> {
|
||||||
// restore drawing
|
|
||||||
this.drawingFile = new PluginAsset(this.fileID, syncID, SVG_MIME);
|
const instance = new PluginEditor(fileID);
|
||||||
|
|
||||||
|
await instance.genToolbar();
|
||||||
|
let syncID = await findSyncIDInProtyle(fileID);
|
||||||
|
|
||||||
|
if(syncID == null) {
|
||||||
|
throw new SyncIDNotFoundError(fileID);
|
||||||
|
}
|
||||||
|
instance.setSyncID(syncID);
|
||||||
|
await instance.restoreOrInitFile(defaultEditorOptions);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async restoreOrInitFile(defaultEditorOptions: DefaultEditorOptions) {
|
||||||
|
|
||||||
|
this.drawingFile = new PluginAsset(this.fileID, this.syncID, SVG_MIME);
|
||||||
await this.drawingFile.loadFromSiYuanFS();
|
await this.drawingFile.loadFromSiYuanFS();
|
||||||
|
|
||||||
if(this.drawingFile.getContent() != null) {
|
if(this.drawingFile.getContent() != null) {
|
||||||
|
@ -66,23 +74,18 @@ export class PluginEditor {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
}).catch((error) => {
|
|
||||||
alert("Error loading drawing: " + error);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async genToolbar() {
|
async genToolbar() {
|
||||||
|
|
||||||
const toolbar = this.editor.addToolbar();
|
const toolbar = this.editor.addToolbar();
|
||||||
|
|
||||||
// restore toolbarFile state
|
// restore toolbarFile state
|
||||||
this.toolbarFile = new PluginFile(STORAGE_PATH, TOOLBAR_FILENAME, JSON_MIME);
|
this.toolbarFile = new PluginFile(STORAGE_PATH, TOOLBAR_FILENAME, JSON_MIME);
|
||||||
this.toolbarFile.loadFromSiYuanFS().then(() => {
|
await this.toolbarFile.loadFromSiYuanFS();
|
||||||
if(this.toolbarFile.getContent() != null) {
|
if(this.toolbarFile.getContent() != null) {
|
||||||
toolbar.deserializeState(this.toolbarFile.getContent());
|
toolbar.deserializeState(this.toolbarFile.getContent());
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// save button
|
// save button
|
||||||
const saveButton = toolbar.addSaveButton(async () => {
|
const saveButton = toolbar.addSaveButton(async () => {
|
||||||
|
@ -109,7 +112,7 @@ export class PluginEditor {
|
||||||
newSyncID = this.drawingFile.getSyncID();
|
newSyncID = this.drawingFile.getSyncID();
|
||||||
if(newSyncID != oldSyncID) { // supposed to replace protyle
|
if(newSyncID != oldSyncID) { // supposed to replace protyle
|
||||||
const changed = await replaceSyncID(this.fileID, oldSyncID, newSyncID); // try to change protyle
|
const changed = await replaceSyncID(this.fileID, oldSyncID, newSyncID); // try to change protyle
|
||||||
if(!changed) throw new Error("Couldn't replace old images in protyle");
|
if(!changed) throw new UnchangedProtyleError();
|
||||||
await this.drawingFile.removeOld(oldSyncID);
|
await this.drawingFile.removeOld(oldSyncID);
|
||||||
}
|
}
|
||||||
saveButton.setDisabled(true);
|
saveButton.setDisabled(true);
|
||||||
|
@ -117,7 +120,10 @@ export class PluginEditor {
|
||||||
saveButton.setDisabled(false);
|
saveButton.setDisabled(false);
|
||||||
}, 500);
|
}, 500);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert("Error saving! The current drawing has been copied to your clipboard. You may need to create a new drawing and paste it there.");
|
showMessage("Error saving! The current drawing has been copied to your clipboard. You may need to create a new drawing and paste it there.", 0, 'error');
|
||||||
|
if(error instanceof UnchangedProtyleError) {
|
||||||
|
showMessage("Make sure the image you're trying to edit still exists in your documents.", 0, 'error');
|
||||||
|
}
|
||||||
await navigator.clipboard.writeText(svgElem.outerHTML);
|
await navigator.clipboard.writeText(svgElem.outerHTML);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
console.log("Couldn't save SVG: ", svgElem.outerHTML)
|
console.log("Couldn't save SVG: ", svgElem.outerHTML)
|
||||||
|
@ -133,26 +139,47 @@ export class PluginEditor {
|
||||||
export class EditorManager {
|
export class EditorManager {
|
||||||
|
|
||||||
private editor: PluginEditor
|
private editor: PluginEditor
|
||||||
|
setEditor(editor: PluginEditor) { this.editor = editor;}
|
||||||
|
|
||||||
constructor(fileID: string, defaultEditorOptions: DefaultEditorOptions) {
|
static async create(fileID: string, p: DrawJSPlugin) {
|
||||||
this.editor = new PluginEditor(fileID, defaultEditorOptions);
|
let instance = new EditorManager();
|
||||||
|
try {
|
||||||
|
let editor = await PluginEditor.create(fileID, p.config.getDefaultEditorOptions());
|
||||||
|
instance.setEditor(editor);
|
||||||
|
}catch (error) {
|
||||||
|
EditorManager.handleCreationError(error, p);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static registerTab(p: DrawJSPlugin) {
|
static registerTab(p: DrawJSPlugin) {
|
||||||
p.addTab({
|
p.addTab({
|
||||||
'type': "whiteboard",
|
'type': "whiteboard",
|
||||||
init() {
|
async init() {
|
||||||
const fileID = this.data.fileID;
|
const fileID = this.data.fileID;
|
||||||
if (fileID == null) {
|
if (fileID == null) {
|
||||||
alert(p.i18n.errNoFileID);
|
alert(p.i18n.errNoFileID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const editor = new PluginEditor(fileID, p.config.getDefaultEditorOptions());
|
try {
|
||||||
|
const editor = await PluginEditor.create(fileID, p.config.getDefaultEditorOptions());
|
||||||
this.element.appendChild(editor.getElement());
|
this.element.appendChild(editor.getElement());
|
||||||
|
}catch (error){
|
||||||
|
EditorManager.handleCreationError(error, p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static handleCreationError(error: any, p: DrawJSPlugin) {
|
||||||
|
console.error(error);
|
||||||
|
let errorTxt = p.i18n.errCreateUnknown;
|
||||||
|
if(error instanceof SyncIDNotFoundError) {
|
||||||
|
errorTxt = p.i18n.errSyncIDNotFound;
|
||||||
|
}
|
||||||
|
showMessage(errorTxt, 0, 'error');
|
||||||
|
}
|
||||||
|
|
||||||
toTab(p: Plugin) {
|
toTab(p: Plugin) {
|
||||||
openTab({
|
openTab({
|
||||||
app: p.app,
|
app: p.app,
|
||||||
|
@ -176,7 +203,7 @@ export class EditorManager {
|
||||||
dialog.element.querySelector("#DrawingPanel").appendChild(this.editor.getElement());
|
dialog.element.querySelector("#DrawingPanel").appendChild(this.editor.getElement());
|
||||||
}
|
}
|
||||||
|
|
||||||
async open(p: DrawJSPlugin) {
|
open(p: DrawJSPlugin) {
|
||||||
if(getFrontend() != "mobile" && !p.config.options.dialogOnDesktop) {
|
if(getFrontend() != "mobile" && !p.config.options.dialogOnDesktop) {
|
||||||
this.toTab(p);
|
this.toTab(p);
|
||||||
} else {
|
} else {
|
||||||
|
|
12
src/errors.ts
Normal file
12
src/errors.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
export class SyncIDNotFoundError extends Error {
|
||||||
|
readonly fileID: string;
|
||||||
|
|
||||||
|
constructor(fileID: string) {
|
||||||
|
super(`SyncID not found for file ${fileID}`);
|
||||||
|
this.fileID = fileID;
|
||||||
|
Object.setPrototypeOf(this, new.target.prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UnchangedProtyleError extends Error {}
|
|
@ -29,12 +29,12 @@ export default class DrawJSPlugin extends Plugin {
|
||||||
id: "insert-drawing",
|
id: "insert-drawing",
|
||||||
filter: ["Insert Drawing", "Add drawing", "whiteboard", "freehand", "graphics", "jsdraw"],
|
filter: ["Insert Drawing", "Add drawing", "whiteboard", "freehand", "graphics", "jsdraw"],
|
||||||
html: getMenuHTML("iconDraw", this.i18n.insertDrawing),
|
html: getMenuHTML("iconDraw", this.i18n.insertDrawing),
|
||||||
callback: (protyle: Protyle) => {
|
callback: async (protyle: Protyle) => {
|
||||||
void this.analytics.sendEvent('create');
|
void this.analytics.sendEvent('create');
|
||||||
const fileID = generateRandomString();
|
const fileID = generateRandomString();
|
||||||
const syncID = generateTimeString() + '-' + generateRandomString();
|
const syncID = generateTimeString() + '-' + generateRandomString();
|
||||||
protyle.insert(getMarkdownBlock(fileID, syncID), true, false);
|
protyle.insert(getMarkdownBlock(fileID, syncID), true, false);
|
||||||
new EditorManager(fileID, this.config.getDefaultEditorOptions()).open(this);
|
(await EditorManager.create(fileID, this)).open(this);
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@ -44,9 +44,9 @@ export default class DrawJSPlugin extends Plugin {
|
||||||
e.detail.menu.addItem({
|
e.detail.menu.addItem({
|
||||||
icon: "iconDraw",
|
icon: "iconDraw",
|
||||||
label: this.i18n.editDrawing,
|
label: this.i18n.editDrawing,
|
||||||
click: () => {
|
click: async () => {
|
||||||
void this.analytics.sendEvent('edit');
|
void this.analytics.sendEvent('edit');
|
||||||
new EditorManager(ids.fileID, this.config.getDefaultEditorOptions()).open(this);
|
(await EditorManager.create(ids.fileID, this)).open(this);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue