From 3fbfb3c869e3d88896cc0b99d15efe1ffd6b5920 Mon Sep 17 00:00:00 2001 From: jelveh Date: Sat, 18 Jul 2026 15:09:41 -0700 Subject: [PATCH] fix: page through all installed apps in the Dashboard instead of capping at 100 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The /installedApps endpoint clamps limit to 100 and paginates, but the Apps tab fetched a single page — a user with more than 100 installed apps silently lost the alphabetically-last ones from both the grid and search, with no way to launch or uninstall them from the dashboard. Loop pages until a short one comes back (the common <100-app case still makes a single request). Verified: the request now carries &page=1 and stops after one page for a small account; the loop pulls all pages otherwise. --- src/gui/src/UI/Dashboard/TabApps.js | 37 +++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/gui/src/UI/Dashboard/TabApps.js b/src/gui/src/UI/Dashboard/TabApps.js index 3146a8cd8..c000766fd 100644 --- a/src/gui/src/UI/Dashboard/TabApps.js +++ b/src/gui/src/UI/Dashboard/TabApps.js @@ -917,15 +917,33 @@ const TabApps = { const $container = $el_window.find('.myapps-container'); try { - // Fetch the two app lists and the saved order together. - const [installedRes, launchRes, savedOrderRaw] = await Promise.all([ - fetch( - `${window.api_origin}/installedApps?orderBy=name&limit=100`, - { - headers: { 'Authorization': `Bearer ${puter.authToken}` }, - method: 'GET', - }, - ), + // Fetch the two app lists and the saved order together. The + // installedApps endpoint caps `limit` at 100 and paginates, so page + // through it — otherwise a user with >100 apps silently loses the + // rest from the grid and from search. Common case is a single page + // (a short page ends the loop before a second request). + const fetchAllInstalledApps = async () => { + const PAGE_SIZE = 100; + const MAX_PAGES = 50; // 5000 apps — a runaway backstop + const all = []; + for ( let page = 1; page <= MAX_PAGES; page++ ) { + const res = await fetch( + `${window.api_origin}/installedApps?orderBy=name&limit=${PAGE_SIZE}&page=${page}`, + { + headers: { 'Authorization': `Bearer ${puter.authToken}` }, + method: 'GET', + }, + ); + const batch = await res.json(); + if ( ! Array.isArray(batch) || batch.length === 0 ) break; + all.push(...batch); + if ( batch.length < PAGE_SIZE ) break; + } + return all; + }; + + const [installedApps, launchRes, savedOrderRaw] = await Promise.all([ + fetchAllInstalledApps(), fetch( `${window.api_origin}/get-launch-apps?icon_size=128`, { @@ -936,7 +954,6 @@ const TabApps = { puter.kv.get(APPS_ORDER_KV_KEY).catch(() => null), ]); - const installedApps = await installedRes.json(); const launchData = await launchRes.json(); // Normalize launch apps (recommended + recent) to same shape