import { app, BrowserWindow } from "electron"; import path from "node:path"; import { getRuntimeConfig } from "../config/runtime-config"; import { showFatalError, log } from "../errors/error-presenter"; import { createNodecgProcessManager } from "../nodecg/process-manager"; import { prepareUserNodecgRuntime } from "../nodecg/runtime-provisioner"; import { scheduleUpdateCheck } from "../updates/update-service"; import { createLoadingWindow, createMainWindow } from "../windows/window-service"; import { createApplicationController } from "./application-controller"; import { getApplicationPaths } from "./paths"; export function bootstrap(): void { const appConfig = getRuntimeConfig(); const isDev = !app.isPackaged; const paths = getApplicationPaths({ appConfig, appDataPath: app.getPath("appData"), compiledMainDir: path.resolve(__dirname, ".."), isDev, resourcesPath: process.resourcesPath, }); app.setName(appConfig.title); app.setPath("userData", paths.userDataPath); const hasSingleInstanceLock = app.requestSingleInstanceLock(); if (!hasSingleInstanceLock) { app.quit(); } const controller = createApplicationController({ appConfig, appVersion: app.getVersion(), isPackaged: app.isPackaged, isWindows: process.platform === "win32", paths, deps: { createLoadingWindow: () => createLoadingWindow({ allowDevTools: isDev, appConfig, rootPath: paths.rootPath, }), createMainWindow: () => createMainWindow({ allowDevTools: isDev, appConfig, rootPath: paths.rootPath, mainDashboardUrl: paths.mainDashboardUrl, }), createNodecgProcessManager: (runtimePath) => createNodecgProcessManager({ isDev, nodecgRootPath: runtimePath, nodecgBaseUrl: paths.nodecgBaseUrl, appConfig, log, }), getAllWindows: () => BrowserWindow.getAllWindows(), log, prepareRuntime: prepareUserNodecgRuntime, relaunch: () => app.relaunch(), scheduleUpdateCheck: ({ getParentWindow, beforeInstall }) => { scheduleUpdateCheck({ appConfig, rootPath: paths.rootPath, getParentWindow: () => getParentWindow() as BrowserWindow | null, beforeInstall, log, }); }, setAppUserModelId: (userModelId) => app.setAppUserModelId(userModelId), exit: (code) => app.exit(code), }, }); app.on("ready", () => { if (!hasSingleInstanceLock) { return; } controller.launch().catch((error: unknown) => { showFatalError("No se pudo iniciar Scoreko.", error); app.exit(1); }); }); app.on("second-instance", () => { controller.focusExistingWindow(); }); app.on("activate", () => { controller.activate().catch((error: unknown) => { showFatalError("No se pudo reactivar Scoreko.", error); }); }); app.on("window-all-closed", () => { if (process.platform !== "darwin") { app.quit(); } }); app.on("before-quit", (event) => { if (controller.getState() === "stopping" || controller.getState() === "stopped") { return; } event.preventDefault(); controller.stopNodecgGracefully().finally(() => { app.quit(); }); }); app.on("will-quit", () => { if (controller.getState() !== "stopping" && controller.getState() !== "stopped") { void controller.stopNodecgGracefully(); } }); process.on("exit", () => { if (controller.getState() !== "stopping" && controller.getState() !== "stopped") { void controller.stopNodecgGracefully(); } }); process.on("uncaughtException", (error) => { showFatalError("Unexpected error in Electron main process.", error); }); process.on("unhandledRejection", (reason) => { showFatalError("Unhandled promise in Electron main process.", reason); }); }