From 22d40a450b9f09aff1cd8e8cb80140d46fb64c2b Mon Sep 17 00:00:00 2001 From: Richard Race <1273965+richrace@users.noreply.github.com> Date: Mon, 22 May 2023 10:37:41 +0100 Subject: [PATCH] Update Electron --- index.html | 55 +++--- index.js | 117 ------------ main.js | 101 ++++------ package-lock.json | 449 ++++++++++++++++++++++++++++++++------------- package.json | 2 +- src/arctis_view.js | 106 +++++++++++ src/preload.js | 7 + src/renderer.js | 15 ++ src/tray.js | 51 +++++ 9 files changed, 566 insertions(+), 337 deletions(-) delete mode 100644 index.js create mode 100644 src/arctis_view.js create mode 100644 src/preload.js create mode 100644 src/renderer.js create mode 100644 src/tray.js diff --git a/index.html b/index.html index ac312eb..d0c7403 100644 --- a/index.html +++ b/index.html @@ -1,31 +1,34 @@ - - - - - - Arctis Monitor - - -
-
-
-

Arctis Monitor

-
-
-
-
-
+ + + + + Arctis Monitor + + + +
+
+
+

Arctis Monitor

+
-
-
- -
-
+
+
+
- - \ No newline at end of file + +
+
+ +
+
+
+ + + + diff --git a/index.js b/index.js deleted file mode 100644 index 6116d2b..0000000 --- a/index.js +++ /dev/null @@ -1,117 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -/* eslint-disable no-multi-str */ -/* eslint-disable no-console */ -const { ipcRenderer } = require("electron"); -const { remote } = require("electron"); -const { getHeadphones } = require("arctis-usb-finder"); - -const deviceHTMLTemplate = - " \ -
\ -
{{ product }}
\ -
\ -
\ - Battery %: {{ batPercent }} {{ chargingStatus }} \ -
\ -
\ - Muted? {{ isMuted }} \ -
\ -
\ -
\ -"; - -const deviceTurnedOffTemplate = - " \ -
\ -
{{ product }}
\ -
This device is not connected or the headset is not turned on.
\ -
\ -"; - -function checkWindowSize() { - const win = remote.getCurrentWindow(); - const width = 300; - const animate = false; - const deviceRow = 62; - const otherPadding = 70; - - let height = otherPadding; - - height += deviceRow * document.querySelectorAll(".device").length; - - height = Math.min(400, height); - - win.setSize(width, height, animate); -} - -function updateStatus() { - const devices = getHeadphones(); - - if (devices.length === 0) { - document.querySelector(".devices").innerHTML = - "
Sorry no devices found!
"; - - return; - } - - devices.map((device, index) => { - let percentage = device.batteryPercent > 100 ? 100 : device.batteryPercent; - percentage = percentage < 0 ? 0 : percentage; - - const muted = device.isMuted ? "Yes" : "No"; - const charging = device.isCharging; - const discharging = device.isDischarging; - const notConnected = !device.isConnected; - - const deviceID = `device-${index}`; - - console.log("Rendering device: ", device); - let html; - - if (notConnected) { - console.log("Not connected"); - html = `${deviceTurnedOffTemplate}` - .replace("{{ deviceID }}", deviceID) - .replace("{{ product }}", device.modelName); - } else { - console.log("Connected"); - const dischargingIcon = discharging ? "down" : false; - const icon = charging ? "up" : dischargingIcon; - let chargingIcon = ""; - - if (icon) { - chargingIcon = ``; - } - - html = `${deviceHTMLTemplate}` - .replace("{{ deviceID }}", deviceID) - .replace("{{ product }}", device.modelName) - .replace("{{ batPercent }}", percentage) - .replace("{{ isMuted }}", muted) - .replace("{{ chargingStatus }}", chargingIcon); - } - - console.log("Rendering:", html); - if (document.querySelector(`#${deviceID}`)) { - document.querySelector(`#${deviceID}`).outerHTML = html; - } else { - document.querySelector(".devices").insertAdjacentHTML("beforeend", html); - } - - checkWindowSize(); - }); -} - -function init() { - document.querySelector("button#quit").addEventListener("click", () => { - ipcRenderer.send("quit-from-tray"); - }); - - updateStatus(); - - // Refresh 5 minutes - const time = 5 * 60 * 1000; - setInterval(updateStatus, time); -} - -document.addEventListener("DOMContentLoaded", init); diff --git a/main.js b/main.js index 7452bdd..b86cf4c 100644 --- a/main.js +++ b/main.js @@ -1,58 +1,12 @@ -const { - app, BrowserWindow, ipcMain, Tray, -// eslint-disable-next-line import/no-extraneous-dependencies -} = require('electron'); -const path = require('path'); +const { app, BrowserWindow, ipcMain } = require("electron"); +const path = require("path"); +const { createTray } = require("./src/tray"); +const { updateStatus, checkWindowSize } = require("./src/arctis_view"); -const assetsDirectory = path.join(__dirname, 'assets'); - -let tray; -let window; - -const getWindowPosition = () => { - const windowBounds = window.getBounds(); - const trayBounds = tray.getBounds(); - - // Center window horizontally below the tray icon - const x = Math.round(trayBounds.x + trayBounds.width / 2 - windowBounds.width / 2); - - // Position window 4 pixels vertically below the tray icon - const y = Math.round(trayBounds.y + trayBounds.height + 4); - - return { x, y }; -}; - -const showWindow = () => { - const position = getWindowPosition(); - window.setPosition(position.x, position.y, false); - window.show(); - window.focus(); -}; - -const toggleWindow = () => { - if (window.isVisible()) { - window.hide(); - } else { - showWindow(); - } -}; - -const createTray = () => { - tray = new Tray(path.join(assetsDirectory, 'headphones.png')); - tray.on('right-click', toggleWindow); - tray.on('double-click', toggleWindow); - tray.on('click', (event) => { - toggleWindow(); - - // Show devtools when command clicked - if (window.isVisible() && process.defaultApp && event.metaKey) { - window.openDevTools({ mode: 'detach' }); - } - }); -}; +let mainWindow; const createWindow = () => { - window = new BrowserWindow({ + mainWindow = new BrowserWindow({ show: false, frame: false, width: 300, @@ -70,31 +24,52 @@ const createWindow = () => { backgroundThrottling: false, nodeIntegration: true, enableRemoteModule: true, + + preload: path.join(__dirname, "src/preload.js"), }, }); - window.loadURL(`file://${path.join(__dirname, 'index.html')}`); + mainWindow.loadFile(path.join(__dirname, "index.html")); // Hide the window when it loses focus - window.on('blur', () => { - if (!window.webContents.isDevToolsOpened()) { - window.hide(); + mainWindow.on("blur", () => { + if (!mainWindow.webContents.isDevToolsOpened()) { + mainWindow.hide(); } }); + + return mainWindow; }; // Don't show the app in the doc app.dock.hide(); -app.on('ready', () => { - createTray(); +function handleQuit() { + app.quit(); +} + +function updateView() { + const status = updateStatus(); + mainWindow.webContents.send("handle-update", status.html); + checkWindowSize(mainWindow, status.numberOfDevices); +} + +function initialize() { + updateView(); +} + +app.whenReady().then(() => { + ipcMain.on("quit", handleQuit); + ipcMain.on("init", initialize); + createWindow(); -}); + createTray(mainWindow); -// Quit the app when the window is closed -app.on('window-all-closed', () => { - app.quit(); + // Refresh 5 minutes + const time = 5 * 60 * 1000; + setInterval(updateView, time); }); -ipcMain.on('quit-from-tray', () => { +// Quit the app when the window is closed +app.on("window-all-closed", () => { app.quit(); }); diff --git a/package-lock.json b/package-lock.json index 996f77f..4bd65f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "arctis-usb-finder": "^0.0.11" }, "devDependencies": { - "electron": "^11.1.1", + "@electron/remote": "^2.0.9", + "electron": "^24.3.1", "electron-packager": "^15.2.0", "eslint": "^7.17.0", "eslint-config-prettier": "^8.8.0", @@ -117,6 +118,15 @@ "semver": "bin/semver.js" } }, + "node_modules/@electron/remote": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.0.9.tgz", + "integrity": "sha512-LR0W0ID6WAKHaSs0x5LX9aiG+5pFBNAJL6eQAJfGkCuZPUa6nZz+czZLdlTDETG45CgF/0raSvCtYOYUpr6c+A==", + "dev": true, + "peerDependencies": { + "electron": ">= 13.0.0" + } + }, "node_modules/@electron/universal": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.3.4.tgz", @@ -247,6 +257,18 @@ "node": ">=6" } }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -258,6 +280,21 @@ "@types/node": "*" } }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "dev": true + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -266,9 +303,9 @@ "optional": true }, "node_modules/@types/node": { - "version": "12.19.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.12.tgz", - "integrity": "sha512-UwfL2uIU9arX/+/PRcIkT08/iBadGN2z6ExOROA2Dh5mAuWTBj6iJbQX4nekiV5H8cTrEG569LeX+HRco9Cbxw==" + "version": "18.16.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.13.tgz", + "integrity": "sha512-uZRomboV1vBL61EBXneL4j9/hEn+1Yqa4LQdpGrKmXFyJmVfWc9JV9+yb2AlnOnuaDnb2PDO3hC6/LKmzJxP1A==" }, "node_modules/@types/node-hid": { "version": "1.3.1", @@ -278,6 +315,15 @@ "@types/node": "*" } }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yauzl": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", @@ -577,11 +623,14 @@ "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", "dev": true }, - "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "engines": { + "node": ">=10.6.0" + } }, "node_modules/cacheable-request": { "version": "6.1.0", @@ -763,21 +812,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "node_modules/config-chain": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", @@ -789,12 +823,6 @@ "proto-list": "~1.2.1" } }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -943,21 +971,21 @@ "dev": true }, "node_modules/electron": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-11.5.0.tgz", - "integrity": "sha512-WjNDd6lGpxyiNjE3LhnFCAk/D9GIj1rU3GSDealVShhkkkPR3Vh4q8ErXGDl1OAO/faomVa10KoFPUN/pLbNxg==", + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-24.3.1.tgz", + "integrity": "sha512-lKfC0umie1k5LW48troHzpPKJrqPEW+5j14/CPTC41K9+dJA98oUPt/05G7QAe8OGD4fHjQQuulfRdZ9MjjXeQ==", "dev": true, "hasInstallScript": true, "dependencies": { - "@electron/get": "^1.0.1", - "@types/node": "^12.0.12", - "extract-zip": "^1.0.3" + "@electron/get": "^2.0.0", + "@types/node": "^18.11.18", + "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" }, "engines": { - "node": ">= 8.6" + "node": ">= 12.20.55" } }, "node_modules/electron-notarize": { @@ -1083,41 +1111,129 @@ "url": "https://github.com/electron/electron-packager?sponsor=1" } }, - "node_modules/electron-packager/node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "node_modules/electron-packager/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">= 10.17.0" + "node": ">=12" + } + }, + "node_modules/electron-packager/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" }, "optionalDependencies": { - "@types/yauzl": "^2.9.1" + "graceful-fs": "^4.1.6" } }, - "node_modules/electron-packager/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/electron-packager/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron/node_modules/@electron/get": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.2.tgz", + "integrity": "sha512-eFZVFoRXb3GFGd7Ak7W4+6jBl9wBtiZ4AaYOse97ej6mKj5tkyO0dUnUChs1IhJZtx1BENo4/p4WUTXpi6vT+g==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" }, "engines": { "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/electron/node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/electron/node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron/node_modules/cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dev": true, + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron/node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron/node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" } }, - "node_modules/electron-packager/node_modules/get-stream": { + "node_modules/electron/node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", @@ -1132,25 +1248,107 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/electron-packager/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/electron/node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, "dependencies": { - "universalify": "^2.0.0" + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" } }, - "node_modules/electron-packager/node_modules/universalify": { + "node_modules/electron/node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/electron/node_modules/keyv": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/electron/node_modules/lowercase-keys": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, "engines": { - "node": ">= 10.0.0" + "node": ">=8" + } + }, + "node_modules/electron/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron/node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron/node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" } }, "node_modules/emoji-regex": { @@ -1468,35 +1666,40 @@ } }, "node_modules/extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "dependencies": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", + "debug": "^4.1.1", + "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "bin": { "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" } }, - "node_modules/extract-zip/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "dependencies": { - "ms": "2.0.0" + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/extract-zip/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1962,6 +2165,19 @@ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -2101,12 +2317,6 @@ "node": ">=8" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "node_modules/isbinaryfile": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", @@ -2320,18 +2530,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -2685,12 +2883,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -2725,6 +2917,18 @@ "node": ">=6" } }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -2778,21 +2982,6 @@ "node": ">=4" } }, - "node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, "node_modules/readline": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", @@ -2832,6 +3021,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3375,12 +3570,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", diff --git a/package.json b/package.json index a594467..706b364 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "author": "Richard Race", "license": "MIT", "devDependencies": { - "electron": "^11.1.1", + "electron": "^24.3.1", "electron-packager": "^15.2.0", "eslint": "^7.17.0", "eslint-config-prettier": "^8.8.0", diff --git a/src/arctis_view.js b/src/arctis_view.js new file mode 100644 index 0000000..f67eb76 --- /dev/null +++ b/src/arctis_view.js @@ -0,0 +1,106 @@ +const { getHeadphones } = require("arctis-usb-finder"); + +const deviceHTMLTemplate = + " \ +
\ +
{{ product }}
\ +
\ +
\ + Battery %: {{ batPercent }} {{ chargingStatus }} \ +
\ +
\ + Muted? {{ isMuted }} \ +
\ +
\ +
\ +"; + +const deviceTurnedOffTemplate = + " \ +
\ +
{{ product }}
\ +
This device is not connected or the headset is not turned on.
\ +
\ +"; + +function checkWindowSize(mainWindow, numberOfDevices) { + const width = 300; + const animate = false; + const deviceRow = 62; + const otherPadding = 70; + + let height = otherPadding; + + if (numberOfDevices.length > 0) { + height += deviceRow * numberOfDevices; + } + + height = Math.min(400, height); + + mainWindow.setSize(width, height, animate); +} + +const renderNotConnected = (device, deviceID) => { + return `${deviceTurnedOffTemplate}` + .replace("{{ deviceID }}", deviceID) + .replace("{{ product }}", device.modelName); +}; + +const renderConnected = (device, deviceID) => { + let percentage = device.batteryPercent > 100 ? 100 : device.batteryPercent; + percentage = percentage < 0 ? 0 : percentage; + + const muted = device.isMuted ? "Yes" : "No"; + const charging = device.isCharging; + const discharging = device.isDischarging; + const dischargingIcon = discharging ? "down" : false; + const icon = charging ? "up" : dischargingIcon; + let chargingIcon = ""; + + if (icon) { + chargingIcon = ``; + } + + return `${deviceHTMLTemplate}` + .replace("{{ deviceID }}", deviceID) + .replace("{{ product }}", device.modelName) + .replace("{{ batPercent }}", percentage) + .replace("{{ isMuted }}", muted) + .replace("{{ chargingStatus }}", chargingIcon); +}; + +const updateStatus = () => { + const devices = getHeadphones(); + + if (devices.length === 0) { + return { + numberOfDevices: devices.length, + html: "
Sorry no devices found!
", + }; + } + + const html = devices.reduce((reducerHtml, device, index) => { + const notConnected = !device.isConnected; + + const deviceID = `device-${index}`; + + let tempHtml = ""; + + if (notConnected) { + tempHtml = renderNotConnected(device, deviceID); + } else { + tempHtml = renderConnected(device, deviceID); + } + + reducerHtml += tempHtml; + + return reducerHtml; + }, ""); + + return { + numberOfDevices: devices.length, + html, + }; +}; + +module.exports = { updateStatus, checkWindowSize }; diff --git a/src/preload.js b/src/preload.js new file mode 100644 index 0000000..dde7503 --- /dev/null +++ b/src/preload.js @@ -0,0 +1,7 @@ +const { contextBridge, ipcRenderer } = require("electron"); + +contextBridge.exposeInMainWorld("electronAPI", { + quit: () => ipcRenderer.send("quit"), + init: () => ipcRenderer.send("init"), + handleUpdate: (callback) => ipcRenderer.on("handle-update", callback), +}); diff --git a/src/renderer.js b/src/renderer.js new file mode 100644 index 0000000..0fb0ad5 --- /dev/null +++ b/src/renderer.js @@ -0,0 +1,15 @@ +const quitButton = document.getElementById("quit"); + +quitButton.addEventListener("click", () => { + window.electronAPI.quit(); +}); + +document.addEventListener("DOMContentLoaded", () => { + window.electronAPI.init(); +}); + +window.electronAPI.handleUpdate((event, value) => { + const devices = document.getElementById("devices"); + + devices.innerHTML = value; +}); diff --git a/src/tray.js b/src/tray.js new file mode 100644 index 0000000..e1cdd0c --- /dev/null +++ b/src/tray.js @@ -0,0 +1,51 @@ +const { Tray } = require("electron"); +const path = require("path"); +const assetsDirectory = path.join(__dirname, "../assets"); + +let mainTray; + +const getWindowPosition = (mainWindow) => { + const windowBounds = mainWindow.getBounds(); + const trayBounds = mainTray.getBounds(); + + // Center window horizontally below the tray icon + const x = Math.round( + trayBounds.x + trayBounds.width / 2 - windowBounds.width / 2 + ); + + // Position window 4 pixels vertically below the tray icon + const y = Math.round(trayBounds.y + trayBounds.height + 4); + + return { x, y }; +}; + +const showWindow = (mainWindow) => { + const position = getWindowPosition(mainWindow); + mainWindow.setPosition(position.x, position.y, false); + mainWindow.show(); + mainWindow.focus(); +}; + +const toggleWindow = (mainWindow) => { + if (mainWindow.isVisible()) { + mainWindow.hide(); + } else { + showWindow(mainWindow); + } +}; + +const createTray = (mainWindow) => { + mainTray = new Tray(path.join(assetsDirectory, "headphones.png")); + mainTray.on("right-click", () => toggleWindow(mainWindow)); + mainTray.on("double-click", () => toggleWindow(mainWindow)); + mainTray.on("click", (event) => { + toggleWindow(mainWindow); + + // Show devtools when command clicked + if (mainWindow.isVisible() && process.defaultApp && event.metaKey) { + mainWindow.openDevTools({ mode: "detach" }); + } + }); +}; + +module.exports = { createTray };