From 9ab996a5f01ef88d72d8d5bb28fea518db8c88e6 Mon Sep 17 00:00:00 2001 From: Bennett Date: Sun, 12 Apr 2020 13:33:11 +0200 Subject: Improve user scripts (#559) * Add template to user.js creation * Add Userscript library * Add internalOpen function * Fix lint * Remove excess line break --- src/stores/ServicesStore.js | 12 +++- src/webview/lib/Userscript.js | 132 ++++++++++++++++++++++++++++++++++++++++++ src/webview/recipe.js | 11 +++- 3 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 src/webview/lib/Userscript.js diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 54befee77..19e6f8299 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -351,7 +351,17 @@ export default class ServicesStore extends Store { // Create and open file const filePath = path.join(directory, file); - await fs.ensureFile(filePath); + if (file === 'user.js') { + if (!await fs.exists(filePath)) { + await fs.writeFile(filePath, `module.exports = (config, Ferdi) => { + // Write your scripts here + console.log("Hello, World!", config); +} +`); + } + } else { + await fs.ensureFile(filePath); + } shell.showItemInFolder(filePath); } diff --git a/src/webview/lib/Userscript.js b/src/webview/lib/Userscript.js new file mode 100644 index 000000000..2043d9fff --- /dev/null +++ b/src/webview/lib/Userscript.js @@ -0,0 +1,132 @@ +import { ipcRenderer } from 'electron'; + +export default class Userscript { + // Current ./lib/RecipeWebview instance + recipe = null; + + // Current ./recipe.js instance + controller = null; + + // Service configuration + config = {}; + + // Ferdi and service settings + settings = {}; + + settingsUpdateHandler = null; + + constructor(recipe, controller, config) { + this.recipe = recipe; + this.controller = controller; + this.internal_setSettings(controller.settings); + this.config = config; + } + + /** + * Set internal copy of Ferdi's settings. + * This is only used internally and can not be used to change any settings + * + * @param {*} settings + */ + // eslint-disable-next-line + internal_setSettings(settings) { + // This is needed to get a clean JS object from the settings itself to provide better accessibility + // Otherwise this will be a mobX instance + this.settings = JSON.parse(JSON.stringify(settings)); + + if (typeof this.settingsUpdateHandler === 'function') { + this.settingsUpdateHandler(); + } + } + + /** + * Register a settings handler to be executed when the settings change + * + * @param {function} handler + */ + onSettingsUpdate(handler) { + this.settingsUpdateHandler = handler; + } + + /** + * Set badge count for the current service + * @param {*} direct Direct messages + * @param {*} indirect Indirect messages + */ + setBadge(direct = 0, indirect = 0) { + if (this.recipe && this.recipe.setBadge) { + this.recipe.setBadge(direct, indirect); + } + } + + /** + * Inject CSS files into the current page + * + * @param {...string} files + */ + injectCSSFiles(...files) { + if (this.recipe && this.recipe.injectCSS) { + this.recipe.injectCSS(...files); + } + } + + /** + * Inject a CSS string into the page + * + * @param {string} css + */ + injectCSS(css) { + const style = document.createElement('style'); + style.textContent = css; + document.head.append(style); + } + + /** + * Open "Find in Page" popup + */ + openFindInPage() { + this.controller.openFindInPage(); + } + + /** + * Set or update value in storage + * + * @param {*} key + * @param {*} value + */ + set(key, value) { + window.localStorage.setItem( + `ferdi-user-${key}`, JSON.stringify(value), + ); + } + + /** + * Get value from storage + * + * @param {*} key + * @return Value of the key + */ + get(key) { + return JSON.parse(window.localStorage.getItem( + `ferdi-user-${key}`, + )); + } + + /** + * Open a URL in an external browser + * + * @param {*} url + */ + externalOpen(url) { + ipcRenderer.sendToHost('new-window', url); + } + + /** + * Open a URL in the current service + * + * @param {*} url + */ + internalOpen(url) { + window.location.href = url; + } +} diff --git a/src/webview/recipe.js b/src/webview/recipe.js index bad5a93b2..7b762af17 100644 --- a/src/webview/recipe.js +++ b/src/webview/recipe.js @@ -21,6 +21,7 @@ import ignoreList from './darkmode/ignore'; import customDarkModeCss from './darkmode/custom'; import RecipeWebview from './lib/RecipeWebview'; +import Userscript from './lib/Userscript'; import spellchecker, { switchDict, disable as disableSpellchecker, getSpellcheckerLocaleByFuzzyIdentifier } from './spellchecker'; import { injectDarkModeStyle, isDarkModeStyleInjected, removeDarkModeStyle } from './darkmode'; @@ -55,6 +56,8 @@ class RecipeController { recipe = null; + userscript = null; + hasUpdatedBeforeRecipeLoaded = false; constructor() { @@ -130,7 +133,8 @@ class RecipeController { const userJsModule = require(userJs); if (typeof userJsModule === 'function') { - userJsModule(config); + this.userscript = new Userscript(this.recipe, this, config); + userJsModule(config, this.userscript); } }; @@ -154,6 +158,10 @@ class RecipeController { debug('System spellcheckerLanguage', this.settings.app.spellcheckerLanguage); debug('Service spellcheckerLanguage', this.settings.service.spellcheckerLanguage); + if (this.userscript && this.userscript.internal_setSettings) { + this.userscript.internal_setSettings(this.settings); + } + if (this.settings.app.enableSpellchecking) { debug('Setting spellchecker language to', this.spellcheckerLanguage); let { spellcheckerLanguage } = this; @@ -322,7 +330,6 @@ new RecipeController(); // Patching window.open const originalWindowOpen = window.open; - window.open = (url, frameName, features) => { if (!url && !frameName && !features) { // The service hasn't yet supplied a URL (as used in Skype). -- cgit v1.2.3-70-g09d2