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.
This commit is contained in:
parent
fce2331701
commit
57177a3439
4 changed files with 43 additions and 8 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "aegis",
|
"id": "aegis",
|
||||||
"name": "Aegis Wallet",
|
"name": "Aegis Wallet",
|
||||||
"version": "0.25.2",
|
"version": "0.26.0",
|
||||||
"category": "plugin",
|
"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.",
|
"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",
|
"author": "Silent Mode",
|
||||||
|
|
|
||||||
|
|
@ -1348,8 +1348,15 @@ function registerPanelMessages(api) {
|
||||||
await Promise.all(targets.map(async (w) => {
|
await Promise.all(targets.map(async (w) => {
|
||||||
const rt = ctx.runtimes.get(w.id);
|
const rt = ctx.runtimes.get(w.id);
|
||||||
if (!rt || !rt.adapter) { out.push({ id: w.id, ok: false, error: "adapter not mounted" }); return; }
|
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 }); }
|
try {
|
||||||
catch (e) { out.push({ id: w.id, ok: false, error: e?.message || String(e) }); }
|
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 };
|
return { chain, network, results: out };
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -282,8 +282,22 @@ module.exports = function makeWallet({ client, keys, tx, cashaddr, sha256, stora
|
||||||
return null;
|
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) {
|
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();
|
state.scanning = true; state.error = null; onChange();
|
||||||
try {
|
try {
|
||||||
if (full || !state.watched.size) await scan();
|
if (full || !state.watched.size) await scan();
|
||||||
|
|
|
||||||
|
|
@ -3145,16 +3145,30 @@ function renderInlineCoinList(el, groupKey, group) {
|
||||||
refreshBtn.dataset.spinning = "1";
|
refreshBtn.dataset.spinning = "1";
|
||||||
const prev = refreshBtn.textContent;
|
const prev = refreshBtn.textContent;
|
||||||
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 {
|
try {
|
||||||
const r = await S.invoke("refreshChain", { chain: gw[0]?.chain, network: gw[0]?.network });
|
const r = await S.invoke("refreshChain", { chain: gw[0]?.chain, network: gw[0]?.network });
|
||||||
const failed = (r?.results || []).filter((x) => !x.ok);
|
const failed = (r?.results || []).filter((x) => !x.ok);
|
||||||
if (failed.length) refreshBtn.title = "Refresh failed: " + failed.map((f) => f.error).join("; ");
|
if (failed.length) {
|
||||||
else refreshBtn.title = "Refresh balances now";
|
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) {
|
} catch (e) {
|
||||||
refreshBtn.title = "Refresh failed: " + (e?.message || e);
|
refreshBtn.title = "Refresh failed: " + cleanErr(e);
|
||||||
|
mark = "⚠";
|
||||||
} finally {
|
} finally {
|
||||||
refreshBtn.textContent = prev;
|
|
||||||
delete refreshBtn.dataset.spinning;
|
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) => {
|
el.querySelectorAll("[data-listpick]").forEach((row) => row.addEventListener("click", async (e) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue