diff --git a/wled00/data/index.css b/wled00/data/index.css index c92c884abb..75ea796902 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -794,7 +794,7 @@ input[type=range]::-moz-range-thumb { /* buttons */ .btn { padding: 8px; - /*margin: 10px 4px;*/ + margin: 10px 4px; width: 230px; font-size: 19px; color: var(--c-d); diff --git a/wled00/data/index.js b/wled00/data/index.js index 2d49a26400..fe011dce37 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -693,6 +693,8 @@ function parseInfo(i) { // gId("filterVol").classList.add("hide"); hideModes(" ♪"); // hide volume reactive effects // gId("filterFreq").classList.add("hide"); hideModes(" ♫"); // hide frequency reactive effects // } + // Check for version upgrades on page load + checkVersionUpgrade(i); } //https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml @@ -3304,6 +3306,195 @@ function simplifyUI() { gId("btns").style.display = "none"; } +// Version reporting feature +var versionCheckDone = false; + +function checkVersionUpgrade(info) { + // Only check once per page load + if (versionCheckDone) return; + versionCheckDone = true; + + // Suppress feature if in AP mode (no internet connection available) + if (info.wifi && info.wifi.ap) return; + + // Fetch version-info.json using existing /edit endpoint + fetch(getURL('/edit?func=edit&path=/version-info.json'), { + method: 'get' + }) + .then(res => { + if (res.status === 404) { + // File doesn't exist - first install, show install prompt + showVersionUpgradePrompt(info, null, info.ver); + return null; + } + if (!res.ok) { + throw new Error('Failed to fetch version-info.json'); + } + return res.json(); + }) + .then(versionInfo => { + if (!versionInfo) return; // 404 case already handled + + // Check if user opted out + if (versionInfo.neverAsk) return; + + // Check if version has changed + const currentVersion = info.ver; + const storedVersion = versionInfo.version || ''; + + if (storedVersion && storedVersion !== currentVersion) { + // Version has changed, show upgrade prompt + showVersionUpgradePrompt(info, storedVersion, currentVersion); + } else if (!storedVersion) { + // Empty version in file, show install prompt + showVersionUpgradePrompt(info, null, currentVersion); + } + }) + .catch(e => { + console.log('Failed to load version-info.json', e); + // On error, save current version for next time + if (info && info.ver) { + updateVersionInfo(info.ver, false); + } + }); +} + +function showVersionUpgradePrompt(info, oldVersion, newVersion) { + // Determine if this is an install or upgrade + const isInstall = !oldVersion; + + // Create overlay and dialog + const overlay = d.createElement('div'); + overlay.id = 'versionUpgradeOverlay'; + overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:10000;display:flex;align-items:center;justify-content:center;'; + + const dialog = d.createElement('div'); + dialog.style.cssText = 'background:var(--c-1);border-radius:10px;padding:25px;max-width:500px;margin:20px;box-shadow:0 4px 6px rgba(0,0,0,0.3);'; + + // Build contextual message based on install vs upgrade + const title = isInstall + ? '🎉 Thank you for installing WLED!' + : '🎉 WLED Upgrade Detected!'; + + const description = isInstall + ? `You are now running WLED ${newVersion}.` + : `Your WLED has been upgraded from ${oldVersion} to ${newVersion}.`; + + const question = 'Help make WLED better with a one-time hardware report? It includes only device details like chip type, LED count, etc. — never personal data or your activities.' + + dialog.innerHTML = ` +

${title}

+

${description}

+

${question}

+

+ Learn more about what data is collected and why +

+
+ + + +
+ `; + + overlay.appendChild(dialog); + d.body.appendChild(overlay); + + // Add event listeners + gId('versionReportYes').addEventListener('click', () => { + reportUpgradeEvent(info, oldVersion); + d.body.removeChild(overlay); + }); + + gId('versionReportNo').addEventListener('click', () => { + // Don't update version, will ask again on next load + d.body.removeChild(overlay); + }); + + gId('versionReportNever').addEventListener('click', () => { + updateVersionInfo(newVersion, true); + d.body.removeChild(overlay); + showToast('You will not be asked again.'); + }); +} + +function reportUpgradeEvent(info, oldVersion) { + showToast('Reporting upgrade...'); + + // Fetch fresh data from /json/info endpoint as requested + fetch(getURL('/json/info'), { + method: 'get' + }) + .then(res => res.json()) + .then(infoData => { + // Map to UpgradeEventRequest structure per OpenAPI spec + // Required fields: deviceId, version, previousVersion, releaseName, chip, ledCount, isMatrix, bootloaderSHA256 + const upgradeData = { + deviceId: infoData.deviceId, // Use anonymous unique device ID + version: infoData.ver || '', // Current version string + previousVersion: oldVersion || '', // Previous version from version-info.json + releaseName: infoData.release || '', // Release name (e.g., "WLED 0.15.0") + chip: infoData.arch || '', // Chip architecture (esp32, esp8266, etc) + ledCount: infoData.leds ? infoData.leds.count : 0, // Number of LEDs + isMatrix: !!(infoData.leds && infoData.leds.matrix), // Whether it's a 2D matrix setup + bootloaderSHA256: infoData.bootloaderSHA256 || '', // Bootloader SHA256 hash + brand: infoData.brand, // Device brand (always present) + product: infoData.product, // Product name (always present) + flashSize: infoData.flash // Flash size (always present) + }; + + // Add optional fields if available + if (infoData.psram !== undefined) upgradeData.psramSize = infoData.psram; + // Note: partitionSizes not currently available in /json/info endpoint + + // Make AJAX call to postUpgradeEvent API + return fetch('https://usage.wled.me/api/usage/upgrade', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(upgradeData) + }); + }) + .then(res => { + if (res.ok) { + showToast('Thank you for reporting!'); + updateVersionInfo(info.ver, false); + } else { + showToast('Report failed. Please try again later.', true); + // Do NOT update version info on failure - user will be prompted again + } + }) + .catch(e => { + console.log('Failed to report upgrade', e); + showToast('Report failed. Please try again later.', true); + // Do NOT update version info on error - user will be prompted again + }); +} + +function updateVersionInfo(version, neverAsk) { + const versionInfo = { + version: version, + neverAsk: neverAsk + }; + + // Create a Blob with JSON content and use /upload endpoint + const blob = new Blob([JSON.stringify(versionInfo)], {type: 'application/json'}); + const formData = new FormData(); + formData.append('data', blob, 'version-info.json'); + + fetch(getURL('/upload'), { + method: 'POST', + body: formData + }) + .then(res => res.text()) + .then(data => { + console.log('Version info updated', data); + }) + .catch(e => { + console.log('Failed to update version-info.json', e); + }); +} + size(); _C.style.setProperty('--n', N);