Merge pull request #5172 from wled/copilot/update-install-prompt-messaging

Simplify upgrade reporting prompt with checkbox-based preference
This commit is contained in:
Will Tatam
2026-03-08 20:06:05 +00:00
committed by Frank
parent 9e3b2131fa
commit d90549f07a

View File

@@ -3708,10 +3708,8 @@ function checkVersionUpgrade(info) {
if (versionCheckDone) return; if (versionCheckDone) return;
versionCheckDone = true; versionCheckDone = true;
// Skip version check in AP mode (no internet connectivity) // Suppress feature if in AP mode (no internet connection available)
if (info.wifi && info.wifi.ap) { if (info.wifi && info.wifi.ap) return;
return;
}
// Fetch version-info.json using existing /edit endpoint // Fetch version-info.json using existing /edit endpoint
fetch('/edit?edit=/version-info.json', { fetch('/edit?edit=/version-info.json', {
@@ -3739,8 +3737,14 @@ function checkVersionUpgrade(info) {
const storedVersion = versionInfo.version || ''; const storedVersion = versionInfo.version || '';
if (storedVersion && storedVersion !== currentVersion) { if (storedVersion && storedVersion !== currentVersion) {
// Version has changed, show upgrade prompt // Version has changed
showVersionUpgradePrompt(info, storedVersion, currentVersion); if (versionInfo.alwaysReport) {
// Automatically report if user opted in for always reporting
reportUpgradeEvent(info, storedVersion, true);
} else {
// Show upgrade prompt
showVersionUpgradePrompt(info, storedVersion, currentVersion);
}
} else if (!storedVersion) { } else if (!storedVersion) {
// Empty version in file, show install prompt // Empty version in file, show install prompt
showVersionUpgradePrompt(info, null, currentVersion); showVersionUpgradePrompt(info, null, currentVersion);
@@ -3748,76 +3752,92 @@ function checkVersionUpgrade(info) {
}) })
.catch(e => { .catch(e => {
console.log('Failed to load version-info.json', 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, false);
}
}); });
} }
function showVersionUpgradePrompt(info, oldVersion, newVersion) { function showVersionUpgradePrompt(info, oldVersion, newVersion) {
// Determine if this is an install or upgrade // Determine if this is an install or upgrade
const isInstall = !oldVersion; const isInstall = !oldVersion;
// Create overlay and dialog // Create overlay and dialog
const overlay = d.createElement('div'); const overlay = d.createElement('div');
overlay.id = 'versionUpgradeOverlay'; 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;'; 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'); 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);'; 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 // Build contextual message based on install vs upgrade
const title = isInstall const title = isInstall
? '🎉 Thank you for installing WLED-MM!' ? '🎉 Thank you for installing WLED-MM!'
: '🎉 WLED-MM Upgrade Detected!'; : '🎉 WLED-MM Upgrade Detected!';
const description = isInstall const description = isInstall
? `You are now running WLED-MM <strong>${newVersion}</strong>.` ? `You are now running WLED-MM <strong style="text-wrap: nowrap">${newVersion}</strong>.`
: `Your WLED-MM has been upgraded from <strong>${oldVersion}</strong> to <strong>${newVersion}</strong>.`; : `Your WLED-MM has been upgraded from <strong style="text-wrap: nowrap">${oldVersion}</strong> to <strong style="text-wrap: nowrap">${newVersion}</strong>.`;
const question = 'Would you like to help the WLED development team by reporting your installation? This helps us understand what hardware and versions are being used.' const question = 'Help make WLED better by sharing hardware details like chip type and LED count? This helps us understand how WLED is used and prioritize features — we never collect personal data or your activities.'
dialog.innerHTML = ` dialog.innerHTML = `
<h2 style="margin-top:0;color:var(--c-f);">${title}</h2> <h2 style="margin-top:0;color:var(--c-f);">${title}</h2>
<p style="color:var(--c-f);">${description}</p> <p style="color:var(--c-f);">${description}</p>
<p style="color:var(--c-f);">${question}</p> <p style="color:var(--c-f);">${question}</p>
<div style="margin-top:20px;"> <p style="color:var(--c-f);font-size:0.9em;">
<button id="versionReportYes" class="btn">Yes</button> <a href="https://kno.wled.ge/about/privacy-policy/" target="_blank" style="color:var(--c-6);">Learn more about what data is collected and why</a>
<button id="versionReportNo" class="btn">Not Now</button> </p>
<button id="versionReportNever" class="btn">Never Ask</button> <div style="margin-top:15px;margin-bottom:15px;">
<label style="display:flex;align-items:center;gap:8px;color:var(--c-f);cursor:pointer;">
<input type="checkbox" id="versionSaveChoice" style="cursor:pointer;">
<span>Save my choice for future updates</span>
</label>
</div>
<div style="margin-top:20px;display:flex;flex-wrap:wrap;gap:8px;">
<button id="versionReportYes" class="btn">Report update</button>
<button id="versionReportNo" class="btn">Skip reporting</button>
</div> </div>
`; `;
overlay.appendChild(dialog); overlay.appendChild(dialog);
d.body.appendChild(overlay); d.body.appendChild(overlay);
// Add event listeners // Add event listeners
gId('versionReportYes').addEventListener('click', () => { gId('versionReportYes').addEventListener('click', () => {
reportUpgradeEvent(oldVersion, newVersion); const saveChoice = gId('versionSaveChoice').checked;
d.body.removeChild(overlay); d.body.removeChild(overlay);
// Pass saveChoice as alwaysReport parameter
reportUpgradeEvent(info, oldVersion, saveChoice);
}); });
gId('versionReportNo').addEventListener('click', () => { gId('versionReportNo').addEventListener('click', () => {
// Don't update version, will ask again on next load const saveChoice = gId('versionSaveChoice').checked;
d.body.removeChild(overlay); d.body.removeChild(overlay);
}); if (saveChoice) {
// Save "never ask" preference
gId('versionReportNever').addEventListener('click', () => { updateVersionInfo(newVersion, true, false);
updateVersionInfo(newVersion, true); showToast('You will not be asked again.');
d.body.removeChild(overlay); } else {
showToast('You will not be asked again.'); // Save current version to prevent re-prompting until version changes
updateVersionInfo(newVersion, false, false);
}
}); });
} }
function reportUpgradeEvent(oldVersion, newVersion) { function reportUpgradeEvent(info, oldVersion, alwaysReport) {
showToast('Reporting upgrade...'); showToast('Reporting upgrade...');
// Fetch fresh data from /json/info endpoint as requested // Fetch fresh data from /json/info endpoint as requested
fetch('/json/info', { fetch('/json/info', {
method: 'get' method: 'get'
}) })
.then(res => res.json()) .then(res => res.json())
.then(infoData => { .then(infoData => {
// Map to UpgradeEventRequest structure per OpenAPI spec // Map to UpgradeEventRequest structure per OpenAPI spec
// Required fields: deviceId, version, previousVersion, releaseName, chip, ledCount, isMatrix, bootloaderSHA256 // Required fields: deviceId, version, previousVersion, releaseName, chip, ledCount, isMatrix, bootloaderSHA256
const upgradeData = { const upgradeData = {
deviceId: infoData.deviceId, // Use anonymous unique device ID deviceId: infoData.deviceId, // Use anonymous unique device ID
version: infoData.ver || '', // Current version string version: infoData.ver || '', // Current version string
previousVersion: oldVersion || '', // Previous version from version-info.json previousVersion: oldVersion || '', // Previous version from version-info.json
@@ -3825,64 +3845,70 @@ function reportUpgradeEvent(oldVersion, newVersion) {
chip: infoData.arch || '', // Chip architecture (esp32, esp8266, etc) chip: infoData.arch || '', // Chip architecture (esp32, esp8266, etc)
ledCount: infoData.leds ? infoData.leds.count : 0, // Number of LEDs ledCount: infoData.leds ? infoData.leds.count : 0, // Number of LEDs
isMatrix: !!(infoData.leds && infoData.leds.matrix), // Whether it's a 2D matrix setup isMatrix: !!(infoData.leds && infoData.leds.matrix), // Whether it's a 2D matrix setup
bootloaderSHA256: infoData.bootloaderSHA256 || '', // Bootloader SHA256 hash - not yet availeable in WLEDMM bootloaderSHA256: infoData.bootloaderSHA256 || '', // Bootloader SHA256 hash
brand: infoData.brand, // Device brand (always present) brand: infoData.brand, // Device brand (always present)
product: infoData.product, // Product name (always present) product: infoData.product, // Product name (always present)
flashSize: infoData.flash, // Flash size (always present) flashSize: infoData.flash, // Flash size (always present)
repo: infoData.repo // GitHub repository (always present) repo: infoData.repo // GitHub repository (always present)
}; };
// Add optional fields if available
if (infoData.tpsram !== undefined) upgradeData.psramSize = Math.round(infoData.tpsram / (1024 * 1024)); // convert bytes to MB - tpsram is MM specific
// Note: partitionSizes not currently available in /json/info endpoint
// it is availeable in WLEDMM => infoData.t = total FS size in bytes
// Make AJAX call to postUpgradeEvent API // Add optional fields if available
return fetch('https://usage.wled.me/api/usage/upgrade', { if (infoData.psrSz !== undefined) upgradeData.psramSize = infoData.psrSz; // Total PSRAM size in MB; can be 0
method: 'POST',
headers: { // Note: partitionSizes not currently available in /json/info endpoint
'Content-Type': 'application/json'
}, // Make AJAX call to postUpgradeEvent API
body: JSON.stringify(upgradeData) return fetch('https://usage.wled.me/api/usage/upgrade', {
}); method: 'POST',
}) headers: {
.then(res => { 'Content-Type': 'application/json'
if (res.ok) { },
showToast('Thank you for reporting!'); body: JSON.stringify(upgradeData)
updateVersionInfo(newVersion, false); });
} else { })
.then(res => {
if (res.ok) {
if (alwaysReport) {
showToast('Thank you! Future upgrades will be reported automatically.');
} else {
showToast('Thank you for reporting!');
}
updateVersionInfo(info.ver, false, !!alwaysReport);
} 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); showToast('Report failed. Please try again later.', true);
// Do NOT update version info on failure - user will be prompted again // Do NOT update version info on error - 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) { function updateVersionInfo(version, neverAsk, alwaysReport) {
const versionInfo = { const versionInfo = {
version: version, version: version,
neverAsk: neverAsk neverAsk: neverAsk,
alwaysReport: !!alwaysReport
}; };
// Create a Blob with JSON content and use /upload endpoint // Create a Blob with JSON content and use /upload endpoint
const blob = new Blob([JSON.stringify(versionInfo)], { type: 'application/json' }); const blob = new Blob([JSON.stringify(versionInfo)], {type: 'application/json'});
const formData = new FormData(); const formData = new FormData();
formData.append('data', blob, 'version-info.json'); formData.append('data', blob, 'version-info.json');
fetch('/upload', { fetch('/upload', {
method: 'POST', method: 'POST',
body: formData body: formData
}) })
.then(res => res.text()) .then(res => res.text())
.then(data => { .then(data => {
console.log('Version info updated', data); console.log('Version info updated', data);
}) })
.catch(e => { .catch(e => {
console.log('Failed to update version-info.json', e); console.log('Failed to update version-info.json', e);
}); });
} }
size(); size();
@@ -3896,4 +3922,4 @@ _C.addEventListener('touchstart', lock, false);
_C.addEventListener('mouseout', move, false); _C.addEventListener('mouseout', move, false);
_C.addEventListener('mouseup', move, false); _C.addEventListener('mouseup', move, false);
_C.addEventListener('touchend', move, false); _C.addEventListener('touchend', move, false);