diff --git a/config/dynamic.js b/config/dynamic.js new file mode 100644 index 00000000..34cb0ac3 --- /dev/null +++ b/config/dynamic.js @@ -0,0 +1,139 @@ +const { ipcRenderer, ipcMain } = require("electron"); + +const defaultConfig = require("./defaults"); +const { getOptions, setOptions, setMenuOptions } = require("./plugins"); + +const activePlugins = {}; +/** + * [!IMPORTANT!] + * The method is **sync** in the main process and **async** in the renderer process. + */ +module.exports.getActivePlugins = + process.type === "renderer" + ? async () => ipcRenderer.invoke("get-active-plugins") + : () => activePlugins; + +if (process.type === "browser") { + ipcMain.handle("get-active-plugins", this.getActivePlugins); +} + +/** + * [!IMPORTANT!] + * The method is **sync** in the main process and **async** in the renderer process. + */ +module.exports.isActive = + process.type === "renderer" + ? async (plugin) => + plugin in (await ipcRenderer.invoke("get-active-plugins")) + : (plugin) => plugin in activePlugins; + +/** + * This class is used to create a dynamic synced config for plugins. + * + * [!IMPORTANT!] + * The methods are **sync** in the main process and **async** in the renderer process. + * + * @param {string} name - The name of the plugin. + * @param {boolean} [options.enableFront] - Whether the config should be available in front.js. Default: false. + * @param {object} [options.initialOptions] - The initial options for the plugin. Default: loaded from store. + * + * @example + * const { PluginConfig } = require("../../config/dynamic"); + * const config = new PluginConfig("plugin-name", { enableFront: true }); + * module.exports = { ...config }; + * + * // or + * + * module.exports = (win, options) => { + * const config = new PluginConfig("plugin-name", { + * enableFront: true, + * initialOptions: options, + * }); + * setupMyPlugin(win, config); + * }; + */ +module.exports.PluginConfig = class PluginConfig { + #name; + #config; + #defaultConfig; + #enableFront; + + constructor(name, { enableFront = false, initialOptions = undefined } = {}) { + const pluginDefaultConfig = defaultConfig.plugins[name] || {}; + const pluginConfig = initialOptions || getOptions(name) || {}; + + this.#name = name; + this.#enableFront = enableFront; + this.#defaultConfig = pluginDefaultConfig; + this.#config = { ...pluginDefaultConfig, ...pluginConfig }; + + if (this.#enableFront) { + this.#setupFront(); + } + + activePlugins[name] = this; + } + + get = (option) => { + return this.#config[option]; + }; + + set = (option, value) => { + this.#config[option] = value; + this.#save(); + }; + + toggle = (option) => { + this.#config[option] = !this.#config[option]; + this.#save(); + }; + + getAll = () => { + return { ...this.#config }; + }; + + setAll = (options) => { + this.#config = { ...this.#config, ...options }; + this.#save(); + }; + + getDefaultConfig = () => { + return this.#defaultConfig; + }; + + /** + * Use this method to set an option and restart the app if `appConfig.restartOnConfigChange === true` + * + * Used for options that require a restart to take effect. + */ + setAndMaybeRestart = (option, value) => { + this.#config[option] = value; + setMenuOptions(this.#name, this.#config); + }; + + /** Called only from back */ + #save() { + setOptions(this.#name, this.#config); + } + + #setupFront() { + if (process.type === "renderer") { + for (const [fnName, fn] of Object.entries(this)) { + if (typeof fn !== "function") return; + this[fnName] = async (...args) => { + return await ipcRenderer.invoke( + `${this.name}-config-${fnName}`, + ...args, + ); + }; + } + } else if (process.type === "browser") { + for (const [fnName, fn] of Object.entries(this)) { + if (typeof fn !== "function") return; + ipcMain.handle(`${this.name}-config-${fnName}`, (_, ...args) => { + return fn(...args); + }); + } + } + } +}; diff --git a/plugins/notifications/back.js b/plugins/notifications/back.js index 419e27d3..385ecca7 100644 --- a/plugins/notifications/back.js +++ b/plugins/notifications/back.js @@ -39,7 +39,6 @@ const setup = () => { /** @param {Electron.BrowserWindow} win */ module.exports = (win, options) => { - config.init(options); // Register the callback for new song information is.windows() && options.interactive ? require("./interactive")(win) : diff --git a/plugins/notifications/config.js b/plugins/notifications/config.js index 38d8100e..d0898dc3 100644 --- a/plugins/notifications/config.js +++ b/plugins/notifications/config.js @@ -1,23 +1,5 @@ -const { setOptions, setMenuOptions } = require("../../config/plugins"); -const defaultConfig = require("../../config/defaults"); +const { PluginConfig } = require("../../config/dynamic"); -let config = defaultConfig.plugins["notifications"]; +const config = new PluginConfig("notifications"); -module.exports.init = (options) => { - config = { ...config, ...options }; -}; - -module.exports.setAndMaybeRestart = (option, value) => { - config[option] = value; - setMenuOptions("notifications", config); -}; - -module.exports.set = (option, value) => { - config[option] = value; - setOptions("notifications", config); -}; - -module.exports.get = (option) => { - let res = config[option]; - return res; -}; +module.exports = { ...config };