mirror of https://github.com/laurent22/joplin.git
Refactoring and got ipc messages to work
parent
776e7dfe3f
commit
7a79d7ef7e
|
@ -91,10 +91,12 @@ CliClient/tests/support/plugins/withExternalModules/src/index.js
|
|||
CliClient/tests/synchronizer_LockHandler.js
|
||||
CliClient/tests/synchronizer_MigrationHandler.js
|
||||
ElectronClient/app.js
|
||||
ElectronClient/bridge.js
|
||||
ElectronClient/commands/copyDevCommand.js
|
||||
ElectronClient/commands/focusElement.js
|
||||
ElectronClient/commands/startExternalEditing.js
|
||||
ElectronClient/commands/stopExternalEditing.js
|
||||
ElectronClient/ElectronAppWrapper.js
|
||||
ElectronClient/global.d.js
|
||||
ElectronClient/gui/Button/Button.js
|
||||
ElectronClient/gui/ConfigScreen/ButtonBar.js
|
||||
|
@ -199,9 +201,11 @@ ElectronClient/gui/ToolbarBase.js
|
|||
ElectronClient/gui/ToolbarButton/styles/index.js
|
||||
ElectronClient/gui/ToolbarButton/ToolbarButton.js
|
||||
ElectronClient/gui/utils/NoteListUtils.js
|
||||
ElectronClient/services/bridge.js
|
||||
ElectronClient/services/plugins/hooks/useThemeCss.js
|
||||
ElectronClient/services/plugins/hooks/useViewIsReady.js
|
||||
ElectronClient/services/plugins/PlatformImplementation.js
|
||||
ElectronClient/services/plugins/PluginRunner.js
|
||||
ElectronClient/services/plugins/UserWebview.js
|
||||
ElectronClient/services/plugins/UserWebviewDialog.js
|
||||
ElectronClient/services/plugins/UserWebviewDialogButtonBar.js
|
||||
|
@ -221,6 +225,7 @@ ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
|||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||
ReactNativeClient/lib/JoplinServerApi.js
|
||||
ReactNativeClient/lib/Logger.js
|
||||
ReactNativeClient/lib/models/Setting.js
|
||||
ReactNativeClient/lib/ntpDate.js
|
||||
ReactNativeClient/lib/reducer.js
|
||||
|
@ -259,6 +264,7 @@ ReactNativeClient/lib/services/plugins/sandboxProxy.js
|
|||
ReactNativeClient/lib/services/plugins/ToolbarButtonController.js
|
||||
ReactNativeClient/lib/services/plugins/utils/executeSandboxCall.js
|
||||
ReactNativeClient/lib/services/plugins/utils/manifestFromObject.js
|
||||
ReactNativeClient/lib/services/plugins/utils/mapEventHandlersToIds.js
|
||||
ReactNativeClient/lib/services/plugins/utils/types.js
|
||||
ReactNativeClient/lib/services/plugins/ViewController.js
|
||||
ReactNativeClient/lib/services/plugins/WebviewController.js
|
||||
|
|
|
@ -84,10 +84,12 @@ CliClient/tests/support/plugins/withExternalModules/src/index.js
|
|||
CliClient/tests/synchronizer_LockHandler.js
|
||||
CliClient/tests/synchronizer_MigrationHandler.js
|
||||
ElectronClient/app.js
|
||||
ElectronClient/bridge.js
|
||||
ElectronClient/commands/copyDevCommand.js
|
||||
ElectronClient/commands/focusElement.js
|
||||
ElectronClient/commands/startExternalEditing.js
|
||||
ElectronClient/commands/stopExternalEditing.js
|
||||
ElectronClient/ElectronAppWrapper.js
|
||||
ElectronClient/global.d.js
|
||||
ElectronClient/gui/Button/Button.js
|
||||
ElectronClient/gui/ConfigScreen/ButtonBar.js
|
||||
|
@ -192,9 +194,11 @@ ElectronClient/gui/ToolbarBase.js
|
|||
ElectronClient/gui/ToolbarButton/styles/index.js
|
||||
ElectronClient/gui/ToolbarButton/ToolbarButton.js
|
||||
ElectronClient/gui/utils/NoteListUtils.js
|
||||
ElectronClient/services/bridge.js
|
||||
ElectronClient/services/plugins/hooks/useThemeCss.js
|
||||
ElectronClient/services/plugins/hooks/useViewIsReady.js
|
||||
ElectronClient/services/plugins/PlatformImplementation.js
|
||||
ElectronClient/services/plugins/PluginRunner.js
|
||||
ElectronClient/services/plugins/UserWebview.js
|
||||
ElectronClient/services/plugins/UserWebviewDialog.js
|
||||
ElectronClient/services/plugins/UserWebviewDialogButtonBar.js
|
||||
|
@ -214,6 +218,7 @@ ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
|||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||
ReactNativeClient/lib/JoplinServerApi.js
|
||||
ReactNativeClient/lib/Logger.js
|
||||
ReactNativeClient/lib/models/Setting.js
|
||||
ReactNativeClient/lib/ntpDate.js
|
||||
ReactNativeClient/lib/reducer.js
|
||||
|
@ -252,6 +257,7 @@ ReactNativeClient/lib/services/plugins/sandboxProxy.js
|
|||
ReactNativeClient/lib/services/plugins/ToolbarButtonController.js
|
||||
ReactNativeClient/lib/services/plugins/utils/executeSandboxCall.js
|
||||
ReactNativeClient/lib/services/plugins/utils/manifestFromObject.js
|
||||
ReactNativeClient/lib/services/plugins/utils/mapEventHandlersToIds.js
|
||||
ReactNativeClient/lib/services/plugins/utils/types.js
|
||||
ReactNativeClient/lib/services/plugins/ViewController.js
|
||||
ReactNativeClient/lib/services/plugins/WebviewController.js
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { netUtils } = require('lib/net-utils.js');
|
||||
|
||||
const http = require('http');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { dirname } = require('lib/path-utils.js');
|
||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||
|
|
|
@ -2,7 +2,7 @@ const yargParser = require('yargs-parser');
|
|||
const { _ } = require('lib/locale.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const stringPadding = require('string-padding');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
|
||||
const cliUtils = {};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const shim = require('lib/shim');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const { dirname } = require('lib/path-utils.js');
|
||||
const { FsDriverNode } = require('./fs-driver-node.js');
|
||||
|
|
|
@ -23,7 +23,7 @@ const NoteTag = require('lib/models/NoteTag.js');
|
|||
const MasterKey = require('lib/models/MasterKey');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Revision = require('lib/models/Revision.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { FsDriverNode } = require('lib/fs-driver-node.js');
|
||||
const { shimInit } = require('lib/shim-init-node.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
|
|
@ -4,6 +4,7 @@ import sandboxProxy from 'lib/services/plugins/sandboxProxy';
|
|||
import BasePluginRunner from 'lib/services/plugins/BasePluginRunner';
|
||||
import executeSandboxCall from 'lib/services/plugins/utils/executeSandboxCall';
|
||||
import Global from 'lib/services/plugins/api/Global';
|
||||
import mapEventHandlersToIds, { EventHandlers } from 'lib/services/plugins/utils/mapEventHandlersToIds';
|
||||
|
||||
function createConsoleWrapper(pluginId:string) {
|
||||
const wrapper:any = {};
|
||||
|
@ -29,6 +30,8 @@ function createConsoleWrapper(pluginId:string) {
|
|||
|
||||
export default class PluginRunner extends BasePluginRunner {
|
||||
|
||||
private eventHandlers_:EventHandlers = {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
|
@ -37,20 +40,12 @@ export default class PluginRunner extends BasePluginRunner {
|
|||
|
||||
private async eventHandler(eventHandlerId:string, args:any[]) {
|
||||
const cb = this.eventHandlers_[eventHandlerId];
|
||||
delete this.eventHandlers_[eventHandlerId];
|
||||
return cb(...args);
|
||||
}
|
||||
|
||||
private newSandboxProxy(pluginId:string, sandbox:Global) {
|
||||
|
||||
// Note: for desktop, the implementation should be like so:
|
||||
// In the target, we post an IPC message with the path, args, etc. as well as a callbackId to the host
|
||||
// The target saves this callbackId and associate it with a Promise that it returns.
|
||||
// When the host responds back (via IPC), we get the promise back using the callbackId, then call resolve
|
||||
// with what was sent from the host.
|
||||
|
||||
const target = async (path:string, args:any[]) => {
|
||||
return executeSandboxCall(pluginId, sandbox, `joplin.${path}`, this.mapEventHandlersToIds(args), this.eventHandler);
|
||||
return executeSandboxCall(pluginId, sandbox, `joplin.${path}`, mapEventHandlersToIds(args, this.eventHandlers_), this.eventHandler);
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
|
@ -85,7 +85,7 @@ describe('services_PluginService', function() {
|
|||
|
||||
// const folder = await Folder.save({ title: 'folder' });
|
||||
|
||||
// const folderFromApi = await service.executeSandboxCall(plugin.id, 'joplin.api.get', ['folders/' + folder.id]);
|
||||
// const folderFromApi = await service.executeSandboxCall(plugin.id, 'joplin.data.get', ['folders/' + folder.id]);
|
||||
// expect(folder.id).toBe(folderFromApi.id);
|
||||
// expect(folder.title).toBe(folderFromApi.title);
|
||||
// }));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
joplin.plugins.register({
|
||||
onStart: async function() {
|
||||
await joplin.api.post('folders', null, { title: "multi - simple1" });
|
||||
await joplin.data.post('folders', null, { title: "multi - simple1" });
|
||||
},
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
joplin.plugins.register({
|
||||
onStart: async function() {
|
||||
await joplin.api.post('folders', null, { title: "multi - simple2" });
|
||||
await joplin.data.post('folders', null, { title: "multi - simple2" });
|
||||
},
|
||||
});
|
|
@ -11,7 +11,7 @@ joplin.plugins.register({
|
|||
let parentId = null;
|
||||
|
||||
for (const noteId of noteIds) {
|
||||
const note = await joplin.api.get('notes/' + noteId, { fields: ['title', 'body', 'parent_id']});
|
||||
const note = await joplin.data.get('notes/' + noteId, { fields: ['title', 'body', 'parent_id']});
|
||||
newNoteBody.push([
|
||||
'# ' + note.title,
|
||||
'',
|
||||
|
@ -27,7 +27,7 @@ joplin.plugins.register({
|
|||
parent_id: parentId,
|
||||
};
|
||||
|
||||
await joplin.api.post('notes', null, newNote);
|
||||
await joplin.data.post('notes', null, newNote);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
joplin.plugins.register({
|
||||
onStart: async function() {
|
||||
const folder = await joplin.api.post('folders', null, { title: "my plugin folder" });
|
||||
await joplin.api.post('notes', null, { parent_id: folder.id, title: "testing plugin!" });
|
||||
const folder = await joplin.data.post('folders', null, { title: "my plugin folder" });
|
||||
await joplin.data.post('notes', null, { parent_id: folder.id, title: "testing plugin!" });
|
||||
},
|
||||
});
|
|
@ -2,6 +2,6 @@ const testImport = require('./testImport');
|
|||
|
||||
joplin.plugins.register({
|
||||
onStart: async function() {
|
||||
await joplin.api.post('folders', null, { title: testImport() });
|
||||
await joplin.data.post('folders', null, { title: testImport() });
|
||||
},
|
||||
});
|
|
@ -1 +1 @@
|
|||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,u){function i(e){try{a(r.next(e))}catch(e){u(e)}}function l(e){try{a(r.throw(e))}catch(e){u(e)}}function a(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,l)}a((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,u,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return u={next:l(0),throw:l(1),return:l(2)},"function"==typeof Symbol&&(u[Symbol.iterator]=function(){return this}),u;function l(u){return function(l){return function(u){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&u[0]?r.return:u[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,u[1])).done)return o;switch(r=0,o&&(u=[2&u[0],o.value]),u[0]){case 0:case 1:o=u;break;case 4:return i.label++,{value:u[1],done:!1};case 5:i.label++,r=u[1],u=[0];continue;case 7:u=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==u[0]&&2!==u[0])){i=0;continue}if(3===u[0]&&(!o||u[1]>o[0]&&u[1]<o[3])){i.label=u[1];break}if(6===u[0]&&i.label<o[1]){i.label=o[1],o=u;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(u);break}o[2]&&i.ops.pop(),i.trys.pop();continue}u=t.call(e,i)}catch(e){u=[6,e],r=0}finally{n=o=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}([u,l])}}},u=n(1);joplin.plugins.register({onStart:function(){return r(this,void 0,void 0,(function(){return o(this,(function(e){switch(e.label){case 0:return[4,joplin.api.post("folders",null,{title:u("foo",5)})];case 1:return e.sent(),[2]}}))}))}})},function(e,t,n){"use strict";e.exports=function(e,t,n){if((t-=(e+="").length)<=0)return e;n||0===n||(n=" ");if(" "===(n+="")&&t<10)return r[t]+e;var o="";for(;1&t&&(o+=n),t>>=1;)n+=n;return o+e};var r=[""," "," "," "," "," "," "," "," "," "]}]);
|
||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,u){function i(e){try{a(r.next(e))}catch(e){u(e)}}function l(e){try{a(r.throw(e))}catch(e){u(e)}}function a(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,l)}a((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,u,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return u={next:l(0),throw:l(1),return:l(2)},"function"==typeof Symbol&&(u[Symbol.iterator]=function(){return this}),u;function l(u){return function(l){return function(u){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&u[0]?r.return:u[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,u[1])).done)return o;switch(r=0,o&&(u=[2&u[0],o.value]),u[0]){case 0:case 1:o=u;break;case 4:return i.label++,{value:u[1],done:!1};case 5:i.label++,r=u[1],u=[0];continue;case 7:u=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==u[0]&&2!==u[0])){i=0;continue}if(3===u[0]&&(!o||u[1]>o[0]&&u[1]<o[3])){i.label=u[1];break}if(6===u[0]&&i.label<o[1]){i.label=o[1],o=u;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(u);break}o[2]&&i.ops.pop(),i.trys.pop();continue}u=t.call(e,i)}catch(e){u=[6,e],r=0}finally{n=o=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}([u,l])}}},u=n(1);joplin.plugins.register({onStart:function(){return r(this,void 0,void 0,(function(){return o(this,(function(e){switch(e.label){case 0:return[4,joplin.data.post("folders",null,{title:u("foo",5)})];case 1:return e.sent(),[2]}}))}))}})},function(e,t,n){"use strict";e.exports=function(e,t,n){if((t-=(e+="").length)<=0)return e;n||0===n||(n=" ");if(" "===(n+="")&&t<10)return r[t]+e;var o="";for(;1&t&&(o+=n),t>>=1;)n+=n;return o+e};var r=[""," "," "," "," "," "," "," "," "," "]}]);
|
|
@ -2,6 +2,6 @@ const leftPad = require('left-pad');
|
|||
|
||||
joplin.plugins.register({
|
||||
onStart: async function() {
|
||||
await joplin.api.post('folders', null, { title: leftPad('foo', 5) });
|
||||
await joplin.data.post('folders', null, { title: leftPad('foo', 5) });
|
||||
},
|
||||
});
|
|
@ -12,7 +12,7 @@ const Resource = require('lib/models/Resource.js');
|
|||
const Tag = require('lib/models/Tag.js');
|
||||
const NoteTag = require('lib/models/NoteTag.js');
|
||||
const Revision = require('lib/models/Revision.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const MasterKey = require('lib/models/MasterKey');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import Logger from "lib/Logger";
|
||||
import { PluginMessage } from './services/plugins/PluginRunner'
|
||||
|
||||
const { BrowserWindow, Tray, screen } = require('electron');
|
||||
const shim = require('lib/shim');
|
||||
const url = require('url');
|
||||
|
@ -6,31 +9,40 @@ const { dirname } = require('lib/path-utils');
|
|||
const fs = require('fs-extra');
|
||||
const { ipcMain } = require('electron');
|
||||
|
||||
// const { ipcMain } = require('electron')
|
||||
// ipcMain.on('pluginMessage', (event, arg) => {
|
||||
// console.info('PPPPPPPPPPPPPPPPPPPPPP', arg);
|
||||
// })
|
||||
interface RendererProcessQuitReply {
|
||||
canClose: boolean,
|
||||
}
|
||||
|
||||
interface PluginWindows {
|
||||
[key: string]: any,
|
||||
}
|
||||
|
||||
class ElectronAppWrapper {
|
||||
export default class ElectronAppWrapper {
|
||||
|
||||
constructor(electronApp, env, profilePath, isDebugMode) {
|
||||
private logger_:Logger = null;
|
||||
private electronApp_:any;
|
||||
private env_:string;
|
||||
private isDebugMode_:boolean;
|
||||
private profilePath_:string;
|
||||
private win_:any = null;
|
||||
private willQuitApp_:boolean = false;
|
||||
private tray_:any = null;
|
||||
private buildDir_:string = null;
|
||||
private rendererProcessQuitReply_:RendererProcessQuitReply = null;
|
||||
private pluginWindows_:PluginWindows = {};
|
||||
|
||||
constructor(electronApp:any, env:string, profilePath:string, isDebugMode:boolean) {
|
||||
this.electronApp_ = electronApp;
|
||||
this.env_ = env;
|
||||
this.isDebugMode_ = isDebugMode;
|
||||
this.profilePath_ = profilePath;
|
||||
this.win_ = null;
|
||||
this.willQuitApp_ = false;
|
||||
this.tray_ = null;
|
||||
this.buildDir_ = null;
|
||||
this.rendererProcessQuitReply_ = null;
|
||||
}
|
||||
|
||||
electronApp() {
|
||||
return this.electronApp_;
|
||||
}
|
||||
|
||||
setLogger(v) {
|
||||
setLogger(v:Logger) {
|
||||
this.logger_ = v;
|
||||
}
|
||||
|
||||
|
@ -53,7 +65,7 @@ class ElectronAppWrapper {
|
|||
const windowStateKeeper = require('electron-window-state');
|
||||
|
||||
|
||||
const stateOptions = {
|
||||
const stateOptions:any = {
|
||||
defaultWidth: Math.round(0.8 * screen.getPrimaryDisplay().workArea.width),
|
||||
defaultHeight: Math.round(0.8 * screen.getPrimaryDisplay().workArea.height),
|
||||
file: `window-state-${this.env_}.json`,
|
||||
|
@ -64,7 +76,7 @@ class ElectronAppWrapper {
|
|||
// Load the previous state with fallback to defaults
|
||||
const windowState = windowStateKeeper(stateOptions);
|
||||
|
||||
const windowOptions = {
|
||||
const windowOptions:any = {
|
||||
x: windowState.x,
|
||||
y: windowState.y,
|
||||
width: windowState.width,
|
||||
|
@ -86,7 +98,7 @@ class ElectronAppWrapper {
|
|||
if (shim.isLinux()) windowOptions.icon = path.join(__dirname, '..', 'build/icons/128x128.png');
|
||||
|
||||
require('electron-context-menu')({
|
||||
shouldShowMenu: (event, params) => {
|
||||
shouldShowMenu: (_event:any, params:any) => {
|
||||
// params.inputFieldType === 'none' when right-clicking the text editor. This is a bit of a hack to detect it because in this
|
||||
// case we don't want to use the built-in context menu but a custom one.
|
||||
return params.isEditable && params.inputFieldType !== 'none';
|
||||
|
@ -123,7 +135,7 @@ class ElectronAppWrapper {
|
|||
}, 3000);
|
||||
}
|
||||
|
||||
this.win_.on('close', (event) => {
|
||||
this.win_.on('close', (event:any) => {
|
||||
// If it's on macOS, the app is completely closed only if the user chooses to close the app (willQuitApp_ will be true)
|
||||
// otherwise the window is simply hidden, and will be re-open once the app is "activated" (which happens when the
|
||||
// user clicks on the icon in the task bar).
|
||||
|
@ -171,7 +183,7 @@ class ElectronAppWrapper {
|
|||
}
|
||||
});
|
||||
|
||||
ipcMain.on('asynchronous-message', (event, message, args) => {
|
||||
ipcMain.on('asynchronous-message', (_event:any, message:string, args:any) => {
|
||||
if (message === 'appCloseReply') {
|
||||
// We got the response from the renderer process:
|
||||
// save the response and try quit again.
|
||||
|
@ -180,10 +192,21 @@ class ElectronAppWrapper {
|
|||
}
|
||||
});
|
||||
|
||||
ipcMain.on('pluginMessage', (event, data) => {
|
||||
// Forward message
|
||||
if (data.target === 'mainWindow') {
|
||||
this.win_.webContents.send('pluginMessage', data);
|
||||
// This handler receives IPC messages from a plugin or from the main window,
|
||||
// and forwards it to the main window or the plugin window.
|
||||
ipcMain.on('pluginMessage', (_event:any, message:PluginMessage) => {
|
||||
if (message.target === 'mainWindow') {
|
||||
this.win_.webContents.send('pluginMessage', message);
|
||||
}
|
||||
|
||||
if (message.target === 'plugin') {
|
||||
const win = this.pluginWindows_[message.pluginId];
|
||||
if (!win) {
|
||||
this.logger().error('Trying to send IPC message to non-existing plugin window: ' + message.pluginId);
|
||||
return;
|
||||
}
|
||||
|
||||
win.webContents.send('pluginMessage', message);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -200,6 +223,10 @@ class ElectronAppWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
registerPluginWindow(pluginId:string, window:any) {
|
||||
this.pluginWindows_[pluginId] = window;
|
||||
}
|
||||
|
||||
async waitForElectronAppReady() {
|
||||
if (this.electronApp().isReady()) return Promise.resolve();
|
||||
|
||||
|
@ -258,7 +285,7 @@ class ElectronAppWrapper {
|
|||
}
|
||||
|
||||
// Note: this must be called only after the "ready" event of the app has been dispatched
|
||||
createTray(contextMenu) {
|
||||
createTray(contextMenu:any) {
|
||||
try {
|
||||
this.tray_ = new Tray(`${this.buildDir()}/icons/${this.trayIconFilename_()}`);
|
||||
this.tray_.setToolTip(this.electronApp_.name);
|
||||
|
@ -325,5 +352,3 @@ class ElectronAppWrapper {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = { ElectronAppWrapper };
|
|
@ -1,5 +1,5 @@
|
|||
const { _ } = require('lib/locale');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const InteropService = require('lib/services/interop/InteropService').default;
|
||||
const CommandService = require('lib/services/CommandService').default;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
|
|
@ -8,6 +8,8 @@ import { utils as pluginUtils } from 'lib/services/plugins/reducer';
|
|||
// import SandboxImplementation from './plugins/SandboxImplementation';
|
||||
import { MenuItemLocation } from 'lib/services/plugins/MenuItemController';
|
||||
import { defaultState, State } from 'lib/reducer';
|
||||
import PluginRunner from './services/plugins/PluginRunner';
|
||||
import PlatformImplementation from './services/plugins/PlatformImplementation';
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
|
@ -18,7 +20,7 @@ const shim = require('lib/shim');
|
|||
const MasterKey = require('lib/models/MasterKey');
|
||||
const Folder = require('lib/models/Folder');
|
||||
const { _, setLocale } = require('lib/locale.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const fs = require('fs-extra');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
|
@ -31,7 +33,7 @@ const ResourceService = require('lib/services/ResourceService');
|
|||
const ClipperServer = require('lib/ClipperServer');
|
||||
const actionApi = require('lib/services/rest/actionApi.desktop').default;
|
||||
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { shell, webFrame, clipboard } = require('electron');
|
||||
const Menu = bridge().Menu;
|
||||
const PluginManager = require('lib/services/PluginManager');
|
||||
|
@ -1357,23 +1359,24 @@ class Application extends BaseApplication {
|
|||
pluginLogger.addTarget('console', { prefix: 'Plugin Service:' });
|
||||
pluginLogger.setLevel(Setting.value('env') == 'dev' ? Logger.LEVEL_DEBUG : Logger.LEVEL_INFO);
|
||||
|
||||
const pluginRunner = new PluginRunner();
|
||||
PluginService.instance().setLogger(pluginLogger);
|
||||
// PluginService.instance().initialize(SandboxImplementation.instance(), this.store());
|
||||
PluginService.instance().initialize(PlatformImplementation.instance(), pluginRunner, this.store());
|
||||
|
||||
// try {
|
||||
// if (await shim.fsDriver().exists(Setting.value('pluginDir'))) await PluginService.instance().loadAndRunPlugins(Setting.value('pluginDir'));
|
||||
// } catch (error) {
|
||||
// this.logger().error(`There was an error loading plugins from ${Setting.value('pluginDir')}:`, error);
|
||||
// }
|
||||
try {
|
||||
if (await shim.fsDriver().exists(Setting.value('pluginDir'))) await PluginService.instance().loadAndRunPlugins(Setting.value('pluginDir'));
|
||||
} catch (error) {
|
||||
this.logger().error(`There was an error loading plugins from ${Setting.value('pluginDir')}:`, error);
|
||||
}
|
||||
|
||||
// try {
|
||||
// if (Setting.value('plugins.devPluginPaths')) {
|
||||
// const paths = Setting.value('plugins.devPluginPaths').split(',').map((p:string) => p.trim());
|
||||
// await PluginService.instance().loadAndRunPlugins(paths);
|
||||
// }
|
||||
// } catch (error) {
|
||||
// this.logger().error(`There was an error loading plugins from ${Setting.value('plugins.devPluginPaths')}:`, error);
|
||||
// }
|
||||
try {
|
||||
if (Setting.value('plugins.devPluginPaths')) {
|
||||
const paths = Setting.value('plugins.devPluginPaths').split(',').map((p:string) => p.trim());
|
||||
await PluginService.instance().loadAndRunPlugins(paths);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger().error(`There was an error loading plugins from ${Setting.value('plugins.devPluginPaths')}:`, error);
|
||||
}
|
||||
|
||||
|
||||
// const url = require('url');
|
||||
|
@ -1395,7 +1398,7 @@ class Application extends BaseApplication {
|
|||
|
||||
// const ipcRenderer = require('electron').ipcRenderer;
|
||||
// ipcRenderer.on('pluginMessage', (event:any, data:any) => {
|
||||
// console.info('GOT MESSAGE ON MAIN WINDOW', event, data);
|
||||
// console.info('RENDERER PROCESS got message', data);
|
||||
// });
|
||||
|
||||
|
||||
|
|
|
@ -1,13 +1,26 @@
|
|||
import ElectronAppWrapper from "./ElectronAppWrapper";
|
||||
|
||||
const { _, setLocale } = require('lib/locale.js');
|
||||
const shim = require('lib/shim');
|
||||
const { dirname, toSystemSlashes } = require('lib/path-utils.js');
|
||||
const { BrowserWindow, nativeTheme } = require('electron');
|
||||
|
||||
class Bridge {
|
||||
interface LastSelectedPath {
|
||||
file: string,
|
||||
directory: string,
|
||||
}
|
||||
|
||||
constructor(electronWrapper) {
|
||||
interface LastSelectedPaths {
|
||||
[key:string]: LastSelectedPath,
|
||||
}
|
||||
|
||||
export class Bridge {
|
||||
|
||||
private electronWrapper_:ElectronAppWrapper;
|
||||
private lastSelectedPaths_:LastSelectedPaths;
|
||||
|
||||
constructor(electronWrapper:ElectronAppWrapper) {
|
||||
this.electronWrapper_ = electronWrapper;
|
||||
this.autoUpdateLogger_ = null;
|
||||
this.lastSelectedPaths_ = {
|
||||
file: null,
|
||||
directory: null,
|
||||
|
@ -30,11 +43,11 @@ class Bridge {
|
|||
return this.electronWrapper_.window();
|
||||
}
|
||||
|
||||
showItemInFolder(fullPath) {
|
||||
showItemInFolder(fullPath:string) {
|
||||
return require('electron').shell.showItemInFolder(toSystemSlashes(fullPath));
|
||||
}
|
||||
|
||||
newBrowserWindow(options) {
|
||||
newBrowserWindow(options:any) {
|
||||
return new BrowserWindow(options);
|
||||
}
|
||||
|
||||
|
@ -50,7 +63,7 @@ class Bridge {
|
|||
return { width: s[0], height: s[1] };
|
||||
}
|
||||
|
||||
windowSetSize(width, height) {
|
||||
windowSetSize(width:number, height:number) {
|
||||
if (!this.window()) return;
|
||||
return this.window().setSize(width, height);
|
||||
}
|
||||
|
@ -63,7 +76,7 @@ class Bridge {
|
|||
return this.window().webContents.closeDevTools();
|
||||
}
|
||||
|
||||
showSaveDialog(options) {
|
||||
showSaveDialog(options:any) {
|
||||
const { dialog } = require('electron');
|
||||
if (!options) options = {};
|
||||
if (!('defaultPath' in options) && this.lastSelectedPaths_.file) options.defaultPath = this.lastSelectedPaths_.file;
|
||||
|
@ -74,7 +87,7 @@ class Bridge {
|
|||
return filePath;
|
||||
}
|
||||
|
||||
showOpenDialog(options) {
|
||||
showOpenDialog(options:any) {
|
||||
const { dialog } = require('electron');
|
||||
if (!options) options = {};
|
||||
let fileType = 'file';
|
||||
|
@ -89,13 +102,13 @@ class Bridge {
|
|||
}
|
||||
|
||||
// Don't use this directly - call one of the showXxxxxxxMessageBox() instead
|
||||
showMessageBox_(window, options) {
|
||||
showMessageBox_(window:any, options:any) {
|
||||
const { dialog } = require('electron');
|
||||
if (!window) window = this.window();
|
||||
return dialog.showMessageBoxSync(window, options);
|
||||
}
|
||||
|
||||
showErrorMessageBox(message) {
|
||||
showErrorMessageBox(message:string) {
|
||||
return this.showMessageBox_(this.window(), {
|
||||
type: 'error',
|
||||
message: message,
|
||||
|
@ -103,7 +116,7 @@ class Bridge {
|
|||
});
|
||||
}
|
||||
|
||||
showConfirmMessageBox(message, options = null) {
|
||||
showConfirmMessageBox(message:string, options:any = null) {
|
||||
if (options === null) options = {};
|
||||
|
||||
const result = this.showMessageBox_(this.window(), Object.assign({}, {
|
||||
|
@ -117,7 +130,7 @@ class Bridge {
|
|||
}
|
||||
|
||||
/* returns the index of the clicked button */
|
||||
showMessageBox(message, options = null) {
|
||||
showMessageBox(message:string, options:any = null) {
|
||||
if (options === null) options = {};
|
||||
|
||||
const result = this.showMessageBox_(this.window(), Object.assign({}, {
|
||||
|
@ -129,7 +142,7 @@ class Bridge {
|
|||
return result;
|
||||
}
|
||||
|
||||
showInfoMessageBox(message, options = {}) {
|
||||
showInfoMessageBox(message:string, options:any = {}) {
|
||||
const result = this.showMessageBox_(this.window(), Object.assign({}, {
|
||||
type: 'info',
|
||||
message: message,
|
||||
|
@ -138,7 +151,7 @@ class Bridge {
|
|||
return result === 0;
|
||||
}
|
||||
|
||||
setLocale(locale) {
|
||||
setLocale(locale:string) {
|
||||
setLocale(locale);
|
||||
}
|
||||
|
||||
|
@ -150,15 +163,15 @@ class Bridge {
|
|||
return require('electron').MenuItem;
|
||||
}
|
||||
|
||||
openExternal(url) {
|
||||
openExternal(url:string) {
|
||||
return require('electron').shell.openExternal(url);
|
||||
}
|
||||
|
||||
openItem(fullPath) {
|
||||
openItem(fullPath:string) {
|
||||
return require('electron').shell.openItem(fullPath);
|
||||
}
|
||||
|
||||
checkForUpdates(inBackground, window, logFilePath, options) {
|
||||
checkForUpdates(inBackground:boolean, window:any, logFilePath:string, options:any) {
|
||||
const { checkForUpdates } = require('./checkForUpdates.js');
|
||||
checkForUpdates(inBackground, window, logFilePath, options);
|
||||
}
|
||||
|
@ -175,7 +188,7 @@ class Bridge {
|
|||
return nativeTheme.shouldUseDarkColors;
|
||||
}
|
||||
|
||||
addEventListener(name, fn) {
|
||||
addEventListener(name:string, fn:Function) {
|
||||
if (name === 'nativeThemeUpdated') {
|
||||
nativeTheme.on('updated', fn);
|
||||
} else {
|
||||
|
@ -205,17 +218,15 @@ class Bridge {
|
|||
|
||||
}
|
||||
|
||||
let bridge_ = null;
|
||||
let bridge_:Bridge = null;
|
||||
|
||||
function initBridge(wrapper) {
|
||||
export function initBridge(wrapper:ElectronAppWrapper) {
|
||||
if (bridge_) throw new Error('Bridge already initialized');
|
||||
bridge_ = new Bridge(wrapper);
|
||||
return bridge_;
|
||||
}
|
||||
|
||||
function bridge() {
|
||||
export default function bridge() {
|
||||
if (!bridge_) throw new Error('Bridge not initialized');
|
||||
return bridge_;
|
||||
}
|
||||
|
||||
module.exports = { bridge, initBridge };
|
|
@ -1,6 +1,6 @@
|
|||
const { dialog } = require('electron');
|
||||
const shim = require('lib/shim');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { _ } = require('lib/locale.js');
|
||||
const fetch = require('node-fetch');
|
||||
const { fileExtension } = require('lib/path-utils.js');
|
||||
|
|
|
@ -2,7 +2,7 @@ import { CommandRuntime, CommandDeclaration } from '../lib/services/CommandServi
|
|||
const { _ } = require('lib/locale');
|
||||
const Note = require('lib/models/Note');
|
||||
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
interface Props {
|
||||
noteId: string
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const ClipperServer = require('lib/ClipperServer');
|
||||
|
|
|
@ -9,7 +9,7 @@ const pathUtils = require('lib/path-utils.js');
|
|||
const { _ } = require('lib/locale.js');
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
|
||||
const shared = require('lib/components/shared/config-shared.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { EncryptionConfigScreen } = require('../EncryptionConfigScreen.min');
|
||||
const { ClipperConfigScreen } = require('../ClipperConfigScreen.min');
|
||||
const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
|||
import ButtonBar from './ConfigScreen/ButtonBar';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Shared = require('lib/components/shared/dropbox-login-shared');
|
||||
|
|
|
@ -8,7 +8,7 @@ const { time } = require('lib/time-utils.js');
|
|||
const shim = require('lib/shim');
|
||||
const dialogs = require('./dialogs');
|
||||
const shared = require('lib/components/shared/encryption-config-shared.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
class EncryptionConfigScreenComponent extends React.Component {
|
||||
constructor() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const React = require('react');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const styleSelector = require('./style/ExtensionBadge');
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import useKeymap from './utils/useKeymap';
|
|||
import useCommandStatus from './utils/useCommandStatus';
|
||||
import styles_ from './styles';
|
||||
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const shim = require('lib/shim');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ const Setting = require('lib/models/Setting').default;
|
|||
const shim = require('lib/shim');
|
||||
const { themeStyle } = require('lib/theme.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const PluginManager = require('lib/services/PluginManager');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
|
|
@ -2,7 +2,7 @@ import { CommandRuntime, CommandDeclaration } from '../../../lib/services/Comman
|
|||
const Note = require('lib/models/Note');
|
||||
const { _ } = require('lib/locale');
|
||||
const shim = require('lib/shim');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const InteropServiceHelper = require('../../../InteropServiceHelper.js');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { CommandDeclaration, CommandRuntime } from '../../../lib/services/CommandService';
|
||||
const { _ } = require('lib/locale');
|
||||
const Folder = require('lib/models/Folder');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'newFolder',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
const { _ } = require('lib/locale');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'print',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
const Folder = require('lib/models/Folder');
|
||||
const { _ } = require('lib/locale');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'renameFolder',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
const Tag = require('lib/models/Tag');
|
||||
const { _ } = require('lib/locale');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'renameTag',
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as React from 'react';
|
|||
import NoteListUtils from './utils/NoteListUtils';
|
||||
|
||||
const { buildStyle } = require('lib/theme');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
interface MultiNoteActionsProps {
|
||||
themeId: number,
|
||||
|
|
|
@ -2,7 +2,7 @@ const React = require('react');
|
|||
const Component = React.Component;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { connect } = require('react-redux');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
class NavigatorComponent extends Component {
|
||||
UNSAFE_componentWillReceiveProps(newProps) {
|
||||
|
|
|
@ -15,7 +15,7 @@ import usePluginServiceRegistration from '../../utils/usePluginServiceRegistrati
|
|||
import Setting from 'lib/models/Setting';
|
||||
|
||||
// @ts-ignore
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
// @ts-ignore
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { clipboard } = require('electron');
|
||||
|
|
|
@ -35,7 +35,7 @@ const usePrevious = require('lib/hooks/usePrevious').default;
|
|||
const Setting = require('lib/models/Setting').default;
|
||||
const { _ } = require('lib/locale');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
|
||||
const NoteRevisionViewer = require('../NoteRevisionViewer.min');
|
||||
const TagList = require('../TagList.min.js');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import ResourceEditWatcher from '../../../lib/services/ResourceEditWatcher/index';
|
||||
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
|
|
|
@ -3,7 +3,7 @@ const Note = require('lib/models/Note.js');
|
|||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const shim = require('lib/shim');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const joplinRendererUtils = require('lib/joplin-renderer').utils;
|
||||
|
|
|
@ -6,7 +6,7 @@ const BaseItem = require('lib/models/BaseItem');
|
|||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { urlDecode } = require('lib/string-utils');
|
||||
const urlUtils = require('lib/urlUtils');
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher.js');
|
||||
|
|
|
@ -8,7 +8,7 @@ const { time } = require('lib/time-utils.js');
|
|||
const { themeStyle } = require('lib/theme');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const Note = require('lib/models/Note');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const NoteListItem = require('../NoteListItem').default;
|
||||
|
|
|
@ -6,7 +6,7 @@ const DialogButtonRow = require('./DialogButtonRow.min');
|
|||
const Datetime = require('react-datetime');
|
||||
const Note = require('lib/models/Note');
|
||||
const formatcoords = require('formatcoords');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const shim = require('lib/shim');
|
||||
|
||||
class NotePropertiesDialog extends React.Component {
|
||||
|
|
|
@ -14,7 +14,7 @@ const { MarkupToHtml } = require('lib/joplin-renderer');
|
|||
const { time } = require('lib/time-utils.js');
|
||||
const ReactTooltip = require('react-tooltip');
|
||||
const { urlDecode, substrWithEllipsis } = require('lib/string-utils');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const markupLanguageUtils = require('lib/markupLanguageUtils');
|
||||
|
||||
class NoteRevisionViewerComponent extends React.PureComponent {
|
||||
|
|
|
@ -4,7 +4,7 @@ import ButtonBar from './ConfigScreen/ButtonBar';
|
|||
const { connect } = require('react-redux');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { OneDriveApiNodeUtils } = require('lib/onedrive-api-node-utils.js');
|
||||
|
|
|
@ -4,7 +4,7 @@ import ButtonBar from './ConfigScreen/ButtonBar';
|
|||
const { connect } = require('react-redux');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const prettyBytes = require('pretty-bytes');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ const { ResourceScreen } = require('./ResourceScreen.js');
|
|||
const { Navigator } = require('./Navigator.min.js');
|
||||
const WelcomeUtils = require('lib/WelcomeUtils');
|
||||
const { ThemeProvider, StyleSheetManager, createGlobalStyle } = require('styled-components');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
interface Props {
|
||||
themeId: number,
|
||||
|
|
|
@ -5,7 +5,7 @@ import useSyncTargetUpgrade, { SyncTargetUpgradeResult } from 'lib/services/sync
|
|||
const { render } = require('react-dom');
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
function useAppCloseHandler(upgradeResult:SyncTargetUpgradeResult) {
|
||||
useEffect(function() {
|
||||
|
|
|
@ -15,7 +15,7 @@ const Tag = require('lib/models/Tag.js');
|
|||
const shim = require('lib/shim');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
const InteropServiceHelper = require('../../InteropServiceHelper.js');
|
||||
|
|
|
@ -4,7 +4,7 @@ import ButtonBar from '../ConfigScreen/ButtonBar';
|
|||
|
||||
const { connect } = require('react-redux');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const React = require('react');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
class VerticalResizer extends React.PureComponent {
|
||||
constructor() {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { utils as pluginUtils, PluginStates } from 'lib/services/plugins/reducer
|
|||
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
const eventManager = require('lib/eventManager').default;
|
||||
|
|
|
@ -23,11 +23,11 @@ const NoteTag = require('lib/models/NoteTag.js');
|
|||
const MasterKey = require('lib/models/MasterKey');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Revision = require('lib/models/Revision.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { FsDriverNode } = require('lib/fs-driver-node.js');
|
||||
const { shimInit } = require('lib/shim-init-node.js');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { FileApiDriverLocal } = require('lib/file-api-driver-local.js');
|
||||
|
||||
if (bridge().env() === 'dev') {
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const electronApp = require('electron').app;
|
||||
const { ElectronAppWrapper } = require('./ElectronAppWrapper');
|
||||
const ElectronAppWrapper = require('./ElectronAppWrapper').default;
|
||||
const { initBridge } = require('./bridge');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { FsDriverNode } = require('lib/fs-driver-node.js');
|
||||
const envFromArgs = require('lib/envFromArgs');
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
// Just a convenient wrapper to get a typed bridge in TypeScript
|
||||
|
||||
import { Bridge } from '../bridge';
|
||||
|
||||
const remoteBridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
export default function bridge():Bridge {
|
||||
return remoteBridge();
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
import Plugin from 'lib/services/plugins/Plugin';
|
||||
import BasePluginRunner from 'lib/services/plugins/BasePluginRunner';
|
||||
import executeSandboxCall from 'lib/services/plugins/utils/executeSandboxCall';
|
||||
import Global from 'lib/services/plugins/api/Global';
|
||||
import bridge from '../bridge';
|
||||
import Setting from 'lib/models/Setting';
|
||||
import { EventHandlers } from 'lib/services/plugins/utils/mapEventHandlersToIds';
|
||||
const shim = require('lib/shim');
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
||||
enum PluginMessageTarget {
|
||||
MainWindow = 'mainWindow',
|
||||
Plugin = 'plugin',
|
||||
}
|
||||
|
||||
export interface PluginMessage {
|
||||
target: PluginMessageTarget,
|
||||
pluginId: string,
|
||||
callbackId?: string,
|
||||
path?: string,
|
||||
args?: any[],
|
||||
result?: any,
|
||||
error?: any,
|
||||
}
|
||||
|
||||
function mapEventIdsToHandlers(pluginId:string, arg:any) {
|
||||
if (Array.isArray(arg)) {
|
||||
for (let i = 0; i < arg.length; i++) {
|
||||
arg[i] = mapEventIdsToHandlers(pluginId, arg[i]);
|
||||
}
|
||||
return arg;
|
||||
} else if (typeof arg === 'string' && arg.indexOf('___plugin_event_') === 0) {
|
||||
const eventId = arg;
|
||||
|
||||
return async (...args:any[]) => {
|
||||
ipcRenderer.send('pluginMessage', {
|
||||
target: PluginMessageTarget.Plugin,
|
||||
pluginId: pluginId,
|
||||
eventId: eventId,
|
||||
args: args,
|
||||
});
|
||||
};
|
||||
} else if (arg === null) {
|
||||
return null;
|
||||
} else if (arg === undefined) {
|
||||
return undefined;
|
||||
} else if (typeof arg === 'object') {
|
||||
for (const n in arg) {
|
||||
arg[n] = mapEventIdsToHandlers(pluginId, arg[n]);
|
||||
}
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
export default class PluginRunner extends BasePluginRunner {
|
||||
|
||||
protected eventHandlers_:EventHandlers = {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.eventHandler = this.eventHandler.bind(this);
|
||||
}
|
||||
|
||||
private async eventHandler(eventHandlerId:string, args:any[]) {
|
||||
const cb = this.eventHandlers_[eventHandlerId];
|
||||
return cb(...args);
|
||||
}
|
||||
|
||||
async run(plugin:Plugin, pluginApi:Global) {
|
||||
const scriptPath = `${Setting.value('tempDir')}/plugin_${plugin.id}.js`;
|
||||
await shim.fsDriver().writeFile(scriptPath, plugin.scriptText, 'utf8');
|
||||
|
||||
const pluginWindow = bridge().newBrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
});
|
||||
|
||||
bridge().electronApp().registerPluginWindow(plugin.id, pluginWindow);
|
||||
|
||||
pluginWindow.loadURL(`${require('url').format({
|
||||
pathname: require('path').join(__dirname, 'plugin_index.html'),
|
||||
protocol: 'file:',
|
||||
slashes: true,
|
||||
})}?pluginId=${plugin.id}&pluginScript=file://${scriptPath}`); // TODO: escape!!
|
||||
|
||||
pluginWindow.webContents.openDevTools();
|
||||
|
||||
ipcRenderer.on('pluginMessage', async (_event:any, message:PluginMessage) => {
|
||||
if (message.target !== PluginMessageTarget.MainWindow) return;
|
||||
if (message.pluginId !== plugin.id) return;
|
||||
|
||||
const mappedArgs = mapEventIdsToHandlers(plugin.id, message.args);
|
||||
|
||||
let result:any = null;
|
||||
let error:any = null;
|
||||
try {
|
||||
result = await executeSandboxCall(plugin.id, pluginApi, `joplin.${message.path}`, mappedArgs, this.eventHandler);
|
||||
} catch (e) {
|
||||
error = e ? e : new Error('Unknown error');
|
||||
}
|
||||
|
||||
ipcRenderer.send('pluginMessage', {
|
||||
target: PluginMessageTarget.Plugin,
|
||||
pluginId: plugin.id,
|
||||
callbackId: message.callbackId,
|
||||
result: result,
|
||||
error: error,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,24 +1,24 @@
|
|||
(function(globalObject) {
|
||||
// TODO: Not sure if that will work once packaged in Electron
|
||||
const sandboxProxy = require('../lib/services/plugins/sandboxProxy.js').default;
|
||||
const sandboxProxy = require('../../lib/services/plugins/sandboxProxy.js').default;
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const pluginId = urlParams.get('pluginId');
|
||||
|
||||
let callbackId_ = 1;
|
||||
const callbacks_ = {};
|
||||
let eventId_ = 1;
|
||||
const eventHandlers_ = {};
|
||||
|
||||
function mapFunctionsToCallbacks(arg) {
|
||||
function mapEventHandlersToIds(argName, arg) {
|
||||
if (Array.isArray(arg)) {
|
||||
for (let i = 0; i < arg.length; i++) {
|
||||
arg[i] = mapFunctionsToCallbacks(arg[i]);
|
||||
arg[i] = mapEventHandlersToIds(`${i}`, arg[i]);
|
||||
}
|
||||
return arg;
|
||||
} else if (typeof arg === 'function') {
|
||||
const id = `__event#${callbackId_}`;
|
||||
callbackId_++;
|
||||
callbacks_[id] = arg;
|
||||
const id = `___plugin_event_${argName}_${eventId_}`;
|
||||
eventId_++;
|
||||
eventHandlers_[id] = arg;
|
||||
return id;
|
||||
} else if (arg === null) {
|
||||
return null;
|
||||
|
@ -26,16 +26,68 @@
|
|||
return undefined;
|
||||
} else if (typeof arg === 'object') {
|
||||
for (const n in arg) {
|
||||
arg[n] = mapFunctionsToCallbacks(arg[n]);
|
||||
arg[n] = mapEventHandlersToIds(n, arg[n]);
|
||||
}
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
const callbackPromises = {};
|
||||
let callbackIndex = 1;
|
||||
|
||||
const target = (path, args) => {
|
||||
ipcRenderer.send('pluginMessage', { target: 'mainWindow', pluginId: pluginId, path: path, args: mapFunctionsToCallbacks(args) });
|
||||
const callbackId = `cb_${pluginId}_${Date.now()}_${callbackIndex++}`;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
callbackPromises[callbackId] = { resolve, reject };
|
||||
});
|
||||
|
||||
ipcRenderer.send('pluginMessage', {
|
||||
target: 'mainWindow',
|
||||
pluginId: pluginId,
|
||||
callbackId: callbackId,
|
||||
path: path,
|
||||
args: mapEventHandlersToIds(null, args),
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
ipcRenderer.on('pluginMessage', (event, message) => {
|
||||
if (message.eventId) {
|
||||
const eventHandler = eventHandlers_[message.eventId];
|
||||
|
||||
if (!eventHandler) {
|
||||
console.error('Got an event ID but no matching event handler: ', message);
|
||||
return;
|
||||
}
|
||||
|
||||
eventHandler(...message.args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.callbackId) {
|
||||
const promise = callbackPromises[message.callbackId];
|
||||
if (!promise) {
|
||||
console.error('Got a callback without matching promise: ', message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.error) {
|
||||
promise.reject(message.error);
|
||||
} else {
|
||||
promise.resolve(message.result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn('Unhandled plugin message:', message);
|
||||
});
|
||||
|
||||
const pluginScriptPath = urlParams.get('pluginScript');
|
||||
const script = document.createElement('script');
|
||||
script.src = pluginScriptPath;
|
||||
document.head.appendChild(script);
|
||||
|
||||
globalObject.joplin = sandboxProxy(target);
|
||||
})(window);
|
||||
|
|
|
@ -10,7 +10,7 @@ const BaseItem = require('lib/models/BaseItem.js');
|
|||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { splitCommandString } = require('lib/string-utils.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const urlParser = require('url');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { randomClipperPort, startPort } = require('lib/randomClipperPort');
|
||||
const enableServerDestroy = require('server-destroy');
|
||||
const Api = require('lib/services/rest/Api');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const shim = require('lib/shim');
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const { time } = require('lib/time-utils');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const shim = require('lib/shim');
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const { rtrimSlashes } = require('lib/path-utils.js');
|
||||
|
|
|
@ -1,22 +1,52 @@
|
|||
const moment = require('moment');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { FsDriverDummy } = require('lib/fs-driver-dummy.js');
|
||||
|
||||
enum TargetType {
|
||||
Database = 'database',
|
||||
File = 'file',
|
||||
Console = 'console',
|
||||
}
|
||||
|
||||
enum LogLevel {
|
||||
None = 0,
|
||||
Error = 10,
|
||||
Warn = 20,
|
||||
Info = 30,
|
||||
Debug = 40,
|
||||
}
|
||||
|
||||
interface Target {
|
||||
type: TargetType,
|
||||
level?: LogLevel,
|
||||
database?: any,
|
||||
console?: any,
|
||||
prefix?: string,
|
||||
path?: string,
|
||||
source?: string,
|
||||
}
|
||||
|
||||
class Logger {
|
||||
constructor() {
|
||||
this.targets_ = [];
|
||||
this.level_ = Logger.LEVEL_INFO;
|
||||
this.fileAppendQueue_ = [];
|
||||
this.lastDbCleanup_ = time.unixMs();
|
||||
}
|
||||
|
||||
// For backward compatibility
|
||||
public static LEVEL_NONE = LogLevel.None;
|
||||
public static LEVEL_ERROR = LogLevel.Error;
|
||||
public static LEVEL_WARN = LogLevel.Warn;
|
||||
public static LEVEL_INFO = LogLevel.Info;
|
||||
public static LEVEL_DEBUG = LogLevel.Debug;
|
||||
|
||||
public static fsDriver_:any = null;
|
||||
|
||||
private targets_:Target[] = [];
|
||||
private level_:LogLevel = LogLevel.Info;
|
||||
private lastDbCleanup_:number = time.unixMs();
|
||||
|
||||
static fsDriver() {
|
||||
if (!Logger.fsDriver_) Logger.fsDriver_ = new FsDriverDummy();
|
||||
return Logger.fsDriver_;
|
||||
}
|
||||
|
||||
setLevel(level) {
|
||||
setLevel(level:LogLevel) {
|
||||
this.level_ = level;
|
||||
}
|
||||
|
||||
|
@ -28,25 +58,22 @@ class Logger {
|
|||
return this.targets_;
|
||||
}
|
||||
|
||||
clearTargets() {
|
||||
this.targets_.clear();
|
||||
}
|
||||
|
||||
addTarget(type, options = null) {
|
||||
addTarget(type:TargetType, options:any = null) {
|
||||
const target = { type: type };
|
||||
for (const n in options) {
|
||||
if (!options.hasOwnProperty(n)) continue;
|
||||
target[n] = options[n];
|
||||
(target as any)[n] = options[n];
|
||||
}
|
||||
|
||||
this.targets_.push(target);
|
||||
}
|
||||
|
||||
objectToString(object) {
|
||||
objectToString(object:any) {
|
||||
let output = '';
|
||||
|
||||
if (typeof object === 'object') {
|
||||
if (object instanceof Error) {
|
||||
object = object as any;
|
||||
output = object.toString();
|
||||
if (object.code) output += `\nCode: ${object.code}`;
|
||||
if (object.headers) output += `\nHeader: ${JSON.stringify(object.headers)}`;
|
||||
|
@ -62,7 +89,7 @@ class Logger {
|
|||
return output;
|
||||
}
|
||||
|
||||
objectsToString(...object) {
|
||||
objectsToString(...object:any[]) {
|
||||
const output = [];
|
||||
for (let i = 0; i < object.length; i++) {
|
||||
output.push(`"${this.objectToString(object[i])}"`);
|
||||
|
@ -84,9 +111,9 @@ class Logger {
|
|||
}
|
||||
|
||||
// Only for database at the moment
|
||||
async lastEntries(limit = 100, options = null) {
|
||||
async lastEntries(limit:number = 100, options:any = null) {
|
||||
if (options === null) options = {};
|
||||
if (!options.levels) options.levels = [Logger.LEVEL_DEBUG, Logger.LEVEL_INFO, Logger.LEVEL_WARN, Logger.LEVEL_ERROR];
|
||||
if (!options.levels) options.levels = [LogLevel.Debug, LogLevel.Info, LogLevel.Warn, LogLevel.Error];
|
||||
if (!options.levels.length) return [];
|
||||
|
||||
for (let i = 0; i < this.targets_.length; i++) {
|
||||
|
@ -100,12 +127,12 @@ class Logger {
|
|||
return [];
|
||||
}
|
||||
|
||||
targetLevel(target) {
|
||||
targetLevel(target:Target) {
|
||||
if ('level' in target) return target.level;
|
||||
return this.level();
|
||||
}
|
||||
|
||||
log(level, ...object) {
|
||||
log(level:LogLevel, ...object:any[]) {
|
||||
if (!this.targets_.length) return;
|
||||
|
||||
const timestamp = moment().format('YYYY-MM-DD HH:mm:ss');
|
||||
|
@ -118,9 +145,9 @@ class Logger {
|
|||
|
||||
if (target.type == 'console') {
|
||||
let fn = 'log';
|
||||
if (level == Logger.LEVEL_ERROR) fn = 'error';
|
||||
if (level == Logger.LEVEL_WARN) fn = 'warn';
|
||||
if (level == Logger.LEVEL_INFO) fn = 'info';
|
||||
if (level == LogLevel.Error) fn = 'error';
|
||||
if (level == LogLevel.Warn) fn = 'warn';
|
||||
if (level == LogLevel.Info) fn = 'info';
|
||||
const consoleObj = target.console ? target.console : console;
|
||||
let items = [moment().format('HH:mm:ss')];
|
||||
if (target.prefix) {
|
||||
|
@ -160,55 +187,41 @@ class Logger {
|
|||
}
|
||||
}
|
||||
|
||||
error(...object) {
|
||||
return this.log(Logger.LEVEL_ERROR, ...object);
|
||||
error(...object:any[]) {
|
||||
return this.log(LogLevel.Error, ...object);
|
||||
}
|
||||
warn(...object) {
|
||||
return this.log(Logger.LEVEL_WARN, ...object);
|
||||
warn(...object:any[]) {
|
||||
return this.log(LogLevel.Warn, ...object);
|
||||
}
|
||||
info(...object) {
|
||||
return this.log(Logger.LEVEL_INFO, ...object);
|
||||
info(...object:any[]) {
|
||||
return this.log(LogLevel.Info, ...object);
|
||||
}
|
||||
debug(...object) {
|
||||
return this.log(Logger.LEVEL_DEBUG, ...object);
|
||||
debug(...object:any[]) {
|
||||
return this.log(LogLevel.Debug, ...object);
|
||||
}
|
||||
|
||||
static levelStringToId(s) {
|
||||
if (s == 'none') return Logger.LEVEL_NONE;
|
||||
if (s == 'error') return Logger.LEVEL_ERROR;
|
||||
if (s == 'warn') return Logger.LEVEL_WARN;
|
||||
if (s == 'info') return Logger.LEVEL_INFO;
|
||||
if (s == 'debug') return Logger.LEVEL_DEBUG;
|
||||
throw new Error(_('Unknown log level: %s', s));
|
||||
static levelStringToId(s:string) {
|
||||
if (s == 'none') return LogLevel.None;
|
||||
if (s == 'error') return LogLevel.Error;
|
||||
if (s == 'warn') return LogLevel.Warn;
|
||||
if (s == 'info') return LogLevel.Info;
|
||||
if (s == 'debug') return LogLevel.Debug;
|
||||
throw new Error('Unknown log level: ' + s);
|
||||
}
|
||||
|
||||
static levelIdToString(id) {
|
||||
if (id == Logger.LEVEL_NONE) return 'none';
|
||||
if (id == Logger.LEVEL_ERROR) return 'error';
|
||||
if (id == Logger.LEVEL_WARN) return 'warn';
|
||||
if (id == Logger.LEVEL_INFO) return 'info';
|
||||
if (id == Logger.LEVEL_DEBUG) return 'debug';
|
||||
throw new Error(_('Unknown level ID: %s', id));
|
||||
static levelIdToString(id:LogLevel) {
|
||||
if (id == LogLevel.None) return 'none';
|
||||
if (id == LogLevel.Error) return 'error';
|
||||
if (id == LogLevel.Warn) return 'warn';
|
||||
if (id == LogLevel.Info) return 'info';
|
||||
if (id == LogLevel.Debug) return 'debug';
|
||||
throw new Error('Unknown level ID: ' + id);
|
||||
}
|
||||
|
||||
static levelIds() {
|
||||
return [Logger.LEVEL_NONE, Logger.LEVEL_ERROR, Logger.LEVEL_WARN, Logger.LEVEL_INFO, Logger.LEVEL_DEBUG];
|
||||
return [LogLevel.None, LogLevel.Error, LogLevel.Warn, LogLevel.Info, LogLevel.Debug];
|
||||
}
|
||||
|
||||
static levelEnum() {
|
||||
const output = {};
|
||||
const ids = this.levelIds();
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
output[ids[i]] = this.levelIdToString(ids[i]);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LEVEL_NONE = 0;
|
||||
Logger.LEVEL_ERROR = 10;
|
||||
Logger.LEVEL_WARN = 20;
|
||||
Logger.LEVEL_INFO = 30;
|
||||
Logger.LEVEL_DEBUG = 40;
|
||||
|
||||
module.exports = { Logger };
|
||||
export default Logger;
|
|
@ -1,6 +1,6 @@
|
|||
const { time } = require('lib/time-utils.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
|
||||
class TaskQueue {
|
||||
constructor(name) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const shim = require('lib/shim');
|
||||
const parseXmlString = require('xml2js').parseString;
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
|
|
|
@ -6,7 +6,7 @@ const { reg } = require('lib/registry.js');
|
|||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { time } = require('lib/time-utils');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const Mutex = require('async-mutex').Mutex;
|
||||
const shim = require('lib/shim');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const { isHidden } = require('lib/path-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const shim = require('lib/shim');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const ntpClient = require('lib/vendor/ntp-client');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Mutex = require('async-mutex').Mutex;
|
||||
|
||||
let nextSyncTime = 0;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const shim = require('lib/shim');
|
||||
const { stringify } = require('query-string');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
class OneDriveApi {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
|
||||
class ReactLogger extends Logger {}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const shim = require('lib/shim');
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const notifier = require('node-notifier');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const shim = require('lib/shim');
|
||||
|
||||
class AlarmServiceDriverNode {
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import Logger from 'lib/Logger';
|
||||
|
||||
export default class BaseService {
|
||||
|
||||
static logger_:any = null;
|
||||
protected instanceLogger_:any = null;
|
||||
static logger_:Logger = null;
|
||||
protected instanceLogger_:Logger = null;
|
||||
|
||||
logger() {
|
||||
logger():Logger {
|
||||
if (this.instanceLogger_) return this.instanceLogger_;
|
||||
if (!BaseService.logger_) throw new Error('BaseService.logger_ not set!!');
|
||||
return BaseService.logger_;
|
||||
}
|
||||
|
||||
setLogger(v:any) {
|
||||
setLogger(v:Logger) {
|
||||
this.instanceLogger_ = v;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ const BaseModel = require('lib/BaseModel');
|
|||
const MasterKey = require('lib/models/MasterKey');
|
||||
const Resource = require('lib/models/Resource');
|
||||
const ResourceService = require('lib/services/ResourceService');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const EventEmitter = require('events');
|
||||
const shim = require('lib/shim');
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const { padLeft } = require('lib/string-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const shim = require('lib/shim');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const MasterKey = require('lib/models/MasterKey');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Note = require('lib/models/Note');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const shim = require('lib/shim');
|
||||
|
@ -7,7 +7,7 @@ const { splitCommandString } = require('lib/string-utils');
|
|||
const { fileExtension, basename } = require('lib/path-utils');
|
||||
const spawn = require('child_process').spawn;
|
||||
const chokidar = require('chokidar');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { ErrorNotFound } = require('./rest/errors');
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const KeymapService = require('lib/services/KeymapService').default;
|
||||
|
||||
class PluginManager {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import AsyncActionQueue from '../../AsyncActionQueue';
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Resource = require('lib/models/Resource');
|
||||
const shim = require('lib/shim');
|
||||
const EventEmitter = require('events');
|
||||
const chokidar = require('chokidar');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
interface WatchedItem {
|
||||
|
|
|
@ -3,7 +3,7 @@ const Setting = require('lib/models/Setting').default;
|
|||
const BaseService = require('lib/services/BaseService').default;
|
||||
const ResourceService = require('lib/services/ResourceService');
|
||||
const { Dirnames } = require('lib/services/synchronizer/utils/types');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const EventEmitter = require('events');
|
||||
const shim = require('lib/shim');
|
||||
|
||||
|
|
|
@ -2,39 +2,8 @@ import Plugin from './Plugin';
|
|||
import BaseService from '../BaseService';
|
||||
import Global from './api/Global';
|
||||
|
||||
interface EventHandlers {
|
||||
[key:string]: Function;
|
||||
}
|
||||
|
||||
export default abstract class BasePluginRunner extends BaseService {
|
||||
|
||||
private eventHandlerIndex_:number = 0;
|
||||
protected eventHandlers_:EventHandlers = {};
|
||||
|
||||
protected mapEventHandlersToIds(arg:any) {
|
||||
if (Array.isArray(arg)) {
|
||||
for (let i = 0; i < arg.length; i++) {
|
||||
arg[i] = this.mapEventHandlersToIds(arg[i]);
|
||||
}
|
||||
return arg;
|
||||
} else if (typeof arg === 'function') {
|
||||
const id = `__event#${this.eventHandlerIndex_}`;
|
||||
this.eventHandlerIndex_++;
|
||||
this.eventHandlers_[id] = arg;
|
||||
return id;
|
||||
} else if (arg === null) {
|
||||
return null;
|
||||
} else if (arg === undefined) {
|
||||
return undefined;
|
||||
} else if (typeof arg === 'object') {
|
||||
for (const n in arg) {
|
||||
arg[n] = this.mapEventHandlersToIds(arg[n]);
|
||||
}
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
async run(plugin:Plugin, sandbox:Global) {
|
||||
throw new Error(`Not implemented: ${plugin} / ${sandbox}`);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import Plugin from 'lib/services/plugins/Plugin';
|
||||
import manifestFromObject from 'lib/services/plugins/utils/manifestFromObject';
|
||||
import Global from 'lib/services/plugins/api/Global';
|
||||
import { SandboxContext } from 'lib/services/plugins/utils/types';
|
||||
// import sandboxProxy from 'lib/services/plugins/sandboxProxy';
|
||||
import BasePluginRunner from 'lib/services/plugins/BasePluginRunner';
|
||||
import BaseService from '../BaseService';
|
||||
const shim = require('lib/shim');
|
||||
|
@ -13,10 +11,6 @@ interface Plugins {
|
|||
[key:string]: Plugin
|
||||
}
|
||||
|
||||
// interface Sandboxes {
|
||||
// [key:string]: Sandbox;
|
||||
// }
|
||||
|
||||
function makePluginId(source:string):string {
|
||||
// https://www.npmjs.com/package/slug#options
|
||||
return nodeSlug(source, nodeSlug.defaults.modes['rfc3986']).substr(0,32);
|
||||
|
@ -37,7 +31,6 @@ export default class PluginService extends BaseService {
|
|||
private store_:any = null;
|
||||
private platformImplementation_:any = null;
|
||||
private plugins_:Plugins = {};
|
||||
// private sandboxes_:Sandboxes = {};
|
||||
private runner_:BasePluginRunner = null;
|
||||
|
||||
initialize(platformImplementation:any, runner:BasePluginRunner, store:any) {
|
||||
|
@ -117,134 +110,10 @@ export default class PluginService extends BaseService {
|
|||
}
|
||||
}
|
||||
|
||||
// cliSandbox(pluginId:string) {
|
||||
// let callbackId_ = 1;
|
||||
// const callbacks_:any = {};
|
||||
|
||||
// function mapFunctionsToCallbacks(arg:any) {
|
||||
// if (Array.isArray(arg)) {
|
||||
// for (let i = 0; i < arg.length; i++) {
|
||||
// arg[i] = mapFunctionsToCallbacks(arg[i]);
|
||||
// }
|
||||
// return arg;
|
||||
// } else if (typeof arg === 'function') {
|
||||
// const id = '__event#' + callbackId_;
|
||||
// callbackId_++;
|
||||
// callbacks_[id] = arg;
|
||||
// return id;
|
||||
// } else if (arg === null) {
|
||||
// return null;
|
||||
// } else if (arg === undefined) {
|
||||
// return undefined;
|
||||
// } else if (typeof arg === 'object') {
|
||||
// for (const n in arg) {
|
||||
// arg[n] = mapFunctionsToCallbacks(arg[n]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return arg;
|
||||
// }
|
||||
|
||||
// const target = (path:string, args:any[]) => {
|
||||
// console.info('GOT PATH', path, mapFunctionsToCallbacks(args));
|
||||
// this.executeSandboxCall(pluginId, 'joplin.' + path, mapFunctionsToCallbacks(args));
|
||||
// };
|
||||
|
||||
// return {
|
||||
// joplin: sandboxProxy(target),
|
||||
// }
|
||||
// }
|
||||
|
||||
async runPlugin(plugin:Plugin) {
|
||||
this.plugins_[plugin.id] = plugin;
|
||||
|
||||
// Context contains the data that is sent from the plugin to the app
|
||||
// Currently it only contains the object that's registered when
|
||||
// the plugin calls `joplin.plugins.register()`
|
||||
const context:SandboxContext = {
|
||||
runtime: null,
|
||||
};
|
||||
|
||||
const sandbox = new Global(this.platformImplementation_, plugin, this.store_, context);
|
||||
// this.sandboxes_[plugin.id] = sandbox;
|
||||
|
||||
await this.runner_.run(plugin, sandbox);
|
||||
|
||||
// const vmSandbox = vm.createContext(this.cliSandbox(plugin.id));
|
||||
|
||||
// try {
|
||||
// vm.runInContext(plugin.scriptText, vmSandbox);
|
||||
// } catch (error) {
|
||||
// this.logger().error(`In plugin ${plugin.id}:`, error);
|
||||
// return;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
if (!context.runtime) {
|
||||
throw new Error(`Plugin ${plugin.id}: The plugin was not registered! Call joplin.plugins.register({.....}) from within the plugin.`);
|
||||
}
|
||||
|
||||
if (context.runtime.onStart) {
|
||||
const startTime = Date.now();
|
||||
|
||||
this.logger().info(`Starting plugin: ${plugin.id}`);
|
||||
|
||||
// We don't use `await` when calling onStart because the plugin might be awaiting
|
||||
// in that call too (for example, when opening a dialog on startup) so we don't
|
||||
// want to get stuck here.
|
||||
context.runtime.onStart({}).catch((error) => {
|
||||
// For some reason, error thrown from the executed script do not have the type "Error"
|
||||
// but are instead plain object. So recreate the Error object here so that it can
|
||||
// be handled correctly by loggers, etc.
|
||||
const newError:Error = new Error(error.message);
|
||||
newError.stack = error.stack;
|
||||
this.logger().error(`In plugin ${plugin.id}:`, newError);
|
||||
}).then(() => {
|
||||
this.logger().info(`Finished running onStart handler: ${plugin.id} (Took ${Date.now() - startTime}ms)`);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// vm.createContext(sandbox);
|
||||
|
||||
// try {
|
||||
// vm.runInContext(plugin.scriptText, sandbox);
|
||||
// } catch (error) {
|
||||
// this.logger().error(`In plugin ${plugin.id}:`, error);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (!context.runtime) {
|
||||
// throw new Error(`Plugin ${plugin.id}: The plugin was not registered! Call joplin.plugins.register({.....}) from within the plugin.`);
|
||||
// }
|
||||
|
||||
// if (context.runtime.onStart) {
|
||||
// const startTime = Date.now();
|
||||
|
||||
// this.logger().info(`Starting plugin: ${plugin.id}`);
|
||||
|
||||
// // We don't use `await` when calling onStart because the plugin might be awaiting
|
||||
// // in that call too (for example, when opening a dialog on startup) so we don't
|
||||
// // want to get stuck here.
|
||||
// context.runtime.onStart({}).catch((error) => {
|
||||
// // For some reason, error thrown from the executed script do not have the type "Error"
|
||||
// // but are instead plain object. So recreate the Error object here so that it can
|
||||
// // be handled correctly by loggers, etc.
|
||||
// const newError:Error = new Error(error.message);
|
||||
// newError.stack = error.stack;
|
||||
// this.logger().error(`In plugin ${plugin.id}:`, newError);
|
||||
// }).then(() => {
|
||||
// this.logger().info(`Finished running onStart handler: ${plugin.id} (Took ${Date.now() - startTime}ms)`);
|
||||
// });
|
||||
// }
|
||||
const pluginApi = new Global(this.logger(), this.platformImplementation_, plugin, this.store_);
|
||||
return this.runner_.run(plugin, pluginApi);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { SandboxContext } from '../utils/types';
|
||||
import Plugin from '../Plugin';
|
||||
import Joplin from './Joplin';
|
||||
import Logger from 'lib/Logger';
|
||||
const builtinModules = require('builtin-modules');
|
||||
const shim = require('lib/shim');
|
||||
|
||||
|
@ -16,14 +16,11 @@ function requireWhiteList():string[] {
|
|||
|
||||
export default class Global {
|
||||
|
||||
private context: SandboxContext;
|
||||
private joplin_: Joplin;
|
||||
private consoleWrapper_:any = null;
|
||||
// private :string[] = null;
|
||||
|
||||
constructor(implementation:any, plugin: Plugin, store: any, context: SandboxContext) {
|
||||
this.context = context;
|
||||
this.joplin_ = new Joplin(implementation.joplin, plugin, store, this.context);
|
||||
constructor(logger:Logger, implementation:any, plugin: Plugin, store: any) {
|
||||
this.joplin_ = new Joplin(logger, implementation.joplin, plugin, store);
|
||||
this.consoleWrapper_ = this.createConsoleWrapper(plugin.id);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { SandboxContext } from '../utils/types';
|
||||
import Plugin from '../Plugin';
|
||||
import JoplinData from './JoplinData';
|
||||
import JoplinPlugins from './JoplinPlugins';
|
||||
|
@ -9,10 +8,11 @@ import JoplinViews from './JoplinViews';
|
|||
import JoplinUtils from './JoplinUtils';
|
||||
import JoplinInterop from './JoplinInterop';
|
||||
import JoplinSettings from './JoplinSettings';
|
||||
import Logger from 'lib/Logger';
|
||||
|
||||
export default class Joplin {
|
||||
|
||||
private api_: JoplinData = null;
|
||||
private data_: JoplinData = null;
|
||||
private plugins_: JoplinPlugins = null;
|
||||
private workspace_: JoplinWorkspace = null;
|
||||
private filters_: JoplinFilters = null;
|
||||
|
@ -22,9 +22,9 @@ export default class Joplin {
|
|||
private interop_: JoplinInterop = null;
|
||||
private settings_: JoplinSettings = null;
|
||||
|
||||
constructor(implementation:any, plugin: Plugin, store: any, context: SandboxContext) {
|
||||
this.api_ = new JoplinData();
|
||||
this.plugins_ = new JoplinPlugins(context);
|
||||
constructor(logger:Logger, implementation:any, plugin: Plugin, store: any) {
|
||||
this.data_ = new JoplinData();
|
||||
this.plugins_ = new JoplinPlugins(logger, plugin);
|
||||
this.workspace_ = new JoplinWorkspace(implementation.workspace, store);
|
||||
this.filters_ = new JoplinFilters();
|
||||
this.commands_ = new JoplinCommands();
|
||||
|
@ -34,8 +34,8 @@ export default class Joplin {
|
|||
this.settings_ = new JoplinSettings(plugin);
|
||||
}
|
||||
|
||||
get api(): JoplinData {
|
||||
return this.api_;
|
||||
get data(): JoplinData {
|
||||
return this.data_;
|
||||
}
|
||||
|
||||
get plugins(): JoplinPlugins {
|
||||
|
|
|
@ -1,13 +1,35 @@
|
|||
import { SandboxContext } from '../utils/types';
|
||||
import Plugin from '../Plugin';
|
||||
import Logger from 'lib/Logger';
|
||||
|
||||
export default class JoplinPlugins {
|
||||
|
||||
private context: SandboxContext;
|
||||
private logger: Logger;
|
||||
private plugin: Plugin;
|
||||
|
||||
constructor(context: SandboxContext) {
|
||||
this.context = context;
|
||||
constructor(logger:Logger, plugin:Plugin) {
|
||||
this.logger = logger;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
register(script: any) {
|
||||
this.context.runtime = script;
|
||||
if (script.onStart) {
|
||||
const startTime = Date.now();
|
||||
|
||||
this.logger.info(`Starting plugin: ${this.plugin.id}`);
|
||||
|
||||
// We don't use `await` when calling onStart because the plugin might be awaiting
|
||||
// in that call too (for example, when opening a dialog on startup) so we don't
|
||||
// want to get stuck here.
|
||||
script.onStart({}).catch((error:any) => {
|
||||
// For some reason, error thrown from the executed script do not have the type "Error"
|
||||
// but are instead plain object. So recreate the Error object here so that it can
|
||||
// be handled correctly by loggers, etc.
|
||||
const newError:Error = new Error(error.message);
|
||||
newError.stack = error.stack;
|
||||
this.logger.error(`In plugin ${this.plugin.id}:`, newError);
|
||||
}).then(() => {
|
||||
this.logger.info(`Finished running onStart handler: ${this.plugin.id} (Took ${Date.now() - startTime}ms)`);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ function createEventHandlers(arg:any, eventHandler:EventHandler) {
|
|||
arg[i] = createEventHandlers(arg[i], eventHandler);
|
||||
}
|
||||
return arg;
|
||||
} else if (typeof arg === 'string' && arg.indexOf('__event#') === 0) {
|
||||
} else if (typeof arg === 'string' && arg.indexOf('___plugin_event_') === 0) {
|
||||
const callbackId = arg;
|
||||
return async (...args:any[]) => {
|
||||
const result = await eventHandler(callbackId, args);
|
||||
|
@ -36,6 +36,7 @@ export default async function executeSandboxCall(pluginId:string, sandbox:Global
|
|||
for (const pathFragment of pathFragments) {
|
||||
parent = fn;
|
||||
fn = fn[pathFragment];
|
||||
if (!fn) throw new Error(`Invalid method call: ${path}`);
|
||||
}
|
||||
|
||||
const convertedArgs = createEventHandlers(args, eventHandler);
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
let eventHandlerIndex_ = 1;
|
||||
|
||||
export interface EventHandlers {
|
||||
[key:string]: Function;
|
||||
}
|
||||
|
||||
export default function mapEventHandlersToIds(arg:any, eventHandlers:EventHandlers) {
|
||||
if (Array.isArray(arg)) {
|
||||
for (let i = 0; i < arg.length; i++) {
|
||||
arg[i] = mapEventHandlersToIds(arg[i], eventHandlers);
|
||||
}
|
||||
return arg;
|
||||
} else if (typeof arg === 'function') {
|
||||
const id = `___plugin_event_${eventHandlerIndex_}`;
|
||||
eventHandlerIndex_++;
|
||||
eventHandlers[id] = arg;
|
||||
return id;
|
||||
} else if (arg === null) {
|
||||
return null;
|
||||
} else if (arg === undefined) {
|
||||
return undefined;
|
||||
} else if (typeof arg === 'object') {
|
||||
for (const n in arg) {
|
||||
arg[n] = mapEventHandlersToIds(arg[n], eventHandlers);
|
||||
}
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
|
@ -10,7 +10,7 @@ const Setting = require('lib/models/Setting').default;
|
|||
const htmlUtils = require('lib/htmlUtils');
|
||||
const markupLanguageUtils = require('lib/markupLanguageUtils');
|
||||
const mimeUtils = require('lib/mime-utils.js').mime;
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const md5 = require('md5');
|
||||
const shim = require('lib/shim');
|
||||
const HtmlToMd = require('lib/HtmlToMd');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
|
|
@ -67,7 +67,7 @@ function shimInit() {
|
|||
|
||||
shim.showMessageBox = (message, options = null) => {
|
||||
if (shim.isElectron()) {
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
return bridge().showMessageBox(message, options);
|
||||
} else {
|
||||
throw new Error('Not implemented');
|
||||
|
@ -402,7 +402,7 @@ function shimInit() {
|
|||
shim.Buffer = Buffer;
|
||||
|
||||
shim.openUrl = url => {
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
// Returns true if it opens the file successfully; returns false if it could
|
||||
// not find the file.
|
||||
return bridge().openExternal(url);
|
||||
|
|
|
@ -9,7 +9,7 @@ const MasterKey = require('lib/models/MasterKey.js');
|
|||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { _ } = require('lib/locale.js');
|
||||
const shim = require('lib/shim');
|
||||
// const { filename, fileExtension } = require('lib/path-utils');
|
||||
|
|
|
@ -16,7 +16,7 @@ const { shimInit } = require('lib/shim-init-react.js');
|
|||
const shim = require('lib/shim');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { AppNav } = require('lib/components/app-nav.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget.js');
|
||||
|
|
|
@ -637,7 +637,13 @@
|
|||
"ReactNativeClient/lib/services/plugins/api/JoplinViews.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinWorkspace.js": true,
|
||||
"ElectronClient/services/plugins/PlatformImplementation.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/usePluginServiceRegistration.js": true
|
||||
"ElectronClient/gui/NoteEditor/utils/usePluginServiceRegistration.js": true,
|
||||
"ElectronClient/bridge.js": true,
|
||||
"ElectronClient/ElectronAppWrapper.js": true,
|
||||
"ElectronClient/services/bridge.js": true,
|
||||
"ElectronClient/services/plugins/PluginRunner.js": true,
|
||||
"ReactNativeClient/lib/Logger.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/utils/mapEventHandlersToIds.js": true
|
||||
},
|
||||
"spellright.language": [
|
||||
"en"
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"watch": "node node_modules/typescript/bin/tsc --watch --project tsconfig.dev.json",
|
||||
"build": "gulp build",
|
||||
"updateIgnored": "gulp updateIgnoredTypeScriptBuild",
|
||||
"copyLib": "gulp copyLib",
|
||||
"buildPluginDoc": "typedoc --name 'Joplin Plugin Documentation' --mode file -theme 'Modules/PluginDocTheme/' --readme 'Modules/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out docs/plugin/api/ ReactNativeClient/lib",
|
||||
"setupNewRelease": "node ./Tools/setupNewRelease",
|
||||
"postinstall": "cd Tools && npm i && cd .. && cd ReactNativeClient && npm i && cd .. && cd ElectronClient && npm i && cd .. && cd CliClient && npm i && cd .. && gulp build"
|
||||
|
|
|
@ -28,4 +28,76 @@ The plugin runner also initialises the sandbox proxy and injects it into the plu
|
|||
|
||||
### Plugin API
|
||||
|
||||
The plugin API is a light wrapper over Joplin's internal functions and services. All the platforms share some of the plugin API but there can also be some differences. For example, the desktop app exposes the text editor component commands, and so this part of the plugin API is available only on desktop. The difference between platforms is implemented using the PlatformImplementation class, which is injected in the plugin service on startup.
|
||||
The plugin API is a light wrapper over Joplin's internal functions and services. All the platforms share some of the plugin API but there can also be some differences. For example, the desktop app exposes the text editor component commands, and so this part of the plugin API is available only on desktop. The difference between platforms is implemented using the PlatformImplementation class, which is injected in the plugin service on startup.
|
||||
|
||||
## Handling events between the plugin and the host
|
||||
|
||||
Handling events in plugins is relatively complicated due to the need to send IPC messages and the limitations of the IPC protocol, which in particular cannot transfer functions.
|
||||
|
||||
For example, let's say we define a command in the plugin:
|
||||
|
||||
```typescript
|
||||
joplin.commands.register({
|
||||
name: 'testCommand1',
|
||||
label: 'My Test Command 1',
|
||||
}, {
|
||||
onExecute: (args:any) => {
|
||||
alert('Testing plugin command 1');
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The "onExecute" event handler needs to be called whenever, for example, a toolbar button associated with this command is clicked. The problem is that it is not possible to send a function via IPC (which can only transfer plain objects), so there has to be a translation layer in between.
|
||||
|
||||
The way it is done in Joplin is like so:
|
||||
|
||||
In the **sandbox proxy**, the event handlers are converted to string event IDs and the original event handler is stored in a map before being sent to host via IPC. So in the example above, the command would be converted to this plain object:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: 'testCommand1',
|
||||
label: 'My Test Command 1',
|
||||
}, {
|
||||
onExecute: '___event_handler_123',
|
||||
}
|
||||
```
|
||||
|
||||
Then, still in the sandbox proxy, we'll have a map called something like `eventHandlers`, which now will have this content:
|
||||
|
||||
```typescript
|
||||
eventHandlers['___event_handler_123'] = (args:any) => {
|
||||
alert('Testing plugin command 1');
|
||||
}
|
||||
```
|
||||
|
||||
In the **plugin runner** (Host side), all the event IDs are converted to functions again, but instead of performing the action directly, it posts an IPC message back to the sandbox proxy using the provided event ID.
|
||||
|
||||
So in the host, the command will now look like this:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: 'testCommand1',
|
||||
label: 'My Test Command 1',
|
||||
}, {
|
||||
onExecute: (args:any) => {
|
||||
postMessage('pluginMessage', { eventId: '___event_handler_123', args: args });
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
At this point, any code in the Joplin application can call the `onExecute` function as normal without having to know about the IPC translation layer.
|
||||
|
||||
When the function onExecute is eventually called, the IPC message is sent back to the sandbox proxy, which will decode it and execute it.
|
||||
|
||||
So on the **sandbox proxy**, we'll have something like this:
|
||||
|
||||
```typescript
|
||||
window.addEventListener('message', ((event) => {
|
||||
const eventId = getEventId(event); // Get back the event ID (implementation might be different)
|
||||
const eventArgs = getEventArgs(event); // Get back the args (implementation might be different)
|
||||
if (eventId) {
|
||||
// And call the event handler
|
||||
eventHandlers[eventId](...eventArgs);
|
||||
}
|
||||
}));
|
||||
```
|
Loading…
Reference in New Issue