diff --git a/CHANGELOG.md b/CHANGELOG.md
index 46bd1b0..6749c44 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,19 @@
+# v0.8.01-beta — "Nog 106 tonen" onder een leeg scherm
+
+De app store toonde geen enkele kaart, met daaronder een knop die beloofde dat
+er nog 106 apps wachtten. Twee bronnen die uit elkaar konden lopen: het raster
+liep over de **repolijst**, de teller over de **opgehaalde apps**. Waren die
+twee het oneens, dan stond er een getal onder een leeg scherm — zonder dat er
+ergens een foutmelding te zien was.
+
+- Teller en raster komen nu uit dezelfde lijst. De knop kan niets meer beloven
+ wat er niet staat.
+- Een lege store zegt nu wát er aan de hand is: geen repository toegevoegd, of
+ geen apps die aan het filter voldoen. Eerder bleef het scherm gewoon leeg.
+- `GET /api/repos` gaf een 500 op een repo zonder `id`. Eén rare regel in de
+ configuratie maakte daarmee de hele store leeg; zulke regels worden nu
+ overgeslagen.
+
# v0.8.00-beta — Een kapotte cache herstelt zichzelf
`fatal: shallow file has changed since we read it` liet een repo achter die zich
diff --git a/VERSION b/VERSION
index 752fcfd..56d8572 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.8.00-beta
+0.8.01-beta
diff --git a/server-up/app.py b/server-up/app.py
index 8b40b95..65529c9 100644
--- a/server-up/app.py
+++ b/server-up/app.py
@@ -1228,7 +1228,14 @@ _BAD_URL_MSG = ("Ongeldige repo-URL. Toegestaan zijn http(s)://, ssh:// en "
def api_repos():
# Nooit het token zelf teruggeven — alleen of er één ingesteld is.
repos = cfg.redact(cfg.load()).get("APP_REPOS", [])
- result = [{**r, "cloned": git.cache_dir(r["id"]).exists()} for r in repos]
+ # Een repo zonder id gaf hier een KeyError, dus een 500, dus een lege
+ # repolijst in de interface — en daarmee een app store zonder enkele kaart.
+ # Eén rare regel in de configuratie hoort de rest niet mee te nemen.
+ result = []
+ for r in repos:
+ if not isinstance(r, dict) or not r.get("id"):
+ continue
+ result.append({**r, "cloned": git.cache_dir(r["id"]).exists()})
return jsonify(result)
diff --git a/server-up/templates/index.html b/server-up/templates/index.html
index b630170..23c7cdc 100644
--- a/server-up/templates/index.html
+++ b/server-up/templates/index.html
@@ -683,6 +683,15 @@ tailwind.config = {
+
+
+
Nog geen repository toegevoegd
+
+
+
@@ -2927,7 +2936,7 @@ function app() {
},
// Hoeveel er nog wachten achter de knop, over alle repo's heen.
get storeVerborgen() {
- return Math.max(0, this.storeResultaat - Object.keys(this.storeStacks || {})
+ return Math.max(0, this.storeResultaat - this.storeRepoIds
.reduce((n, id) => n + this.zichtbareStore(id).length, 0));
},
// Alle labels uit alle repo's met hun aantal, op frequentie gesorteerd
@@ -2967,9 +2976,15 @@ function app() {
return (this.storeCat ? 1 : 0) + (this.storeTag ? 1 : 0);
},
// Hoeveel apps er over blijven met de huidige keuze, over alle repo's heen.
+ // Tellen over dezelfde lijst die ook de kaarten oplevert. Eerder telde dit
+ // over `storeStacks` terwijl het raster over `repos` liep: liepen die twee
+ // uit elkaar, dan beloofde de knop "Nog 106 tonen" apps die nergens op het
+ // scherm stonden, zonder dat er ergens iets van een fout te zien was.
+ get storeRepoIds() {
+ return (this.repos || []).map(r => r.id).filter(Boolean);
+ },
get storeResultaat() {
- return Object.keys(this.storeStacks || {})
- .reduce((n, id) => n + this.filteredStore(id).length, 0);
+ return this.storeRepoIds.reduce((n, id) => n + this.filteredStore(id).length, 0);
},
// Wat er op een telefoon onder de meer-knop komt te staan. Met namen
// erbij: twaalf icoontjes zonder tekst zeggen niets.
diff --git a/server-up/translations/en.json b/server-up/translations/en.json
index 2006eab..0dd93b3 100644
--- a/server-up/translations/en.json
+++ b/server-up/translations/en.json
@@ -49,7 +49,7 @@
"docker": "Docker",
"no_stacks": "No apps found",
"no_images": "No images found",
- "no_repos": "No repositories",
+ "no_repos": "No repository added yet — add one to see apps.",
"add_repo": "Add repository",
"repo_name": "Name",
"repo_url": "Git URL",
@@ -465,5 +465,6 @@
"move_data_confirm": "Move {n} folder(s)? Apps are stopped, data is copied and verified, and only then is the old cleared out.",
"manual_started": "Started manually",
"manual_hint": "These apps run on this host but were not created by Server Up — started manually, or from before you began using Server Up. You can start, stop and view their logs here.",
- "containers_n": "{n} containers"
+ "containers_n": "{n} containers",
+ "no_apps_found": "No apps found"
}
diff --git a/server-up/translations/nl.json b/server-up/translations/nl.json
index 616f678..5c707d2 100644
--- a/server-up/translations/nl.json
+++ b/server-up/translations/nl.json
@@ -49,7 +49,7 @@
"docker": "Docker",
"no_stacks": "Geen apps gevonden",
"no_images": "Geen images gevonden",
- "no_repos": "Geen repositories",
+ "no_repos": "Nog geen repository toegevoegd — voeg er een toe om apps te zien.",
"add_repo": "Repository toevoegen",
"repo_name": "Naam",
"repo_url": "Git URL",
@@ -465,5 +465,6 @@
"move_data_confirm": "{n} map(pen) verplaatsen? De apps worden gestopt, de data wordt gekopieerd en gecontroleerd, en pas daarna wordt het oude opgeruimd.",
"manual_started": "Handmatig gestart",
"manual_hint": "Deze apps draaien op deze host maar zijn niet door Server Up aangemaakt — handmatig gestart, of van vóór je Server Up ging gebruiken. Je kunt ze hier starten, stoppen en hun logs bekijken.",
- "containers_n": "{n} containers"
+ "containers_n": "{n} containers",
+ "no_apps_found": "Geen apps gevonden"
}
diff --git a/tests/js/menu.test.js b/tests/js/menu.test.js
index d3a3727..8eac5d1 100644
--- a/tests/js/menu.test.js
+++ b/tests/js/menu.test.js
@@ -163,6 +163,8 @@ check('doorklikken opent de stackspagina', c.page === 'stacks');
// ── Filtermenu van de app store ─────────────────────────────────────────────
// De labelrij was afgekapt op veertien, waardoor je de rest niet kon kiezen. Het
// menu zoekt nu in de volledige lijst.
+// De teller volgt de repolijst, want die bepaalt ook welke kaarten er komen.
+c.repos = [{id:'r', name:'Proefrepo'}];
c.storeStacks = { r: [
{dir:'sonarr', name:'Sonarr', categories:['media'], tags:['media','series']},
{dir:'radarr', name:'Radarr', categories:['media'], tags:['media','films']},
@@ -395,6 +397,33 @@ check('balk staat halverwege', c.job.progress.pct === 50);
await c.verplaatsPaden();
check('afbreken bij de bevestiging start niets', verstuurd === null);
+ // ── De store telt wat hij ook laat zien ───────────────────────────────────
+ // Het raster liep over `repos`, de teller over `storeStacks`. Liepen die twee
+ // uit elkaar, dan stond er "Nog 106 tonen" onder een leeg scherm — zonder dat
+ // er ergens een fout te zien was.
+ const nepApps = n => Array.from({length: n},
+ (_, i) => ({dir: 'a' + i, name: 'App' + i, categories: [], tags: []}));
+ const kaarten = () => c.repos.reduce((n, r) => n + c.zichtbareStore(r.id).length, 0);
+
+ c._storeCache = {sleutel: null, lijsten: {}};
+ c.storeQuery = ''; c.storeCat = ''; c.storeTag = ''; c.storeLimiet = 60;
+ c.repos = [{id: 'server-up', name: 'Server Up'}];
+ c.storeStacks = {'server-up': nepApps(106)};
+ check('de eerste zestig staan in beeld', kaarten() === 60);
+ check('en de rest wacht achter de knop', c.storeVerborgen === 46);
+ c.meerTonen();
+ c._storeCache = {sleutel: null, lijsten: {}};
+ check('meer tonen laat de rest zien', kaarten() === 106);
+ check('en de knop verdwijnt', c.storeVerborgen === 0);
+
+ // Zonder repo's is er niets te tonen — en dus ook niets te beloven.
+ c._storeCache = {sleutel: null, lijsten: {}};
+ c.storeLimiet = 60;
+ c.repos = [];
+ check('geen repo\'s: geen kaarten', kaarten() === 0);
+ check('en geen knop die apps belooft', c.storeVerborgen === 0);
+ check('de teller is ook nul', c.storeResultaat === 0);
+
c.rpc = poortRpc;
// ── Eén lijst voor beheerde en handmatig gestarte apps ────────────────────