From ea4a6c157103c6667b4ceae4a01208415645adcd Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 12 Mar 2021 20:50:56 +0100 Subject: [PATCH 01/10] feat: Start working on plugins --- common/config/Config.ts | 7 +++++- common/plugins/Plugin.ts | 31 +++++++++++++++++++++++++ common/plugins/PluginManager.ts | 41 +++++++++++++++++++++++++++++++++ common/plugins/TestPlugin.ts | 8 +++++++ common/utils/utils.ts | 3 +++ src/app/App.ts | 9 ++++++-- src/window/WindowFactory.ts | 11 ++++++--- 7 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 common/plugins/Plugin.ts create mode 100644 common/plugins/PluginManager.ts create mode 100644 common/plugins/TestPlugin.ts diff --git a/common/config/Config.ts b/common/config/Config.ts index 4391bf1..462b755 100644 --- a/common/config/Config.ts +++ b/common/config/Config.ts @@ -6,6 +6,8 @@ import watch from 'node-watch'; import { FontWeight } from 'xterm'; import { UndefinedObject } from '@common/types/types'; import { IShortcut } from '@common/config/shortcuts'; +import { callHook } from '@common/plugins/PluginManager'; +import { processHook } from '@common/plugins/Plugin'; export default class Config { @@ -85,7 +87,10 @@ export default class Config { private readFile(): IConfig { const data = fs.readFileSync(this.CONFIG); - const config: IConfig = JSON.parse(data.toString()); + let config: IConfig = JSON.parse(data.toString()); + + const { param } = callHook('hookConfig', processHook(config)); + config = param; this.config = config; diff --git a/common/plugins/Plugin.ts b/common/plugins/Plugin.ts new file mode 100644 index 0000000..d402387 --- /dev/null +++ b/common/plugins/Plugin.ts @@ -0,0 +1,31 @@ +import { BrowserWindowConstructorOptions } from 'electron'; +import { IConfig } from '@common/config/Config'; +import { isMainProcess } from '@common/utils/utils'; + +export type Process = 'main' | 'renderer'; + +export type ProcessHookParam

= { + + param: P; + process: Process; +} + +export type HookParams = + BrowserWindowConstructorOptions | + ProcessHookParam; + +export const processHook =

(param: P): ProcessHookParam

=> ({ + + param, + process: isMainProcess ? 'main' : 'renderer', +}); + +export interface Plugin { + + onAppLoaded?: () => void; + onAppClosed?: () => void; + onWindowShow?: () => void; + + hookWindowOptions?: (options: BrowserWindowConstructorOptions) => BrowserWindowConstructorOptions; + hookConfig?: (options: ProcessHookParam) => ProcessHookParam; +} diff --git a/common/plugins/PluginManager.ts b/common/plugins/PluginManager.ts new file mode 100644 index 0000000..19373ba --- /dev/null +++ b/common/plugins/PluginManager.ts @@ -0,0 +1,41 @@ +import { HookParams, Plugin } from '@common/plugins/Plugin'; +import { isMainProcess } from '@common/utils/utils'; + +let pluginsLoaded = false; +const plugins: Plugin[] = []; + +const loadPlugins = () => { + + // eslint-disable-next-line @typescript-eslint/no-var-requires + const plugin = require('./TestPlugin').default; + + if(isMainProcess) + callPluginHook(plugin, 'onAppLoaded'); + + plugins.push(plugin); + + pluginsLoaded = true; +} + +export const callHook = (hook: keyof Plugin, param?: T): T => { + + if(!pluginsLoaded) + loadPlugins(); + + let cache: T | undefined = param; + + plugins.forEach((plugin) => { + + cache = callPluginHook(plugin, hook, param); + }); + + return cache as T; +} + +const callPluginHook = (plugin: Plugin, hook: keyof Plugin, param?: T): T => { + + if(hook in plugin) + return param ? plugin[hook](param) : plugin[hook](); + + return param as T; +} diff --git a/common/plugins/TestPlugin.ts b/common/plugins/TestPlugin.ts new file mode 100644 index 0000000..577b6e3 --- /dev/null +++ b/common/plugins/TestPlugin.ts @@ -0,0 +1,8 @@ +import { Plugin } from '@common/plugins/Plugin'; + +const HelloWorld: Plugin = { + + +} + +export default HelloWorld; diff --git a/common/utils/utils.ts b/common/utils/utils.ts index 4eb3d12..d105034 100644 --- a/common/utils/utils.ts +++ b/common/utils/utils.ts @@ -4,10 +4,13 @@ import { IShell } from '@common/config/Config'; import crypto from 'crypto'; export const userDataPath = (electron.app || electron.remote.app).getPath('userData'); + export const isDev = process.env.NODE_ENV !== 'production'; export const isWin = process.platform === 'win32'; export const isMac = process.platform === 'darwin'; +export const isMainProcess = (process && process.type === 'browser'); + const winPathRegex = /^[A-Z]:\\.+/; const wslBasePath = '/mnt/'; diff --git a/src/app/App.ts b/src/app/App.ts index 910ab15..c7d8f4e 100644 --- a/src/app/App.ts +++ b/src/app/App.ts @@ -3,6 +3,7 @@ import WindowFactory from '@src/window/WindowFactory'; import NativeContextMenu from '@src/window/NativeContextMenu'; import { app, protocol } from 'electron'; import Updater from '@src/updater/Updater'; +import { callHook } from '@common/plugins/PluginManager'; export default class App { @@ -41,7 +42,11 @@ export default class App { */ private listenAppEvents() { - app.on('window-all-closed', () => app.quit()); + app.on('window-all-closed', () => { + + callHook('onAppClosed'); + app.quit(); + }); app.on('activate', () => { @@ -57,7 +62,7 @@ export default class App { callback({ path: url }); }); - this.createWindow() + this.createWindow(); }); } } diff --git a/src/window/WindowFactory.ts b/src/window/WindowFactory.ts index 8be9926..b3d1397 100644 --- a/src/window/WindowFactory.ts +++ b/src/window/WindowFactory.ts @@ -1,4 +1,4 @@ -import { BrowserWindow } from 'electron'; +import { BrowserWindow, BrowserWindowConstructorOptions } from 'electron'; import { Factory } from '@common/factories/Factory'; import { format as formatUrl } from 'url'; import path from 'path'; @@ -7,6 +7,7 @@ import Config from '@common/config/Config'; import windowStateKeeper, { State } from 'electron-window-state'; import { IConfig } from '@common/config/Config'; import { isDev } from '@common/utils/utils'; +import { callHook } from '@common/plugins/PluginManager'; export default class WindowFactory implements Factory { @@ -72,7 +73,7 @@ export default class WindowFactory implements Factory { }; } - const window = new BrowserWindow({ + const options = callHook('hookWindowOptions', { ...params, minWidth: 600, @@ -90,7 +91,9 @@ export default class WindowFactory implements Factory { nodeIntegration: true, enableRemoteModule: true, }, - }); + } as BrowserWindowConstructorOptions); + + const window = new BrowserWindow(options); window.setMenuBarVisibility(false); @@ -106,6 +109,8 @@ export default class WindowFactory implements Factory { window.show(); window.focus(); + + callHook('onWindowShow'); }); window.on('moved', () => window.webContents.send('focus')); From 3a5fb1fbbc65e7638680cea89245374e98229cda Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 12 Mar 2021 21:06:24 +0100 Subject: [PATCH 02/10] refactor: Add BrowserWindow to onWindowShow event --- common/plugins/Plugin.ts | 7 ++++--- common/plugins/TestPlugin.ts | 3 +++ src/window/WindowFactory.ts | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/common/plugins/Plugin.ts b/common/plugins/Plugin.ts index d402387..5e829fc 100644 --- a/common/plugins/Plugin.ts +++ b/common/plugins/Plugin.ts @@ -1,4 +1,4 @@ -import { BrowserWindowConstructorOptions } from 'electron'; +import { BrowserWindow, BrowserWindowConstructorOptions } from 'electron'; import { IConfig } from '@common/config/Config'; import { isMainProcess } from '@common/utils/utils'; @@ -12,7 +12,8 @@ export type ProcessHookParam

= { export type HookParams = BrowserWindowConstructorOptions | - ProcessHookParam; + ProcessHookParam | + BrowserWindow; export const processHook =

(param: P): ProcessHookParam

=> ({ @@ -24,7 +25,7 @@ export interface Plugin { onAppLoaded?: () => void; onAppClosed?: () => void; - onWindowShow?: () => void; + onWindowShow?: (options: BrowserWindow) => void; hookWindowOptions?: (options: BrowserWindowConstructorOptions) => BrowserWindowConstructorOptions; hookConfig?: (options: ProcessHookParam) => ProcessHookParam; diff --git a/common/plugins/TestPlugin.ts b/common/plugins/TestPlugin.ts index 577b6e3..79e0d7c 100644 --- a/common/plugins/TestPlugin.ts +++ b/common/plugins/TestPlugin.ts @@ -2,7 +2,10 @@ import { Plugin } from '@common/plugins/Plugin'; const HelloWorld: Plugin = { + onWindowShow: (window) => { + console.log(window); + } } export default HelloWorld; diff --git a/src/window/WindowFactory.ts b/src/window/WindowFactory.ts index b3d1397..9787df8 100644 --- a/src/window/WindowFactory.ts +++ b/src/window/WindowFactory.ts @@ -110,7 +110,7 @@ export default class WindowFactory implements Factory { window.show(); window.focus(); - callHook('onWindowShow'); + callHook('onWindowShow', window); }); window.on('moved', () => window.webContents.send('focus')); From cf9405ed3639a6c4222344c1f8226693dc7d15ab Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 13 Mar 2021 21:01:22 +0100 Subject: [PATCH 03/10] refactor: Improve plugin system, add doc (#65) --- common/config/Config.ts | 6 +-- common/plugins/Plugin.ts | 32 ------------- common/plugins/PluginManager.ts | 41 ----------------- common/plugins/TestPlugin.ts | 2 +- common/plugins/hooks.ts | 31 +++++++++++++ common/plugins/plugin.ts | 59 ++++++++++++++++++++++++ common/plugins/plugins.ts | 79 +++++++++++++++++++++++++++++++++ common/utils/utils.ts | 2 + src/app/App.ts | 6 ++- src/window/WindowFactory.ts | 6 +-- 10 files changed, 182 insertions(+), 82 deletions(-) delete mode 100644 common/plugins/Plugin.ts delete mode 100644 common/plugins/PluginManager.ts create mode 100644 common/plugins/hooks.ts create mode 100644 common/plugins/plugin.ts create mode 100644 common/plugins/plugins.ts diff --git a/common/config/Config.ts b/common/config/Config.ts index 462b755..8e7219b 100644 --- a/common/config/Config.ts +++ b/common/config/Config.ts @@ -6,8 +6,8 @@ import watch from 'node-watch'; import { FontWeight } from 'xterm'; import { UndefinedObject } from '@common/types/types'; import { IShortcut } from '@common/config/shortcuts'; -import { callHook } from '@common/plugins/PluginManager'; -import { processHook } from '@common/plugins/Plugin'; +import { callTrigger } from '@common/plugins/plugins'; +import { getProcessTrigger } from '@common/plugins/hooks'; export default class Config { @@ -89,7 +89,7 @@ export default class Config { const data = fs.readFileSync(this.CONFIG); let config: IConfig = JSON.parse(data.toString()); - const { param } = callHook('hookConfig', processHook(config)); + const { param } = callTrigger('hookConfig', getProcessTrigger(config)); config = param; this.config = config; diff --git a/common/plugins/Plugin.ts b/common/plugins/Plugin.ts deleted file mode 100644 index 5e829fc..0000000 --- a/common/plugins/Plugin.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { BrowserWindow, BrowserWindowConstructorOptions } from 'electron'; -import { IConfig } from '@common/config/Config'; -import { isMainProcess } from '@common/utils/utils'; - -export type Process = 'main' | 'renderer'; - -export type ProcessHookParam

= { - - param: P; - process: Process; -} - -export type HookParams = - BrowserWindowConstructorOptions | - ProcessHookParam | - BrowserWindow; - -export const processHook =

(param: P): ProcessHookParam

=> ({ - - param, - process: isMainProcess ? 'main' : 'renderer', -}); - -export interface Plugin { - - onAppLoaded?: () => void; - onAppClosed?: () => void; - onWindowShow?: (options: BrowserWindow) => void; - - hookWindowOptions?: (options: BrowserWindowConstructorOptions) => BrowserWindowConstructorOptions; - hookConfig?: (options: ProcessHookParam) => ProcessHookParam; -} diff --git a/common/plugins/PluginManager.ts b/common/plugins/PluginManager.ts deleted file mode 100644 index 19373ba..0000000 --- a/common/plugins/PluginManager.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { HookParams, Plugin } from '@common/plugins/Plugin'; -import { isMainProcess } from '@common/utils/utils'; - -let pluginsLoaded = false; -const plugins: Plugin[] = []; - -const loadPlugins = () => { - - // eslint-disable-next-line @typescript-eslint/no-var-requires - const plugin = require('./TestPlugin').default; - - if(isMainProcess) - callPluginHook(plugin, 'onAppLoaded'); - - plugins.push(plugin); - - pluginsLoaded = true; -} - -export const callHook = (hook: keyof Plugin, param?: T): T => { - - if(!pluginsLoaded) - loadPlugins(); - - let cache: T | undefined = param; - - plugins.forEach((plugin) => { - - cache = callPluginHook(plugin, hook, param); - }); - - return cache as T; -} - -const callPluginHook = (plugin: Plugin, hook: keyof Plugin, param?: T): T => { - - if(hook in plugin) - return param ? plugin[hook](param) : plugin[hook](); - - return param as T; -} diff --git a/common/plugins/TestPlugin.ts b/common/plugins/TestPlugin.ts index 79e0d7c..95f3e07 100644 --- a/common/plugins/TestPlugin.ts +++ b/common/plugins/TestPlugin.ts @@ -1,4 +1,4 @@ -import { Plugin } from '@common/plugins/Plugin'; +import { Plugin } from '@common/plugins/plugin'; const HelloWorld: Plugin = { diff --git a/common/plugins/hooks.ts b/common/plugins/hooks.ts new file mode 100644 index 0000000..45763e0 --- /dev/null +++ b/common/plugins/hooks.ts @@ -0,0 +1,31 @@ +import { BrowserWindow, BrowserWindowConstructorOptions, app } from 'electron'; +import { IConfig } from '@common/config/Config'; +import { isMainProcess, Process } from '@common/utils/utils'; + +// The list of availables parameters for the triggers +export type TriggerParams = + BrowserWindowConstructorOptions | + ProcessTriggerParam | + BrowserWindow | + typeof app; + + +// A helper type to specify the parameter for a trigger which can +// be called in both main and renderer process. +export type ProcessTriggerParam

= { + + param: P; + process: Process; +} + +/** + * Get a process trigger param with the given param, and automaticaly + * fill the process. + * + * @param param - The param to use + */ +export const getProcessTrigger =

(param: P): ProcessTriggerParam

=> ({ + + param, + process: isMainProcess ? 'main' : 'renderer', +}); diff --git a/common/plugins/plugin.ts b/common/plugins/plugin.ts new file mode 100644 index 0000000..8db4367 --- /dev/null +++ b/common/plugins/plugin.ts @@ -0,0 +1,59 @@ +import { BrowserWindow, BrowserWindowConstructorOptions, app } from 'electron'; +import { IConfig } from '@common/config/Config'; +import { ProcessTriggerParam } from '@common/plugins/hooks'; + +export interface Plugin { + + /** + * Event when the plugin load. + * + * Process: main + */ + onLoad?: () => void; + + /** + * Event when the app has loaded. + * + * Process: main + * + * @param options - The electron app + */ + onAppLoaded?: (options: typeof app) => void; + + /** + * Event when the app close. + * + * Process: main + */ + onAppClose?: () => void; + + /** + * Event called when the window has finished building and has + * been shown after ready-to-show event. + * + * @param options - The browser window + */ + onWindowShow?: (options: BrowserWindow) => void; + + /** + * Hook the window options. Called when the browser window is + * building. + * + * Process: main + * + * @param options - The browser window options + * @returns The hooked browser window options + */ + hookWindowOptions?: (options: BrowserWindowConstructorOptions) => BrowserWindowConstructorOptions; + + /** + * Hook the config. Called when the config is loaded for the + * first time, and when the config change. + * + * Process: both + * + * @param options - The base config + * @returns The hooked config + */ + hookConfig?: (options: ProcessTriggerParam) => ProcessTriggerParam; +} diff --git a/common/plugins/plugins.ts b/common/plugins/plugins.ts new file mode 100644 index 0000000..f7181e9 --- /dev/null +++ b/common/plugins/plugins.ts @@ -0,0 +1,79 @@ +import { Plugin } from '@common/plugins/plugin'; +import { isMainProcess } from '@common/utils/utils'; +import { TriggerParams } from '@common/plugins/hooks'; + +// Keep track of is the plugins has been loaded in the current process +let pluginsLoaded = false; +let plugins: Plugin[] = []; + +/** + * Load a single plugin. + * + * @param path - The path to the plugin + * @returns The loaded plugin + */ +const loadPlugin = (path: string): Plugin => { + + // eslint-disable-next-line @typescript-eslint/no-var-requires + const plugin = require(path).default; + + if(isMainProcess) + callPluginTrigger(plugin, 'onLoad'); + + return plugin; +} + +/** + * Load all the plugins. Can be used to reload the plugin. + */ +const loadPlugins = () => { + + // Reset the plugins list + plugins = []; + + // TODO for in the plugins directory + plugins.push(loadPlugin('./TestPlugin')); + + pluginsLoaded = true; +} + +/** + * Call a trigger for the given plugin, and use optional parameter which + * is a HookParam. + * + * @param plugin - The plugin to call the trigger on + * @param trigger - The trigger to call + * @param param - An optional parameter + * @returns The modifier parameter or undefined if no parameter was provided + */ +const callPluginTrigger = (plugin: Plugin, trigger: keyof Plugin, param?: T): T => { + + if(trigger in plugin) + return param ? plugin[trigger](param) : plugin[trigger](); + + return param as T; +} + +/** + * Call a trigger for all the plugins, and and optional parameter which + * is a HookParam. We cache the result of each plugin's trigger and return + * it after trigger all the plugins. + * + * @param trigger - The trigger to call + * @param param - An optional parameter + * @returns The modifier parameter or undefined if no parameter was provided + */ +export const callTrigger = (trigger: keyof Plugin, param?: T): T => { + + if(!pluginsLoaded) + loadPlugins(); + + let cache: T | undefined = param; + + plugins.forEach((plugin) => { + + cache = callPluginTrigger(plugin, trigger, param); + }); + + return cache as T; +} diff --git a/common/utils/utils.ts b/common/utils/utils.ts index d105034..bc345a8 100644 --- a/common/utils/utils.ts +++ b/common/utils/utils.ts @@ -11,6 +11,8 @@ export const isMac = process.platform === 'darwin'; export const isMainProcess = (process && process.type === 'browser'); +export type Process = 'main' | 'renderer'; + const winPathRegex = /^[A-Z]:\\.+/; const wslBasePath = '/mnt/'; diff --git a/src/app/App.ts b/src/app/App.ts index c7d8f4e..d713c49 100644 --- a/src/app/App.ts +++ b/src/app/App.ts @@ -3,7 +3,7 @@ import WindowFactory from '@src/window/WindowFactory'; import NativeContextMenu from '@src/window/NativeContextMenu'; import { app, protocol } from 'electron'; import Updater from '@src/updater/Updater'; -import { callHook } from '@common/plugins/PluginManager'; +import { callTrigger } from '@common/plugins/plugins'; export default class App { @@ -17,6 +17,8 @@ export default class App { app.allowRendererProcessReuse = false; this.listenAppEvents(); + + callTrigger('onAppLoaded', app); } /** @@ -44,7 +46,7 @@ export default class App { app.on('window-all-closed', () => { - callHook('onAppClosed'); + callTrigger('onAppClose'); app.quit(); }); diff --git a/src/window/WindowFactory.ts b/src/window/WindowFactory.ts index 9787df8..88d1661 100644 --- a/src/window/WindowFactory.ts +++ b/src/window/WindowFactory.ts @@ -7,7 +7,7 @@ import Config from '@common/config/Config'; import windowStateKeeper, { State } from 'electron-window-state'; import { IConfig } from '@common/config/Config'; import { isDev } from '@common/utils/utils'; -import { callHook } from '@common/plugins/PluginManager'; +import { callTrigger } from '@common/plugins/plugins'; export default class WindowFactory implements Factory { @@ -73,7 +73,7 @@ export default class WindowFactory implements Factory { }; } - const options = callHook('hookWindowOptions', { + const options = callTrigger('hookWindowOptions', { ...params, minWidth: 600, @@ -110,7 +110,7 @@ export default class WindowFactory implements Factory { window.show(); window.focus(); - callHook('onWindowShow', window); + callTrigger('onWindowShow', window); }); window.on('moved', () => window.webContents.send('focus')); From be4b6da81fa61e681020d652d02d8d37e99f6694 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 15 Mar 2021 08:58:19 +0100 Subject: [PATCH 04/10] feat: Add providers system and notifications provider --- common/config/Config.ts | 2 +- common/plugins/TestPlugin.ts | 26 ++++++++++++++++++++++++-- common/plugins/{ => features}/hooks.ts | 5 ++++- common/plugins/features/providers.ts | 9 +++++++++ common/plugins/plugin.ts | 16 +++++++++++++++- common/plugins/plugins.ts | 12 +++++------- ui/contexts/NotificationsContext.tsx | 16 +++++++++++++++- 7 files changed, 73 insertions(+), 13 deletions(-) rename common/plugins/{ => features}/hooks.ts (82%) create mode 100644 common/plugins/features/providers.ts diff --git a/common/config/Config.ts b/common/config/Config.ts index 8e7219b..9ed5900 100644 --- a/common/config/Config.ts +++ b/common/config/Config.ts @@ -7,7 +7,7 @@ import { FontWeight } from 'xterm'; import { UndefinedObject } from '@common/types/types'; import { IShortcut } from '@common/config/shortcuts'; import { callTrigger } from '@common/plugins/plugins'; -import { getProcessTrigger } from '@common/plugins/hooks'; +import { getProcessTrigger } from '@common/plugins/features/hooks'; export default class Config { diff --git a/common/plugins/TestPlugin.ts b/common/plugins/TestPlugin.ts index 95f3e07..82f208b 100644 --- a/common/plugins/TestPlugin.ts +++ b/common/plugins/TestPlugin.ts @@ -1,10 +1,32 @@ import { Plugin } from '@common/plugins/plugin'; +import { Provider } from '@common/plugins/features/providers'; +import { INotification, INotificationLevel } from '@app/notifications/notification'; + +let provideNotification: Provider; const HelloWorld: Plugin = { - onWindowShow: (window) => { + hookConfig: (options) => { + + if(options.process === 'main') + return options; + + setTimeout(() => { + + provideNotification({ + time: 3, + title: 'Test notification', + content: 'Hello notification provider', + level: INotificationLevel.INFO, + }); + + }, 5000); + + return options; + }, + provideNotifications: (options) => { - console.log(window); + provideNotification = options; } } diff --git a/common/plugins/hooks.ts b/common/plugins/features/hooks.ts similarity index 82% rename from common/plugins/hooks.ts rename to common/plugins/features/hooks.ts index 45763e0..51e050c 100644 --- a/common/plugins/hooks.ts +++ b/common/plugins/features/hooks.ts @@ -1,13 +1,16 @@ import { BrowserWindow, BrowserWindowConstructorOptions, app } from 'electron'; import { IConfig } from '@common/config/Config'; import { isMainProcess, Process } from '@common/utils/utils'; +import { Provider } from '@common/plugins/features/providers'; +import { INotification } from '@app/notifications/notification'; // The list of availables parameters for the triggers export type TriggerParams = BrowserWindowConstructorOptions | ProcessTriggerParam | BrowserWindow | - typeof app; + typeof app | + Provider; // A helper type to specify the parameter for a trigger which can diff --git a/common/plugins/features/providers.ts b/common/plugins/features/providers.ts new file mode 100644 index 0000000..50ceef2 --- /dev/null +++ b/common/plugins/features/providers.ts @@ -0,0 +1,9 @@ +import { INotification } from '@app/notifications/notification'; + +// All the differents providers availables +export type Providers = + INotification; + +// A provider is a callback which contains the object to provide and +// dispatch the action. +export type Provider = (provider: T) => void; diff --git a/common/plugins/plugin.ts b/common/plugins/plugin.ts index 8db4367..06abb3d 100644 --- a/common/plugins/plugin.ts +++ b/common/plugins/plugin.ts @@ -1,6 +1,8 @@ import { BrowserWindow, BrowserWindowConstructorOptions, app } from 'electron'; import { IConfig } from '@common/config/Config'; -import { ProcessTriggerParam } from '@common/plugins/hooks'; +import { ProcessTriggerParam } from '@common/plugins/features/hooks'; +import { Provider } from '@common/plugins/features/providers'; +import { INotification } from '@app/notifications/notification'; export interface Plugin { @@ -31,10 +33,22 @@ export interface Plugin { * Event called when the window has finished building and has * been shown after ready-to-show event. * + * Process: main + * * @param options - The browser window */ onWindowShow?: (options: BrowserWindow) => void; + /** + * Provide a notifications provider to add a notification. Called + * at the startup of the app. + * + * Process: renderer + * + * @param options - The notification provider + */ + provideNotifications?: (options: Provider) => void; + /** * Hook the window options. Called when the browser window is * building. diff --git a/common/plugins/plugins.ts b/common/plugins/plugins.ts index f7181e9..8a6856e 100644 --- a/common/plugins/plugins.ts +++ b/common/plugins/plugins.ts @@ -1,6 +1,6 @@ import { Plugin } from '@common/plugins/plugin'; import { isMainProcess } from '@common/utils/utils'; -import { TriggerParams } from '@common/plugins/hooks'; +import { TriggerParams } from '@common/plugins/features/hooks'; // Keep track of is the plugins has been loaded in the current process let pluginsLoaded = false; @@ -9,13 +9,10 @@ let plugins: Plugin[] = []; /** * Load a single plugin. * - * @param path - The path to the plugin + * @param plugin - The path to the plugin * @returns The loaded plugin */ -const loadPlugin = (path: string): Plugin => { - - // eslint-disable-next-line @typescript-eslint/no-var-requires - const plugin = require(path).default; +const loadPlugin = (plugin: Plugin): Plugin => { if(isMainProcess) callPluginTrigger(plugin, 'onLoad'); @@ -32,7 +29,8 @@ const loadPlugins = () => { plugins = []; // TODO for in the plugins directory - plugins.push(loadPlugin('./TestPlugin')); + // eslint-disable-next-line @typescript-eslint/no-var-requires + plugins.push(loadPlugin(require('./TestPlugin').default)); pluginsLoaded = true; } diff --git a/ui/contexts/NotificationsContext.tsx b/ui/contexts/NotificationsContext.tsx index 166ce04..3f1e61e 100644 --- a/ui/contexts/NotificationsContext.tsx +++ b/ui/contexts/NotificationsContext.tsx @@ -1,8 +1,10 @@ -import React, { FC, ReactElement, createContext, useReducer, Reducer } from 'react'; +import React, { FC, ReactElement, createContext, useReducer, Reducer, useEffect } from 'react'; import { NotificationsContextType } from '@app/store/notifications/types'; import { INotification } from '@app/notifications/notification'; import { notificationsReducer } from '@app/store/notifications/reducers/NotificationsReducer'; import { NotificationsActions } from '@app/store/notifications/actions/NotificationsActions'; +import { callTrigger } from '@common/plugins/plugins'; +import { Provider } from '@common/plugins/features/providers'; interface Props { @@ -22,6 +24,18 @@ const NotificationsProvider: FC = ({ children }: Props): ReactElement => const [notifications, dispatch] = useReducer>(notificationsReducer, []); + // Setup the notifications provider on first mount + useEffect(() => { + + const notificationProvider: Provider = (notification: INotification) => { + + dispatch({ type: 'ADD', notification }); + } + + callTrigger('provideNotifications', notificationProvider); + + }, []); + return ( { children } From 7a2bb00fab96d717cb67487bfa55f39bf962a7cf Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 15 Mar 2021 18:12:36 +0100 Subject: [PATCH 05/10] feat: Add material theme --- common/plugins/MaterialTheme.ts | 43 +++++++++++++++++++++++++++++++++ common/plugins/plugins.ts | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 common/plugins/MaterialTheme.ts diff --git a/common/plugins/MaterialTheme.ts b/common/plugins/MaterialTheme.ts new file mode 100644 index 0000000..f34f46a --- /dev/null +++ b/common/plugins/MaterialTheme.ts @@ -0,0 +1,43 @@ +import { Plugin } from '@common/plugins/plugin'; + +const MaterialTheme: Plugin = { + + hookConfig: (options) => { + + return { + ...options, + param: { + ...options.param, + theme: { + ...options.param.theme, + background: '#263238', + border: '#2E3C43', + text: '#425B67', + textHover: '#B0BEC5', + foreground: '#B0BEC5', + cursor: '#009688', + cursorAccent: '#1E272C', + selection: '#546E7A', + black: '#1E272C', + red: '#f78c6c', + green: '#c3e88d', + yellow: '#ffcb6b', + blue: '#82aaff', + magenta: '#c792ea', + cyan: '#89ddff', + white: '#eeffff', + brightBlack: '#1E272C', + brightRed: '#f78c6c', + brightGreen: '#c3e88d', + brightYellow: '#ffcb6b', + brightBlue: '#82aaff', + brightMagenta: '#c792ea', + brightCyan: '#89ddff', + brightWhite: '#eeffff', + }, + }, + }; + }, +} + +export default MaterialTheme; diff --git a/common/plugins/plugins.ts b/common/plugins/plugins.ts index 8a6856e..c65799c 100644 --- a/common/plugins/plugins.ts +++ b/common/plugins/plugins.ts @@ -30,7 +30,7 @@ const loadPlugins = () => { // TODO for in the plugins directory // eslint-disable-next-line @typescript-eslint/no-var-requires - plugins.push(loadPlugin(require('./TestPlugin').default)); + plugins.push(loadPlugin(require('./MaterialTheme').default)); pluginsLoaded = true; } From 61df14bccd39fcdc6712008128e37534a0484abd Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 15 Mar 2021 18:38:01 +0100 Subject: [PATCH 06/10] feat: Load plugins from right folder --- .gitignore | 2 ++ common/plugins/MaterialTheme.ts | 43 --------------------------------- common/plugins/TestPlugin.ts | 33 ------------------------- common/plugins/plugins.ts | 27 +++++++++++++++------ 4 files changed, 22 insertions(+), 83 deletions(-) delete mode 100644 common/plugins/MaterialTheme.ts delete mode 100644 common/plugins/TestPlugin.ts diff --git a/.gitignore b/.gitignore index 136cc2f..257eb56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ dist/ node_modules/ .idea/ +common/plugins/local/ + .DS_STORE diff --git a/common/plugins/MaterialTheme.ts b/common/plugins/MaterialTheme.ts deleted file mode 100644 index f34f46a..0000000 --- a/common/plugins/MaterialTheme.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Plugin } from '@common/plugins/plugin'; - -const MaterialTheme: Plugin = { - - hookConfig: (options) => { - - return { - ...options, - param: { - ...options.param, - theme: { - ...options.param.theme, - background: '#263238', - border: '#2E3C43', - text: '#425B67', - textHover: '#B0BEC5', - foreground: '#B0BEC5', - cursor: '#009688', - cursorAccent: '#1E272C', - selection: '#546E7A', - black: '#1E272C', - red: '#f78c6c', - green: '#c3e88d', - yellow: '#ffcb6b', - blue: '#82aaff', - magenta: '#c792ea', - cyan: '#89ddff', - white: '#eeffff', - brightBlack: '#1E272C', - brightRed: '#f78c6c', - brightGreen: '#c3e88d', - brightYellow: '#ffcb6b', - brightBlue: '#82aaff', - brightMagenta: '#c792ea', - brightCyan: '#89ddff', - brightWhite: '#eeffff', - }, - }, - }; - }, -} - -export default MaterialTheme; diff --git a/common/plugins/TestPlugin.ts b/common/plugins/TestPlugin.ts deleted file mode 100644 index 82f208b..0000000 --- a/common/plugins/TestPlugin.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Plugin } from '@common/plugins/plugin'; -import { Provider } from '@common/plugins/features/providers'; -import { INotification, INotificationLevel } from '@app/notifications/notification'; - -let provideNotification: Provider; - -const HelloWorld: Plugin = { - - hookConfig: (options) => { - - if(options.process === 'main') - return options; - - setTimeout(() => { - - provideNotification({ - time: 3, - title: 'Test notification', - content: 'Hello notification provider', - level: INotificationLevel.INFO, - }); - - }, 5000); - - return options; - }, - provideNotifications: (options) => { - - provideNotification = options; - } -} - -export default HelloWorld; diff --git a/common/plugins/plugins.ts b/common/plugins/plugins.ts index c65799c..684874c 100644 --- a/common/plugins/plugins.ts +++ b/common/plugins/plugins.ts @@ -1,6 +1,13 @@ import { Plugin } from '@common/plugins/plugin'; -import { isMainProcess } from '@common/utils/utils'; +import { isDev, isMainProcess } from '@common/utils/utils'; import { TriggerParams } from '@common/plugins/features/hooks'; +import electron from 'electron'; +import path from 'path'; +import fs from 'fs'; + +const PLUGINS_FOLDER = isDev ? + path.join(__dirname, 'local') : + path.join((electron.app || electron.remote.app).getPath('home'), '.squid', 'plugins'); // Keep track of is the plugins has been loaded in the current process let pluginsLoaded = false; @@ -9,10 +16,13 @@ let plugins: Plugin[] = []; /** * Load a single plugin. * - * @param plugin - The path to the plugin + * @param pluginDir - The path to the plugin * @returns The loaded plugin */ -const loadPlugin = (plugin: Plugin): Plugin => { +const loadPlugin = (pluginDir: string): Plugin => { + + // eslint-disable-next-line @typescript-eslint/no-var-requires + const plugin = require(`${(isDev ? './local' : PLUGINS_FOLDER) + '/' + path.parse(pluginDir).name}`).default; if(isMainProcess) callPluginTrigger(plugin, 'onLoad'); @@ -28,9 +38,12 @@ const loadPlugins = () => { // Reset the plugins list plugins = []; - // TODO for in the plugins directory - // eslint-disable-next-line @typescript-eslint/no-var-requires - plugins.push(loadPlugin(require('./MaterialTheme').default)); + const pluginsDir = fs.readdirSync(PLUGINS_FOLDER); + + pluginsDir.forEach((pluginDir) => { + + plugins.push(loadPlugin(pluginDir)); + }); pluginsLoaded = true; } @@ -70,7 +83,7 @@ export const callTrigger = (trigger: keyof Plugin, para plugins.forEach((plugin) => { - cache = callPluginTrigger(plugin, trigger, param); + cache = callPluginTrigger(plugin, trigger, cache ? cache : param); }); return cache as T; From 7dc1ef50a8072dc7517af84b4394d306936a77e4 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 15 Mar 2021 20:20:54 +0100 Subject: [PATCH 07/10] fix: Plugins location --- common/plugins/plugins.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/common/plugins/plugins.ts b/common/plugins/plugins.ts index 684874c..943c6eb 100644 --- a/common/plugins/plugins.ts +++ b/common/plugins/plugins.ts @@ -7,7 +7,7 @@ import fs from 'fs'; const PLUGINS_FOLDER = isDev ? path.join(__dirname, 'local') : - path.join((electron.app || electron.remote.app).getPath('home'), '.squid', 'plugins'); + path.join((electron.app || electron.remote.app).getPath('home'), '.squid'); // Keep track of is the plugins has been loaded in the current process let pluginsLoaded = false; @@ -21,8 +21,12 @@ let plugins: Plugin[] = []; */ const loadPlugin = (pluginDir: string): Plugin => { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const plugin = require(`${(isDev ? './local' : PLUGINS_FOLDER) + '/' + path.parse(pluginDir).name}`).default; + let plugin: Plugin; + + if(isDev) + plugin = require('./local/' + pluginDir + '/index').default; + else + plugin = __non_webpack_require__(path.join(PLUGINS_FOLDER, pluginDir, 'index')); if(isMainProcess) callPluginTrigger(plugin, 'onLoad'); @@ -38,6 +42,9 @@ const loadPlugins = () => { // Reset the plugins list plugins = []; + if(!fs.existsSync(PLUGINS_FOLDER)) + fs.mkdirSync(PLUGINS_FOLDER); + const pluginsDir = fs.readdirSync(PLUGINS_FOLDER); pluginsDir.forEach((pluginDir) => { From 9ad655dea850e7f4eed2b10469f5dd17deebee4e Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 15 Mar 2021 20:28:22 +0100 Subject: [PATCH 08/10] chore: Disable eslint for require --- common/plugins/plugins.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/common/plugins/plugins.ts b/common/plugins/plugins.ts index 943c6eb..8c4d290 100644 --- a/common/plugins/plugins.ts +++ b/common/plugins/plugins.ts @@ -24,6 +24,7 @@ const loadPlugin = (pluginDir: string): Plugin => { let plugin: Plugin; if(isDev) + // eslint-disable-next-line @typescript-eslint/no-var-requires plugin = require('./local/' + pluginDir + '/index').default; else plugin = __non_webpack_require__(path.join(PLUGINS_FOLDER, pluginDir, 'index')); From 3f66c98a0f436726e9f298c4390fd58bbbe69ad9 Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 16 Mar 2021 18:48:20 +0100 Subject: [PATCH 09/10] feat: Add squid-plugins package and example theme plugin --- .gitignore | 1 - common/config/Config.ts | 2 +- common/plugins/{features => }/hooks.ts | 2 +- .../plugins/local/material-theme/package.json | 26 +++++++++++ .../plugins/local/material-theme/src/index.ts | 43 +++++++++++++++++++ .../local/material-theme/tsconfig.json | 9 ++++ common/plugins/local/material-theme/yarn.lock | 13 ++++++ common/plugins/package/package.json | 22 ++++++++++ .../{plugin.ts => package/src/index.ts} | 6 +-- common/plugins/package/yarn.lock | 8 ++++ common/plugins/plugins.ts | 24 ++++++----- common/plugins/{features => }/providers.ts | 0 ui/contexts/NotificationsContext.tsx | 2 +- 13 files changed, 140 insertions(+), 18 deletions(-) rename common/plugins/{features => }/hooks.ts (93%) create mode 100644 common/plugins/local/material-theme/package.json create mode 100644 common/plugins/local/material-theme/src/index.ts create mode 100644 common/plugins/local/material-theme/tsconfig.json create mode 100644 common/plugins/local/material-theme/yarn.lock create mode 100644 common/plugins/package/package.json rename common/plugins/{plugin.ts => package/src/index.ts} (91%) create mode 100644 common/plugins/package/yarn.lock rename common/plugins/{features => }/providers.ts (100%) diff --git a/.gitignore b/.gitignore index 257eb56..84d5424 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ dist/ node_modules/ .idea/ -common/plugins/local/ .DS_STORE diff --git a/common/config/Config.ts b/common/config/Config.ts index 9ed5900..8e7219b 100644 --- a/common/config/Config.ts +++ b/common/config/Config.ts @@ -7,7 +7,7 @@ import { FontWeight } from 'xterm'; import { UndefinedObject } from '@common/types/types'; import { IShortcut } from '@common/config/shortcuts'; import { callTrigger } from '@common/plugins/plugins'; -import { getProcessTrigger } from '@common/plugins/features/hooks'; +import { getProcessTrigger } from '@common/plugins/hooks'; export default class Config { diff --git a/common/plugins/features/hooks.ts b/common/plugins/hooks.ts similarity index 93% rename from common/plugins/features/hooks.ts rename to common/plugins/hooks.ts index 51e050c..42d1348 100644 --- a/common/plugins/features/hooks.ts +++ b/common/plugins/hooks.ts @@ -1,7 +1,7 @@ import { BrowserWindow, BrowserWindowConstructorOptions, app } from 'electron'; import { IConfig } from '@common/config/Config'; import { isMainProcess, Process } from '@common/utils/utils'; -import { Provider } from '@common/plugins/features/providers'; +import { Provider } from '@common/plugins/providers'; import { INotification } from '@app/notifications/notification'; // The list of availables parameters for the triggers diff --git a/common/plugins/local/material-theme/package.json b/common/plugins/local/material-theme/package.json new file mode 100644 index 0000000..6f50ae2 --- /dev/null +++ b/common/plugins/local/material-theme/package.json @@ -0,0 +1,26 @@ +{ + "name": "squid-material-theme", + "version": "1.0.0", + "description": "Material theme for Squid terminal.", + "author": { + "email": "quiibzdev@gmail.com", + "name": "QuiiBz", + "url": "https://github.com/QuiiBz" + }, + "license": "MIT", + "repository": "https://github.com/squidjs/terminal", + "keywords": [ + "squid-theme", + "theme", + "shell", + "terminal", + "squid" + ], + "scripts": { + "dist": "tsc" + }, + "devDependencies": { + "squid-plugins": "^0.0.1", + "typescript": "^4.2.3" + } +} diff --git a/common/plugins/local/material-theme/src/index.ts b/common/plugins/local/material-theme/src/index.ts new file mode 100644 index 0000000..c9b362d --- /dev/null +++ b/common/plugins/local/material-theme/src/index.ts @@ -0,0 +1,43 @@ +import { SquidPlugin } from 'squid-plugins/src'; + +const MaterialTheme: SquidPlugin = { + + hookConfig: (options) => { + + return { + ...options, + param: { + ...options.param, + theme: { + ...options.param.theme, + background: '#263238', + border: '#2E3C43', + text: '#425B67', + textHover: '#B0BEC5', + foreground: '#B0BEC5', + cursor: '#009688', + cursorAccent: '#1E272C', + selection: '#546E7A', + black: '#1E272C', + red: '#f78c6c', + green: '#c3e88d', + yellow: '#ffcb6b', + blue: '#82aaff', + magenta: '#c792ea', + cyan: '#89ddff', + white: '#eeffff', + brightBlack: '#1E272C', + brightRed: '#f78c6c', + brightGreen: '#c3e88d', + brightYellow: '#ffcb6b', + brightBlue: '#82aaff', + brightMagenta: '#c792ea', + brightCyan: '#89ddff', + brightWhite: '#eeffff', + }, + }, + }; + }, +} + +export default MaterialTheme; diff --git a/common/plugins/local/material-theme/tsconfig.json b/common/plugins/local/material-theme/tsconfig.json new file mode 100644 index 0000000..9d865ca --- /dev/null +++ b/common/plugins/local/material-theme/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "outDir": "./dist", + "skipLibCheck": true + }, + "include": ["./src/**/*"] +} diff --git a/common/plugins/local/material-theme/yarn.lock b/common/plugins/local/material-theme/yarn.lock new file mode 100644 index 0000000..27a5287 --- /dev/null +++ b/common/plugins/local/material-theme/yarn.lock @@ -0,0 +1,13 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +squid-plugins@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/squid-plugins/-/squid-plugins-0.0.1.tgz#8250105ba82a0890e333c7403f78596394741cba" + integrity sha512-rgX37w5dql4yRp5GU45G8WIgTuqOnBxpeFNqlPLnreUKUEly/rpYQ+1nSeYPYYzZtzO4SJBQDeljC/61jh4GPw== + +typescript@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3" + integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw== diff --git a/common/plugins/package/package.json b/common/plugins/package/package.json new file mode 100644 index 0000000..9a42590 --- /dev/null +++ b/common/plugins/package/package.json @@ -0,0 +1,22 @@ +{ + "name": "squid-plugins", + "version": "0.0.1", + "description": "Package to create plugins and themes for Squid.", + "author": { + "email": "quiibzdev@gmail.com", + "name": "QuiiBz", + "url": "https://github.com/QuiiBz" + }, + "license": "MIT", + "repository": "https://github.com/squidjs/terminal", + "keywords": [ + "plugin", + "theme", + "shell", + "terminal", + "squid" + ], + "devDependencies": { + "typescript": "^4.2.3" + } +} diff --git a/common/plugins/plugin.ts b/common/plugins/package/src/index.ts similarity index 91% rename from common/plugins/plugin.ts rename to common/plugins/package/src/index.ts index 06abb3d..1bfc82b 100644 --- a/common/plugins/plugin.ts +++ b/common/plugins/package/src/index.ts @@ -1,10 +1,10 @@ import { BrowserWindow, BrowserWindowConstructorOptions, app } from 'electron'; import { IConfig } from '@common/config/Config'; -import { ProcessTriggerParam } from '@common/plugins/features/hooks'; -import { Provider } from '@common/plugins/features/providers'; +import { ProcessTriggerParam } from '@common/plugins/hooks'; +import { Provider } from '@common/plugins/providers'; import { INotification } from '@app/notifications/notification'; -export interface Plugin { +export interface SquidPlugin { /** * Event when the plugin load. diff --git a/common/plugins/package/yarn.lock b/common/plugins/package/yarn.lock new file mode 100644 index 0000000..70734dc --- /dev/null +++ b/common/plugins/package/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +typescript@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3" + integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw== diff --git a/common/plugins/plugins.ts b/common/plugins/plugins.ts index 8c4d290..4ce6cf7 100644 --- a/common/plugins/plugins.ts +++ b/common/plugins/plugins.ts @@ -1,6 +1,6 @@ -import { Plugin } from '@common/plugins/plugin'; -import { isDev, isMainProcess } from '@common/utils/utils'; -import { TriggerParams } from '@common/plugins/features/hooks'; +import { SquidPlugin } from '@common/plugins/package/src'; +import { isDev, isMainProcess } from '../utils/utils'; +import { TriggerParams } from '@common/plugins/hooks'; import electron from 'electron'; import path from 'path'; import fs from 'fs'; @@ -11,7 +11,7 @@ const PLUGINS_FOLDER = isDev ? // Keep track of is the plugins has been loaded in the current process let pluginsLoaded = false; -let plugins: Plugin[] = []; +let plugins: SquidPlugin[] = []; /** * Load a single plugin. @@ -19,15 +19,15 @@ let plugins: Plugin[] = []; * @param pluginDir - The path to the plugin * @returns The loaded plugin */ -const loadPlugin = (pluginDir: string): Plugin => { +const loadPlugin = (pluginDir: string): SquidPlugin => { - let plugin: Plugin; + let plugin: SquidPlugin; if(isDev) // eslint-disable-next-line @typescript-eslint/no-var-requires - plugin = require('./local/' + pluginDir + '/index').default; + plugin = require('./local/' + pluginDir + '/dist/index').default; else - plugin = __non_webpack_require__(path.join(PLUGINS_FOLDER, pluginDir, 'index')); + plugin = __non_webpack_require__(path.join(PLUGINS_FOLDER, pluginDir, 'dist', 'index')); if(isMainProcess) callPluginTrigger(plugin, 'onLoad'); @@ -65,10 +65,12 @@ const loadPlugins = () => { * @param param - An optional parameter * @returns The modifier parameter or undefined if no parameter was provided */ -const callPluginTrigger = (plugin: Plugin, trigger: keyof Plugin, param?: T): T => { +const callPluginTrigger = (plugin: SquidPlugin, trigger: keyof SquidPlugin, param?: T): T => { if(trigger in plugin) - return param ? plugin[trigger](param) : plugin[trigger](); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return param ? plugin[trigger](param) : plugin[trigger](); return param as T; } @@ -82,7 +84,7 @@ const callPluginTrigger = (plugin: Plugin, trigger: key * @param param - An optional parameter * @returns The modifier parameter or undefined if no parameter was provided */ -export const callTrigger = (trigger: keyof Plugin, param?: T): T => { +export const callTrigger = (trigger: keyof SquidPlugin, param?: T): T => { if(!pluginsLoaded) loadPlugins(); diff --git a/common/plugins/features/providers.ts b/common/plugins/providers.ts similarity index 100% rename from common/plugins/features/providers.ts rename to common/plugins/providers.ts diff --git a/ui/contexts/NotificationsContext.tsx b/ui/contexts/NotificationsContext.tsx index 3f1e61e..87bc890 100644 --- a/ui/contexts/NotificationsContext.tsx +++ b/ui/contexts/NotificationsContext.tsx @@ -4,7 +4,7 @@ import { INotification } from '@app/notifications/notification'; import { notificationsReducer } from '@app/store/notifications/reducers/NotificationsReducer'; import { NotificationsActions } from '@app/store/notifications/actions/NotificationsActions'; import { callTrigger } from '@common/plugins/plugins'; -import { Provider } from '@common/plugins/features/providers'; +import { Provider } from '@common/plugins/providers'; interface Props { From 0469d43d5eafc4e2e7e2b51f78f577424816f43b Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 16 Mar 2021 20:24:00 +0100 Subject: [PATCH 10/10] feat: Add loadable plugins in config --- common/config/Config.ts | 15 +++++++++++++++ common/config/defaultConfig.ts | 1 + common/plugins/plugins.ts | 13 +++++++++---- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/common/config/Config.ts b/common/config/Config.ts index 8e7219b..f724169 100644 --- a/common/config/Config.ts +++ b/common/config/Config.ts @@ -117,6 +117,17 @@ export default class Config { }); } + /** + * Read the config file synchronously without hooking. + * + * @returns A IConfig + */ + public getHooklessConfig(): IConfig { + + const data = fs.readFileSync(this.CONFIG); + return JSON.parse(data.toString()); + } + /** * Get the singleton instance of this Config object. */ @@ -175,6 +186,10 @@ export interface IConfig { * The url to use for the cloud. */ cloudUrl: string; + /** + * The list of names of enabled plugin and theme. + */ + plugins: string[]; } export interface ITheme { diff --git a/common/config/defaultConfig.ts b/common/config/defaultConfig.ts index 77d809b..64ad3af 100644 --- a/common/config/defaultConfig.ts +++ b/common/config/defaultConfig.ts @@ -155,4 +155,5 @@ export const defaultConfig: IConfig = { localSSHHosts, // TODO cloudUrl: '', + plugins: [], } diff --git a/common/plugins/plugins.ts b/common/plugins/plugins.ts index 4ce6cf7..d89b0e4 100644 --- a/common/plugins/plugins.ts +++ b/common/plugins/plugins.ts @@ -4,6 +4,7 @@ import { TriggerParams } from '@common/plugins/hooks'; import electron from 'electron'; import path from 'path'; import fs from 'fs'; +import Config from '@common/config/Config'; const PLUGINS_FOLDER = isDev ? path.join(__dirname, 'local') : @@ -40,6 +41,8 @@ const loadPlugin = (pluginDir: string): SquidPlugin => { */ const loadPlugins = () => { + const { plugins: enabledPlugins } = Config.getInstance().getHooklessConfig(); + // Reset the plugins list plugins = []; @@ -48,10 +51,12 @@ const loadPlugins = () => { const pluginsDir = fs.readdirSync(PLUGINS_FOLDER); - pluginsDir.forEach((pluginDir) => { + pluginsDir + .filter((pluginDir) => enabledPlugins.includes(pluginDir)) + .forEach((pluginDir) => { - plugins.push(loadPlugin(pluginDir)); - }); + plugins.push(loadPlugin(pluginDir)); + }); pluginsLoaded = true; } @@ -86,7 +91,7 @@ const callPluginTrigger = (plugin: SquidPlugin, trigger */ export const callTrigger = (trigger: keyof SquidPlugin, param?: T): T => { - if(!pluginsLoaded) + if(!pluginsLoaded || trigger === 'hookConfig') loadPlugins(); let cache: T | undefined = param;