From 5af506e26811d11dd58927d1c3e9eff424b06e89 Mon Sep 17 00:00:00 2001 From: zadam Date: Sat, 17 Dec 2022 21:46:51 +0100 Subject: [PATCH] reimplementation of frontend's api.addButtonToToolbar() to smoothen the upgrade --- docs/backend_api/Note.html | 100 ++++++------ docs/backend_api/becca_entities_note.js.html | 22 ++- docs/frontend_api/FrontendScriptApi.html | 145 ++++++++++++------ docs/frontend_api/NoteShort.html | 64 ++++---- docs/frontend_api/entities_note_short.js.html | 19 ++- docs/frontend_api/global.html | 34 +++- .../services_frontend_script_api.js.html | 16 +- src/becca/entities/note.js | 22 ++- src/public/app/entities/note_short.js | 17 +- .../app/services/frontend_script_api.js | 18 ++- .../buttons/launcher/script_launcher.js | 12 +- src/routes/api/special_notes.js | 7 +- src/routes/routes.js | 1 + src/services/special_notes.js | 66 ++++++-- 14 files changed, 382 insertions(+), 161 deletions(-) diff --git a/docs/backend_api/Note.html b/docs/backend_api/Note.html index 292a16dbf..2c9c217f3 100644 --- a/docs/backend_api/Note.html +++ b/docs/backend_api/Note.html @@ -1226,7 +1226,7 @@ See addLabel, addRelation for more specific methods.
Source:
@@ -1513,7 +1513,7 @@ See addLabel, addRelation for more specific methods.
Source:
@@ -1692,7 +1692,7 @@ returned.
Source:
@@ -1927,7 +1927,7 @@ returned.
Source:
@@ -2127,7 +2127,7 @@ returned.
Source:
@@ -2389,7 +2389,7 @@ returned.
Source:
@@ -2495,7 +2495,7 @@ returned.
Source:
@@ -2669,7 +2669,7 @@ returned.
Source:
@@ -2847,7 +2847,7 @@ returned.
Source:
@@ -3674,7 +3674,7 @@ returned.
Source:
@@ -3931,7 +3931,7 @@ returned.
Source:
@@ -4089,7 +4089,7 @@ returned.
Source:
@@ -4259,7 +4259,7 @@ returned.
Source:
@@ -4426,7 +4426,7 @@ returned.
Source:
@@ -4532,7 +4532,7 @@ returned.
Source:
@@ -4712,7 +4712,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -4977,7 +4977,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5132,7 +5132,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5290,7 +5290,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5460,7 +5460,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5627,7 +5627,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5782,7 +5782,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5940,7 +5940,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6110,7 +6110,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6558,7 +6558,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6716,7 +6716,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6886,7 +6886,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7101,7 +7101,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7309,7 +7309,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7411,7 +7411,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7513,7 +7513,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7615,7 +7615,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7811,7 +7811,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8037,7 +8037,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8333,7 +8333,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8562,7 +8562,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8760,7 +8760,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8958,7 +8958,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9156,7 +9156,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9306,7 +9306,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -10228,7 +10228,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -10408,7 +10408,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -10588,7 +10588,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -10783,7 +10783,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11015,7 +11015,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11195,7 +11195,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11355,7 +11355,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11597,7 +11597,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11808,7 +11808,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -12019,7 +12019,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
diff --git a/docs/backend_api/becca_entities_note.js.html b/docs/backend_api/becca_entities_note.js.html index a7da74aec..bcca49db7 100644 --- a/docs/backend_api/becca_entities_note.js.html +++ b/docs/backend_api/becca_entities_note.js.html @@ -360,7 +360,7 @@ class Note extends AbstractEntity { /** @returns {boolean} true if this note is JavaScript (code or attachment) */ isJavaScript() { - return (this.type === "code" || this.type === "file") + return (this.type === "code" || this.type === "file" || this.type === 'launcher') && (this.mime.startsWith("application/javascript") || this.mime === "application/x-javascript" || this.mime === "text/javascript"); @@ -400,6 +400,8 @@ class Note extends AbstractEntity { * @returns {Attribute[]} all note's attributes, including inherited ones */ getAttributes(type, name) { + this.__validateTypeName(type, name); + this.__getAttributes([]); if (type && name) { @@ -489,6 +491,19 @@ class Note extends AbstractEntity { return this.inheritableAttributeCache; } + __validateTypeName(type, name) { + if (type && type !== 'label' && type !== 'relation') { + throw new Error(`Unrecognized attribute type '${type}'. Only 'label' and 'relation' are possible values.`); + } + + if (name) { + const firstLetter = name.charAt(0); + if (firstLetter === '#' || firstLetter === '~') { + throw new Error(`Detect '#' or '~' in the attribute's name. In the API, attribute names should be set without these characters.`); + } + } + } + /** * @param type * @param name @@ -693,10 +708,7 @@ class Note extends AbstractEntity { * @returns {Attribute[]} note's "owned" attributes - excluding inherited ones */ getOwnedAttributes(type = null, name = null, value = null) { - // it's a common mistake to include # or ~ into attribute name - if (name && ["#", "~"].includes(name[0])) { - name = name.substr(1); - } + this.__validateTypeName(type, name); if (type && name && value !== undefined && value !== null) { return this.ownedAttributes.filter(attr => attr.type === type && attr.name === name && attr.value === value); diff --git a/docs/frontend_api/FrontendScriptApi.html b/docs/frontend_api/FrontendScriptApi.html index 0ccd21eac..4b296420c 100644 --- a/docs/frontend_api/FrontendScriptApi.html +++ b/docs/frontend_api/FrontendScriptApi.html @@ -1212,13 +1212,16 @@ -

addButtonToToolbar()

+

addButtonToToolbar(opts)

+
+ Adds a new launcher to the launchbar. If the launcher (id) already exists, it will be updated. +
@@ -1228,6 +1231,56 @@ +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
opts + + +CreateOrUpdateLauncherOptions + + + +
+ + + @@ -1248,7 +1301,7 @@ -
Deprecated:
+
Deprecated:
@@ -1262,7 +1315,7 @@
Source:
@@ -1399,7 +1452,7 @@
Source:
@@ -1538,7 +1591,7 @@
Source:
@@ -1746,7 +1799,7 @@
Source:
@@ -2110,7 +2163,7 @@
Source:
@@ -2243,7 +2296,7 @@
Source:
@@ -2353,7 +2406,7 @@
Source:
@@ -2459,7 +2512,7 @@
Source:
@@ -2565,7 +2618,7 @@
Source:
@@ -2675,7 +2728,7 @@
Source:
@@ -2786,7 +2839,7 @@ implementation of actual widget type.
Source:
@@ -2890,7 +2943,7 @@ implementation of actual widget type.
Source:
@@ -2998,7 +3051,7 @@ implementation of actual widget type.
Source:
@@ -3166,7 +3219,7 @@ implementation of actual widget type.
Source:
@@ -3303,7 +3356,7 @@ implementation of actual widget type.
Source:
@@ -3460,7 +3513,7 @@ implementation of actual widget type.
Source:
@@ -3615,7 +3668,7 @@ implementation of actual widget type.
Source:
@@ -3722,7 +3775,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -3877,7 +3930,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -4033,7 +4086,7 @@ if some action needs to happen on only one specific instance.
Source:
@@ -4234,7 +4287,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -4340,7 +4393,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -4495,7 +4548,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -4650,7 +4703,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -4800,7 +4853,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -5289,7 +5342,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -5397,7 +5450,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -5553,7 +5606,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -5709,7 +5762,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -5846,7 +5899,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -6000,7 +6053,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -6086,7 +6139,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -6223,7 +6276,7 @@ otherwise (by e.g. createNoteLink())
Source:
@@ -6384,7 +6437,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -6492,7 +6545,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -6630,7 +6683,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -6786,7 +6839,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -6941,7 +6994,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7092,7 +7145,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7229,7 +7282,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7366,7 +7419,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7526,7 +7579,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7686,7 +7739,7 @@ Internally this serializes the anonymous function into string and sends it to ba
Source:
@@ -7778,7 +7831,7 @@ Typical use case is when new note has been created, we should wait until it is s
Source:
diff --git a/docs/frontend_api/NoteShort.html b/docs/frontend_api/NoteShort.html index a5a3f6060..6a35fdec0 100644 --- a/docs/frontend_api/NoteShort.html +++ b/docs/frontend_api/NoteShort.html @@ -1103,7 +1103,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -1281,7 +1281,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2150,7 +2150,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2305,7 +2305,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2472,7 +2472,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2582,7 +2582,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2756,7 +2756,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -2934,7 +2934,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -3289,7 +3289,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -3444,7 +3444,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -3611,7 +3611,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -3766,7 +3766,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -3921,7 +3921,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -4088,7 +4088,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -4651,7 +4651,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -4806,7 +4806,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -4976,7 +4976,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5127,7 +5127,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5294,7 +5294,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5400,7 +5400,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5513,7 +5513,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5619,7 +5619,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5721,7 +5721,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -5895,7 +5895,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6152,7 +6152,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6330,7 +6330,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6485,7 +6485,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6640,7 +6640,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6795,7 +6795,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6903,7 +6903,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -6987,7 +6987,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
@@ -7093,7 +7093,7 @@ This note's representation is used in note tree and is kept in Froca.
Source:
diff --git a/docs/frontend_api/entities_note_short.js.html b/docs/frontend_api/entities_note_short.js.html index 85b0bad4d..d07ab3507 100644 --- a/docs/frontend_api/entities_note_short.js.html +++ b/docs/frontend_api/entities_note_short.js.html @@ -382,6 +382,8 @@ class NoteShort { return a.isSearch ? 1 : -1; } else if (a.isArchived !== b.isArchived) { return a.isArchived ? 1 : -1; + } else if (a.isHidden !== b.isHidden) { + return a.isHidden ? 1 : -1; } else { return a.notePath.length - b.notePath.length; } @@ -391,6 +393,8 @@ class NoteShort { } __filterAttrs(attributes, type, name) { + this.__validateTypeName(type, name); + if (!type && !name) { return attributes; } else if (type && name) { @@ -408,6 +412,19 @@ class NoteShort { return attrs.filter(attr => attr.isInheritable); } + __validateTypeName(type, name) { + if (type && type !== 'label' && type !== 'relation') { + throw new Error(`Unrecognized attribute type '${type}'. Only 'label' and 'relation' are possible values.`); + } + + if (name) { + const firstLetter = name.charAt(0); + if (firstLetter === '#' || firstLetter === '~') { + throw new Error(`Detect '#' or '~' in the attribute's name. In the API, attribute names should be set without these characters.`); + } + } + } + /** * @param {string} [name] - label name to filter * @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones @@ -784,7 +801,7 @@ class NoteShort { /** @returns {boolean} true if this note is JavaScript (code or attachment) */ isJavaScript() { - return (this.type === "code" || this.type === "file") + return (this.type === "code" || this.type === "file" || this.type === 'launcher') && (this.mime.startsWith("application/javascript") || this.mime === "application/x-javascript" || this.mime === "text/javascript"); diff --git a/docs/frontend_api/global.html b/docs/frontend_api/global.html index d721331e5..ebe94b12e 100644 --- a/docs/frontend_api/global.html +++ b/docs/frontend_api/global.html @@ -194,7 +194,7 @@ -

ToolbarButtonOptions

+

CreateOrUpdateLauncherOptions

@@ -241,6 +241,38 @@ + + + id + + + + + +string + + + + + + + + + <optional>
+ + + + + + + + + id of the button, used to identify the old instances of this button to be replaced + ID is optional because of BC, but not specifying it is deprecated. ID can be alphanumeric only. + + + + title diff --git a/docs/frontend_api/services_frontend_script_api.js.html b/docs/frontend_api/services_frontend_script_api.js.html index 81a9e2905..2a2bf55af 100644 --- a/docs/frontend_api/services_frontend_script_api.js.html +++ b/docs/frontend_api/services_frontend_script_api.js.html @@ -134,7 +134,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain }; /** - * @typedef {Object} ToolbarButtonOptions + * @typedef {Object} CreateOrUpdateLauncherOptions + * @property {string} [id] - id of the button, used to identify the old instances of this button to be replaced + * ID is optional because of BC, but not specifying it is deprecated. ID can be alphanumeric only. * @property {string} title * @property {string} [icon] - name of the boxicon to be used (e.g. "time" for "bx-time" icon) * @property {function} action - callback handling the click on the button @@ -142,9 +144,17 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain */ /** - * @deprecated this API has no effect anymore. Use bookmarks or launchpad shortcuts instead. + * Adds a new launcher to the launchbar. If the launcher (id) already exists, it will be updated. + * + * @deprecated you can now create/modify launchers in the + * @param {CreateOrUpdateLauncherOptions} opts */ - this.addButtonToToolbar = () => console.warn("api.addButtonToToolbar() calls are deprecated and have no effect"); + this.addButtonToToolbar = async opts => { + const {action, ...reqBody} = opts; + reqBody.action = action.toString(); + + await server.put('special-notes/api-script-launcher', reqBody); + }; function prepareParams(params) { if (!params) { diff --git a/src/becca/entities/note.js b/src/becca/entities/note.js index a07228378..c71607dd5 100644 --- a/src/becca/entities/note.js +++ b/src/becca/entities/note.js @@ -332,7 +332,7 @@ class Note extends AbstractEntity { /** @returns {boolean} true if this note is JavaScript (code or attachment) */ isJavaScript() { - return (this.type === "code" || this.type === "file") + return (this.type === "code" || this.type === "file" || this.type === 'launcher') && (this.mime.startsWith("application/javascript") || this.mime === "application/x-javascript" || this.mime === "text/javascript"); @@ -372,6 +372,8 @@ class Note extends AbstractEntity { * @returns {Attribute[]} all note's attributes, including inherited ones */ getAttributes(type, name) { + this.__validateTypeName(type, name); + this.__getAttributes([]); if (type && name) { @@ -461,6 +463,19 @@ class Note extends AbstractEntity { return this.inheritableAttributeCache; } + __validateTypeName(type, name) { + if (type && type !== 'label' && type !== 'relation') { + throw new Error(`Unrecognized attribute type '${type}'. Only 'label' and 'relation' are possible values.`); + } + + if (name) { + const firstLetter = name.charAt(0); + if (firstLetter === '#' || firstLetter === '~') { + throw new Error(`Detect '#' or '~' in the attribute's name. In the API, attribute names should be set without these characters.`); + } + } + } + /** * @param type * @param name @@ -665,10 +680,7 @@ class Note extends AbstractEntity { * @returns {Attribute[]} note's "owned" attributes - excluding inherited ones */ getOwnedAttributes(type = null, name = null, value = null) { - // it's a common mistake to include # or ~ into attribute name - if (name && ["#", "~"].includes(name[0])) { - name = name.substr(1); - } + this.__validateTypeName(type, name); if (type && name && value !== undefined && value !== null) { return this.ownedAttributes.filter(attr => attr.type === type && attr.name === name && attr.value === value); diff --git a/src/public/app/entities/note_short.js b/src/public/app/entities/note_short.js index 6635d3a3d..af08e045f 100644 --- a/src/public/app/entities/note_short.js +++ b/src/public/app/entities/note_short.js @@ -365,6 +365,8 @@ class NoteShort { } __filterAttrs(attributes, type, name) { + this.__validateTypeName(type, name); + if (!type && !name) { return attributes; } else if (type && name) { @@ -382,6 +384,19 @@ class NoteShort { return attrs.filter(attr => attr.isInheritable); } + __validateTypeName(type, name) { + if (type && type !== 'label' && type !== 'relation') { + throw new Error(`Unrecognized attribute type '${type}'. Only 'label' and 'relation' are possible values.`); + } + + if (name) { + const firstLetter = name.charAt(0); + if (firstLetter === '#' || firstLetter === '~') { + throw new Error(`Detect '#' or '~' in the attribute's name. In the API, attribute names should be set without these characters.`); + } + } + } + /** * @param {string} [name] - label name to filter * @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones @@ -758,7 +773,7 @@ class NoteShort { /** @returns {boolean} true if this note is JavaScript (code or attachment) */ isJavaScript() { - return (this.type === "code" || this.type === "file") + return (this.type === "code" || this.type === "file" || this.type === 'launcher') && (this.mime.startsWith("application/javascript") || this.mime === "application/x-javascript" || this.mime === "text/javascript"); diff --git a/src/public/app/services/frontend_script_api.js b/src/public/app/services/frontend_script_api.js index fd0368e31..32f4af3b0 100644 --- a/src/public/app/services/frontend_script_api.js +++ b/src/public/app/services/frontend_script_api.js @@ -106,7 +106,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain }; /** - * @typedef {Object} ToolbarButtonOptions + * @typedef {Object} CreateOrUpdateLauncherOptions + * @property {string} [id] - id of the button, used to identify the old instances of this button to be replaced + * ID is optional because of BC, but not specifying it is deprecated. ID can be alphanumeric only. * @property {string} title * @property {string} [icon] - name of the boxicon to be used (e.g. "time" for "bx-time" icon) * @property {function} action - callback handling the click on the button @@ -114,9 +116,19 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain */ /** - * @deprecated this API has no effect anymore. Use bookmarks or launchpad shortcuts instead. + * Adds a new launcher to the launchbar. If the launcher (id) already exists, it will be updated. + * + * @deprecated you can now create/modify launchers in the top-left Menu -> Configure Launchbar + * @param {CreateOrUpdateLauncherOptions} opts */ - this.addButtonToToolbar = () => console.warn("api.addButtonToToolbar() calls are deprecated and have no effect"); + this.addButtonToToolbar = async opts => { + console.warn("api.addButtonToToolbar() has been deprecated since v0.58 and may be removed in the future. Use Menu -> Configure Launchbar to create/update launchers instead."); + + const {action, ...reqBody} = opts; + reqBody.action = action.toString(); + + await server.put('special-notes/api-script-launcher', reqBody); + }; function prepareParams(params) { if (!params) { diff --git a/src/public/app/widgets/buttons/launcher/script_launcher.js b/src/public/app/widgets/buttons/launcher/script_launcher.js index de3d3c0fb..c8ee4c6b4 100644 --- a/src/public/app/widgets/buttons/launcher/script_launcher.js +++ b/src/public/app/widgets/buttons/launcher/script_launcher.js @@ -6,12 +6,16 @@ export default class ScriptLauncher extends AbstractLauncher { this.title(this.launcherNote.title) .icon(this.launcherNote.getIcon()) - .onClick(this.handler); + .onClick(() => this.launch()); } async launch() { - const script = await this.launcherNote.getRelationTarget('script'); + if (this.launcherNote.hasLabel('scriptInLauncherContent')) { + await this.launcherNote.executeScript(); + } else { + const script = await this.launcherNote.getRelationTarget('script'); - await script.executeScript(); + await script.executeScript(); + } } -} \ No newline at end of file +} diff --git a/src/routes/api/special_notes.js b/src/routes/api/special_notes.js index fb9979e54..04e41e22b 100644 --- a/src/routes/api/special_notes.js +++ b/src/routes/api/special_notes.js @@ -74,6 +74,10 @@ function resetLauncher(req) { return specialNotesService.resetLauncher(req.params.noteId); } +function createOrUpdateScriptLauncherFromApi(req) { + return specialNotesService.createOrUpdateScriptLauncherFromApi(req.body); +} + module.exports = { getInboxNote, getDayNote, @@ -86,5 +90,6 @@ module.exports = { createSearchNote, saveSearchNote, createLauncher, - resetLauncher + resetLauncher, + createOrUpdateScriptLauncherFromApi }; diff --git a/src/routes/routes.js b/src/routes/routes.js index ff1dde1fa..8c64c46a1 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -343,6 +343,7 @@ function register(app) { apiRoute(POST, '/api/special-notes/save-search-note', specialNotesRoute.saveSearchNote); apiRoute(POST, '/api/special-notes/launchers/:noteId/reset', specialNotesRoute.resetLauncher); apiRoute(POST, '/api/special-notes/launchers/:parentNoteId/:launcherType', specialNotesRoute.createLauncher); + apiRoute(PUT, '/api/special-notes/api-script-launcher', specialNotesRoute.createOrUpdateScriptLauncherFromApi); // :filename is not used by trilium, but instead used for "save as" to assign a human-readable filename route(GET, '/api/images/:noteId/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnImage); diff --git a/src/services/special_notes.js b/src/services/special_notes.js index 577f01535..d5c2f5dd2 100644 --- a/src/services/special_notes.js +++ b/src/services/special_notes.js @@ -143,6 +143,19 @@ function getHoistedNote() { return becca.getNote(cls.getHoistedNoteId()); } +function createScriptLauncher(parentNoteId, forceNoteId = null) { + const note = noteService.createNewNote({ + noteId: forceNoteId, + title: "Script Launcher", + type: 'launcher', + content: '', + parentNoteId: parentNoteId + }).note; + + note.addRelation('template', 'lbTplScriptLauncher'); + return note; +} + function createLauncher(parentNoteId, launcherType) { let note; @@ -156,14 +169,7 @@ function createLauncher(parentNoteId, launcherType) { note.addRelation('template', 'lbTplNoteLauncher'); } else if (launcherType === 'script') { - note = noteService.createNewNote({ - title: "Script Launcher", - type: 'launcher', - content: '', - parentNoteId: parentNoteId - }).note; - - note.addRelation('template', 'lbTplScriptLauncher'); + note = createScriptLauncher(parentNoteId); } else if (launcherType === 'customWidget') { note = noteService.createNewNote({ title: "Widget Launcher", @@ -215,6 +221,47 @@ function resetLauncher(noteId) { hiddenSubtreeService.checkHiddenSubtree(); } +/** + * This exists to ease transition into the new launchbar, but it's not meant to be a permanent functionality. + * Previously, the launchbar was fixed and the only way to add buttons was through this API, so a lot of buttons have been + * created just to fill this user hole. + * + * Another use case would be for script-packages (of which only few exists) which could this way register automatically + * into the launchbar. For such use cases this might be a usable replacement, but I'm not yet clear on that. + */ +function createOrUpdateScriptLauncherFromApi(opts) { + const launcherId = opts.id || ("tb" + opts.title.replace(/[^[a-z0-9]/gi, "")); + + if (!opts.title) { + throw new Error("Title is mandatory property to create or update a launcher."); + } else if (!/^[a-z0-9]+$/i.test(launcherId)) { + throw new Error(`Launcher ID can be alphanumeric only, '${launcherId}' given`); + } + + const launcherNote = becca.getNote(launcherId) + || createScriptLauncher('lbVisibleLaunchers', launcherId); + + launcherNote.title = opts.title; + launcherNote.setContent("(" + opts.action + ")()"); + launcherNote.setLabel('scriptInLauncherContent'); // there's no target note, the script is in the launcher's content + launcherNote.mime = 'application/javascript;env=frontend'; + launcherNote.save(); + + if (opts.shortcut) { + launcherNote.setLabel('keyboardShortcut', opts.shortcut); + } else { + launcherNote.removeLabel('keyboardShortcut'); + } + + if (opts.icon) { + launcherNote.setLabel('iconClass', "bx bx-" + opts.icon); + } else { + launcherNote.removeLabel('iconClass'); + } + + return launcherNote; +} + module.exports = { getInboxNote, createSqlConsole, @@ -222,5 +269,6 @@ module.exports = { createSearchNote, saveSearchNote, createLauncher, - resetLauncher + resetLauncher, + createOrUpdateScriptLauncherFromApi };