public/electron.ts (129 lines of code) (raw):
/***********************************************************
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License
**********************************************************/
import { app, Menu, BrowserWindow, dialog, ipcMain } from 'electron';
import * as windowState from 'electron-window-state';
import * as path from 'path';
import { generateMenu } from './factories/menuFactory';
import { PLATFORMS, MESSAGE_CHANNELS } from './constants';
import { onSettingsHighContrast } from './handlers/settingsHandler';
import { formatError } from './utils/errorHelper';
import { AuthProvider } from './utils/authProvider';
import '../dist/server/serverElectron';
class Main {
private static application: Electron.App;
private static mainWindow: BrowserWindow;
private static readonly target = path.join(__dirname, '/../dist/index.html');
private static authProvider: AuthProvider;
public static start() {
Main.application = app;
Main.application.on('window-all-closed', Main.onAllWindowClosed);
Main.application.on('ready', Main.onReady);
Main.application.on('activate', Main.onActivate);
Main.application.on('second-instance', Main.onSecondInstance);
}
private static setMessageHandlers(): void {
Main.registerHandler(MESSAGE_CHANNELS.SETTING_HIGH_CONTRAST, onSettingsHighContrast);
Main.registerHandler(MESSAGE_CHANNELS.AUTHENTICATION_LOGIN, Main.onLogin);
Main.registerHandler(MESSAGE_CHANNELS.AUTHENTICATION_LOGOUT, Main.onLogout);
Main.registerHandler(MESSAGE_CHANNELS.AUTHENTICATION_GET_PROFILE_TOKEN, Main.onGetProfileToken);
}
private static async loadTarget(redirect?: string): Promise<void> {
Main.mainWindow.loadFile(Main.target, { query: {redirect: redirect || ''} });
}
private static async onLogin(): Promise<void> {
await Main.authProvider.login(Main.mainWindow)
await Main.loadTarget();
}
private static async onLogout(): Promise<void> {
await Main.authProvider.logout();
await Main.loadTarget();
}
private static async onGetProfileToken(): Promise<string> {
const token = await Main.authProvider.getProfileTokenIfPresent();
return token;
}
private static setApplicationLock(): void {
const lock = Main.application.requestSingleInstanceLock();
if (!lock) {
app.quit();
}
}
private static setErrorBoundary(): void {
process.on('uncaughtException', async (error: Error) =>
dialog.showMessageBox({
message: error?.message || 'Something went wrong. Please try restarting the app.',
title: 'Error in controller process',
type: 'error',
})
);
}
private static onSecondInstance(): void {
if (Main.mainWindow) {
if (Main.mainWindow.isMinimized()) {
Main.mainWindow.restore();
}
Main.mainWindow.focus();
}
}
private static onReady(): void {
Main.createMainWindow();
Main.createMenu();
}
private static onActivate(): void {
if (!Main.mainWindow) {
Main.createMainWindow();
}
}
private static onWindowClosed(): void {
Main.mainWindow = null;
}
private static onAllWindowClosed(): void {
if (process.platform !== PLATFORMS.MAC) {
Main.application.quit();
}
}
private static createMenu(): void {
const menu = generateMenu({ platform: process.platform });
Menu.setApplicationMenu(menu);
}
private static createMainWindow(): void {
const mainWindowState = windowState({
defaultHeight: 1200,
defaultWidth: 900
});
Main.mainWindow = new BrowserWindow({
height: mainWindowState.height,
width: mainWindowState.width,
webPreferences: { // tslint:disable-line:object-literal-sort-keys
contextIsolation: true, // protect against prototype pollution
nodeIntegration: false,
preload: __dirname + '/contextBridge.js' // use a preload script
},
});
mainWindowState.manage(Main.mainWindow);
Main.mainWindow.loadFile(Main.target);
try {
const customPort = parseInt(process.env.AZURE_IOT_EXPLORER_PORT); // tslint:disable-line:radix
if (customPort && !isNaN(customPort)) {
Main.mainWindow.webContents.executeJavaScript(`localStorage.setItem("CUSTOM_CONTROLLER_PORT", ${customPort});`);
} else {
Main.mainWindow.webContents.executeJavaScript(`localStorage.removeItem("CUSTOM_CONTROLLER_PORT");`);
}
} catch {
// nothing
}
Main.mainWindow.on('closed', Main.onWindowClosed);
Main.setErrorBoundary();
Main.setApplicationLock();
Main.authProvider = new AuthProvider();
Main.setMessageHandlers();
}
// tslint:disable-next-line: no-any
private static registerHandler(channel: string, handler: (...args: any[]) => any) {
ipcMain.handle(channel, async (...args) => {
try {
return { result: await Promise.resolve(handler(...args)) };
} catch (e) {
const error = formatError(e);
return { error };
}
});
}
}
Main.start();