fix: a balance refresh that returns after a network switch writes nothing (closes #577)
The popup's doRefreshAndRender() and the background's backgroundRefresh() each note the network a refresh starts on and write its balances only if that network is still the active one when the requests return. Otherwise nothing is written and lastBalanceRefresh stays as the switch left it. The background checks inside updateState(). The popup refreshes a copy of its wallets and copies the results onto `state` from inside its save, under the lock, only if both its own network and the stored record's still match, so a switch the background makes is caught too. saveState() takes an optional function, run there on the stored record before the page's state is compared, that may change `state` or return false to cancel the write. Model: opus-5-5
This commit was merged in pull request #648.
This commit is contained in:
+35
-4
@@ -68,23 +68,54 @@ let refreshInFlight = false;
|
||||
// the recovery screen: there is no profile left to refresh.
|
||||
let refreshTimer = null;
|
||||
|
||||
// The refresh writes into a copy of the wallets. Inside the save, under the
|
||||
// lock, its results are copied onto the address records in `state` only if the
|
||||
// network it ran on is still the active one, on this page and in the stored
|
||||
// record; the background's switch, after the user approves a site's request,
|
||||
// changes only the stored record. A switch while the refresh ran cleared every
|
||||
// address's balances (applyChainSwitchFields() in
|
||||
// src/shared/chainSwitchFields.js), and the previous network's must not come
|
||||
// back over them (https://git.eeqj.de/sneak/AutistMask/issues/577). A dropped
|
||||
// refresh is not recorded as done. Results are copied by address, so an address
|
||||
// added or removed meanwhile is left as it is.
|
||||
async function doRefreshAndRender() {
|
||||
if (refreshInFlight) return;
|
||||
refreshInFlight = true;
|
||||
try {
|
||||
const networkId = state.networkId;
|
||||
const wallets = structuredClone(state.wallets);
|
||||
await Promise.all([
|
||||
refreshPrices(),
|
||||
refreshBalances(
|
||||
state.wallets,
|
||||
wallets,
|
||||
state.rpcUrl,
|
||||
state.blockscoutUrl,
|
||||
state.trackedTokens,
|
||||
state.networkId,
|
||||
networkId,
|
||||
pageClosed.signal,
|
||||
),
|
||||
]);
|
||||
state.lastBalanceRefresh = Date.now();
|
||||
await saveState();
|
||||
const refreshed = new Map();
|
||||
for (const wallet of wallets) {
|
||||
for (const addr of wallet.addresses) {
|
||||
refreshed.set(addr.address, addr);
|
||||
}
|
||||
}
|
||||
await saveState((stored) => {
|
||||
if (state.networkId !== networkId) return false;
|
||||
if (stored.networkId !== networkId) return false;
|
||||
for (const wallet of state.wallets) {
|
||||
for (const addr of wallet.addresses) {
|
||||
const got = refreshed.get(addr.address);
|
||||
if (!got) continue;
|
||||
for (const key of ["balance", "ensName", "tokenBalances"]) {
|
||||
if (got[key] !== undefined) addr[key] = got[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
state.lastBalanceRefresh = Date.now();
|
||||
return true;
|
||||
});
|
||||
renderWalletList();
|
||||
} catch (e) {
|
||||
// Every call site fires this and walks away — the boot below, the ten
|
||||
|
||||
Reference in New Issue
Block a user