Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dynamic synced plugin config provider #1064

Merged
merged 4 commits into from
Mar 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions config/dynamic.js
Original file line number Diff line number Diff line change
@@ -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);
});
}
}
}
};
1 change: 0 additions & 1 deletion plugins/notifications/back.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) :
Expand Down
24 changes: 3 additions & 21 deletions plugins/notifications/config.js
Original file line number Diff line number Diff line change
@@ -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 };