diff options
author | 2021-08-05 08:58:28 +0200 | |
---|---|---|
committer | 2021-08-05 08:05:49 +0000 | |
commit | 305c2c5ecb49a1349be2efb1ef557d61da9a64dc (patch) | |
tree | 19a05ed4ddfc1e5b5bb7fc3ecc071ad562da820f /src/webview/recipe.js | |
parent | refactor: minor refactoring: solve name-clash of env vars vs vars in the program (diff) | |
download | ferdium-app-305c2c5ecb49a1349be2efb1ef557d61da9a64dc.tar.gz ferdium-app-305c2c5ecb49a1349be2efb1ef557d61da9a64dc.tar.zst ferdium-app-305c2c5ecb49a1349be2efb1ef557d61da9a64dc.zip |
refactor: general code improvements
- replace deprecated fs.exists with fs.existsSync
- replace console.log with debug
- replace hardcoded FERDI_VERSION in start.js with dynamic one from package.json
- correct JSDoc annotations in Handler.js
- simplify macOSPermissions.js
- updates to various eslint rules
- add FileReader to known globals
Diffstat (limited to 'src/webview/recipe.js')
-rw-r--r-- | src/webview/recipe.js | 164 |
1 files changed, 108 insertions, 56 deletions
diff --git a/src/webview/recipe.js b/src/webview/recipe.js index d143675dc..598c3eb9a 100644 --- a/src/webview/recipe.js +++ b/src/webview/recipe.js | |||
@@ -23,11 +23,25 @@ import Userscript from './lib/Userscript'; | |||
23 | 23 | ||
24 | import { BadgeHandler } from './badge'; | 24 | import { BadgeHandler } from './badge'; |
25 | import contextMenu from './contextMenu'; | 25 | import contextMenu from './contextMenu'; |
26 | import { injectDarkModeStyle, isDarkModeStyleInjected, removeDarkModeStyle } from './darkmode'; | 26 | import { |
27 | injectDarkModeStyle, | ||
28 | isDarkModeStyleInjected, | ||
29 | removeDarkModeStyle, | ||
30 | } from './darkmode'; | ||
27 | import FindInPage from './find'; | 31 | import FindInPage from './find'; |
28 | import { NotificationsHandler, notificationsClassDefinition } from './notifications'; | 32 | import { |
29 | import { getDisplayMediaSelector, screenShareCss, screenShareJs } from './screenshare'; | 33 | NotificationsHandler, |
30 | import { switchDict, getSpellcheckerLocaleByFuzzyIdentifier } from './spellchecker'; | 34 | notificationsClassDefinition, |
35 | } from './notifications'; | ||
36 | import { | ||
37 | getDisplayMediaSelector, | ||
38 | screenShareCss, | ||
39 | screenShareJs, | ||
40 | } from './screenshare'; | ||
41 | import { | ||
42 | switchDict, | ||
43 | getSpellcheckerLocaleByFuzzyIdentifier, | ||
44 | } from './spellchecker'; | ||
31 | 45 | ||
32 | import { DEFAULT_APP_SETTINGS } from '../environment'; | 46 | import { DEFAULT_APP_SETTINGS } from '../environment'; |
33 | 47 | ||
@@ -86,15 +100,19 @@ window.open = (url, frameName, features) => { | |||
86 | // then overwrite the corresponding field of the window object by injected JS. | 100 | // then overwrite the corresponding field of the window object by injected JS. |
87 | contextBridge.exposeInMainWorld('ferdi', { | 101 | contextBridge.exposeInMainWorld('ferdi', { |
88 | open: window.open, | 102 | open: window.open, |
89 | setBadge: (direct, indirect) => badgeHandler.setBadge(direct || 0, indirect || 0), | 103 | setBadge: (direct, indirect) => |
90 | displayNotification: (title, options) => notificationsHandler.displayNotification(title, options), | 104 | badgeHandler.setBadge(direct || 0, indirect || 0), |
105 | displayNotification: (title, options) => | ||
106 | notificationsHandler.displayNotification(title, options), | ||
91 | getDisplayMediaSelector, | 107 | getDisplayMediaSelector, |
92 | }); | 108 | }); |
93 | 109 | ||
94 | ipcRenderer.sendToHost('inject-js-unsafe', | 110 | ipcRenderer.sendToHost( |
111 | 'inject-js-unsafe', | ||
95 | 'window.open = window.ferdi.open;', | 112 | 'window.open = window.ferdi.open;', |
96 | notificationsClassDefinition, | 113 | notificationsClassDefinition, |
97 | screenShareJs); | 114 | screenShareJs, |
115 | ); | ||
98 | 116 | ||
99 | class RecipeController { | 117 | class RecipeController { |
100 | @observable settings = { | 118 | @observable settings = { |
@@ -129,7 +147,9 @@ class RecipeController { | |||
129 | } | 147 | } |
130 | 148 | ||
131 | @computed get spellcheckerLanguage() { | 149 | @computed get spellcheckerLanguage() { |
132 | const selected = this.settings.service.spellcheckerLanguage || this.settings.app.spellcheckerLanguage; | 150 | const selected = |
151 | this.settings.service.spellcheckerLanguage || | ||
152 | this.settings.app.spellcheckerLanguage; | ||
133 | return selected; | 153 | return selected; |
134 | } | 154 | } |
135 | 155 | ||
@@ -138,7 +158,7 @@ class RecipeController { | |||
138 | findInPage = null; | 158 | findInPage = null; |
139 | 159 | ||
140 | async initialize() { | 160 | async initialize() { |
141 | Object.keys(this.ipcEvents).forEach((channel) => { | 161 | Object.keys(this.ipcEvents).forEach(channel => { |
142 | ipcRenderer.on(channel, (...args) => { | 162 | ipcRenderer.on(channel, (...args) => { |
143 | debug('Received IPC event for channel', channel, 'with', ...args); | 163 | debug('Received IPC event for channel', channel, 'with', ...args); |
144 | this[this.ipcEvents[channel]](...args); | 164 | this[this.ipcEvents[channel]](...args); |
@@ -176,7 +196,7 @@ class RecipeController { | |||
176 | try { | 196 | try { |
177 | this.recipe = new RecipeWebview(badgeHandler, notificationsHandler); | 197 | this.recipe = new RecipeWebview(badgeHandler, notificationsHandler); |
178 | // eslint-disable-next-line | 198 | // eslint-disable-next-line |
179 | require(modulePath)(this.recipe, {...config, recipe,}); | 199 | require(modulePath)(this.recipe, { ...config, recipe }); |
180 | debug('Initialize Recipe', config, recipe); | 200 | debug('Initialize Recipe', config, recipe); |
181 | 201 | ||
182 | this.settings.service = Object.assign(config, { recipe }); | 202 | this.settings.service = Object.assign(config, { recipe }); |
@@ -195,14 +215,14 @@ class RecipeController { | |||
195 | styles.innerHTML = screenShareCss; | 215 | styles.innerHTML = screenShareCss; |
196 | 216 | ||
197 | const userCss = path.join(recipe.path, 'user.css'); | 217 | const userCss = path.join(recipe.path, 'user.css'); |
198 | if (await fs.exists(userCss)) { | 218 | if (fs.existsSync(userCss)) { |
199 | const data = await fs.readFile(userCss); | 219 | const data = await fs.readFile(userCss); |
200 | styles.innerHTML += data.toString(); | 220 | styles.innerHTML += data.toString(); |
201 | } | 221 | } |
202 | document.querySelector('head').appendChild(styles); | 222 | document.querySelector('head').appendChild(styles); |
203 | 223 | ||
204 | const userJs = path.join(recipe.path, 'user.js'); | 224 | const userJs = path.join(recipe.path, 'user.js'); |
205 | if (await fs.exists(userJs)) { | 225 | if (fs.existsSync(userJs)) { |
206 | const loadUserJs = () => { | 226 | const loadUserJs = () => { |
207 | // eslint-disable-next-line | 227 | // eslint-disable-next-line |
208 | const userJsModule = require(userJs); | 228 | const userJsModule = require(userJs); |
@@ -230,8 +250,14 @@ class RecipeController { | |||
230 | update() { | 250 | update() { |
231 | debug('enableSpellchecking', this.settings.app.enableSpellchecking); | 251 | debug('enableSpellchecking', this.settings.app.enableSpellchecking); |
232 | debug('isDarkModeEnabled', this.settings.service.isDarkModeEnabled); | 252 | debug('isDarkModeEnabled', this.settings.service.isDarkModeEnabled); |
233 | debug('System spellcheckerLanguage', this.settings.app.spellcheckerLanguage); | 253 | debug( |
234 | debug('Service spellcheckerLanguage', this.settings.service.spellcheckerLanguage); | 254 | 'System spellcheckerLanguage', |
255 | this.settings.app.spellcheckerLanguage, | ||
256 | ); | ||
257 | debug( | ||
258 | 'Service spellcheckerLanguage', | ||
259 | this.settings.service.spellcheckerLanguage, | ||
260 | ); | ||
235 | debug('darkReaderSettigs', this.settings.service.darkReaderSettings); | 261 | debug('darkReaderSettigs', this.settings.service.darkReaderSettings); |
236 | debug('searchEngine', this.settings.app.searchEngine); | 262 | debug('searchEngine', this.settings.app.searchEngine); |
237 | 263 | ||
@@ -244,7 +270,10 @@ class RecipeController { | |||
244 | let { spellcheckerLanguage } = this; | 270 | let { spellcheckerLanguage } = this; |
245 | if (spellcheckerLanguage.includes('automatic')) { | 271 | if (spellcheckerLanguage.includes('automatic')) { |
246 | this.automaticLanguageDetection(); | 272 | this.automaticLanguageDetection(); |
247 | debug('Found `automatic` locale, falling back to user locale until detected', this.settings.app.locale); | 273 | debug( |
274 | 'Found `automatic` locale, falling back to user locale until detected', | ||
275 | this.settings.app.locale, | ||
276 | ); | ||
248 | spellcheckerLanguage = this.settings.app.locale; | 277 | spellcheckerLanguage = this.settings.app.locale; |
249 | } | 278 | } |
250 | switchDict(spellcheckerLanguage); | 279 | switchDict(spellcheckerLanguage); |
@@ -256,7 +285,7 @@ class RecipeController { | |||
256 | this.hasUpdatedBeforeRecipeLoaded = true; | 285 | this.hasUpdatedBeforeRecipeLoaded = true; |
257 | } | 286 | } |
258 | 287 | ||
259 | console.log( | 288 | debug( |
260 | 'Darkmode enabled?', | 289 | 'Darkmode enabled?', |
261 | this.settings.service.isDarkModeEnabled, | 290 | this.settings.service.isDarkModeEnabled, |
262 | 'Dark theme active?', | 291 | 'Dark theme active?', |
@@ -267,22 +296,29 @@ class RecipeController { | |||
267 | removeDarkModeStyle, | 296 | removeDarkModeStyle, |
268 | disableDarkMode, | 297 | disableDarkMode, |
269 | enableDarkMode, | 298 | enableDarkMode, |
270 | injectDarkModeStyle: () => injectDarkModeStyle(this.settings.service.recipe.path), | 299 | injectDarkModeStyle: () => |
300 | injectDarkModeStyle(this.settings.service.recipe.path), | ||
271 | isDarkModeStyleInjected, | 301 | isDarkModeStyleInjected, |
272 | }; | 302 | }; |
273 | 303 | ||
274 | if (this.settings.service.isDarkModeEnabled && this.settings.app.isDarkThemeActive !== false) { | 304 | if ( |
305 | this.settings.service.isDarkModeEnabled && | ||
306 | this.settings.app.isDarkThemeActive !== false | ||
307 | ) { | ||
275 | debug('Enable dark mode'); | 308 | debug('Enable dark mode'); |
276 | 309 | ||
277 | // Check if recipe has a darkmode.css | 310 | // Check if recipe has a darkmode.css |
278 | const darkModeStyle = path.join(this.settings.service.recipe.path, 'darkmode.css'); | 311 | const darkModeStyle = path.join( |
312 | this.settings.service.recipe.path, | ||
313 | 'darkmode.css', | ||
314 | ); | ||
279 | const darkModeExists = fs.pathExistsSync(darkModeStyle); | 315 | const darkModeExists = fs.pathExistsSync(darkModeStyle); |
280 | 316 | ||
281 | console.log('darkmode.css exists? ', darkModeExists ? 'Yes' : 'No'); | 317 | debug('darkmode.css exists? ', darkModeExists ? 'Yes' : 'No'); |
282 | 318 | ||
283 | // Check if recipe has a custom dark mode handler | 319 | // Check if recipe has a custom dark mode handler |
284 | if (this.recipe && this.recipe.darkModeHandler) { | 320 | if (this.recipe && this.recipe.darkModeHandler) { |
285 | console.log('Using custom dark mode handler'); | 321 | debug('Using custom dark mode handler'); |
286 | 322 | ||
287 | // Remove other dark mode styles if they were already loaded | 323 | // Remove other dark mode styles if they were already loaded |
288 | if (this.hasUpdatedBeforeRecipeLoaded) { | 324 | if (this.hasUpdatedBeforeRecipeLoaded) { |
@@ -293,25 +329,32 @@ class RecipeController { | |||
293 | 329 | ||
294 | this.recipe.darkModeHandler(true, handlerConfig); | 330 | this.recipe.darkModeHandler(true, handlerConfig); |
295 | } else if (darkModeExists) { | 331 | } else if (darkModeExists) { |
296 | console.log('Injecting darkmode.css'); | 332 | debug('Injecting darkmode.css'); |
297 | injectDarkModeStyle(this.settings.service.recipe.path); | 333 | injectDarkModeStyle(this.settings.service.recipe.path); |
298 | 334 | ||
299 | // Make sure universal dark mode is disabled | 335 | // Make sure universal dark mode is disabled |
300 | disableDarkMode(); | 336 | disableDarkMode(); |
301 | this.universalDarkModeInjected = false; | 337 | this.universalDarkModeInjected = false; |
302 | } else if (this.settings.app.universalDarkMode && !ignoreList.includes(window.location.host)) { | 338 | } else if ( |
303 | console.log('Injecting Dark Reader'); | 339 | this.settings.app.universalDarkMode && |
340 | !ignoreList.includes(window.location.host) | ||
341 | ) { | ||
342 | debug('Injecting Dark Reader'); | ||
304 | 343 | ||
305 | // Use Dark Reader instead | 344 | // Use Dark Reader instead |
306 | const { brightness, contrast, sepia } = this.settings.service.darkReaderSettings; | 345 | const { brightness, contrast, sepia } = |
307 | enableDarkMode({ brightness, contrast, sepia }, { | 346 | this.settings.service.darkReaderSettings; |
308 | css: customDarkModeCss[window.location.host] || '', | 347 | enableDarkMode( |
309 | }); | 348 | { brightness, contrast, sepia }, |
349 | { | ||
350 | css: customDarkModeCss[window.location.host] || '', | ||
351 | }, | ||
352 | ); | ||
310 | this.universalDarkModeInjected = true; | 353 | this.universalDarkModeInjected = true; |
311 | } | 354 | } |
312 | } else { | 355 | } else { |
313 | debug('Remove dark mode'); | 356 | debug('Remove dark mode'); |
314 | console.log('DarkMode disabled - removing remaining styles'); | 357 | debug('DarkMode disabled - removing remaining styles'); |
315 | 358 | ||
316 | if (this.recipe && this.recipe.darkModeHandler) { | 359 | if (this.recipe && this.recipe.darkModeHandler) { |
317 | // Remove other dark mode styles if they were already loaded | 360 | // Remove other dark mode styles if they were already loaded |
@@ -323,10 +366,10 @@ class RecipeController { | |||
323 | 366 | ||
324 | this.recipe.darkModeHandler(false, handlerConfig); | 367 | this.recipe.darkModeHandler(false, handlerConfig); |
325 | } else if (isDarkModeStyleInjected()) { | 368 | } else if (isDarkModeStyleInjected()) { |
326 | console.log('Removing injected darkmode.css'); | 369 | debug('Removing injected darkmode.css'); |
327 | removeDarkModeStyle(); | 370 | removeDarkModeStyle(); |
328 | } else { | 371 | } else { |
329 | console.log('Removing Dark Reader'); | 372 | debug('Removing Dark Reader'); |
330 | 373 | ||
331 | disableDarkMode(); | 374 | disableDarkMode(); |
332 | this.universalDarkModeInjected = false; | 375 | this.universalDarkModeInjected = false; |
@@ -336,9 +379,9 @@ class RecipeController { | |||
336 | // Remove dark reader if (universal) dark mode was just disabled | 379 | // Remove dark reader if (universal) dark mode was just disabled |
337 | if (this.universalDarkModeInjected) { | 380 | if (this.universalDarkModeInjected) { |
338 | if ( | 381 | if ( |
339 | !this.settings.app.darkMode | 382 | !this.settings.app.darkMode || |
340 | || !this.settings.service.isDarkModeEnabled | 383 | !this.settings.service.isDarkModeEnabled || |
341 | || !this.settings.app.universalDarkMode | 384 | !this.settings.app.universalDarkMode |
342 | ) { | 385 | ) { |
343 | disableDarkMode(); | 386 | disableDarkMode(); |
344 | this.universalDarkModeInjected = false; | 387 | this.universalDarkModeInjected = false; |
@@ -360,30 +403,39 @@ class RecipeController { | |||
360 | } | 403 | } |
361 | 404 | ||
362 | async automaticLanguageDetection() { | 405 | async automaticLanguageDetection() { |
363 | window.addEventListener('keyup', debounce(async (e) => { | 406 | window.addEventListener( |
364 | const element = e.target; | 407 | 'keyup', |
365 | 408 | debounce(async e => { | |
366 | if (!element) return; | 409 | const element = e.target; |
367 | 410 | ||
368 | let value = ''; | 411 | if (!element) return; |
369 | if (element.isContentEditable) { | 412 | |
370 | value = element.textContent; | 413 | let value = ''; |
371 | } else if (element.value) { | 414 | if (element.isContentEditable) { |
372 | value = element.value; | 415 | value = element.textContent; |
373 | } | 416 | } else if (element.value) { |
417 | value = element.value; | ||
418 | } | ||
374 | 419 | ||
375 | // Force a minimum length to get better detection results | 420 | // Force a minimum length to get better detection results |
376 | if (value.length < 25) return; | 421 | if (value.length < 25) return; |
377 | 422 | ||
378 | debug('Detecting language for', value); | 423 | debug('Detecting language for', value); |
379 | const locale = await ipcRenderer.invoke('detect-language', { sample: value }); | 424 | const locale = await ipcRenderer.invoke('detect-language', { |
425 | sample: value, | ||
426 | }); | ||
380 | 427 | ||
381 | const spellcheckerLocale = getSpellcheckerLocaleByFuzzyIdentifier(locale); | 428 | const spellcheckerLocale = |
382 | debug('Language detected reliably, setting spellchecker language to', spellcheckerLocale); | 429 | getSpellcheckerLocaleByFuzzyIdentifier(locale); |
383 | if (spellcheckerLocale) { | 430 | debug( |
384 | switchDict(spellcheckerLocale); | 431 | 'Language detected reliably, setting spellchecker language to', |
385 | } | 432 | spellcheckerLocale, |
386 | }, 225)); | 433 | ); |
434 | if (spellcheckerLocale) { | ||
435 | switchDict(spellcheckerLocale); | ||
436 | } | ||
437 | }, 225), | ||
438 | ); | ||
387 | } | 439 | } |
388 | } | 440 | } |
389 | 441 | ||