const path = require("path"); const { app, BrowserWindow, Tray, Menu, ipcMain, dialog, shell, globalShortcut, } = require("electron"); const isDev = require("electron-is-dev"); const { default: ipcTypes } = require("../src/ipc.types"); const { store } = require("./electron-store"); const { autoUpdater } = require("electron-updater"); const log = require("electron-log"); const contextMenu = require("electron-context-menu"); const Sentry = require("@sentry/electron"); //const Nucleus = require("nucleus-nodejs"); require("./ipc-main-handler"); require("./analytics"); Sentry.init({ dsn: "https://9840e0f304124299e379d9347e12d2e6@o492140.ingest.sentry.io/4505728058523648", ignoreErrors: [ "SimpleURLLoaderWrapper", "Cannot read properties of null (reading 'webContents')", "EBUSY: resource busy or locked", ], }); autoUpdater.autoDownload = true; autoUpdater.logger = log; autoUpdater.logger.transports.file.level = "info"; log.info("App starting...", app.getVersion()); // Conditionally include the dev tools installer to load React Dev Tools let installExtension, REACT_DEVELOPER_TOOLS; if (isDev) { const devTools = require("electron-devtools-installer"); installExtension = devTools.default; REACT_DEVELOPER_TOOLS = devTools.REACT_DEVELOPER_TOOLS; } var menu = Menu.buildFromTemplate([ { label: "File", submenu: [ { label: "Relaunch", click() { app.exit(); app.relaunch(); }, }, { label: "Clear Settings", click() { store.reset("filePaths"); mainWindow.webContents.session.clearStorageData(); }, }, { label: "Sign Out", click() { mainWindow.webContents.send(ipcTypes.app.toRenderer.signOut); }, }, { label: "Exit", click() { app.quit(); }, }, ], // Other code removed for brevity }, { label: isDev ? "DEVELOPMENT -- HELP" : "Help", submenu: [ { label: "Rescue", click() { shell.openExternal("http://imexrescue.com"); }, }, { label: `Check for Updates (currently ${app.getVersion()})`, click() { checkForUpdates(); }, }, { label: `Show Release Notes`, click() { mainWindow.webContents.send( ipcTypes.app.toRenderer.setReleaseNotes, require("./changelog.json")[app.getVersion()] ); }, }, { label: "Open Config File", click() { shell.openPath(store.path); }, }, { label: "Open Log File", click() { shell.openPath(path.join(app.getPath("appData"), "ImeX RPS\\logs")); }, }, { label: "Third Party Notices", click() { openNoticeWindow(); }, }, ], }, ]); let mainWindow; let noticeWindow; let tray = null; contextMenu({ showInspectElement: false }); function createWindow() { // Create the browser window. Menu.setApplicationMenu(menu); mainWindow = new BrowserWindow({ // width: 800, // height: 600, title: `ImEX RPS ${app.getVersion()}`, icon: path.join(__dirname, "../src/assets/logo192.png"), webPreferences: { nodeIntegration: false, enableRemoteModule: false, webSecurity: true, worldSafeExecuteJavaScript: true, contextIsolation: true, preload: path.join(__dirname, "preload.js"), // use a preload script }, }); const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) { app.quit(); } else { app.on("second-instance", (event, commandLine, workingDirectory) => { // Someone tried to run a second instance, we should focus our window. if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore(); mainWindow.show(); mainWindow.focus(); mainWindow.maximize(); } }); // and load the index.html of the app. // win.loadFile("index.html"); mainWindow.loadURL( isDev ? "http://localhost:3000" : `file://${path.join(__dirname, "/../build/index.html")}` ); // mainWindow.on("close", function (event) { // event.preventDefault(); // mainWindow.hide(); // if (!tray) { // tray = createTray(); // } // }); mainWindow.on("minimize", function (event) { event.preventDefault(); mainWindow.hide(); if (!tray) { tray = createTray(); } }); ipcMain.on(ipcTypes.quit, (event, arg) => { app.isQuiting = true; app.quit(); }); // Open the DevTools. if (isDev) { mainWindow.webContents.openDevTools({ // mode: "detach" }); } mainWindow.maximize(); globalShortcut.register("CommandOrControl+Shift+I", () => { mainWindow.webContents.toggleDevTools(); }); } } exports.mainWindow = mainWindow; // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.whenReady().then(() => { createWindow(); if (isDev) { console.log(`Path to Settings File: ${store.path}`); console.log(`Path to Log File: ${log.log}`); installExtension(REACT_DEVELOPER_TOOLS) .then((name) => console.log(`Added Extension: ${name}`)) .catch((error) => console.log(`An error occurred: , ${error}`)); } setInterval(() => { checkForUpdates(); }, 1000 * 60 * 30); //Added auto update check for RPS-38 // ipcMain.on(ipcTypes.default.webcontent, (event, ...obj) => { // console.log("event", event); // mainWindow.webContents.send(event, obj); // }); }); if (isDev) app.setAppUserModelId(process.execPath); else app.setAppUserModelId("com.imex.rps"); // Quit when all windows are closed, except on macOS. There, it's common // for applications and their menu bar to stay active until the user quits // explicitly with Cmd + Q. app.on("window-all-closed", () => { if (process.platform !== "darwin") { app.quit(); } }); app.on("activate", () => { // On macOS it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. console.log("Activate"); if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); function createTray() { let appIcon = new Tray(path.join(__dirname, "../src/assets/logo192.png")); const contextMenu = Menu.buildFromTemplate([ { label: "Show", click: function () { mainWindow.show(); }, }, { label: "Exit", click: function () { app.isQuiting = true; app.quit(); }, }, ]); appIcon.on("double-click", function (event) { mainWindow.show(); mainWindow.maximize(); }); appIcon.setToolTip("ImEX RPS"); appIcon.setContextMenu(contextMenu); return appIcon; } autoUpdater.on("update-available", (ev) => { log.log("Update available.", ev); mainWindow.webContents.send(ipcTypes.app.toRenderer.updateAvailable, ev); }); autoUpdater.on("update-not-available", (ev) => { log.log("Update not available.", ev); }); autoUpdater.on("error", (ev, err) => { log.log("Error in auto-updater.", ev, err); }); function openNoticeWindow() { if (noticeWindow) { noticeWindow.focus(); return; } noticeWindow = new BrowserWindow({ height: 600, width: 800, title: "ImEX RPS - Third Party Notices", }); noticeWindow.loadURL("file://" + __dirname + "/licenses.txt"); noticeWindow.on("closed", function () { noticeWindow = null; }); } ipcMain.on(ipcTypes.app.toMain.checkForUpdates, (event, args) => { checkForUpdates(); }); ipcMain.on(ipcTypes.app.toMain.downloadUpdates, (event, args) => { //Nucleus.track("DOWNLOAD_UPDATE_FROM_RENDERER"); autoUpdater.downloadUpdate(); }); ipcMain.on(ipcTypes.app.toMain.installUpdates, (event, args) => { //Nucleus.track("INSTALL_UPDATE_FROM_RENDERER"); const isSilent = true; const isForceRunAfter = true; autoUpdater.quitAndInstall(isSilent, isForceRunAfter); }); autoUpdater.on("download-progress", (ev) => { log.log("Download Progress:", ev); mainWindow.webContents.send(ipcTypes.app.toRenderer.downloadProgress, ev); }); autoUpdater.on("update-downloaded", (ev, info) => { //Nucleus.track("UPDATE_DOWNLOADED", ev); // if (process.env.NODE_ENV === "production") { mainWindow.webContents.send(ipcTypes.app.toRenderer.downloadProgress, { ...ev, percent: 100, }); dialog .showMessageBox({ type: "info", title: "ImeX RPS Update Manager", message: `ImEX RPS is ready to update to Version ${ev.version}. It is highly recommended that you update immediately. Would you like to update now? RPS will automatically restart.`, buttons: ["Yes", "No"], }) .then(({ response }) => { if (response === 0) { const isSilent = true; const isForceRunAfter = true; store.set("showChangeLog", true); autoUpdater.quitAndInstall(isSilent, isForceRunAfter); } else { log.warn("Updated ignored."); } }); }); async function checkForUpdates() { try { log.info("Checking for updates."); autoUpdater.checkForUpdates(); } catch (error) { log.error("Error while checking for update", error); } }