1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-30 10:36:35 +02:00
joplin/ReactNativeClient/lib/services/plugins/reducer.ts
Laurent Cozic 3d8577a689 Plugins: Added support for content scripts
- For now, supports Markdown-it plugins
- Also fixed slow rendering of notes in some cases
- Simplified how Markdown-It plugins are created and cleaned MdToHtml code

commit 89576de289
Merge: c75aa21f 5292fc14
Author: Laurent Cozic <laurent@cozic.net>
Date:   Wed Oct 21 00:23:00 2020 +0100

    Merge branch 'release-1.3' into plugin_content_scripts

commit c75aa21ffd
Author: Laurent Cozic <laurent@cozic.net>
Date:   Wed Oct 21 00:19:52 2020 +0100

    Fixed tests

commit 075187729d
Author: Laurent Cozic <laurent@cozic.net>
Date:   Wed Oct 21 00:11:53 2020 +0100

    Fixed tests

commit 14696b8c65
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 20 23:27:58 2020 +0100

    Fixed slow rendering of note

commit 61c09f5bf8
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 20 22:35:21 2020 +0100

    Clean up

commit 9f7ea7d865
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 20 20:05:31 2020 +0100

    Updated doc

commit 98bf3bde8d
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 20 19:56:34 2020 +0100

    Finished converting plugins

commit fe90d92e01
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 20 17:52:02 2020 +0100

    Simplified how Markdown-It plugins are created

commit 47c7b864cb
Author: Laurent Cozic <laurent@cozic.net>
Date:   Mon Oct 19 16:40:11 2020 +0100

    Clean up rules

commit d927a238bb
Author: Laurent Cozic <laurent@cozic.net>
Date:   Mon Oct 19 14:29:40 2020 +0100

    Fixed tests

commit 388a56c5dd
Author: Laurent Cozic <laurent@cozic.net>
Date:   Mon Oct 19 14:00:47 2020 +0100

    Add support for content scripts
2020-10-21 00:23:55 +01:00

144 lines
3.4 KiB
TypeScript

import { Draft } from 'immer';
export interface ViewInfo {
view: any,
plugin: any,
}
interface PluginViewState {
id: string,
type: string,
}
interface PluginViewStates {
[key:string]: PluginViewState,
}
interface PluginContentScriptState {
id: string,
path: string,
}
interface PluginContentScriptStates {
[type:string]: PluginContentScriptState[];
}
interface PluginState {
id:string,
contentScripts: PluginContentScriptStates,
views:PluginViewStates,
}
export interface PluginStates {
[key:string]: PluginState;
}
export interface State {
plugins: PluginStates,
}
export const stateRootKey = 'pluginService';
export const defaultState:State = {
plugins: {},
};
export const utils = {
// It is best to use viewsByType instead as this method creates new objects
// which might trigger unecessary renders even when plugin and views haven't changed.
viewInfosByType: function(plugins:PluginStates, type:string):ViewInfo[] {
const output:ViewInfo[] = [];
for (const pluginId in plugins) {
const plugin = plugins[pluginId];
for (const viewId in plugin.views) {
const view = plugin.views[viewId];
if (view.type !== type) continue;
output.push({
plugin: plugin,
view: view,
});
}
}
return output;
},
viewsByType: function(plugins:PluginStates, type:string):any[] {
const output:any[] = [];
for (const pluginId in plugins) {
const plugin = plugins[pluginId];
for (const viewId in plugin.views) {
const view = plugin.views[viewId];
if (view.type !== type) continue;
output.push(view);
}
}
return output;
},
commandNamesFromViews: function(plugins:PluginStates, toolbarType:string):string[] {
const infos = utils.viewInfosByType(plugins, 'toolbarButton');
return infos
.filter((info:ViewInfo) => info.view.location === toolbarType)
.map((info:ViewInfo) => info.view.commandName);
},
};
const reducer = (draft: Draft<any>, action:any) => {
if (action.type.indexOf('PLUGIN_') !== 0) return;
// All actions should be scoped to a plugin, except when adding a new plugin
if (!action.pluginId && action.type !== 'PLUGIN_ADD') throw new Error(`action.pluginId is required. Action was: ${JSON.stringify(action)}`);
try {
switch (action.type) {
case 'PLUGIN_ADD':
if (draft.pluginService.plugins[action.plugin.id]) throw new Error(`Plugin is already loaded: ${JSON.stringify(action)}`);
draft.pluginService.plugins[action.plugin.id] = action.plugin;
break;
case 'PLUGIN_VIEW_ADD':
draft.pluginService.plugins[action.pluginId].views[action.view.id] = { ...action.view };
break;
case 'PLUGIN_VIEW_PROP_SET':
draft.pluginService.plugins[action.pluginId].views[action.id][action.name] = action.value;
break;
case 'PLUGIN_VIEW_PROP_PUSH':
draft.pluginService.plugins[action.pluginId].views[action.id][action.name].push(action.value);
break;
case 'PLUGIN_CONTENT_SCRIPTS_ADD': {
const type = action.contentScript.type;
if (!draft.pluginService.plugins[action.pluginId].contentScripts[type]) draft.pluginService.plugins[action.pluginId].contentScripts[type] = [];
draft.pluginService.plugins[action.pluginId].contentScripts[type].push({
id: action.contentScript.id,
path: action.contentScript.path,
});
break;
}
}
} catch (error) {
error.message = `In plugin reducer: ${error.message} Action: ${JSON.stringify(action)}`;
throw error;
}
};
export default reducer;