const { app, dialog, shell, net, BrowserWindow, Notification, nativeImage } = require('electron'); let win; // to get rid of "Electron Security Warning (Insecure Content-Security-Policy)" warnings; // using recommanded <meta http-equiv="Content-Security-Policy"> will cause Matomo to // fail, among other problems process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'; function createWindow () { // Create the browser window. win = new BrowserWindow({ width: 800, height: 600 }); // and load the index.html of the app. win.loadFile("./dist/index.html"); // Hide menu bar. win.setMenuBarVisibility(false) // Open the DevTools. // win.webContents.openDevTools(); // Ask confirmation before closing the app. win.on('close', (e) => { e.preventDefault(); const choice = dialog.showMessageBoxSync( { type: "question", buttons: [ "Ok", "Cancel"] , // "OK" and "Cancel" are automatically translated defaultId: 1, cancelId: 1, title: "Exit Cassiopée", message: "Are you sure you want to exit Cassiopée ?" } ); if(choice == 1){ e.preventDefault(); } else { win.destroy(); } }); // Emitted when the window is closed. win.on('closed', () => { win = null; }); // Try to detect updates lookForUpdates(); }; /** * Compares semver strings * @see https://github.com/substack/semver-compare */ function semverCompare(a, b) { var pa = a.split('.'); var pb = b.split('.'); for (var i = 0; i < 3; i++) { var na = Number(pa[i]); var nb = Number(pb[i]); if (na > nb) return 1; if (nb > na) return -1; if (!isNaN(na) && isNaN(nb)) return 1; if (isNaN(na) && !isNaN(nb)) return -1; } return 0; } // debug function alert(text) { dialog.showMessageBox(null, { message: text, buttons: ['Cancel'] }); } /** * Calls the Cassiopee update server to check for available updates; * if so, displays a desktop notification which, if clicked, will * trigger the download of the latest version installer for the * current platform * * Web updates system prerequisites * -------------------------------- * * ${URL} should point to an http-accessible directory containing: * * 1. a file named "releases.json" with a contents of the form * { * "latest": "4.5.0", * "4.5.0": { * "darwin": "cassiopee-setup-4.5.0.dmg", * "linux": "fr.irstea.cassiopee_4.5.0_amd64.deb", * "win32": "Cassiopée Setup 4.5.0.exe" * }, * "4.4.2": { * "darwin": "cassiopee-setup-4.4.2.dmg", * "linux": "fr.irstea.cassiopee_4.4.2_amd64.deb", * "win32": "Cassiopée Setup 4.4.2.exe" * } * } * * 2. all platform-dependent installer files specified in "releases.json", * at the root of the directory (ex: "fr.irstea.cassiopee_4.5.0_amd64.deb") */ const lookForUpdates = function() { // Web update resources root directory const URL = "https://cassiopee.g-eau.fr/cassiopee-releases/"; // detect current app version and platform // const version = "4.4.2"; // debug const version = app.getVersion(); const platform = process.platform; // "win32", "linux" or "darwin" // console.log(process.arch); // "x32" or "x64" // fetch releases information const req = net.request(URL + "releases.json"); req.setHeader("Cache-Control", "no-cache"); req.on("response", (response) => { if (response.statusCode === 200) { response.on('data', (chunk) => { const data = JSON.parse(chunk); // compare current version to latest version if (data.latest && semverCompare(data.latest, version) == 1) { // get download link for latest version, depending on platform if (data[data.latest] && data[data.latest][platform]) { const latestVersionURL = URL + data[data.latest][platform]; // show notification on desktop if (Notification.isSupported()) { // @see https://stackoverflow.com/questions/41823184/how-to-get-icon-path-image-in-electron-builder let iconPath = "assets/icons/electron/icon.png"; if (! app.isPackaged) { iconPath = "dist/" + iconPath; // marche pas !? Comment obtenir le chemin de l'icône dans app.asar ? } let notif = new Notification({ title: `Cassiopee version ${data.latest} is available`, body: `The desktop distribution of Cassiopeia will no longer be updated in 2024. You are invited to use the progressive web app Cassiopée instead. Click to download update installer.`, icon: nativeImage.createFromPath(iconPath) // @see https://github.com/electron/electron/issues/24221 }); notif.addListener("click", () => { // ask system to open URL with default browser shell.openExternal(latestVersionURL); }); setTimeout(() => { notif.show(); }, 2000); } } else { console.error(`could not find download link for version ${data.latest} on platform ${platform}`); } } // else you already have the latest version }); } else { console.error("error fetching releases information"); } }); req.on("error", (err) => { // generally net::ERR_INTERNET_DISCONNECTED, which has no incidence // other than preventing available updates detection console.error(err); }); req.end(); }; // 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.on('ready', () => { createWindow(); if (process.platform === 'win32') { // for notifications to work on windows; see https://stackoverflow.com/a/53510850/5986614 app.setAppUserModelId(app.getName()); } }); // Quit when all windows are closed. app.on('window-all-closed', () => { // On macOS it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q 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. if (win === null) { createWindow(); } });