diff options
Diffstat (limited to 'src/models/Service.js')
-rw-r--r-- | src/models/Service.js | 142 |
1 files changed, 99 insertions, 43 deletions
diff --git a/src/models/Service.js b/src/models/Service.js index 4ee054b2b..cc001f98d 100644 --- a/src/models/Service.js +++ b/src/models/Service.js | |||
@@ -8,7 +8,11 @@ import { todosStore } from '../features/todos'; | |||
8 | import { isValidExternalURL } from '../helpers/url-helpers'; | 8 | import { isValidExternalURL } from '../helpers/url-helpers'; |
9 | import UserAgent from './UserAgent'; | 9 | import UserAgent from './UserAgent'; |
10 | import { DEFAULT_SERVICE_ORDER } from '../config'; | 10 | import { DEFAULT_SERVICE_ORDER } from '../config'; |
11 | import { ifUndefinedString, ifUndefinedBoolean, ifUndefinedNumber } from '../jsUtils'; | 11 | import { |
12 | ifUndefinedString, | ||
13 | ifUndefinedBoolean, | ||
14 | ifUndefinedNumber, | ||
15 | } from '../jsUtils'; | ||
12 | 16 | ||
13 | const debug = require('debug')('Ferdi:Service'); | 17 | const debug = require('debug')('Ferdi:Service'); |
14 | 18 | ||
@@ -95,11 +99,11 @@ export default class Service { | |||
95 | 99 | ||
96 | constructor(data, recipe) { | 100 | constructor(data, recipe) { |
97 | if (!data) { | 101 | if (!data) { |
98 | throw Error('Service config not valid'); | 102 | throw new Error('Service config not valid'); |
99 | } | 103 | } |
100 | 104 | ||
101 | if (!recipe) { | 105 | if (!recipe) { |
102 | throw Error('Service recipe not valid'); | 106 | throw new Error('Service recipe not valid'); |
103 | } | 107 | } |
104 | 108 | ||
105 | this.recipe = recipe; | 109 | this.recipe = recipe; |
@@ -115,22 +119,51 @@ export default class Service { | |||
115 | 119 | ||
116 | this.order = ifUndefinedNumber(data.order, this.order); | 120 | this.order = ifUndefinedNumber(data.order, this.order); |
117 | this.isEnabled = ifUndefinedBoolean(data.isEnabled, this.isEnabled); | 121 | this.isEnabled = ifUndefinedBoolean(data.isEnabled, this.isEnabled); |
118 | this.isNotificationEnabled = ifUndefinedBoolean(data.isNotificationEnabled, this.isNotificationEnabled); | 122 | this.isNotificationEnabled = ifUndefinedBoolean( |
119 | this.isBadgeEnabled = ifUndefinedBoolean(data.isBadgeEnabled, this.isBadgeEnabled); | 123 | data.isNotificationEnabled, |
120 | this.isIndirectMessageBadgeEnabled = ifUndefinedBoolean(data.isIndirectMessageBadgeEnabled, this.isIndirectMessageBadgeEnabled); | 124 | this.isNotificationEnabled, |
125 | ); | ||
126 | this.isBadgeEnabled = ifUndefinedBoolean( | ||
127 | data.isBadgeEnabled, | ||
128 | this.isBadgeEnabled, | ||
129 | ); | ||
130 | this.isIndirectMessageBadgeEnabled = ifUndefinedBoolean( | ||
131 | data.isIndirectMessageBadgeEnabled, | ||
132 | this.isIndirectMessageBadgeEnabled, | ||
133 | ); | ||
121 | this.isMuted = ifUndefinedBoolean(data.isMuted, this.isMuted); | 134 | this.isMuted = ifUndefinedBoolean(data.isMuted, this.isMuted); |
122 | this.isDarkModeEnabled = ifUndefinedBoolean(data.isDarkModeEnabled, this.isDarkModeEnabled); | 135 | this.isDarkModeEnabled = ifUndefinedBoolean( |
123 | this.darkReaderSettings = ifUndefinedString(data.darkReaderSettings, this.darkReaderSettings); | 136 | data.isDarkModeEnabled, |
124 | this.hasCustomUploadedIcon = ifUndefinedBoolean(data.hasCustomIcon, this.hasCustomUploadedIcon); | 137 | this.isDarkModeEnabled, |
138 | ); | ||
139 | this.darkReaderSettings = ifUndefinedString( | ||
140 | data.darkReaderSettings, | ||
141 | this.darkReaderSettings, | ||
142 | ); | ||
143 | this.hasCustomUploadedIcon = ifUndefinedBoolean( | ||
144 | data.hasCustomIcon, | ||
145 | this.hasCustomUploadedIcon, | ||
146 | ); | ||
125 | this.proxy = ifUndefinedString(data.proxy, this.proxy); | 147 | this.proxy = ifUndefinedString(data.proxy, this.proxy); |
126 | this.spellcheckerLanguage = ifUndefinedString(data.spellcheckerLanguage, this.spellcheckerLanguage); | 148 | this.spellcheckerLanguage = ifUndefinedString( |
127 | this.userAgentPref = ifUndefinedString(data.userAgentPref, this.userAgentPref); | 149 | data.spellcheckerLanguage, |
128 | this.isHibernationEnabled = ifUndefinedBoolean(data.isHibernationEnabled, this.isHibernationEnabled); | 150 | this.spellcheckerLanguage, |
151 | ); | ||
152 | this.userAgentPref = ifUndefinedString( | ||
153 | data.userAgentPref, | ||
154 | this.userAgentPref, | ||
155 | ); | ||
156 | this.isHibernationEnabled = ifUndefinedBoolean( | ||
157 | data.isHibernationEnabled, | ||
158 | this.isHibernationEnabled, | ||
159 | ); | ||
129 | 160 | ||
130 | // Check if "Hibernate on Startup" is enabled and hibernate all services except active one | 161 | // Check if "Hibernate on Startup" is enabled and hibernate all services except active one |
131 | const { hibernateOnStartup } = window.ferdi.stores.settings.app; | 162 | const { hibernateOnStartup } = window.ferdi.stores.settings.app; |
132 | // The service store is probably not loaded yet so we need to use localStorage data to get active service | 163 | // The service store is probably not loaded yet so we need to use localStorage data to get active service |
133 | const isActive = window.localStorage.service && JSON.parse(window.localStorage.service).activeService === this.id; | 164 | const isActive = |
165 | window.localStorage.service && | ||
166 | JSON.parse(window.localStorage.service).activeService === this.id; | ||
134 | if (hibernateOnStartup && !isActive) { | 167 | if (hibernateOnStartup && !isActive) { |
135 | this.isHibernationRequested = true; | 168 | this.isHibernationRequested = true; |
136 | } | 169 | } |
@@ -189,9 +222,14 @@ export default class Service { | |||
189 | if (this.recipe.hasCustomUrl && this.customUrl) { | 222 | if (this.recipe.hasCustomUrl && this.customUrl) { |
190 | let url; | 223 | let url; |
191 | try { | 224 | try { |
192 | url = normalizeUrl(this.customUrl, { stripWWW: false, removeTrailingSlash: false }); | 225 | url = normalizeUrl(this.customUrl, { |
193 | } catch (err) { | 226 | stripWWW: false, |
194 | console.error(`Service (${this.recipe.name}): '${this.customUrl}' is not a valid Url.`); | 227 | removeTrailingSlash: false, |
228 | }); | ||
229 | } catch { | ||
230 | console.error( | ||
231 | `Service (${this.recipe.name}): '${this.customUrl}' is not a valid Url.`, | ||
232 | ); | ||
195 | } | 233 | } |
196 | 234 | ||
197 | if (typeof this.recipe.buildUrl === 'function') { | 235 | if (typeof this.recipe.buildUrl === 'function') { |
@@ -241,7 +279,9 @@ export default class Service { | |||
241 | } | 279 | } |
242 | 280 | ||
243 | initializeWebViewEvents({ handleIPCMessage, openWindow, stores }) { | 281 | initializeWebViewEvents({ handleIPCMessage, openWindow, stores }) { |
244 | const webviewWebContents = webContents.fromId(this.webview.getWebContentsId()); | 282 | const webviewWebContents = webContents.fromId( |
283 | this.webview.getWebContentsId(), | ||
284 | ); | ||
245 | 285 | ||
246 | this.userAgentModel.setWebviewReference(this.webview); | 286 | this.userAgentModel.setWebviewReference(this.webview); |
247 | 287 | ||
@@ -270,9 +310,15 @@ export default class Service { | |||
270 | debug(this.name, 'knownCertificateHosts is not defined in the recipe'); | 310 | debug(this.name, 'knownCertificateHosts is not defined in the recipe'); |
271 | } | 311 | } |
272 | 312 | ||
273 | this.webview.addEventListener('ipc-message', async (e) => { | 313 | this.webview.addEventListener('ipc-message', async e => { |
274 | if (e.channel === 'inject-js-unsafe') { | 314 | if (e.channel === 'inject-js-unsafe') { |
275 | await Promise.all(e.args.map((script) => this.webview.executeJavaScript(`"use strict"; (() => { ${script} })();`))); | 315 | await Promise.all( |
316 | e.args.map(script => | ||
317 | this.webview.executeJavaScript( | ||
318 | `"use strict"; (() => { ${script} })();`, | ||
319 | ), | ||
320 | ), | ||
321 | ); | ||
276 | } else { | 322 | } else { |
277 | handleIPCMessage({ | 323 | handleIPCMessage({ |
278 | serviceId: this.id, | 324 | serviceId: this.id, |
@@ -282,27 +328,33 @@ export default class Service { | |||
282 | } | 328 | } |
283 | }); | 329 | }); |
284 | 330 | ||
285 | this.webview.addEventListener('new-window', (event, url, frameName, options) => { | 331 | this.webview.addEventListener( |
286 | debug('new-window', event, url, frameName, options); | 332 | 'new-window', |
287 | if (!isValidExternalURL(event.url)) { | 333 | (event, url, frameName, options) => { |
288 | return; | 334 | debug('new-window', event, url, frameName, options); |
289 | } | 335 | if (!isValidExternalURL(event.url)) { |
290 | if (event.disposition === 'foreground-tab' || event.disposition === 'background-tab') { | 336 | return; |
291 | openWindow({ | 337 | } |
292 | event, | 338 | if ( |
293 | url, | 339 | event.disposition === 'foreground-tab' || |
294 | frameName, | 340 | event.disposition === 'background-tab' |
295 | options, | 341 | ) { |
296 | }); | 342 | openWindow({ |
297 | } else { | 343 | event, |
298 | ipcRenderer.send('open-browser-window', { | 344 | url, |
299 | url: event.url, | 345 | frameName, |
300 | serviceId: this.id, | 346 | options, |
301 | }); | 347 | }); |
302 | } | 348 | } else { |
303 | }); | 349 | ipcRenderer.send('open-browser-window', { |
350 | url: event.url, | ||
351 | serviceId: this.id, | ||
352 | }); | ||
353 | } | ||
354 | }, | ||
355 | ); | ||
304 | 356 | ||
305 | this.webview.addEventListener('did-start-loading', (event) => { | 357 | this.webview.addEventListener('did-start-loading', event => { |
306 | debug('Did start load', this.name, event); | 358 | debug('Did start load', this.name, event); |
307 | 359 | ||
308 | this.hasCrashed = false; | 360 | this.hasCrashed = false; |
@@ -321,9 +373,13 @@ export default class Service { | |||
321 | this.webview.addEventListener('did-frame-finish-load', didLoad.bind(this)); | 373 | this.webview.addEventListener('did-frame-finish-load', didLoad.bind(this)); |
322 | this.webview.addEventListener('did-navigate', didLoad.bind(this)); | 374 | this.webview.addEventListener('did-navigate', didLoad.bind(this)); |
323 | 375 | ||
324 | this.webview.addEventListener('did-fail-load', (event) => { | 376 | this.webview.addEventListener('did-fail-load', event => { |
325 | debug('Service failed to load', this.name, event); | 377 | debug('Service failed to load', this.name, event); |
326 | if (event.isMainFrame && event.errorCode !== -21 && event.errorCode !== -3) { | 378 | if ( |
379 | event.isMainFrame && | ||
380 | event.errorCode !== -21 && | ||
381 | event.errorCode !== -3 | ||
382 | ) { | ||
327 | this.isError = true; | 383 | this.isError = true; |
328 | this.errorMessage = event.errorDescription; | 384 | this.errorMessage = event.errorDescription; |
329 | this.isLoading = false; | 385 | this.isLoading = false; |
@@ -365,12 +421,12 @@ export default class Service { | |||
365 | 421 | ||
366 | initializeWebViewListener() { | 422 | initializeWebViewListener() { |
367 | if (this.webview && this.recipe.events) { | 423 | if (this.webview && this.recipe.events) { |
368 | Object.keys(this.recipe.events).forEach((eventName) => { | 424 | for (const eventName of Object.keys(this.recipe.events)) { |
369 | const eventHandler = this.recipe[this.recipe.events[eventName]]; | 425 | const eventHandler = this.recipe[this.recipe.events[eventName]]; |
370 | if (typeof eventHandler === 'function') { | 426 | if (typeof eventHandler === 'function') { |
371 | this.webview.addEventListener(eventName, eventHandler); | 427 | this.webview.addEventListener(eventName, eventHandler); |
372 | } | 428 | } |
373 | }); | 429 | } |
374 | } | 430 | } |
375 | } | 431 | } |
376 | 432 | ||