mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
Typescript - handle multiple/single instance of the client and add safety checks
This commit is contained in:
parent
767ef15f9f
commit
3177e54b6e
@ -1,6 +1,6 @@
|
||||
import { app } from 'electron';
|
||||
|
||||
import { isDevEnv } from '../common/env';
|
||||
import { isDevEnv, isMac } from '../common/env';
|
||||
import { logger } from '../common/logger';
|
||||
import { getCommandLineArgs } from '../common/utils';
|
||||
import { cleanUpAppCache, createAppCacheFile } from './app-cache-handler';
|
||||
@ -10,15 +10,14 @@ import { config } from './config-handler';
|
||||
import './dialog-handler';
|
||||
import './main-api-handler';
|
||||
import { SpellChecker } from './spell-checker-handler';
|
||||
import { windowHandler } from './window-handler';
|
||||
import { ICustomBrowserWindow, windowHandler } from './window-handler';
|
||||
|
||||
const allowMultiInstance: string | boolean = getCommandLineArgs(process.argv, '--multiInstance', true) || isDevEnv;
|
||||
const singleInstanceLock: boolean = allowMultiInstance ? true : app.requestSingleInstanceLock();
|
||||
|
||||
/**
|
||||
* Main function that init the application
|
||||
*/
|
||||
const main = async () => {
|
||||
const startApplication = async () => {
|
||||
await app.whenReady();
|
||||
const spellchecker = new SpellChecker();
|
||||
logger.info(`initialized spellchecker module with locale ${spellchecker.locale}`);
|
||||
@ -42,10 +41,34 @@ const main = async () => {
|
||||
setChromeFlags();
|
||||
};
|
||||
|
||||
if (!singleInstanceLock) {
|
||||
app.quit();
|
||||
// Handle multiple/single instances
|
||||
if (!allowMultiInstance) {
|
||||
logger.info('Multiple instance not allowed requesting lock', { allowMultiInstance });
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
|
||||
// quit if another instance is already running, ignore for dev env or if app was started with multiInstance flag
|
||||
if (!gotTheLock) {
|
||||
logger.info('Got the lock hence closing the instance', { gotTheLock });
|
||||
app.quit();
|
||||
} else {
|
||||
logger.info('Creating the first instance of the application');
|
||||
app.on('second-instance', (_event) => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
const mainWindow = windowHandler.getMainWindow();
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
if (isMac) return mainWindow.show();
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore();
|
||||
}
|
||||
mainWindow.focus();
|
||||
// TODO: Handle protocol action
|
||||
}
|
||||
});
|
||||
startApplication();
|
||||
}
|
||||
} else {
|
||||
main();
|
||||
logger.info('Multiple instance allowed hence create application', { allowMultiInstance });
|
||||
startApplication();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,4 +80,18 @@ app.on('window-all-closed', () => app.quit());
|
||||
/**
|
||||
* Creates a new empty cache file when the app is quit
|
||||
*/
|
||||
app.on('quit', () => cleanUpAppCache());
|
||||
app.on('quit', () => cleanUpAppCache());
|
||||
|
||||
/**
|
||||
* Cleans up reference before quiting
|
||||
*/
|
||||
app.on('before-quit', () => windowHandler.willQuitApp = true);
|
||||
|
||||
app.on('activate', () => {
|
||||
const mainWindow: ICustomBrowserWindow | null = windowHandler.getMainWindow();
|
||||
if (!mainWindow || mainWindow.isDestroyed()) {
|
||||
startApplication();
|
||||
} else {
|
||||
mainWindow.show();
|
||||
}
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
import * as electron from 'electron';
|
||||
import { BrowserWindow, crashReporter, ipcMain } from 'electron';
|
||||
import { app, BrowserWindow, crashReporter, ipcMain } from 'electron';
|
||||
import * as path from 'path';
|
||||
import { format, parse } from 'url';
|
||||
|
||||
@ -139,6 +139,7 @@ export class WindowHandler {
|
||||
public isAutoReload: boolean;
|
||||
public isOnline: boolean;
|
||||
public url: string | undefined;
|
||||
public willQuitApp: boolean = false;
|
||||
|
||||
private readonly windowOpts: ICustomBrowserWindowConstructorOpts;
|
||||
private readonly globalConfig: IConfig;
|
||||
@ -157,7 +158,7 @@ export class WindowHandler {
|
||||
|
||||
constructor(opts?: Electron.BrowserViewConstructorOptions) {
|
||||
// Settings
|
||||
this.config = config.getConfigFields([ 'isCustomTitleBar', 'mainWinPos' ]);
|
||||
this.config = config.getConfigFields([ 'isCustomTitleBar', 'mainWinPos', 'minimizeOnClose' ]);
|
||||
this.globalConfig = config.getGlobalConfigFields([ 'url', 'crashReporter' ]);
|
||||
|
||||
this.windows = {};
|
||||
@ -236,6 +237,20 @@ export class WindowHandler {
|
||||
this.mainWindow.show();
|
||||
});
|
||||
|
||||
// Handle main window close
|
||||
this.mainWindow.on('close', (event) => {
|
||||
if (!this.mainWindow || this.mainWindow.isDestroyed()) return;
|
||||
|
||||
if (this.willQuitApp) return this.destroyAllWindow();
|
||||
|
||||
if (this.config.minimizeOnClose) {
|
||||
event.preventDefault();
|
||||
isMac ? this.mainWindow.hide() : this.mainWindow.minimize();
|
||||
} else {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
// Start monitoring window actions
|
||||
monitorWindowActions(this.mainWindow);
|
||||
|
||||
@ -307,9 +322,8 @@ export class WindowHandler {
|
||||
public showLoadingScreen(): void {
|
||||
this.loadingWindow = createComponentWindow('loading-screen', WindowHandler.getLoadingWindowOpts());
|
||||
this.loadingWindow.webContents.once('did-finish-load', () => {
|
||||
if (this.loadingWindow) {
|
||||
this.loadingWindow.webContents.send('data');
|
||||
}
|
||||
if (!this.loadingWindow || this.loadingWindow.isDestroyed()) return;
|
||||
this.loadingWindow.webContents.send('data');
|
||||
});
|
||||
|
||||
this.loadingWindow.once('closed', () => this.loadingWindow = null);
|
||||
@ -321,9 +335,8 @@ export class WindowHandler {
|
||||
public createAboutAppWindow(): void {
|
||||
this.aboutAppWindow = createComponentWindow('about-app');
|
||||
this.aboutAppWindow.webContents.once('did-finish-load', () => {
|
||||
if (this.aboutAppWindow) {
|
||||
this.aboutAppWindow.webContents.send('about-app-data', { buildNumber, clientVersion, version });
|
||||
}
|
||||
if (!this.aboutAppWindow || this.aboutAppWindow.isDestroyed()) return;
|
||||
this.aboutAppWindow.webContents.send('about-app-data', { buildNumber, clientVersion, version });
|
||||
});
|
||||
}
|
||||
|
||||
@ -333,9 +346,8 @@ export class WindowHandler {
|
||||
public createMoreInfoWindow(): void {
|
||||
this.moreInfoWindow = createComponentWindow('more-info');
|
||||
this.moreInfoWindow.webContents.once('did-finish-load', () => {
|
||||
if (this.aboutAppWindow) {
|
||||
this.aboutAppWindow.webContents.send('more-info-data');
|
||||
}
|
||||
if (!this.moreInfoWindow || this.moreInfoWindow.isDestroyed()) return;
|
||||
this.moreInfoWindow.webContents.send('more-info-data');
|
||||
});
|
||||
}
|
||||
|
||||
@ -350,18 +362,17 @@ export class WindowHandler {
|
||||
const opts = WindowHandler.getScreenPickerWindowOpts();
|
||||
this.screenPickerWindow = createComponentWindow('screen-picker', opts);
|
||||
this.screenPickerWindow.webContents.once('did-finish-load', () => {
|
||||
if (this.screenPickerWindow) {
|
||||
this.screenPickerWindow.webContents.send('screen-picker-data', { sources, id });
|
||||
this.addWindow(opts.winKey, this.screenPickerWindow);
|
||||
this.screenPickerWindow.once('closed', () => {
|
||||
this.removeWindow(opts.winKey);
|
||||
this.screenPickerWindow = null;
|
||||
});
|
||||
if (!this.screenPickerWindow || this.screenPickerWindow.isDestroyed()) return;
|
||||
this.screenPickerWindow.webContents.send('screen-picker-data', { sources, id });
|
||||
this.addWindow(opts.winKey, this.screenPickerWindow);
|
||||
this.screenPickerWindow.once('closed', () => {
|
||||
this.removeWindow(opts.winKey);
|
||||
this.screenPickerWindow = null;
|
||||
});
|
||||
|
||||
ipcMain.once('screen-source-selected', (_event, source) => {
|
||||
win.send('start-share' + id, source);
|
||||
});
|
||||
}
|
||||
ipcMain.once('screen-source-selected', (_event, source) => {
|
||||
win.send('start-share' + id, source);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -392,29 +403,28 @@ export class WindowHandler {
|
||||
this.screenSharingIndicatorWindow = createComponentWindow('screen-sharing-indicator', opts);
|
||||
this.screenSharingIndicatorWindow.setVisibleOnAllWorkspaces(true);
|
||||
this.screenSharingIndicatorWindow.webContents.once('did-finish-load', () => {
|
||||
if (this.screenSharingIndicatorWindow) {
|
||||
this.screenSharingIndicatorWindow.webContents.send('screen-sharing-indicator-data', { id });
|
||||
if (!this.screenSharingIndicatorWindow || this.screenSharingIndicatorWindow.isDestroyed()) return;
|
||||
this.screenSharingIndicatorWindow.webContents.send('screen-sharing-indicator-data', { id });
|
||||
|
||||
const stopScreenSharing = (_event, indicatorId) => {
|
||||
if (id === indicatorId) {
|
||||
screenSharingWebContents.send('screen-sharing-stopped', id);
|
||||
}
|
||||
};
|
||||
const stopScreenSharing = (_event, indicatorId) => {
|
||||
if (id === indicatorId) {
|
||||
screenSharingWebContents.send('screen-sharing-stopped', id);
|
||||
}
|
||||
};
|
||||
|
||||
const destroyScreenSharingIndicator = (_event, indicatorId) => {
|
||||
if (id === indicatorId && this.screenSharingIndicatorWindow && !this.screenSharingIndicatorWindow.isDestroyed()) {
|
||||
this.screenSharingIndicatorWindow.close();
|
||||
}
|
||||
};
|
||||
const destroyScreenSharingIndicator = (_event, indicatorId) => {
|
||||
if (id === indicatorId && this.screenSharingIndicatorWindow && !this.screenSharingIndicatorWindow.isDestroyed()) {
|
||||
this.screenSharingIndicatorWindow.close();
|
||||
}
|
||||
};
|
||||
|
||||
this.screenSharingIndicatorWindow.once('close', () => {
|
||||
ipcMain.removeListener('stop-screen-sharing', stopScreenSharing);
|
||||
ipcMain.removeListener('destroy-screen-sharing-indicator', destroyScreenSharingIndicator);
|
||||
});
|
||||
this.screenSharingIndicatorWindow.once('close', () => {
|
||||
ipcMain.removeListener('stop-screen-sharing', stopScreenSharing);
|
||||
ipcMain.removeListener('destroy-screen-sharing-indicator', destroyScreenSharingIndicator);
|
||||
});
|
||||
|
||||
ipcMain.once('stop-screen-sharing', stopScreenSharing);
|
||||
ipcMain.once('destroy-screen-sharing-indicator', destroyScreenSharingIndicator);
|
||||
}
|
||||
ipcMain.once('stop-screen-sharing', stopScreenSharing);
|
||||
ipcMain.once('destroy-screen-sharing-indicator', destroyScreenSharingIndicator);
|
||||
});
|
||||
}
|
||||
|
||||
@ -436,7 +446,7 @@ export class WindowHandler {
|
||||
this.basicAuthWindow = createComponentWindow('basic-auth', opts);
|
||||
this.basicAuthWindow.setVisibleOnAllWorkspaces(true);
|
||||
this.basicAuthWindow.webContents.once('did-finish-load', () => {
|
||||
if (!this.basicAuthWindow) return;
|
||||
if (!this.basicAuthWindow || this.basicAuthWindow.isDestroyed()) return;
|
||||
this.basicAuthWindow.webContents.send('basic-auth-data', { hostname, isValidCredentials: isMultipleTries });
|
||||
|
||||
const closeBasicAuth = (shouldClearSettings = true) => {
|
||||
@ -489,10 +499,23 @@ export class WindowHandler {
|
||||
*
|
||||
* @param key {string}
|
||||
*/
|
||||
public removeWindow(key): void {
|
||||
public removeWindow(key: string): void {
|
||||
delete this.windows[ key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up reference
|
||||
*/
|
||||
private destroyAllWindow(): void {
|
||||
for (const key in this.windows) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.windows, key)) {
|
||||
const winKey = this.windows[key];
|
||||
this.removeWindow(winKey);
|
||||
}
|
||||
}
|
||||
this.mainWindow = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main window opts
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user