From 1bb5b09d75609b7580b595d8eaf114ae2beb403a Mon Sep 17 00:00:00 2001 From: jelveh Date: Sat, 18 Jul 2026 14:46:51 -0700 Subject: [PATCH] fix: only offer Uninstall for Dashboard apps where it actually sticks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uninstall was suppressed via a hardcoded 8-name allowlist that had drifted out of sync with the backend's ~26 recommended apps. For the ~18 unlisted recommended apps (Calculator, Code, the games, …) the menu offered Uninstall, revokeApp resolved, the tile vanished — then get-launch-apps re-added it on the next load and it reappeared, so the uninstall silently reverted. Compute uninstallability from the actual lists: an app is uninstallable only if it's in the user's installedApps AND not in the recommended list (and not a protected core app). Recommended apps — installed or not — resurrect on reload, so Uninstall is hidden for them. Verified live: a synthetic installed-not-recommended app shows Uninstall; a recommended app shows none. --- src/gui/src/UI/Dashboard/TabApps.js | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/gui/src/UI/Dashboard/TabApps.js b/src/gui/src/UI/Dashboard/TabApps.js index 2f5aba676..4f028a55b 100644 --- a/src/gui/src/UI/Dashboard/TabApps.js +++ b/src/gui/src/UI/Dashboard/TabApps.js @@ -73,7 +73,7 @@ function buildTileHtml (app) { const { title, targetLink } = resolveTileDisplay(app); const iconUrl = app.iconUrl || window.icons['app.svg']; - let h = `
`; + let h = `
`; h += '
'; h += ``; h += '
'; @@ -292,10 +292,10 @@ const TabApps = { const appName = $(this).attr('data-app-name'); const appTitle = $(this).attr('data-app-title'); const appUid = $(this).attr('data-app-uid'); - const nameLower = (appName || '').toLowerCase(); - const noUninstall = APP_NAMES_NO_UNINSTALL.has(nameLower); + // Only offer Uninstall when it will actually stick (see loadApps). + const canUninstall = $(this).attr('data-app-uninstallable') === '1'; - const items = noUninstall + const items = ! canUninstall ? [] : [ { @@ -943,6 +943,22 @@ const TabApps = { iconUrl: app.iconUrl || app.icon || null, })); + // Uninstall only sticks for apps the user actually installed that + // are NOT in the hardcoded recommended list — revokeApp on a + // recommended app does nothing lasting because get-launch-apps + // re-adds it on the next load, so the tile "comes back" and the + // uninstall looks broken. Compute installability against both lists. + const recommendedNames = new Set( + (launchData.recommended || []).map(a => a.name), + ); + const installedNames = new Set( + (Array.isArray(installedApps) ? installedApps : []).map(a => a.name), + ); + const isUninstallable = name => + installedNames.has(name) + && ! recommendedNames.has(name) + && ! APP_NAMES_NO_UNINSTALL.has((name || '').toLowerCase()); + // Build seen set from launch apps const seen = new Set(); const merged = []; @@ -950,14 +966,14 @@ const TabApps = { for ( const app of launchApps ) { if ( seen.has(app.name) ) continue; seen.add(app.name); - merged.push(app); + merged.push({ ...app, uninstallable: isUninstallable(app.name) }); } // Append installed apps that aren't already in the list for ( const app of installedApps ) { if ( seen.has(app.name) ) continue; seen.add(app.name); - merged.push(app); + merged.push({ ...app, uninstallable: isUninstallable(app.name) }); } // Overlay the user's saved ordering (if any). New apps are appended