diff --git a/main.js b/main.js index 5832f27..cfbbddf 100644 --- a/main.js +++ b/main.js @@ -1,4 +1,4 @@ -const { app, BrowserWindow, shell, dialog, ipcMain, Menu, Tray } = require('electron'); +const { app, BrowserWindow, shell, dialog, ipcMain, Menu, Tray, screen } = require('electron'); require('@electron/remote/main').initialize() const windowStateKeeper = require('electron-window-state'); const DiscordRPC = require('discord-rpc'); @@ -8,7 +8,7 @@ const fs = require('fs'); const downloadsFolder = require('downloads-folder'); const isOnline = require('on-line'); -let mainWindow, updateModal, quitConfirm, updateStatus = (statusobject) => { mainWindow.webContents.send('updateStatus', statusobject); }, changeColorMode = (color) => { mainWindow.webContents.send('changeColorMode', color); }, preferences = { "colorScheme": "dark","discordRPC": true,"startup": false,"betaEnabled": true,"closeToTray": true }, tray, contextMenuHidden, contextMenuVisible, currentDownloads = [], onlineState, currentlydownloadedupdate; +let mainWindow, updateModal, quitConfirm, notificationWindow, updateStatus = (statusobject) => { mainWindow.webContents.send('updateStatus', statusobject); }, changeColorMode = (color) => { mainWindow.webContents.send('changeColorMode', color); }, preferences = { "colorScheme": "dark","discordRPC": true,"startup": false,"betaEnabled": true,"closeToTray": true }, tray, contextMenuHidden, contextMenuVisible, currentDownloads = [], onlineState, currentlydownloadedupdate, notificationQ = [], isNotificationShowing = false; function askToQuit() { /*let questionString = 'There '; questionString += Object.keys(currentDownloads).length == 1 ? 'is ' : 'are currently '; questionString += Object.keys(currentDownloads).length; questionString += Object.keys(currentDownloads).length == 1 ? ' item being downloaded:\n' : ' downloads being downloaded:\n'; questionString += currentDownloads.map(item => { const key = Object.keys(item)[0]; const value = item[key]; return `${value.string} ${value.version}`; }).join('\n'); questionString += '\n\nAre you sure you want to quit?' dialog.showMessageBox({ @@ -197,7 +197,7 @@ app.whenReady().then(() => { } }); }); - checkUpdates() + checkUpdates(); setInterval(checkUpdates, 10 * 60 * 1000); }, 2500) @@ -333,7 +333,9 @@ async function checkUpdates() { updateStatus({ "status": "downloading", "current": version, "latest": latestversion }); currentDownloads.push({ "launcherUpdate": { "string": "DeadForge", "version": latestversion } }); var downloadProgress = 0; - + + createNotification({ "title": "Launcher Update", "message": "Downloading update " + latestversion }); + const response = await axios({ url: downloadLinksByOS[platform], method: 'GET', @@ -355,7 +357,7 @@ async function checkUpdates() { }); } - await downloadUpdate().then(() => { if (installerPathGoal != undefined) if (installerPath != installerPathGoal) { fs.renameSync(installerPath, installerPathGoal); }; ipcMain.on('update', (event, updateNow) => { console.log(updateNow); if (updateNow == true) { app.quit(); } else {updateModal.destroy();}}); currentlydownloadedupdate = latestversion; currentDownloads.splice(currentDownloads.findIndex(item => Object.keys(item)[0] === 'launcherUpdate'), 1); disableUpdateButton(false); updateStatus({ 'status': 'downloaded', 'current': version, 'latest': latestversion }); showInstallDialog(); downloadProgress = 0; mainWindow.webContents.send('launcherUpdateDownloadProgress', downloadProgress)}).catch(err => { disableUpdateButton(false); updateStatus({"status": "fail", "current": version, "latest": latestversion, "failType": "download"}); console.error(err) }); + await downloadUpdate().then(() => { if (installerPathGoal != undefined) if (installerPath != installerPathGoal) { fs.renameSync(installerPath, installerPathGoal); }; ipcMain.on('update', (event, updateNow) => { console.log(updateNow); if (updateNow == true) { app.quit(); } else { updateModal.destroy(); } }); currentlydownloadedupdate = latestversion; currentDownloads.splice(currentDownloads.findIndex(item => Object.keys(item)[0] === 'launcherUpdate'), 1); disableUpdateButton(false); updateStatus({ 'status': 'downloaded', 'current': version, 'latest': latestversion }); showInstallDialog(); downloadProgress = 0; mainWindow.webContents.send('launcherUpdateDownloadProgress', downloadProgress);}).catch(err => { disableUpdateButton(false); updateStatus({"status": "fail", "current": version, "latest": latestversion, "failType": "download"}); console.error(err) }); } ipcMain.on('color-preference', (event, colorPreference) => { @@ -407,4 +409,61 @@ async function checkUpdates() { ipcMain.on('checkForConnection', (event) => { isOnline(function (error, online) { onlineState = online; mainWindow.webContents.send('connectionCheck', online) }); - }) \ No newline at end of file + }) + +const notificationCallbacks = { + + } + +function createNotification(notificationData) { + notificationQ.push(notificationData); + + if (isNotificationShowing) return; + + showNextNotification(); +} + +function showNextNotification() { + if (notificationQ.length == 0) { + isNotificationShowing = false; + return; + }; + + const notificationData = notificationQ.shift(); + + notificationWindow = new BrowserWindow({ + x: screen.getPrimaryDisplay().bounds.width - 375, + y: 25, + width: 350, + height: 200, + resizable: false, + closable: false, + maximizable: false, + minimizable: false, + frame: false, + transparent: true, + show: false, + alwaysOnTop: true, + skipTaskbar: true, + webPreferences: { + nodeIntegration: true, + contextIsolation: false, + devTools: false + } + }); + + require("@electron/remote/main").enable(notificationWindow.webContents); + + notificationWindow.loadFile('notification.html'); + + notificationWindow.webContents.on('did-finish-load', () => { + notificationWindow.webContents.send('notification', notificationData); + notificationWindow.show(); + }); + + ipcMain.on('notificationclose', (event, notificationCallback) => { + notificationWindow.destroy(); + if (notificationCallbacks[notificationCallback] != undefined && notificationCallback != null) { notificationCallbacks[notificationCallback](); } + showNextNotification(); + }) +} \ No newline at end of file diff --git a/notification.css b/notification.css new file mode 100644 index 0000000..2a9fe5e --- /dev/null +++ b/notification.css @@ -0,0 +1,115 @@ +@import url("./fonts/fonts.css"); + +:root { + user-select: none; + -webkit-user-select: none; +} + +body { + margin: 0; +} +div#notification { + background: rgba(255, 255, 255, 0.5); + backdrop-filter: blur(8px); + border-radius: 8px; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1), 0 4px 11px rgba(0, 0, 0, 0.1); + opacity: 1; + transform: scale(1); + transition: background-color 0.125s ease-in-out, opacity 0.125s ease-in-out, transform 0.125s ease-in-out; +} + +div#notification.hidden { + opacity: 0; + transform: scale(0.9); +} + +div#notification:hover { + background: rgba(255, 255, 255, 0.75); +} + +img#notificationicon { + height: 24px; + margin-right: 8px; +} + +div#notificationheader { + display: flex; + flex-direction: row; + padding: 12px; + justify-content: space-between; + border-bottom: rgba(0, 0, 0, 0.125) 1px solid; +} + +div#notificationheader, div#notificationheader>:not(div#notificationclose) { + -webkit-app-region: drag; +} + +div#notificationclose { + -webkit-app-region: no-drag; +} + +left { + display: flex; + flex-direction: row; + align-items: center; +} + +div#notificationtitle { + text-transform: uppercase; + font-family: "Uni Sans CAPS"; + font-weight: bold; +} + +div#notificationcontent { + padding: 12px 16px; +} + +div#notificationinfotitle { + font-weight: bold; + font-family: "Montserrat"; + margin-bottom: 4px; +} + +div#notificationinfo { + font-family: 'Montserrat'; + font-size: 12px; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + white-space: pre-wrap; + display: -webkit-box; + overflow: hidden; +} + +div#notification:has(button:not(:empty)) div#notificationinfo { + margin-bottom: 16px; +} + +div#notificationbuttons { + display: flex; + flex-direction: row; + justify-content: space-around; +} + +button { + all: unset; + cursor: pointer; + padding: 8px; + background: rgba(255, 255, 255, 0.5); + font-family: 'Montserrat'; + font-weight: 666; + font-size: 12px; + text-align: center; + border-radius: 8px +} + +div#notificationbuttons>button:not(:empty) { + width: 1000%; +} + +div#notificationbuttons:not(:has(button:empty))>button:not(:last-child) { + margin-right: 25px +} + +button:empty { + display: none; +} \ No newline at end of file diff --git a/notification.html b/notification.html new file mode 100644 index 0000000..48cc9f2 --- /dev/null +++ b/notification.html @@ -0,0 +1,27 @@ + + + + + +
+
+ + +
deadforge
+
+ +
close
+
+
+
+
+
+
+ + +
+
+
+ + + \ No newline at end of file diff --git a/notification.js b/notification.js new file mode 100644 index 0000000..23ddd0c --- /dev/null +++ b/notification.js @@ -0,0 +1,52 @@ +const { ipcRenderer } = require('electron'); +const { BrowserWindow } = require('@electron/remote'); + +let notificationActionA, notificationActionB, timer; + +function closeNotification(callbackFn = null) { + document.querySelector('div#notification').classList.add('hidden'); + setTimeout(() => { + ipcRenderer.send('notificationclose', callbackFn); + }, 125); +} + +ipcRenderer.on('notification', (event, notification) => { + console.log(notification); + const title = notification.title; + const message = notification.message; + const actionA = notification.actionA && notification.actionA.message ? notification.actionA : null; + const actionB = notification.actionB && notification.actionB.message ? notification.actionB : null; + + document.querySelector('div#notificationinfotitle').textContent = title; + document.querySelector('div#notificationinfo').textContent = message; + + if (actionA != null) { + document.querySelector('button#notificationbuttonA').textContent = actionA.message; + notificationActionA = () => { closeNotification(actionA.callbackFn) }; + } + + if (actionB != null) { + document.querySelector('button#notificationbuttonB').textContent = actionB.message; + notificationActionB = () => { closeNotification(actionB.callbackFn) }; + } +}) + +const startTimeout = () => { + timer = setTimeout(closeNotification, 5000); +}; + +const resetTimeout = () => { + clearTimeout(timer); +}; + +window.addEventListener('load', () => { + startTimeout(); +}); + +document.querySelector('div#notification').addEventListener('mouseenter', () => { + resetTimeout(); +}); + +document.querySelector('div#notification').addEventListener('mouseleave', () => { + startTimeout(); +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 326a7a7..6251f5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "deadforge", - "version": "v1.2.2", + "version": "v1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "deadforge", - "version": "v1.2.2", + "version": "v1.3.0", "license": "BSD-3-Clause", "dependencies": { "@electron/remote": "^2.1.2", diff --git a/package.json b/package.json index d0cb9cb..2ce7860 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "deadforge", - "version": "v1.2.2", + "version": "v1.3.0", "description": "An Electron Launcher for DeadCode Projects.", "main": "main.js", "scripts": {