fix: a network switch in Settings leaves no balance from the previous network stored (closes #653)
The switch is now made on the stored record that its save has just read. The switched fields are then copied into the page's state. A save writes only the fields that the page changed since its last save. A switch made on the page's state alone mishandled what another page stored since then: - A balance from the previous network stayed under the new network. - A token tracked on the previous network moved to the new network. - An endpoint URL saved for the previous network was replaced by the page's older one. Tests store each of these before the switch and check the stored record after it. Model: opus-5-5
This commit is contained in:
@@ -2,8 +2,10 @@
|
||||
//
|
||||
// A network switch reaches the popup's `state` in one of two ways:
|
||||
//
|
||||
// - onChainSwitch() below makes the switch the user picks in Settings, with
|
||||
// applyChainSwitchFields() (chainSwitchFields.js).
|
||||
// - onChainSwitch() below makes the switch the user picks in Settings. It
|
||||
// makes the switch on the stored record with applyChainSwitchFields(), and
|
||||
// copies the switched fields into `state` with copyChainSwitchFields()
|
||||
// (chainSwitchFields.js).
|
||||
// - followStoredSettings() in src/popup/index.js takes a switch the background
|
||||
// or another popup page saved, from the stored record, with
|
||||
// takeStoredNetwork() (state.js), which copies it with
|
||||
@@ -27,7 +29,10 @@
|
||||
// against storage, and the singleton is not reachable from the background
|
||||
// bundle at all (enforced by the ESLint rule in eslint.config.js).
|
||||
|
||||
const { applyChainSwitchFields } = require("./chainSwitchFields");
|
||||
const {
|
||||
applyChainSwitchFields,
|
||||
copyChainSwitchFields,
|
||||
} = require("./chainSwitchFields");
|
||||
const { clearPrices } = require("./prices");
|
||||
|
||||
// The popup's own caches a network switch resets. Prices are chain-specific
|
||||
@@ -47,12 +52,23 @@ function clearPopupCaches() {
|
||||
// lock, so it is never switched while a follow of a switch saved elsewhere
|
||||
// waits for the lock (followStoredSettings() in src/popup/index.js): that
|
||||
// follow runs first, and this switch is made after it.
|
||||
//
|
||||
// The switch is made on the stored record that the save has just read, as the
|
||||
// background makes its own switch. The switched fields are then copied from
|
||||
// that record into `state`. So the switch also covers what another page stored
|
||||
// since this page's last save:
|
||||
//
|
||||
// - A token tracked on the previous network stays tracked on that network.
|
||||
// - An endpoint URL saved for the previous network stays with that network.
|
||||
// - A balance from the previous network is cleared
|
||||
// (https://git.eeqj.de/sneak/AutistMask/issues/653).
|
||||
async function onChainSwitch(newNetworkId) {
|
||||
const { state, saveState } = require("./state");
|
||||
let net = null;
|
||||
await saveState(() => {
|
||||
await saveState((stored) => {
|
||||
if (state.networkId === newNetworkId) return false;
|
||||
net = applyChainSwitchFields(state, newNetworkId);
|
||||
net = applyChainSwitchFields(stored, newNetworkId);
|
||||
copyChainSwitchFields(state, stored);
|
||||
clearPopupCaches();
|
||||
});
|
||||
return net;
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
//
|
||||
// Split out of chainSwitch.js so the background can perform a chain switch
|
||||
// without the singleton being reachable from its bundle at all. The popup
|
||||
// still goes through onChainSwitch() (chainSwitch.js), which applies this to
|
||||
// the singleton and saves; the background applies it to the detached record of
|
||||
// its own read-modify-write (src/background/state.js).
|
||||
// still goes through onChainSwitch() (chainSwitch.js). That function applies
|
||||
// this to the stored record that the switch's save reads, and copies the
|
||||
// result into the singleton. The background applies it to the detached record
|
||||
// of its own read-modify-write (src/background/state.js).
|
||||
//
|
||||
// Everything here is synchronous and touches nothing but the objects it is
|
||||
// given: no storage, no caches, no imports beyond the network table. That is
|
||||
|
||||
+4
-2
@@ -591,8 +591,10 @@ function reportSaveFailure(err) {
|
||||
//
|
||||
// `beforeWrite`, when given, is called inside the lock with the stored record
|
||||
// as just read, before this page's state is compared against it. It may change
|
||||
// `state`, and this save writes that change. When it returns false, nothing is
|
||||
// written; any other return lets the save go ahead.
|
||||
// `state`, and this save writes that change. It may also change the stored
|
||||
// record: a field that this page did not change is written as `beforeWrite`
|
||||
// left it. When it returns false, nothing is written; any other return lets the
|
||||
// save go ahead.
|
||||
function saveState(beforeWrite) {
|
||||
const turn = withLock("autistmask", () => saveStateOnce(beforeWrite));
|
||||
// Every failed save is reported, whether or not the caller awaited this
|
||||
|
||||
Reference in New Issue
Block a user