diff --git a/receivers/common/assets/icons/app/network-light.svg b/receivers/common/assets/icons/app/network-light.svg new file mode 100644 index 0000000..4462f0f --- /dev/null +++ b/receivers/common/assets/icons/app/network-light.svg @@ -0,0 +1,10 @@ + + + diff --git a/receivers/common/assets/icons/app/warning.svg b/receivers/common/assets/icons/app/warning.svg new file mode 100644 index 0000000..15091f9 --- /dev/null +++ b/receivers/common/assets/icons/app/warning.svg @@ -0,0 +1,4 @@ + + + + diff --git a/receivers/common/assets/icons/app/wifi-strength-1.svg b/receivers/common/assets/icons/app/wifi-strength-1.svg new file mode 100644 index 0000000..c3cc86c --- /dev/null +++ b/receivers/common/assets/icons/app/wifi-strength-1.svg @@ -0,0 +1,10 @@ + + + diff --git a/receivers/common/assets/icons/app/wifi-strength-2.svg b/receivers/common/assets/icons/app/wifi-strength-2.svg new file mode 100644 index 0000000..0ac450f --- /dev/null +++ b/receivers/common/assets/icons/app/wifi-strength-2.svg @@ -0,0 +1,10 @@ + + + diff --git a/receivers/common/assets/icons/app/wifi-strength-3.svg b/receivers/common/assets/icons/app/wifi-strength-3.svg new file mode 100644 index 0000000..cfd8fb5 --- /dev/null +++ b/receivers/common/assets/icons/app/wifi-strength-3.svg @@ -0,0 +1,10 @@ + + + diff --git a/receivers/common/assets/icons/app/wifi-strength-4.svg b/receivers/common/assets/icons/app/wifi-strength-4.svg new file mode 100644 index 0000000..cf83874 --- /dev/null +++ b/receivers/common/assets/icons/app/wifi-strength-4.svg @@ -0,0 +1,10 @@ + + + diff --git a/receivers/common/assets/icons/app/wifi-strength-outline.svg b/receivers/common/assets/icons/app/wifi-strength-outline.svg new file mode 100644 index 0000000..d9ebf0b --- /dev/null +++ b/receivers/common/assets/icons/app/wifi-strength-outline.svg @@ -0,0 +1,10 @@ + + + diff --git a/receivers/common/web/NetworkService.ts b/receivers/common/web/NetworkService.ts index bfcd8da..a318a1f 100644 --- a/receivers/common/web/NetworkService.ts +++ b/receivers/common/web/NetworkService.ts @@ -5,6 +5,7 @@ import * as url from 'url'; import { AddressInfo } from 'modules/ws'; import { v4 as uuidv4 } from 'modules/uuid'; import { Main } from 'src/Main'; +import si from 'modules/systeminformation'; export class NetworkService { static key: string = null; @@ -12,6 +13,8 @@ export class NetworkService { static proxyServer: http.Server; static proxyServerAddress: AddressInfo; static proxiedFiles: Map = new Map(); + static networkStateChangeListenerTimeout = 2500; + private static networkStateChangeListenerInterfaces = []; private static setupProxyServer(): Promise { return new Promise((resolve, reject) => { @@ -104,21 +107,54 @@ export class NetworkService { return proxiedUrl; } - static getAllIPv4Addresses() { - const interfaces = os.networkInterfaces(); - const ipv4Addresses: string[] = []; + static async networkStateChangeListener(forceUpdate: boolean, networkChangedCb: (networkInfo: any) => void) { + const queriedInterfaces: si.Systeminformation.NetworkInterfacesData[] = await new Promise((resolve, reject) => { + si.networkInterfaces((data) => { + // console.log(data); - for (const interfaceName in interfaces) { - const addresses = interfaces[interfaceName]; - if (!addresses) continue; + if (Array.isArray(data)) { + resolve(data); + } + else { + resolve([data]); + } + }); + }); - for (const addressInfo of addresses) { - if (addressInfo.family === 'IPv4' && !addressInfo.internal) { - ipv4Addresses.push(addressInfo.address); + const wifiConnections: si.Systeminformation.WifiConnectionData[] = await new Promise((resolve, reject) => { + si.wifiConnections((data) => { + // console.log(data); + + if (Array.isArray(data)) { + resolve(data); + } + else { + resolve([data]); + } + }); + }); + + const interfaces = []; + for (const iface of queriedInterfaces) { + if (iface.ip4 !== '' && !iface.internal && !iface.virtual) { + const isWireless = wifiConnections.some(e => { + if (e.iface === iface.iface) { + interfaces.push({ type: 'wireless', name: e.ssid, address: iface.ip4, signalLevel: e.quality }); + return true; + } + + return false; + }); + + if (!isWireless) { + interfaces.push({ type: 'wired', name: iface.ifaceName, address: iface.ip4 }); } } } - return ipv4Addresses; + if (forceUpdate || (JSON.stringify(interfaces) !== JSON.stringify(NetworkService.networkStateChangeListenerInterfaces))) { + NetworkService.networkStateChangeListenerInterfaces = interfaces; + networkChangedCb(interfaces); + } } } diff --git a/receivers/common/web/components/Toast.ts b/receivers/common/web/components/Toast.ts index 32c44e9..a9624f9 100644 --- a/receivers/common/web/components/Toast.ts +++ b/receivers/common/web/components/Toast.ts @@ -1,5 +1,6 @@ export enum ToastIcon { INFO, + WARNING, ERROR, } @@ -40,6 +41,10 @@ function renderToast(message: string, icon: ToastIcon = ToastIcon.INFO, duration toastIcon.style.backgroundImage = 'url(../assets/icons/app/info.svg)'; break; + case ToastIcon.WARNING: + toastIcon.style.backgroundImage = 'url(../assets/icons/app/warning.svg)'; + break; + case ToastIcon.ERROR: toastIcon.style.backgroundImage = 'url(../assets/icons/app/error.svg)'; break; diff --git a/receivers/common/web/main/Renderer.ts b/receivers/common/web/main/Renderer.ts index 663894a..28b7275 100644 --- a/receivers/common/web/main/Renderer.ts +++ b/receivers/common/web/main/Renderer.ts @@ -7,6 +7,7 @@ const connectionStatusText = document.getElementById("connection-status-text"); const connectionStatusSpinner = document.getElementById("connection-spinner"); const connectionStatusCheck = document.getElementById("connection-check"); let connections = []; +let renderedAddresses = null; // Window might be re-created while devices are still connected window.targetAPI.onPing((_event, value: any) => { @@ -56,14 +57,41 @@ function renderIPsAndQRCode() { const value = window.targetAPI.getDeviceInfo(); console.log("device info", value); - const ipsElement = document.getElementById('ips'); - if (ipsElement) { - ipsElement.innerHTML = `IPs
${value.addresses.join('
')}`; + const addresses = []; + value.interfaces.forEach((e) => addresses.push(e.address)); + const connInfo = document.getElementById('connection-information'); + const connError = document.getElementById('connection-error'); + + if (renderedAddresses !== null && addresses.length > 0) { + toast("Network connections has changed, please reconnect sender devices to receiver if you experience issues", ToastIcon.WARNING); } + else if (addresses.length === 0) { + connInfo.setAttribute("style", "display: none"); + connError.setAttribute("style", "display: block"); + + if (renderedAddresses !== null) { + toast("Lost network connection, please reconnect to a network", ToastIcon.ERROR); + } + + renderedAddresses = [] + return; + } + + if (renderedAddresses !== null && renderedAddresses.length === 0) { + connInfo.setAttribute("style", "display: block"); + connError.setAttribute("style", "display: none"); + } + + renderIPs(value.interfaces); + + if (JSON.stringify(addresses) === JSON.stringify(renderedAddresses)) { + return; + } + renderedAddresses = addresses; const fcastConfig = { name: value.name, - addresses: value.addresses, + addresses: addresses, services: [ { port: 46899, type: 0 }, //TCP { port: 46898, type: 1 }, //WS @@ -98,3 +126,54 @@ function renderIPsAndQRCode() { onQRCodeRendered(); } + +function renderIPs(interfaces: any) { + const ipsElement = document.getElementById('ips'); + + if (ipsElement) { + const ipsIconColumn = document.getElementById('ips-iface-icon'); + ipsIconColumn.innerHTML = ''; + + const ipsTextColumn = document.getElementById('ips-iface-text'); + ipsTextColumn.innerHTML = ''; + + const ipsNameColumn = document.getElementById('ips-iface-name'); + ipsNameColumn.innerHTML = ''; + + for (const iface of interfaces) { + const ipIcon = document.createElement("div"); + let icon = 'iconSize '; + if (iface.type === 'wired') { + icon += 'ip-wired-icon'; + } + else if (iface.type === 'wireless' && (iface.signalLevel === 0 || iface.signalLevel >= 90)) { + icon += 'ip-wireless-4-icon'; + } + else if (iface.type === 'wireless' && iface.signalLevel >= 70) { + icon += 'ip-wireless-3-icon'; + } + else if (iface.type === 'wireless' && iface.signalLevel >= 50) { + icon += 'ip-wireless-2-icon'; + } + else if (iface.type === 'wireless' && iface.signalLevel >= 30) { + icon += 'ip-wireless-1-icon'; + } + else if (iface.type === 'wireless') { + icon += 'ip-wireless-0-icon'; + } + + ipIcon.className = icon; + ipsIconColumn.append(ipIcon); + + const ipText = document.createElement("div"); + ipText.className = 'ip-entry-text'; + ipText.textContent = iface.address; + ipsTextColumn.append(ipText); + + const ipName = document.createElement("div"); + ipName.className = 'ip-entry-text'; + ipName.textContent = iface.name; + ipsNameColumn.append(ipName); + } + } +} diff --git a/receivers/common/web/main/common.css b/receivers/common/web/main/common.css index 1954f84..d0009b6 100644 --- a/receivers/common/web/main/common.css +++ b/receivers/common/web/main/common.css @@ -45,6 +45,12 @@ body, html { margin-bottom: 3px; } +.iconSize { + width: 32px; + height: 32px; + background-size: cover; +} + #ui-container { display: flex; flex-direction: column; @@ -134,11 +140,85 @@ body, html { font-weight: bold; } -#connection-status-text, #ips, #automatic-discovery { +#connection-error { + display: none; +} + +#connection-error-icon { + width: 84px; + height: 84px; + margin: auto; + margin-top: 20px; + + background-image: url(../assets/icons/app/error.svg); + background-size: cover; +} + +#connection-error-text { + margin-top: 20px; + font-weight: bold; +} + +#connection-status-text { margin-top: 20px; white-space: pre; } +#ips { + display: flex; + margin-top: 20px; + white-space: pre; + gap: 10px; + justify-content: center; +} + +#ips-iface-icon { + display: flex; + flex-direction: column; +} + +#ips-iface-text { + display: flex; + flex-direction: column; + margin-right: 20px; + align-items: start; +} + +#ips-iface-name { + display: flex; + flex-direction: column; + align-items: start; +} + +.ip-entry-text { + margin-top: 4px; + margin-bottom: 4px; +} + +.ip-wired-icon { + background-image: url(../assets/icons/app/network-light.svg); +} + +.ip-wireless-4-icon { + background-image: url(../assets/icons/app/wifi-strength-4.svg); +} + +.ip-wireless-3-icon { + background-image: url(../assets/icons/app/wifi-strength-3.svg); +} + +.ip-wireless-2-icon { + background-image: url(../assets/icons/app/wifi-strength-2.svg); +} + +.ip-wireless-1-icon { + background-image: url(../assets/icons/app/wifi-strength-1.svg); +} + +.ip-wireless-0-icon { + background-image: url(../assets/icons/app/wifi-strength-outline.svg); +} + #connection-spinner { padding: 20px; } diff --git a/receivers/electron/package-lock.json b/receivers/electron/package-lock.json index 567c49b..6964443 100644 --- a/receivers/electron/package-lock.json +++ b/receivers/electron/package-lock.json @@ -19,6 +19,7 @@ "https": "^1.0.0", "log4js": "^6.9.1", "qrcode": "^1.5.3", + "systeminformation": "^5.25.11", "url": "^0.11.4", "uuid": "^11.0.3", "ws": "^8.18.0", @@ -34,7 +35,7 @@ "@electron-forge/plugin-auto-unpack-natives": "^7.8.0", "@electron-forge/plugin-fuses": "^7.8.0", "@electron/fuses": "^1.8.0", - "@eslint/js": "^9.10.0", + "@eslint/js": "^9.25.0", "@futo/forge-maker-wix-linux": "^7.5.0", "@types/electron-json-storage": "^4.5.4", "@types/jest": "^29.5.11", @@ -44,18 +45,18 @@ "@types/workerpool": "^6.1.1", "@types/ws": "^8.5.10", "@types/yargs": "^17.0.33", - "copy-webpack-plugin": "^12.0.2", - "electron": "^32.2.1", - "eslint": "^9.10.0", - "globals": "^15.9.0", + "copy-webpack-plugin": "^13.0.0", + "electron": "^35.2.0", + "eslint": "^9.25.0", + "globals": "^16.0.0", "jest": "^29.7.0", "mdns-js": "github:mdns-js/node-mdns-js", "ts-jest": "^29.1.1", "ts-loader": "^9.4.2", "typescript": "^5.5.4", "typescript-eslint": "^8.4.0", - "webpack": "^5.75.0", - "webpack-cli": "^5.0.1" + "webpack": "^5.99.6", + "webpack-cli": "^6.0.1" } }, "node_modules/@ampproject/remapping": { @@ -598,13 +599,13 @@ "optional": true }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=14.17.0" } }, "node_modules/@electron-forge/cli": { @@ -1480,9 +1481,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1530,9 +1531,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "version": "9.25.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.0.tgz", + "integrity": "sha512-iWhsUS8Wgxz9AXNfvfOPFSW4VfMXdVhp1hjkZVhXCrpgh/aLcc45rX6MPu+tIVUWDw0HfNwth7O28M1xDxNf9w==", "dev": true, "license": "MIT", "engines": { @@ -1563,19 +1564,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@futo/electron-wix-msi-linux": { "version": "5.1.3", "resolved": "https://gitlab.futo.org/api/v4/projects/305/packages/npm/@futo/electron-wix-msi-linux/-/@futo/electron-wix-msi-linux-5.1.3.tgz", @@ -2414,19 +2402,6 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@sindresorhus/merge-streams": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", - "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -3136,45 +3111,45 @@ } }, "node_modules/@webpack-cli/configtest": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", - "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, "node_modules/@webpack-cli/info": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", - "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, "node_modules/@webpack-cli/serve": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", - "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" }, "peerDependenciesMeta": { "webpack-dev-server": { @@ -4434,18 +4409,17 @@ "license": "MIT" }, "node_modules/copy-webpack-plugin": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", - "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.0.tgz", + "integrity": "sha512-FgR/h5a6hzJqATDGd9YG41SeDViH+0bkHn6WNXCi5zKAZkeESeSxLySSsFLHqLEVCh0E+rITmCf0dusXWYukeQ==", "dev": true, "license": "MIT", "dependencies": { - "fast-glob": "^3.3.2", "glob-parent": "^6.0.1", - "globby": "^14.0.0", "normalize-path": "^3.0.0", "schema-utils": "^4.2.0", - "serialize-javascript": "^6.0.2" + "serialize-javascript": "^6.0.2", + "tinyglobby": "^0.2.12" }, "engines": { "node": ">= 18.12.0" @@ -4886,15 +4860,15 @@ } }, "node_modules/electron": { - "version": "32.3.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-32.3.3.tgz", - "integrity": "sha512-7FT8tDg+MueAw8dBn5LJqDvlM4cZkKJhXfgB3w7P5gvSoUQVAY6LIQcXJxgL+vw2rIRY/b9ak7ZBFbCMF2Bk4w==", + "version": "35.2.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-35.2.0.tgz", + "integrity": "sha512-GHda7oCkN0pA23qzah735DEbRa06IPwlzP3uvjAmf9af8gxdj5i93JEHeQVGVmSVpd7sSb1pfecs9nz7B1q5ag==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", - "@types/node": "^20.9.0", + "@types/node": "^22.7.7", "extract-zip": "^2.0.1" }, "bin": { @@ -5403,16 +5377,6 @@ "global-agent": "^3.0.0" } }, - "node_modules/electron/node_modules/@types/node": { - "version": "20.17.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", - "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, "node_modules/electron/node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -5448,13 +5412,6 @@ "semver": "bin/semver.js" } }, - "node_modules/electron/node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, - "license": "MIT" - }, "node_modules/electron/node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -5641,20 +5598,20 @@ } }, "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "version": "9.25.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.0.tgz", + "integrity": "sha512-MsBdObhM4cEwkzCiraDv7A6txFXEqtNXOb877TsSp2FCkBNl8JfVQrmiuDqC1IkejT6JLPzYBXx/xAiYhyzgGA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.13.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.25.0", + "@eslint/plugin-kit": "^0.2.8", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -6592,9 +6549,9 @@ } }, "node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", + "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", "dev": true, "license": "MIT", "engines": { @@ -6622,37 +6579,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", - "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^2.1.0", - "fast-glob": "^3.3.3", - "ignore": "^7.0.3", - "path-type": "^6.0.0", - "slash": "^5.1.0", - "unicorn-magic": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby/node_modules/ignore": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz", - "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -9507,19 +9433,6 @@ "dev": true, "license": "MIT" }, - "node_modules/path-type": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", - "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pe-library": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-1.0.1.tgz", @@ -10794,19 +10707,6 @@ "dev": true, "license": "MIT" }, - "node_modules/slash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/slice-ansi": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", @@ -11231,6 +11131,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/systeminformation": { + "version": "5.25.11", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.25.11.tgz", + "integrity": "sha512-jI01fn/t47rrLTQB0FTlMCC+5dYx8o0RRF+R4BPiUNsvg5OdY0s9DKMFmJGrx5SwMZQ4cag0Gl6v8oycso9b/g==", + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -11404,6 +11330,51 @@ "license": "MIT", "optional": true }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -11718,19 +11689,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/unicorn-magic": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", - "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/unique-filename": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", @@ -11940,9 +11898,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.99.5", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.5.tgz", - "integrity": "sha512-q+vHBa6H9qwBLUlHL4Y7L0L1/LlyBKZtS9FHNCQmtayxjI5RKC9yD8gpvLeqGv5lCQp1Re04yi0MF40pf30Pvg==", + "version": "5.99.6", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz", + "integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11987,43 +11945,40 @@ } }, "node_modules/webpack-cli": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", - "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, "license": "MIT", "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", "colorette": "^2.0.14", - "commander": "^10.0.1", + "commander": "^12.1.0", "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", + "envinfo": "^7.14.0", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^3.1.1", "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" + "webpack-merge": "^6.0.1" }, "bin": { "webpack-cli": "bin/cli.js" }, "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "5.x.x" + "webpack": "^5.82.0" }, "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, "webpack-bundle-analyzer": { "optional": true }, @@ -12033,28 +11988,28 @@ } }, "node_modules/webpack-cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "license": "MIT", "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", - "wildcard": "^2.0.0" + "wildcard": "^2.0.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0" } }, "node_modules/webpack-sources": { diff --git a/receivers/electron/package.json b/receivers/electron/package.json index dd9af22..26faa20 100644 --- a/receivers/electron/package.json +++ b/receivers/electron/package.json @@ -22,7 +22,7 @@ "@electron-forge/plugin-auto-unpack-natives": "^7.8.0", "@electron-forge/plugin-fuses": "^7.8.0", "@electron/fuses": "^1.8.0", - "@eslint/js": "^9.10.0", + "@eslint/js": "^9.25.0", "@futo/forge-maker-wix-linux": "^7.5.0", "@types/electron-json-storage": "^4.5.4", "@types/jest": "^29.5.11", @@ -32,18 +32,18 @@ "@types/workerpool": "^6.1.1", "@types/ws": "^8.5.10", "@types/yargs": "^17.0.33", - "copy-webpack-plugin": "^12.0.2", - "electron": "^32.2.1", - "eslint": "^9.10.0", - "globals": "^15.9.0", + "copy-webpack-plugin": "^13.0.0", + "electron": "^35.2.0", + "eslint": "^9.25.0", + "globals": "^16.0.0", "jest": "^29.7.0", "mdns-js": "github:mdns-js/node-mdns-js", "ts-jest": "^29.1.1", "ts-loader": "^9.4.2", "typescript": "^5.5.4", "typescript-eslint": "^8.4.0", - "webpack": "^5.75.0", - "webpack-cli": "^5.0.1" + "webpack": "^5.99.6", + "webpack-cli": "^6.0.1" }, "dependencies": { "@vscode/sudo-prompt": "^9.3.1", @@ -56,6 +56,7 @@ "https": "^1.0.0", "log4js": "^6.9.1", "qrcode": "^1.5.3", + "systeminformation": "^5.25.11", "url": "^0.11.4", "uuid": "^11.0.3", "ws": "^8.18.0", diff --git a/receivers/electron/src/Main.ts b/receivers/electron/src/Main.ts index 2849347..59046f8 100644 --- a/receivers/electron/src/Main.ts +++ b/receivers/electron/src/Main.ts @@ -206,30 +206,30 @@ export class Main { ipcMain.on('send-volume-update', (event: IpcMainEvent, value: VolumeUpdateMessage) => { l.send(Opcode.VolumeUpdate, value); }); + }); - ipcMain.on('send-download-request', async () => { - if (!Updater.isDownloading) { - try { - await Updater.downloadUpdate(); - Main.mainWindow.webContents.send("download-complete"); - } catch (err) { - await dialog.showMessageBox({ - type: 'error', - title: 'Failed to download update', - message: err, - buttons: ['OK'], - defaultId: 0 - }); + ipcMain.on('send-download-request', async () => { + if (!Updater.isDownloading) { + try { + await Updater.downloadUpdate(); + Main.mainWindow.webContents.send("download-complete"); + } catch (err) { + await dialog.showMessageBox({ + type: 'error', + title: 'Failed to download update', + message: err, + buttons: ['OK'], + defaultId: 0 + }); - Main.logger.error('Failed to download update:', err); - Main.mainWindow.webContents.send("download-failed"); - } + Main.logger.error('Failed to download update:', err); + Main.mainWindow.webContents.send("download-failed"); } - }); + } + }); - ipcMain.on('send-restart-request', async () => { - Updater.restart(); - }); + ipcMain.on('send-restart-request', async () => { + Updater.restart(); }); ipcMain.handle('updater-progress', async () => { return Updater.updateProgress; }); @@ -302,16 +302,27 @@ export class Main { } }); + let networkStateChangeListener = null; Main.mainWindow.loadFile(path.join(__dirname, 'main/index.html')); Main.mainWindow.on('closed', () => { Main.mainWindow = null; + clearInterval(networkStateChangeListener); }); Main.mainWindow.maximize(); Main.mainWindow.show(); Main.mainWindow.on('ready-to-show', () => { - Main.mainWindow.webContents.send("device-info", {name: os.hostname(), addresses: NetworkService.getAllIPv4Addresses()}); + NetworkService.networkStateChangeListener(true, (interfaces: any) => { + Main.mainWindow.webContents.send("device-info", { name: os.hostname(), interfaces: interfaces }); + + networkStateChangeListener = setInterval(() => { + NetworkService.networkStateChangeListener(false, (interfaces: any) => { + Main.mainWindow.webContents.send("device-info", { name: os.hostname(), interfaces: interfaces }); + }); + }, + NetworkService.networkStateChangeListenerTimeout); + }); }); } diff --git a/receivers/electron/src/main/index.html b/receivers/electron/src/main/index.html index a8a6f2b..b318f28 100644 --- a/receivers/electron/src/main/index.html +++ b/receivers/electron/src/main/index.html @@ -44,16 +44,30 @@
-
Manual connection information
+
Connection Information
-
-
IPs

-
Port
46899 (TCP), 46898 (WS)
+ +
+
Scan with a FCast sender app
+ +
Need a sender app?
Download Grayjay at https://grayjay.app
+ +
+
Connection Details
+
+
+
+
+
+
+

+
Port
46899 (TCP), 46898 (WS)
+
+
+
+
+
Device not connected to a network
-
Automatic discovery is available via mDNS
- -
Scan with a FCast sender app.
-
Need a sender app?
Download Grayjay at https://grayjay.app