From 57177a3439b7b8a108638abe49a76fd2ec43a914 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Fri, 2 Oct 2026 20:34:01 +0200 Subject: [PATCH] fix(aegis): refresh was a silent no-op, and reported success either way Three separate reasons the Refresh button looked dead: - wallet.js refresh() returned immediately when state.scanning was set, so a manual press during a background poll did nothing at all. A forced refresh now awaits the in-flight pass and then does real work; background polls still yield. scanning is only ever written inside doRefresh, which only refresh() calls, so scanning implies a pending inflight to wait on. - The adapters catch their own fetch failures onto state.error instead of rejecting, so awaiting refresh() proved nothing and refreshChain reported ok:true for a wallet that had just failed against a dead server. It now reads the snapshot back. - Success changed only a tooltip. Balances that were already current left the screen identical, which is indistinguishable from a broken button. It now flashes a result and says how many wallets were refreshed, or how many failed and why. Version bumped once for this batch; not published yet. --- bundled-addons/aegis/addon.json | 2 +- bundled-addons/aegis/index.js | 11 +++++++++-- bundled-addons/aegis/lib/wallet.js | 16 +++++++++++++++- bundled-addons/aegis/panel.js | 22 ++++++++++++++++++---- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/bundled-addons/aegis/addon.json b/bundled-addons/aegis/addon.json index 18c26c52..680d428a 100644 --- a/bundled-addons/aegis/addon.json +++ b/bundled-addons/aegis/addon.json @@ -1,7 +1,7 @@ { "id": "aegis", "name": "Aegis Wallet", - "version": "0.25.2", + "version": "0.26.0", "category": "plugin", "description": "Multi-chain wallet (BCH, BTC, TRX, ETH, SOL, SC, DGB) derived from your Theseus vault. Dapps get window.bitcoincash on .x sites; window.tronWeb / window.tronLink / window.ethereum / window.solana on any https page.", "author": "Silent Mode", diff --git a/bundled-addons/aegis/index.js b/bundled-addons/aegis/index.js index 8bf656bb..f696f5b0 100644 --- a/bundled-addons/aegis/index.js +++ b/bundled-addons/aegis/index.js @@ -1348,8 +1348,15 @@ function registerPanelMessages(api) { await Promise.all(targets.map(async (w) => { const rt = ctx.runtimes.get(w.id); if (!rt || !rt.adapter) { out.push({ id: w.id, ok: false, error: "adapter not mounted" }); return; } - try { await rt.adapter.refresh(true); out.push({ id: w.id, ok: true }); } - catch (e) { out.push({ id: w.id, ok: false, error: e?.message || String(e) }); } + try { + await rt.adapter.refresh(true); + // The adapters catch their own fetch failures onto state.error rather + // than rejecting, so awaiting refresh() proves nothing. Read the + // snapshot back, or a dead server reports a successful refresh. + let snapErr = null; + try { snapErr = rt.adapter.snapshot()?.error || null; } catch { snapErr = null; } + out.push(snapErr ? { id: w.id, ok: false, error: snapErr } : { id: w.id, ok: true }); + } catch (e) { out.push({ id: w.id, ok: false, error: e?.message || String(e) }); } })); return { chain, network, results: out }; }); diff --git a/bundled-addons/aegis/lib/wallet.js b/bundled-addons/aegis/lib/wallet.js index c28bc313..7ab3b373 100644 --- a/bundled-addons/aegis/lib/wallet.js +++ b/bundled-addons/aegis/lib/wallet.js @@ -282,8 +282,22 @@ module.exports = function makeWallet({ client, keys, tx, cashaddr, sha256, stora return null; } + // A manual refresh used to return here the moment a background poll was + // in flight, and refreshChain reported ok:true for it — so the Refresh + // button no-opped and claimed success, which is exactly when a user is + // most likely to press it. A forced refresh now waits for the in-flight + // pass and then does real work; a background poll still yields. + let inflight = null; async function refresh(full = false) { - if (state.scanning) return; + if (state.scanning) { + if (!full) return; + try { await inflight; } catch { /* its own error is already on state */ } + if (state.scanning) return; // another forced pass won the race + } + inflight = doRefresh(full); + return inflight; + } + async function doRefresh(full) { state.scanning = true; state.error = null; onChange(); try { if (full || !state.watched.size) await scan(); diff --git a/bundled-addons/aegis/panel.js b/bundled-addons/aegis/panel.js index 77a7f3c0..98a15319 100644 --- a/bundled-addons/aegis/panel.js +++ b/bundled-addons/aegis/panel.js @@ -3145,16 +3145,30 @@ function renderInlineCoinList(el, groupKey, group) { refreshBtn.dataset.spinning = "1"; const prev = refreshBtn.textContent; refreshBtn.textContent = "…"; + // Say what happened. A tooltip-only result is indistinguishable from the + // button doing nothing, which is how this one came to be reported as + // broken even when it worked: balances that were already current changed + // nothing on screen. + let mark = null; try { const r = await S.invoke("refreshChain", { chain: gw[0]?.chain, network: gw[0]?.network }); const failed = (r?.results || []).filter((x) => !x.ok); - if (failed.length) refreshBtn.title = "Refresh failed: " + failed.map((f) => f.error).join("; "); - else refreshBtn.title = "Refresh balances now"; + if (failed.length) { + refreshBtn.title = `Refresh failed on ${failed.length} of ${r.results.length}: ` + failed.map((f) => f.error).join("; "); + mark = "⚠"; + } else { + refreshBtn.title = `Refreshed ${r.results.length} wallet${r.results.length === 1 ? "" : "s"}`; + mark = "✓"; + } } catch (e) { - refreshBtn.title = "Refresh failed: " + (e?.message || e); + refreshBtn.title = "Refresh failed: " + cleanErr(e); + mark = "⚠"; } finally { - refreshBtn.textContent = prev; delete refreshBtn.dataset.spinning; + if (mark) { + refreshBtn.textContent = mark; + setTimeout(() => { if (refreshBtn.isConnected) refreshBtn.textContent = prev; }, 1200); + } else refreshBtn.textContent = prev; } }); el.querySelectorAll("[data-listpick]").forEach((row) => row.addEventListener("click", async (e) => {