diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2022-08-24 11:07:00 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2022-08-26 13:39:26 +0200 |
| commit | 100fc2d1fd26e01a8fe79f6cf696891891fe5c4d (patch) | |
| tree | e3b7ab3065de80603b244402c2afe6e3b7ac1da3 | |
| parent | 1499e2ab919b853e714cb18a323b77db640e0a51 (diff) | |
| download | mullvadvpn-100fc2d1fd26e01a8fe79f6cf696891891fe5c4d.tar.xz mullvadvpn-100fc2d1fd26e01a8fe79f6cf696891891fe5c4d.zip | |
Simplify app quit procedure
| -rw-r--r-- | gui/src/main/index.ts | 73 | ||||
| -rw-r--r-- | gui/src/main/user-interface.ts | 31 | ||||
| -rw-r--r-- | gui/src/shared/logging.ts | 8 |
3 files changed, 49 insertions, 63 deletions
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts index cba52d61e1..ef6fdc894b 100644 --- a/gui/src/main/index.ts +++ b/gui/src/main/index.ts @@ -59,12 +59,6 @@ enum CommandLineOptions { disableResetNavigation = '--disable-reset-navigation', // development only } -export enum AppQuitStage { - unready, - initiated, - ready, -} - const ALLOWED_PERMISSIONS = ['clipboard-sanitized-write']; const SANDBOX_DISABLED = app.commandLine.hasSwitch('no-sandbox'); @@ -94,7 +88,7 @@ class ApplicationMain private reconnectBackoff = new ReconnectionBackoff(); private beforeFirstDaemonConnection = true; private isPerformingPostUpgrade = false; - private quitStage = AppQuitStage.unready; + private quitInitiated = false; private tunnelStateExpectation?: Expectation; @@ -171,9 +165,16 @@ class ApplicationMain app.on('activate', this.onActivate); app.on('ready', this.onReady); - app.on('window-all-closed', () => app.quit()); + app.on('before-quit', this.onBeforeQuit); - app.on('quit', this.onQuit); + app.on('will-quit', () => { + log.info('will-quit received'); + this.onQuit(); + }); + app.on('quit', () => { + log.info('quit received'); + this.onQuit(); + }); } public async performPostUpgradeCheck(): Promise<void> { @@ -293,45 +294,30 @@ class ApplicationMain // This is a last try to disconnect and quit gracefully if the app quits without having received // the before-quit event. private onQuit = () => { - if (this.quitStage !== AppQuitStage.ready) { + if (!this.quitInitiated) { this.prepareToQuit(); } }; private onBeforeQuit = (event: Electron.Event) => { - switch (this.quitStage) { - case AppQuitStage.unready: - // postpone the app shutdown - event.preventDefault(); - - this.quitStage = AppQuitStage.initiated; - - log.info('Quit initiated'); - - this.prepareToQuit(); - - // terminate the app - this.quitStage = AppQuitStage.ready; - app.quit(); - break; - - case AppQuitStage.initiated: - // prevent immediate exit, the app will quit after running the shutdown routine - event.preventDefault(); - return; - - case AppQuitStage.ready: - // let the app quit freely at this point - break; + log.info('before-quit received'); + if (this.quitInitiated) { + event.preventDefault(); + } else { + this.prepareToQuit(); } }; private prepareToQuit() { + this.quitInitiated = true; + log.info('Quit initiated'); + + this.userInterface?.dispose(); + // Unsubscribe the event handler try { if (this.daemonEventListener) { this.daemonRpc.unsubscribeDaemonEventListener(this.daemonEventListener); - log.info('Unsubscribed from the daemon events'); } } catch (e) { @@ -339,29 +325,21 @@ class ApplicationMain log.error(`Failed to unsubscribe from daemon events: ${error.message}`); } - // The window is not closable on macOS to be able to hide the titlebar and workaround - // a shadow bug rendered above the invisible title bar. This also prevents the window from - // closing normally, even programmatically. Therefore re-enable the close button just before - // quitting the app. - // Github issue: https://github.com/electron/electron/issues/15008 - if (process.platform === 'darwin') { - this.userInterface?.setWindowClosable(true); - } - if (this.daemonRpc.isConnected) { this.daemonRpc.disconnect(); } for (const logger of [log, this.rendererLog]) { try { - logger?.dispose(); + logger?.disposeDisposableOutputs(); } catch (e) { const error = e as Error; - console.error('Failed to dispose logger:', error); + log.error('Failed to dispose logger:', error); } } - this.userInterface?.dispose(); + log.info('Disposable logging outputs disposed'); + log.info('Quit preperations finished'); } private detectLocale(): string { @@ -947,7 +925,6 @@ class ApplicationMain public resetTunnelStateAnnouncements = () => this.notificationController.resetTunnelStateAnnouncements(); public isUnpinnedWindow = () => this.settings.gui.unpinnedWindow; - public getAppQuitStage = () => this.quitStage; public updateAccountData = () => this.account.updateAccountData(); public getAccountData = () => this.account.accountData; diff --git a/gui/src/main/user-interface.ts b/gui/src/main/user-interface.ts index b7a05c7d23..712d7e42b8 100644 --- a/gui/src/main/user-interface.ts +++ b/gui/src/main/user-interface.ts @@ -9,7 +9,6 @@ import { messages, relayLocations } from '../shared/gettext'; import log from '../shared/logging'; import { Scheduler } from '../shared/scheduler'; import { DaemonRpc } from './daemon-rpc'; -import { AppQuitStage } from './index'; import { changeIpcWebContents, IpcMainEventChannel } from './ipc-event-channel'; import { isMacOs11OrNewer } from './platform-version'; import TrayIconController, { TrayIconType } from './tray-icon-controller'; @@ -24,7 +23,6 @@ export interface UserInterfaceDelegate { disconnectTunnel(): void; isUnpinnedWindow(): boolean; isLoggedIn(): boolean; - getAppQuitStage(): AppQuitStage; getAccountData(): IAccountData | undefined; getTunnelState(): TunnelState; } @@ -158,18 +156,27 @@ export default class UserInterface implements WindowControllerDelegate { this.trayIconController?.setUseMonochromaticIcon(value); public setWindowIcon = (icon: string) => this.windowController.window?.setIcon(icon); - public setWindowClosable = (value: boolean) => { - if (this.windowController.window) { - this.windowController.window.closable = value; - } - }; - public updateTrayIcon(tunnelState: TunnelState, blockWhenDisconnected: boolean) { const type = this.trayIconType(tunnelState, blockWhenDisconnected); this.trayIconController?.animateToIcon(type); } - public dispose = () => this.trayIconController?.dispose(); + public dispose = () => { + this.tray.removeAllListeners(); + this.windowController.window?.removeAllListeners(); + + // The window is not closable on macOS to be able to hide the titlebar and workaround + // a shadow bug rendered above the invisible title bar. This also prevents the window from + // closing normally, even programmatically. Therefore re-enable the close button just before + // quitting the app. + // Github issue: https://github.com/electron/electron/issues/15008 + if (process.platform === 'darwin' && this.windowController.window) { + this.windowController.window.closable = true; + } + + this.windowController.close(); + this.trayIconController?.dispose(); + }; private createTray(): Tray { const tray = new Tray(nativeImage.createEmpty()); @@ -472,10 +479,8 @@ export default class UserInterface implements WindowControllerDelegate { } this.windowController.window?.on('close', (closeEvent: Event) => { - if (this.delegate.getAppQuitStage() !== AppQuitStage.ready) { - closeEvent.preventDefault(); - this.windowController.hide(); - } + closeEvent.preventDefault(); + this.windowController.hide(); }); } diff --git a/gui/src/shared/logging.ts b/gui/src/shared/logging.ts index 77d0168701..c2537e00d4 100644 --- a/gui/src/shared/logging.ts +++ b/gui/src/shared/logging.ts @@ -25,8 +25,12 @@ export class Logger { public verbose = (...data: unknown[]) => this.log(LogLevel.verbose, ...data); public debug = (...data: unknown[]) => this.log(LogLevel.debug, ...data); - public dispose() { - this.outputs.forEach((output) => output.dispose?.()); + public disposeDisposableOutputs() { + // Keep the outputs that aren't disposable to continue to forward log messages to them. + this.outputs = this.outputs.filter((output) => { + output.dispose?.(); + return output.dispose === undefined; + }); } private getDateString(): string { |
