Plugins: Allow custom commands to return a result

openProfileDirectory
Laurent Cozic 2020-10-21 01:43:06 +01:00
parent 60a6f714bc
commit 5d39860707
4 changed files with 70 additions and 24 deletions

View File

@ -21,8 +21,12 @@ export interface PluginMessage {
args?: any[],
result?: any,
error?: any,
mainWindowCallbackId?: string,
}
let callbackIndex = 1;
const callbackPromises:any = {};
function mapEventIdsToHandlers(pluginId:string, arg:any) {
if (Array.isArray(arg)) {
for (let i = 0; i < arg.length; i++) {
@ -33,12 +37,21 @@ function mapEventIdsToHandlers(pluginId:string, arg:any) {
const eventId = arg;
return async (...args:any[]) => {
const callbackId = `cb_${pluginId}_${Date.now()}_${callbackIndex++}`;
const promise = new Promise((resolve, reject) => {
callbackPromises[callbackId] = { resolve, reject };
});
ipcRenderer.send('pluginMessage', {
callbackId: callbackId,
target: PluginMessageTarget.Plugin,
pluginId: pluginId,
eventId: eventId,
args: args,
});
return promise;
};
} else if (arg === null) {
return null;
@ -95,26 +108,41 @@ export default class PluginRunner extends BasePluginRunner {
if (message.target !== PluginMessageTarget.MainWindow) return;
if (message.pluginId !== plugin.id) return;
const mappedArgs = mapEventIdsToHandlers(plugin.id, message.args);
const fullPath = `joplin.${message.path}`;
if (message.mainWindowCallbackId) {
const promise = callbackPromises[message.mainWindowCallbackId];
this.logger().debug(`PluginRunner: execute call: ${fullPath}: ${mappedArgs}`);
if (!promise) {
console.error('Got a callback without matching promise: ', message);
return;
}
let result:any = null;
let error:any = null;
try {
result = await executeSandboxCall(plugin.id, pluginApi, fullPath, mappedArgs, this.eventHandler);
} catch (e) {
error = e ? e : new Error('Unknown error');
if (message.error) {
promise.reject(message.error);
} else {
promise.resolve(message.result);
}
} else {
const mappedArgs = mapEventIdsToHandlers(plugin.id, message.args);
const fullPath = `joplin.${message.path}`;
this.logger().debug(`PluginRunner: execute call: ${fullPath}: ${mappedArgs}`);
let result:any = null;
let error:any = null;
try {
result = await executeSandboxCall(plugin.id, pluginApi, fullPath, mappedArgs, this.eventHandler);
} catch (e) {
error = e ? e : new Error('Unknown error');
}
ipcRenderer.send('pluginMessage', {
target: PluginMessageTarget.Plugin,
pluginId: plugin.id,
pluginCallbackId: message.callbackId,
result: result,
error: error,
});
}
ipcRenderer.send('pluginMessage', {
target: PluginMessageTarget.Plugin,
pluginId: plugin.id,
callbackId: message.callbackId,
result: result,
error: error,
});
});
}

View File

@ -53,7 +53,7 @@
return promise;
};
ipcRenderer.on('pluginMessage', (event, message) => {
ipcRenderer.on('pluginMessage', async (_event, message) => {
if (message.eventId) {
const eventHandler = eventHandlers_[message.eventId];
@ -62,12 +62,28 @@
return;
}
eventHandler(...message.args);
let result = null;
let error = null;
try {
result = await eventHandler(...message.args);
} catch (e) {
error = e;
}
if (message.callbackId) {
ipcRenderer.send('pluginMessage', {
target: 'mainWindow',
pluginId: pluginId,
mainWindowCallbackId: message.callbackId,
result: result,
error: error,
});
}
return;
}
if (message.callbackId) {
const promise = callbackPromises[message.callbackId];
if (message.pluginCallbackId) {
const promise = callbackPromises[message.pluginCallbackId];
if (!promise) {
console.error('Got a callback without matching promise: ', message);
return;

View File

@ -203,7 +203,7 @@ export default class CommandService extends BaseService {
delete command.runtime;
}
public async execute(commandName:string, ...args:any[]):Promise<any> {
public async execute(commandName:string, ...args:any[]):Promise<any | void> {
const command = this.commandByName(commandName);
this.logger().info('CommandService::execute:', commandName, args);
return command.runtime.execute({ state: this.store_.getState() }, ...args);

View File

@ -1,4 +1,4 @@
import CommandService, { CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
import CommandService, { CommandContext, CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
import { Command } from './types';
/**
@ -62,7 +62,9 @@ export default class JoplinCommands {
if ('iconName' in command) declaration.iconName = command.iconName;
const runtime:CommandRuntime = {
execute: command.execute,
execute: (_context:CommandContext, ...args:any[]) => {
return command.execute(...args);
},
};
if ('enabledCondition' in command) runtime.enabledCondition = command.enabledCondition;