diff options
Diffstat (limited to 'src/webview')
-rw-r--r-- | src/webview/contextMenu.js | 2 | ||||
-rw-r--r-- | src/webview/darkmode.js | 2 | ||||
-rw-r--r-- | src/webview/darkmode/custom.js | 22 | ||||
-rw-r--r-- | src/webview/darkmode/ignore.js | 3 | ||||
-rw-r--r-- | src/webview/lib/RecipeWebview.js | 10 | ||||
-rw-r--r-- | src/webview/notifications.js | 2 | ||||
-rw-r--r-- | src/webview/recipe.js | 43 | ||||
-rw-r--r-- | src/webview/spellchecker.js | 77 |
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 { | |||
8 | import { isDevMode, isMac } from '../environment'; | 8 | import { isDevMode, isMac } from '../environment'; |
9 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; | 9 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; |
10 | 10 | ||
11 | const debug = require('debug')('Franz:contextMenu'); | 11 | const debug = require('debug')('Ferdi:contextMenu'); |
12 | 12 | ||
13 | const { Menu } = remote; | 13 | const { 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 @@ | |||
3 | import path from 'path'; | 3 | import path from 'path'; |
4 | import fs from 'fs-extra'; | 4 | import fs from 'fs-extra'; |
5 | 5 | ||
6 | const debug = require('debug')('Franz:DarkMode'); | 6 | const debug = require('debug')('Ferdi:DarkMode'); |
7 | 7 | ||
8 | const chars = [...'abcdefghijklmnopqrstuvwxyz']; | 8 | const 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 | ||
2 | export 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 @@ | |||
1 | export 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 @@ | |||
1 | import { ipcRenderer } from 'electron'; | 1 | import { ipcRenderer } from 'electron'; |
2 | import uuidV1 from 'uuid/v1'; | 2 | import uuidV1 from 'uuid/v1'; |
3 | 3 | ||
4 | const debug = require('debug')('Franz:Notifications'); | 4 | const debug = require('debug')('Ferdi:Notifications'); |
5 | 5 | ||
6 | class Notification { | 6 | class 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 @@ | |||
1 | import { ipcRenderer } from 'electron'; | 1 | import { ipcRenderer } from 'electron'; |
2 | import path from 'path'; | 2 | import path from 'path'; |
3 | import { autorun, computed, observable } from 'mobx'; | 3 | import { autorun, computed, observable } from 'mobx'; |
4 | import fs from 'fs-extra'; | ||
4 | import { loadModule } from 'cld3-asm'; | 5 | import { loadModule } from 'cld3-asm'; |
5 | import { debounce } from 'lodash'; | 6 | import { debounce } from 'lodash'; |
7 | import { | ||
8 | enable as enableDarkMode, | ||
9 | disable as disableDarkMode, | ||
10 | } from 'darkreader'; | ||
11 | |||
12 | import ignoreList from './darkmode/ignore'; | ||
13 | import customDarkModeCss from './darkmode/custom'; | ||
6 | 14 | ||
7 | import RecipeWebview from './lib/RecipeWebview'; | 15 | import RecipeWebview from './lib/RecipeWebview'; |
8 | 16 | ||
@@ -14,7 +22,7 @@ import './notifications'; | |||
14 | import { DEFAULT_APP_SETTINGS } from '../config'; | 22 | import { DEFAULT_APP_SETTINGS } from '../config'; |
15 | import { isDevMode } from '../environment'; | 23 | import { isDevMode } from '../environment'; |
16 | 24 | ||
17 | const debug = require('debug')('Franz:Plugin'); | 25 | const debug = require('debug')('Ferdi:Plugin'); |
18 | 26 | ||
19 | class RecipeController { | 27 | class 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 @@ | |||
1 | import { webFrame } from 'electron'; | 1 | import { webFrame } from 'electron'; |
2 | import { attachSpellCheckProvider, SpellCheckerProvider } from 'electron-hunspell'; | 2 | import { SpellCheckHandler, ContextMenuListener, ContextMenuBuilder } from 'electron-spellchecker'; |
3 | import path from 'path'; | ||
4 | import { readFileSync } from 'fs'; | ||
5 | 3 | ||
6 | import { DICTIONARY_PATH } from '../config'; | ||
7 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; | 4 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; |
8 | 5 | ||
9 | const debug = require('debug')('Franz:spellchecker'); | 6 | const debug = require('debug')('Franz:spellchecker'); |
10 | 7 | ||
11 | let provider; | 8 | let handler; |
12 | let currentDict; | 9 | let currentDict; |
10 | let contextMenuBuilder; | ||
13 | let _isEnabled = false; | 11 | let _isEnabled = false; |
14 | let attached; | ||
15 | 12 | ||
16 | const DEFAULT_LOCALE = 'en-us'; | 13 | export async function switchDict(locale) { |
17 | |||
18 | async 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 | |||
28 | export 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 | ||
59 | export function getSpellcheckerLocaleByFuzzyIdentifier(identifier) { | 40 | export 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 | |||
69 | export 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 | ||
97 | export function disable() { | 67 | export 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 | |||
76 | export 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 | } | ||