diff options
author | Markus Hatvan <markus_hatvan@aon.at> | 2021-10-10 10:42:20 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-10 10:42:20 +0200 |
commit | a673c2b577a31437c91a0b498e69b0753d62aa58 (patch) | |
tree | 50d52df3ff9d78828ca06a24ef438e31b062ca56 /src/index.js | |
parent | remove unused 'conventional-changelog' npm package. Change the url for the ch... (diff) | |
download | ferdium-app-a673c2b577a31437c91a0b498e69b0753d62aa58.tar.gz ferdium-app-a673c2b577a31437c91a0b498e69b0753d62aa58.tar.zst ferdium-app-a673c2b577a31437c91a0b498e69b0753d62aa58.zip |
chore: convert index file to TS (#2049)
Diffstat (limited to 'src/index.js')
-rw-r--r-- | src/index.js | 635 |
1 files changed, 0 insertions, 635 deletions
diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 933637688..000000000 --- a/src/index.js +++ /dev/null | |||
@@ -1,635 +0,0 @@ | |||
1 | /* eslint-disable import/first */ | ||
2 | |||
3 | import { app, BrowserWindow, globalShortcut, ipcMain, session, dialog } from 'electron'; | ||
4 | |||
5 | import { emptyDirSync, ensureFileSync } from 'fs-extra'; | ||
6 | import { join } from 'path'; | ||
7 | import windowStateKeeper from 'electron-window-state'; | ||
8 | import ms from 'ms'; | ||
9 | import { initializeRemote } from './electron-util'; | ||
10 | import { enforceMacOSAppLocation } from './enforce-macos-app-location'; | ||
11 | |||
12 | initializeRemote(); | ||
13 | |||
14 | import { DEFAULT_APP_SETTINGS, DEFAULT_WINDOW_OPTIONS } from './config'; | ||
15 | |||
16 | import { | ||
17 | isMac, | ||
18 | isWindows, | ||
19 | isLinux, | ||
20 | altKey, | ||
21 | } from './environment'; | ||
22 | import { | ||
23 | isDevMode, | ||
24 | aboutAppDetails, | ||
25 | userDataRecipesPath, | ||
26 | userDataPath, | ||
27 | } from './environment-remote'; | ||
28 | import { ifUndefinedBoolean } from './jsUtils'; | ||
29 | |||
30 | import { mainIpcHandler as basicAuthHandler } from './features/basicAuth'; | ||
31 | import ipcApi from './electron/ipc-api'; | ||
32 | import Tray from './lib/Tray'; | ||
33 | import DBus from './lib/DBus'; | ||
34 | import Settings from './electron/Settings'; | ||
35 | import handleDeepLink from './electron/deepLinking'; | ||
36 | import { isPositionValid } from './electron/windowUtils'; | ||
37 | import { appId } from './package.json'; // eslint-disable-line import/no-unresolved | ||
38 | import './electron/exception'; | ||
39 | |||
40 | import { asarPath } from './helpers/asar-helpers'; | ||
41 | import { openExternalUrl } from './helpers/url-helpers'; | ||
42 | import userAgent from './helpers/userAgent-helpers'; | ||
43 | |||
44 | const debug = require('debug')('Ferdi:App'); | ||
45 | |||
46 | // Globally set useragent to fix user agent override in service workers | ||
47 | debug('Set userAgent to ', userAgent()); | ||
48 | app.userAgentFallback = userAgent(); | ||
49 | |||
50 | // Keep a global reference of the window object, if you don't, the window will | ||
51 | // be closed automatically when the JavaScript object is garbage collected. | ||
52 | let mainWindow; | ||
53 | let willQuitApp = false; | ||
54 | |||
55 | // Register methods to be called once the window has been loaded. | ||
56 | let onDidLoadFns = []; | ||
57 | |||
58 | function onDidLoad(fn) { | ||
59 | if (onDidLoadFns) { | ||
60 | onDidLoadFns.push(fn); | ||
61 | } else if (mainWindow) { | ||
62 | fn(mainWindow); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | // Ensure that the recipe directory exists | ||
67 | emptyDirSync(userDataRecipesPath('temp')); | ||
68 | ensureFileSync(userDataPath('window-state.json')); | ||
69 | |||
70 | // Set App ID for Windows | ||
71 | if (isWindows) { | ||
72 | app.setAppUserModelId(appId); | ||
73 | } | ||
74 | |||
75 | // Initialize Settings | ||
76 | const settings = new Settings('app', DEFAULT_APP_SETTINGS); | ||
77 | const proxySettings = new Settings('proxy'); | ||
78 | |||
79 | const retrieveSettingValue = (key, defaultValue) => | ||
80 | ifUndefinedBoolean(settings.get(key), defaultValue); | ||
81 | |||
82 | if (retrieveSettingValue('sentry', DEFAULT_APP_SETTINGS.sentry)) { | ||
83 | // eslint-disable-next-line global-require | ||
84 | require('./sentry'); | ||
85 | } | ||
86 | |||
87 | const liftSingleInstanceLock = retrieveSettingValue( | ||
88 | 'liftSingleInstanceLock', | ||
89 | false, | ||
90 | ); | ||
91 | |||
92 | // Force single window | ||
93 | const gotTheLock = liftSingleInstanceLock | ||
94 | ? true | ||
95 | : app.requestSingleInstanceLock(); | ||
96 | if (!gotTheLock) { | ||
97 | app.quit(); | ||
98 | } else { | ||
99 | app.on('second-instance', (event, argv) => { | ||
100 | // Someone tried to run a second instance, we should focus our window. | ||
101 | if (mainWindow) { | ||
102 | if (!mainWindow.isVisible()) { | ||
103 | mainWindow.show(); | ||
104 | } | ||
105 | if (mainWindow.isMinimized()) { | ||
106 | mainWindow.restore(); | ||
107 | } | ||
108 | mainWindow.focus(); | ||
109 | |||
110 | if (isWindows) { | ||
111 | onDidLoad(window => { | ||
112 | // Keep only command line / deep linked arguments | ||
113 | const url = argv.slice(1); | ||
114 | if (url) { | ||
115 | handleDeepLink(window, url.toString()); | ||
116 | } | ||
117 | |||
118 | if (argv.includes('--reset-window')) { | ||
119 | // Needs to be delayed to not interfere with mainWindow.restore(); | ||
120 | setTimeout(() => { | ||
121 | debug('Resetting windows via Task'); | ||
122 | window.setPosition( | ||
123 | DEFAULT_WINDOW_OPTIONS.x + 100, | ||
124 | DEFAULT_WINDOW_OPTIONS.y + 100, | ||
125 | ); | ||
126 | window.setSize( | ||
127 | DEFAULT_WINDOW_OPTIONS.width, | ||
128 | DEFAULT_WINDOW_OPTIONS.height, | ||
129 | ); | ||
130 | }, 1); | ||
131 | } else if (argv.includes('--quit')) { | ||
132 | // Needs to be delayed to not interfere with mainWindow.restore(); | ||
133 | setTimeout(() => { | ||
134 | debug('Quitting Ferdi via Task'); | ||
135 | app.quit(); | ||
136 | }, 1); | ||
137 | } | ||
138 | }); | ||
139 | } | ||
140 | } | ||
141 | }); | ||
142 | } | ||
143 | |||
144 | // Fix Unity indicator issue | ||
145 | // https://github.com/electron/electron/issues/9046 | ||
146 | if ( | ||
147 | isLinux && | ||
148 | ['Pantheon', 'Unity:Unity7'].includes(process.env.XDG_CURRENT_DESKTOP) | ||
149 | ) { | ||
150 | process.env.XDG_CURRENT_DESKTOP = 'Unity'; | ||
151 | } | ||
152 | |||
153 | // Disable GPU acceleration | ||
154 | if (!retrieveSettingValue('enableGPUAcceleration', false)) { | ||
155 | debug('Disable GPU Acceleration'); | ||
156 | app.disableHardwareAcceleration(); | ||
157 | } | ||
158 | |||
159 | app.setAboutPanelOptions({ | ||
160 | applicationVersion: aboutAppDetails(), | ||
161 | version: '', | ||
162 | }); | ||
163 | |||
164 | const createWindow = () => { | ||
165 | // Remember window size | ||
166 | const mainWindowState = windowStateKeeper({ | ||
167 | defaultWidth: DEFAULT_WINDOW_OPTIONS.width, | ||
168 | defaultHeight: DEFAULT_WINDOW_OPTIONS.height, | ||
169 | maximize: true, // Automatically maximizes the window, if it was last closed maximized | ||
170 | fullScreen: true, // Automatically restores the window to full screen, if it was last closed full screen | ||
171 | }); | ||
172 | |||
173 | let posX = mainWindowState.x || DEFAULT_WINDOW_OPTIONS.x; | ||
174 | let posY = mainWindowState.y || DEFAULT_WINDOW_OPTIONS.y; | ||
175 | |||
176 | if (!isPositionValid({ x: posX, y: posY })) { | ||
177 | debug('Window is out of screen bounds, resetting window'); | ||
178 | posX = DEFAULT_WINDOW_OPTIONS.x; | ||
179 | posY = DEFAULT_WINDOW_OPTIONS.y; | ||
180 | } | ||
181 | |||
182 | // Create the browser window. | ||
183 | const backgroundColor = retrieveSettingValue('darkMode', false) | ||
184 | ? '#1E1E1E' | ||
185 | : settings.get('accentColor'); | ||
186 | |||
187 | mainWindow = new BrowserWindow({ | ||
188 | x: posX, | ||
189 | y: posY, | ||
190 | width: mainWindowState.width, | ||
191 | height: mainWindowState.height, | ||
192 | minWidth: 600, | ||
193 | minHeight: 500, | ||
194 | show: false, | ||
195 | titleBarStyle: isMac ? 'hidden' : 'default', | ||
196 | frame: isLinux, | ||
197 | backgroundColor, | ||
198 | webPreferences: { | ||
199 | spellcheck: retrieveSettingValue('enableSpellchecking', DEFAULT_APP_SETTINGS.enableSpellchecking), | ||
200 | nodeIntegration: true, | ||
201 | contextIsolation: false, | ||
202 | webviewTag: true, | ||
203 | preload: join(__dirname, 'sentry.js'), | ||
204 | enableRemoteModule: true, | ||
205 | }, | ||
206 | }); | ||
207 | |||
208 | app.on('web-contents-created', (e, contents) => { | ||
209 | if (contents.getType() === 'webview') { | ||
210 | contents.on('new-window', event => { | ||
211 | event.preventDefault(); | ||
212 | }); | ||
213 | } | ||
214 | }); | ||
215 | |||
216 | mainWindow.webContents.on('did-finish-load', () => { | ||
217 | const fns = onDidLoadFns; | ||
218 | onDidLoadFns = null; | ||
219 | |||
220 | if (!fns) return; | ||
221 | |||
222 | for (const fn of fns) { | ||
223 | fn(mainWindow); | ||
224 | } | ||
225 | }); | ||
226 | |||
227 | // Initialize System Tray | ||
228 | const trayIcon = new Tray(); | ||
229 | |||
230 | // Initialize DBus interface | ||
231 | const dbus = new DBus(trayIcon); | ||
232 | |||
233 | // Initialize ipcApi | ||
234 | ipcApi({ | ||
235 | mainWindow, | ||
236 | settings: { | ||
237 | app: settings, | ||
238 | proxy: proxySettings, | ||
239 | }, | ||
240 | trayIcon, | ||
241 | }); | ||
242 | |||
243 | // Connect to the DBus after ipcApi took care of the System Tray | ||
244 | dbus.start(); | ||
245 | |||
246 | // Manage Window State | ||
247 | mainWindowState.manage(mainWindow); | ||
248 | |||
249 | // and load the index.html of the app. | ||
250 | mainWindow.loadURL(`file://${__dirname}/index.html`); | ||
251 | |||
252 | // Open the DevTools. | ||
253 | if (isDevMode || process.argv.includes('--devtools')) { | ||
254 | mainWindow.webContents.openDevTools(); | ||
255 | } | ||
256 | |||
257 | // Windows deep linking handling on app launch | ||
258 | if (isWindows) { | ||
259 | onDidLoad(window => { | ||
260 | const url = process.argv.slice(1); | ||
261 | if (url) { | ||
262 | handleDeepLink(window, url.toString()); | ||
263 | } | ||
264 | }); | ||
265 | } | ||
266 | |||
267 | // Emitted when the window is closed. | ||
268 | mainWindow.on('close', e => { | ||
269 | debug('Window: close window'); | ||
270 | // Dereference the window object, usually you would store windows | ||
271 | // in an array if your app supports multi windows, this is the time | ||
272 | // when you should delete the corresponding element. | ||
273 | if (!willQuitApp && retrieveSettingValue('runInBackground', DEFAULT_APP_SETTINGS.runInBackground)) { | ||
274 | e.preventDefault(); | ||
275 | if (isWindows) { | ||
276 | debug('Window: minimize'); | ||
277 | mainWindow.minimize(); | ||
278 | |||
279 | if (retrieveSettingValue('closeToSystemTray', DEFAULT_APP_SETTINGS.closeToSystemTray)) { | ||
280 | debug('Skip taskbar: true'); | ||
281 | mainWindow.setSkipTaskbar(true); | ||
282 | } | ||
283 | } else if (isMac && mainWindow.isFullScreen()) { | ||
284 | debug('Window: leaveFullScreen and hide'); | ||
285 | mainWindow.once('show', () => mainWindow.setFullScreen(true)); | ||
286 | mainWindow.once('leave-full-screen', () => mainWindow.hide()); | ||
287 | mainWindow.setFullScreen(false); | ||
288 | } else { | ||
289 | debug('Window: hide'); | ||
290 | mainWindow.hide(); | ||
291 | } | ||
292 | } else { | ||
293 | dbus.stop(); | ||
294 | app.quit(); | ||
295 | } | ||
296 | }); | ||
297 | |||
298 | // For Windows we need to store a flag to properly restore the window | ||
299 | // if the window was maximized before minimizing it so system tray | ||
300 | mainWindow.on('minimize', () => { | ||
301 | app.wasMaximized = app.isMaximized; | ||
302 | |||
303 | if (retrieveSettingValue('minimizeToSystemTray', DEFAULT_APP_SETTINGS.minimizeToSystemTray)) { | ||
304 | debug('Skip taskbar: true'); | ||
305 | mainWindow.setSkipTaskbar(true); | ||
306 | trayIcon.show(); | ||
307 | } | ||
308 | }); | ||
309 | |||
310 | mainWindow.on('maximize', () => { | ||
311 | debug('Window: maximize'); | ||
312 | app.isMaximized = true; | ||
313 | }); | ||
314 | |||
315 | mainWindow.on('unmaximize', () => { | ||
316 | debug('Window: unmaximize'); | ||
317 | app.isMaximized = false; | ||
318 | }); | ||
319 | |||
320 | mainWindow.on('restore', () => { | ||
321 | debug('Window: restore'); | ||
322 | mainWindow.setSkipTaskbar(false); | ||
323 | |||
324 | if (app.wasMaximized) { | ||
325 | debug('Window: was maximized before, maximize window'); | ||
326 | mainWindow.maximize(); | ||
327 | } | ||
328 | |||
329 | if (!retrieveSettingValue('enableSystemTray', DEFAULT_APP_SETTINGS.enableSystemTray)) { | ||
330 | debug('Tray: hiding tray icon'); | ||
331 | trayIcon.hide(); | ||
332 | } | ||
333 | }); | ||
334 | |||
335 | if (isMac) { | ||
336 | // eslint-disable-next-line global-require | ||
337 | const { askFormacOSPermissions } = require('./electron/macOSPermissions'); | ||
338 | setTimeout(() => askFormacOSPermissions(mainWindow), ms('30s')); | ||
339 | } | ||
340 | |||
341 | mainWindow.on('show', () => { | ||
342 | debug('Skip taskbar: true'); | ||
343 | mainWindow.setSkipTaskbar(false); | ||
344 | }); | ||
345 | |||
346 | app.mainWindow = mainWindow; | ||
347 | app.isMaximized = mainWindow.isMaximized(); | ||
348 | |||
349 | mainWindow.webContents.on('new-window', (e, url) => { | ||
350 | e.preventDefault(); | ||
351 | openExternalUrl(url); | ||
352 | }); | ||
353 | |||
354 | if (retrieveSettingValue('startMinimized', DEFAULT_APP_SETTINGS.startMinimized)) { | ||
355 | mainWindow.hide(); | ||
356 | } else { | ||
357 | mainWindow.show(); | ||
358 | } | ||
359 | |||
360 | app.whenReady().then(() => { | ||
361 | if (retrieveSettingValue('enableGlobalHideShortcut', DEFAULT_APP_SETTINGS.enableGlobalHideShortcut)) { | ||
362 | // Toggle the window on 'Alt+X' | ||
363 | globalShortcut.register(`${altKey()}+X`, () => { | ||
364 | trayIcon.trayMenuTemplate[0].click(); | ||
365 | }); | ||
366 | } | ||
367 | }); | ||
368 | }; | ||
369 | |||
370 | // Allow passing command line parameters/switches to electron | ||
371 | // https://electronjs.org/docs/api/chrome-command-line-switches | ||
372 | // used for Kerberos support | ||
373 | // Usage e.g. MACOS | ||
374 | // $ Ferdi.app/Contents/MacOS/Ferdi --auth-server-whitelist *.mydomain.com --auth-negotiate-delegate-whitelist *.mydomain.com | ||
375 | const argv = require('minimist')(process.argv.slice(1)); | ||
376 | |||
377 | if (argv['auth-server-whitelist']) { | ||
378 | app.commandLine.appendSwitch( | ||
379 | 'auth-server-whitelist', | ||
380 | argv['auth-server-whitelist'], | ||
381 | ); | ||
382 | } | ||
383 | if (argv['auth-negotiate-delegate-whitelist']) { | ||
384 | app.commandLine.appendSwitch( | ||
385 | 'auth-negotiate-delegate-whitelist', | ||
386 | argv['auth-negotiate-delegate-whitelist'], | ||
387 | ); | ||
388 | } | ||
389 | |||
390 | // Disable Chromium's poor MPRIS implementation | ||
391 | // and apply workaround for https://github.com/electron/electron/pull/26432 | ||
392 | app.commandLine.appendSwitch( | ||
393 | 'disable-features', | ||
394 | 'HardwareMediaKeyHandling,MediaSessionService,CrossOriginOpenerPolicy', | ||
395 | ); | ||
396 | |||
397 | // This method will be called when Electron has finished | ||
398 | // initialization and is ready to create browser windows. | ||
399 | // Some APIs can only be used after this event occurs. | ||
400 | app.on('ready', () => { | ||
401 | // force app to live in /Applications | ||
402 | enforceMacOSAppLocation(); | ||
403 | |||
404 | // Register App URL | ||
405 | const protocolClient = isDevMode ? 'ferdi-dev' : 'ferdi'; | ||
406 | if (!app.isDefaultProtocolClient(protocolClient)) { | ||
407 | app.setAsDefaultProtocolClient(protocolClient); | ||
408 | } | ||
409 | |||
410 | if (isWindows) { | ||
411 | const extraArgs = isDevMode ? `${__dirname} ` : ''; | ||
412 | const iconPath = asarPath( | ||
413 | join( | ||
414 | isDevMode ? `${__dirname}../src/` : __dirname, | ||
415 | 'assets/images/taskbar/win32/display.ico', | ||
416 | ), | ||
417 | ); | ||
418 | app.setUserTasks([ | ||
419 | { | ||
420 | program: process.execPath, | ||
421 | arguments: `${extraArgs}--reset-window`, | ||
422 | iconPath, | ||
423 | iconIndex: 0, | ||
424 | title: 'Move Ferdi to Current Display', | ||
425 | description: 'Restore the position and size of Ferdi', | ||
426 | }, | ||
427 | { | ||
428 | program: process.execPath, | ||
429 | arguments: `${extraArgs}--quit`, | ||
430 | iconPath, | ||
431 | iconIndex: 0, | ||
432 | title: 'Quit Ferdi', | ||
433 | description: null, | ||
434 | }, | ||
435 | ]); | ||
436 | } | ||
437 | |||
438 | // eslint-disable-next-line global-require | ||
439 | require('electron-react-titlebar/main').initialize(); | ||
440 | |||
441 | createWindow(); | ||
442 | }); | ||
443 | |||
444 | // This is the worst possible implementation as the webview.webContents based callback doesn't work 🖕 | ||
445 | // TODO: rewrite to handle multiple login calls | ||
446 | const noop = () => null; | ||
447 | let authCallback = noop; | ||
448 | |||
449 | app.on('login', (event, webContents, request, authInfo, callback) => { | ||
450 | authCallback = callback; | ||
451 | debug('browser login event', authInfo); | ||
452 | event.preventDefault(); | ||
453 | |||
454 | if (!authInfo.isProxy && authInfo.scheme === 'basic') { | ||
455 | debug('basic auth handler', authInfo); | ||
456 | basicAuthHandler(mainWindow, authInfo); | ||
457 | } | ||
458 | }); | ||
459 | |||
460 | // TODO: evaluate if we need to store the authCallback for every service | ||
461 | ipcMain.on('feature-basic-auth-credentials', (e, { user, password }) => { | ||
462 | debug('Received basic auth credentials', user, '********'); | ||
463 | |||
464 | authCallback(user, password); | ||
465 | authCallback = noop; | ||
466 | }); | ||
467 | |||
468 | ipcMain.on('open-browser-window', (e, { url, serviceId }) => { | ||
469 | const serviceSession = session.fromPartition(`persist:service-${serviceId}`); | ||
470 | const child = new BrowserWindow({ | ||
471 | parent: mainWindow, | ||
472 | webPreferences: { | ||
473 | session: serviceSession, | ||
474 | // TODO: Aren't these needed here? | ||
475 | // contextIsolation: false, | ||
476 | // enableRemoteModule: true, | ||
477 | }, | ||
478 | }); | ||
479 | child.show(); | ||
480 | child.loadURL(url); | ||
481 | debug('Received open-browser-window', url); | ||
482 | }); | ||
483 | |||
484 | ipcMain.on( | ||
485 | 'modifyRequestHeaders', | ||
486 | (e, { modifiedRequestHeaders, serviceId }) => { | ||
487 | debug( | ||
488 | `Received modifyRequestHeaders ${modifiedRequestHeaders} for serviceId ${serviceId}`, | ||
489 | ); | ||
490 | for (const headerFilterSet of modifiedRequestHeaders) { | ||
491 | const { headers, requestFilters } = headerFilterSet; | ||
492 | session | ||
493 | .fromPartition(`persist:service-${serviceId}`) | ||
494 | .webRequest.onBeforeSendHeaders(requestFilters, (details, callback) => { | ||
495 | for (const key in headers) { | ||
496 | if (Object.prototype.hasOwnProperty.call(headers, key)) { | ||
497 | const value = headers[key]; | ||
498 | details.requestHeaders[key] = value; | ||
499 | } | ||
500 | } | ||
501 | callback({ requestHeaders: details.requestHeaders }); | ||
502 | }); | ||
503 | } | ||
504 | }, | ||
505 | ); | ||
506 | |||
507 | ipcMain.on('knownCertificateHosts', (e, { knownHosts, serviceId }) => { | ||
508 | debug( | ||
509 | `Received knownCertificateHosts ${knownHosts} for serviceId ${serviceId}`, | ||
510 | ); | ||
511 | session | ||
512 | .fromPartition(`persist:service-${serviceId}`) | ||
513 | .setCertificateVerifyProc((request, callback) => { | ||
514 | // To know more about these callbacks: https://www.electronjs.org/docs/api/session#sessetcertificateverifyprocproc | ||
515 | const { hostname } = request; | ||
516 | if (knownHosts.find(item => item.includes(hostname)).length > 0) { | ||
517 | callback(0); | ||
518 | } else { | ||
519 | callback(-2); | ||
520 | } | ||
521 | }); | ||
522 | }); | ||
523 | |||
524 | ipcMain.on('feature-basic-auth-cancel', () => { | ||
525 | debug('Cancel basic auth'); | ||
526 | |||
527 | authCallback(null); | ||
528 | authCallback = noop; | ||
529 | }); | ||
530 | |||
531 | // Handle synchronous messages from service webviews. | ||
532 | |||
533 | ipcMain.on('find-in-page', (e, text, options) => { | ||
534 | const { sender: webContents } = e; | ||
535 | if (webContents !== mainWindow.webContents && typeof text === 'string') { | ||
536 | const sanitizedOptions = {}; | ||
537 | for (const option of ['forward', 'findNext', 'matchCase']) { | ||
538 | if (option in options) { | ||
539 | sanitizedOptions[option] = !!options[option]; | ||
540 | } | ||
541 | } | ||
542 | const requestId = webContents.findInPage(text, sanitizedOptions); | ||
543 | debug('Find in page', text, options, requestId); | ||
544 | e.returnValue = requestId; | ||
545 | } else { | ||
546 | e.returnValue = null; | ||
547 | } | ||
548 | }); | ||
549 | |||
550 | ipcMain.on('stop-find-in-page', (e, action) => { | ||
551 | const { sender: webContents } = e; | ||
552 | if (webContents !== mainWindow.webContents) { | ||
553 | const validActions = [ | ||
554 | 'clearSelection', | ||
555 | 'keepSelection', | ||
556 | 'activateSelection', | ||
557 | ]; | ||
558 | if (validActions.includes(action)) { | ||
559 | webContents.stopFindInPage(action); | ||
560 | } | ||
561 | } | ||
562 | e.returnValue = null; | ||
563 | }); | ||
564 | |||
565 | ipcMain.on('set-spellchecker-locales', (e, { locale, serviceId }) => { | ||
566 | if (serviceId === undefined) { | ||
567 | return; | ||
568 | } | ||
569 | |||
570 | const serviceSession = session.fromPartition(`persist:service-${serviceId}`); | ||
571 | const [defaultLocale] = serviceSession.getSpellCheckerLanguages(); | ||
572 | debug(`Spellchecker default locale is: ${defaultLocale}`); | ||
573 | |||
574 | const locales = [locale, defaultLocale, DEFAULT_APP_SETTINGS.fallbackLocale]; | ||
575 | debug(`Setting spellchecker locales to: ${locales}`); | ||
576 | serviceSession.setSpellCheckerLanguages(locales); | ||
577 | }); | ||
578 | |||
579 | // Quit when all windows are closed. | ||
580 | app.on('window-all-closed', () => { | ||
581 | // On OS X it is common for applications and their menu bar | ||
582 | // to stay active until the user quits explicitly with Cmd + Q | ||
583 | if (retrieveSettingValue('runInBackground', DEFAULT_APP_SETTINGS.runInBackground)) { | ||
584 | debug('Window: all windows closed, quit app'); | ||
585 | app.quit(); | ||
586 | } else { | ||
587 | debug("Window: don't quit app"); | ||
588 | } | ||
589 | }); | ||
590 | |||
591 | app.on('before-quit', event => { | ||
592 | const yesButtonIndex = 0; | ||
593 | let selection = yesButtonIndex; | ||
594 | if (retrieveSettingValue('confirmOnQuit', DEFAULT_APP_SETTINGS.confirmOnQuit)) { | ||
595 | selection = dialog.showMessageBoxSync(app.mainWindow, { | ||
596 | type: 'question', | ||
597 | message: 'Quit', | ||
598 | detail: 'Do you really want to quit Ferdi?', | ||
599 | buttons: ['Yes', 'No'], | ||
600 | }); | ||
601 | } | ||
602 | if (selection === yesButtonIndex) { | ||
603 | willQuitApp = true; | ||
604 | } else { | ||
605 | event.preventDefault(); | ||
606 | } | ||
607 | }); | ||
608 | |||
609 | app.on('activate', () => { | ||
610 | // On OS X it's common to re-create a window in the app when the | ||
611 | // dock icon is clicked and there are no other windows open. | ||
612 | if (mainWindow === null) { | ||
613 | createWindow(); | ||
614 | } else { | ||
615 | mainWindow.show(); | ||
616 | } | ||
617 | }); | ||
618 | |||
619 | app.on('web-contents-created', (createdEvent, contents) => { | ||
620 | contents.on('new-window', (event, url, frameNme, disposition) => { | ||
621 | if (disposition === 'foreground-tab') event.preventDefault(); | ||
622 | }); | ||
623 | }); | ||
624 | |||
625 | app.on('will-finish-launching', () => { | ||
626 | // Protocol handler for macOS | ||
627 | app.on('open-url', (event, url) => { | ||
628 | event.preventDefault(); | ||
629 | |||
630 | onDidLoad(window => { | ||
631 | debug('open-url event', url); | ||
632 | handleDeepLink(window, url); | ||
633 | }); | ||
634 | }); | ||
635 | }); | ||