UI: default to Updatable filter, add Update All, move All to the right

The Updatable chip is now the first segment and selected on load — the
common case (looking at what needs an update) doesn't need a click. All
moves to the right end of the strip.

Update All queues a download for every game whose status is "available"
and whose source/policy allow it (skips downloading/downloaded jobs).
With install_after_download on, the existing auto-install pipeline
picks each finished download up automatically.
This commit is contained in:
Knutwurst committed 2026-06-24 20:51:11 +02:00
1 parent c9d721fea6
commit 3f4c4a15f5
2 files changed
+30 -3

No files matched your search

+24 -1
View File
@@ -83,7 +83,7 @@ let state = {
downloads: fallback.downloads,
logs: fallback.logs,
view: "games",
filter: "all",
filter: "updatable",
query: "",
usingFallback: false,
};
@@ -126,6 +126,7 @@ function bindElements() {
connMinus: document.getElementById("connMinus"),
connPlus: document.getElementById("connPlus"),
refreshBtn: document.getElementById("refreshBtn"),
updateAllBtn: document.getElementById("updateAllBtn"),
saveBtn: document.getElementById("saveBtn"),
clearLogBtn: document.getElementById("clearLogBtn"),
toast: document.getElementById("toast"),
@@ -155,6 +156,7 @@ function bindEvents() {
});
els.refreshBtn.addEventListener("click", loadInitialData);
if (els.updateAllBtn) els.updateAllBtn.addEventListener("click", updateAll);
els.saveBtn.addEventListener("click", saveConfig);
els.clearLogBtn.addEventListener("click", () => { state.logs = []; renderLogs(); });
if (els.connMinus)
@@ -750,6 +752,27 @@ async function saveConfig() {
/* ---------------- actions (data layer) ---------------- */
// Queue a download for every game that currently has an available, allowed
// update. The server tolerates duplicate requests (already-queued/active jobs
// return their existing state), so a second click is harmless. If
// install_after_download is on, the per-job auto-install pipeline kicks in
// once each download finishes — no further client action needed.
async function updateAll() {
const targets = state.games.filter((g) =>
g.status === "available" && isDownloadAllowed(g) &&
!g.downloading && !g.downloaded);
if (!targets.length) {
showToast("No updates to queue.");
return;
}
showToast(`Queueing ${targets.length} update${targets.length === 1 ? "" : "s"}…`);
for (const g of targets) {
// Sequential await: the pool returns quickly (202 Accepted) and we want
// a stable order in the queue, not a thundering-herd of concurrent POSTs.
try { await doDownload(g); } catch (_) { /* per-job errors already toast */ }
}
}
// Enqueue a download. The pool returns immediately (202); progress, completion
// and (if configured) auto-install are driven by reconcileFromJobs() on poll.
async function doDownload(game) {
+6 -2
View File
@@ -70,6 +70,10 @@
<h1>Games</h1>
</div>
<div class="topbar-actions">
<button class="primary-button" id="updateAllBtn" title="Download (and optionally install) every game that has an available, allowed update">
<svg><use href="#icon-download"></use></svg>
Update all
</button>
<button class="icon-button" id="refreshBtn" title="Refresh status and games" aria-label="Refresh">
<svg><use href="#icon-refresh"></use></svg>
</button>
@@ -105,11 +109,11 @@
<input id="searchInput" type="search" placeholder="Search title, Title ID, or Content ID" />
</div>
<div class="segmented" role="group" aria-label="Filter">
<button class="is-selected" data-filter="all" aria-pressed="true">All</button>
<button data-filter="updatable" aria-pressed="false">Updatable</button>
<button class="is-selected" data-filter="updatable" aria-pressed="true">Updatable</button>
<button data-filter="uptodate" aria-pressed="false">Up to date</button>
<button data-filter="needsfw" aria-pressed="false">Needs FW</button>
<button data-filter="blocked" aria-pressed="false">Can't update</button>
<button data-filter="all" aria-pressed="false">All</button>
</div>
</div>