aboutsummaryrefslogtreecommitdiffstats
path: root/src/webview
diff options
context:
space:
mode:
Diffstat (limited to 'src/webview')
-rw-r--r--src/webview/contextMenu.js2
-rw-r--r--src/webview/darkmode.js2
-rw-r--r--src/webview/darkmode/custom.js22
-rw-r--r--src/webview/darkmode/ignore.js3
-rw-r--r--src/webview/lib/RecipeWebview.js10
-rw-r--r--src/webview/notifications.js2
-rw-r--r--src/webview/recipe.js43
-rw-r--r--src/webview/spellchecker.js77
8 files changed, 104 insertions, 57 deletions
diff --git a/src/webview/contextMenu.js b/src/webview/contextMenu.js
index d3b976554..acd62d675 100644
--- a/src/webview/contextMenu.js
+++ b/src/webview/contextMenu.js
@@ -8,7 +8,7 @@ import {
8import { isDevMode, isMac } from '../environment'; 8import { isDevMode, isMac } from '../environment';
9import { SPELLCHECKER_LOCALES } from '../i18n/languages'; 9import { SPELLCHECKER_LOCALES } from '../i18n/languages';
10 10
11const debug = require('debug')('Franz:contextMenu'); 11const debug = require('debug')('Ferdi:contextMenu');
12 12
13const { Menu } = remote; 13const { Menu } = remote;
14 14
diff --git a/src/webview/darkmode.js b/src/webview/darkmode.js
index 73c7007c6..ab629435c 100644
--- a/src/webview/darkmode.js
+++ b/src/webview/darkmode.js
@@ -3,7 +3,7 @@
3import path from 'path'; 3import path from 'path';
4import fs from 'fs-extra'; 4import fs from 'fs-extra';
5 5
6const debug = require('debug')('Franz:DarkMode'); 6const debug = require('debug')('Ferdi:DarkMode');
7 7
8const chars = [...'abcdefghijklmnopqrstuvwxyz']; 8const chars = [...'abcdefghijklmnopqrstuvwxyz'];
9 9
diff --git a/src/webview/darkmode/custom.js b/src/webview/darkmode/custom.js
new file mode 100644
index 000000000..f767f5755
--- /dev/null
+++ b/src/webview/darkmode/custom.js
@@ -0,0 +1,22 @@
1// CSS for pages that need custom styles to work correctly in darkmode
2export default {
3 'web.whatsapp.com': `
4 div.landing-window > div.landing-main {
5 background-color: #FFFFFF !important;
6 }
7 div.landing-window > div.landing-main * {
8 color: #212121 !important;
9 }
10 `,
11 'web.threema.ch': `
12 .scan {
13 background-color: #FFF;
14 }
15 .scan * {
16 color: #212121;
17 }
18 .scan input.md-input {
19 color: #212121;
20 }
21 `,
22};
diff --git a/src/webview/darkmode/ignore.js b/src/webview/darkmode/ignore.js
new file mode 100644
index 000000000..110df364f
--- /dev/null
+++ b/src/webview/darkmode/ignore.js
@@ -0,0 +1,3 @@
1export default [
2 'discordapp.com',
3];
diff --git a/src/webview/lib/RecipeWebview.js b/src/webview/lib/RecipeWebview.js
index 859f7787f..74d05fc2d 100644
--- a/src/webview/lib/RecipeWebview.js
+++ b/src/webview/lib/RecipeWebview.js
@@ -42,9 +42,15 @@ class RecipeWebview {
42 if (this.countCache.direct === direct 42 if (this.countCache.direct === direct
43 && this.countCache.indirect === indirect) return; 43 && this.countCache.indirect === indirect) return;
44 44
45 // Parse number to integer
46 // This will correct errors that recipes may introduce, e.g.
47 // by sending a String instead of an integer
48 const directInt = parseInt(direct, 10);
49 const indirectInt = parseInt(indirect, 10);
50
45 const count = { 51 const count = {
46 direct: direct > 0 ? direct : 0, 52 direct: directInt > 0 ? directInt : 0,
47 indirect: indirect > 0 ? indirect : 0, 53 indirect: indirectInt > 0 ? indirectInt : 0,
48 }; 54 };
49 55
50 56
diff --git a/src/webview/notifications.js b/src/webview/notifications.js
index f8fe53e1b..021f05cc3 100644
--- a/src/webview/notifications.js
+++ b/src/webview/notifications.js
@@ -1,7 +1,7 @@
1import { ipcRenderer } from 'electron'; 1import { ipcRenderer } from 'electron';
2import uuidV1 from 'uuid/v1'; 2import uuidV1 from 'uuid/v1';
3 3
4const debug = require('debug')('Franz:Notifications'); 4const debug = require('debug')('Ferdi:Notifications');
5 5
6class Notification { 6class Notification {
7 static permission = 'granted'; 7 static permission = 'granted';
diff --git a/src/webview/recipe.js b/src/webview/recipe.js
index 027d22c0a..2bf8f757a 100644
--- a/src/webview/recipe.js
+++ b/src/webview/recipe.js
@@ -1,8 +1,16 @@
1import { ipcRenderer } from 'electron'; 1import { ipcRenderer } from 'electron';
2import path from 'path'; 2import path from 'path';
3import { autorun, computed, observable } from 'mobx'; 3import { autorun, computed, observable } from 'mobx';
4import fs from 'fs-extra';
4import { loadModule } from 'cld3-asm'; 5import { loadModule } from 'cld3-asm';
5import { debounce } from 'lodash'; 6import { debounce } from 'lodash';
7import {
8 enable as enableDarkMode,
9 disable as disableDarkMode,
10} from 'darkreader';
11
12import ignoreList from './darkmode/ignore';
13import customDarkModeCss from './darkmode/custom';
6 14
7import RecipeWebview from './lib/RecipeWebview'; 15import RecipeWebview from './lib/RecipeWebview';
8 16
@@ -14,7 +22,7 @@ import './notifications';
14import { DEFAULT_APP_SETTINGS } from '../config'; 22import { DEFAULT_APP_SETTINGS } from '../config';
15import { isDevMode } from '../environment'; 23import { isDevMode } from '../environment';
16 24
17const debug = require('debug')('Franz:Plugin'); 25const debug = require('debug')('Ferdi:Plugin');
18 26
19class RecipeController { 27class RecipeController {
20 @observable settings = { 28 @observable settings = {
@@ -35,6 +43,8 @@ class RecipeController {
35 'get-service-id': 'serviceIdEcho', 43 'get-service-id': 'serviceIdEcho',
36 }; 44 };
37 45
46 universalDarkModeInjected = false;
47
38 constructor() { 48 constructor() {
39 this.initialize(); 49 this.initialize();
40 } 50 }
@@ -112,10 +122,35 @@ class RecipeController {
112 122
113 if (this.settings.service.isDarkModeEnabled) { 123 if (this.settings.service.isDarkModeEnabled) {
114 debug('Enable dark mode'); 124 debug('Enable dark mode');
115 injectDarkModeStyle(this.settings.service.recipe.path); 125
116 } else if (isDarkModeStyleInjected()) { 126 // Check if recipe has a darkmode.css
127 const darkModeStyle = path.join(this.settings.service.recipe.path, 'darkmode.css');
128 const darkModeExists = fs.pathExistsSync(darkModeStyle);
129
130 if (darkModeExists) {
131 injectDarkModeStyle(this.settings.service.recipe.path);
132 } else if (this.settings.app.universalDarkMode && !ignoreList.includes(window.location.host)) {
133 // Use darkreader instead
134 enableDarkMode({}, {
135 css: customDarkModeCss[window.location.host] || '',
136 });
137 this.universalDarkModeInjected = true;
138 }
139 } else {
117 debug('Remove dark mode'); 140 debug('Remove dark mode');
118 removeDarkModeStyle(); 141
142 if (isDarkModeStyleInjected()) {
143 removeDarkModeStyle();
144 } else {
145 disableDarkMode();
146 this.universalDarkModeInjected = false;
147 }
148 }
149
150 // Remove dark reader if (universal) dark mode was just disabled
151 if (this.universalDarkModeInjected && (!this.settings.service.isDarkModeEnabled || !this.settings.app.universalDarkMode)) {
152 disableDarkMode();
153 this.universalDarkModeInjected = false;
119 } 154 }
120 } 155 }
121 156
diff --git a/src/webview/spellchecker.js b/src/webview/spellchecker.js
index d8f71f61f..47fc6dcd1 100644
--- a/src/webview/spellchecker.js
+++ b/src/webview/spellchecker.js
@@ -1,36 +1,21 @@
1import { webFrame } from 'electron'; 1import { webFrame } from 'electron';
2import { attachSpellCheckProvider, SpellCheckerProvider } from 'electron-hunspell'; 2import { SpellCheckHandler, ContextMenuListener, ContextMenuBuilder } from 'electron-spellchecker';
3import path from 'path';
4import { readFileSync } from 'fs';
5 3
6import { DICTIONARY_PATH } from '../config';
7import { SPELLCHECKER_LOCALES } from '../i18n/languages'; 4import { SPELLCHECKER_LOCALES } from '../i18n/languages';
8 5
9const debug = require('debug')('Franz:spellchecker'); 6const debug = require('debug')('Franz:spellchecker');
10 7
11let provider; 8let handler;
12let currentDict; 9let currentDict;
10let contextMenuBuilder;
13let _isEnabled = false; 11let _isEnabled = false;
14let attached;
15 12
16const DEFAULT_LOCALE = 'en-us'; 13export async function switchDict(locale) {
17
18async function loadDictionary(locale) {
19 try {
20 const fileLocation = path.join(DICTIONARY_PATH, `hunspell-dict-${locale}/${locale}`);
21 debug('Loaded dictionary', locale, 'from', fileLocation);
22 return provider.loadDictionary(locale, readFileSync(`${fileLocation}.dic`), readFileSync(`${fileLocation}.aff`));
23 } catch (err) {
24 console.error('Could not load dictionary', err);
25 }
26}
27
28export async function switchDict(locale = DEFAULT_LOCALE) {
29 try { 14 try {
30 debug('Trying to load dictionary', locale); 15 debug('Trying to load dictionary', locale);
31 16
32 if (!provider) { 17 if (!handler) {
33 console.warn('SpellcheckProvider not initialized'); 18 console.warn('SpellcheckHandler not initialized');
34 19
35 return; 20 return;
36 } 21 }
@@ -41,11 +26,7 @@ export async function switchDict(locale = DEFAULT_LOCALE) {
41 return; 26 return;
42 } 27 }
43 28
44 if (currentDict) { 29 handler.switchLanguage(locale);
45 provider.unloadDictionary(locale);
46 }
47 await loadDictionary(locale);
48 await attached.switchLanguage(locale);
49 30
50 debug('Switched dictionary to', locale); 31 debug('Switched dictionary to', locale);
51 32
@@ -56,34 +37,23 @@ export async function switchDict(locale = DEFAULT_LOCALE) {
56 } 37 }
57} 38}
58 39
59export function getSpellcheckerLocaleByFuzzyIdentifier(identifier) { 40export default async function initialize(languageCode = 'en-us') {
60 const locales = Object.keys(SPELLCHECKER_LOCALES).filter(key => key === identifier.toLowerCase() || key.split('-')[0] === identifier.toLowerCase());
61
62 if (locales.length >= 1) {
63 return locales[0];
64 }
65
66 return null;
67}
68
69export default async function initialize(languageCode = DEFAULT_LOCALE) {
70 try { 41 try {
71 provider = new SpellCheckerProvider(); 42 handler = new SpellCheckHandler();
72 const locale = getSpellcheckerLocaleByFuzzyIdentifier(languageCode); 43 handler.attachToInput();
44 const locale = languageCode.toLowerCase();
73 45
74 debug('Init spellchecker'); 46 debug('Init spellchecker');
75 await provider.initialize();
76
77 debug('Attaching spellcheck provider');
78 attached = await attachSpellCheckProvider(provider);
79
80 const availableDictionaries = await provider.getAvailableDictionaries();
81 47
82 debug('Available spellchecker dictionaries', availableDictionaries); 48 switchDict(locale);
83 49
84 await switchDict(locale); 50 contextMenuBuilder = new ContextMenuBuilder(handler);
51 // eslint-disable-next-line no-new
52 new ContextMenuListener((info) => {
53 contextMenuBuilder.showPopupMenu(info);
54 });
85 55
86 return provider; 56 return handler;
87 } catch (err) { 57 } catch (err) {
88 console.error(err); 58 console.error(err);
89 return false; 59 return false;
@@ -96,8 +66,19 @@ export function isEnabled() {
96 66
97export function disable() { 67export function disable() {
98 if (isEnabled()) { 68 if (isEnabled()) {
69 handler.unsubscribe();
99 webFrame.setSpellCheckProvider(currentDict, { spellCheck: () => true }); 70 webFrame.setSpellCheckProvider(currentDict, { spellCheck: () => true });
100 _isEnabled = false; 71 _isEnabled = false;
101 currentDict = null; 72 currentDict = null;
102 } 73 }
103} 74}
75
76export function getSpellcheckerLocaleByFuzzyIdentifier(identifier) {
77 const locales = Object.keys(SPELLCHECKER_LOCALES).filter(key => key === identifier.toLowerCase() || key.split('-')[0] === identifier.toLowerCase());
78
79 if (locales.length >= 1) {
80 return locales[0];
81 }
82
83 return null;
84}