From d3199c42de3652f1e4b81a59b8b0756b9c2c4eb7 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 24 Jun 2026 16:25:22 -0700 Subject: Don't celebrate pre-existing payments on opening a wallet The "payment received" screen is for payments that arrive while you're using the wallet, but the seen-index is persisted per-seed, so re-importing/unlocking/switching to a wallet used before would resurface the celebration for a payment that had already arrived (and confirmed) in between. Opening a wallet (import, unlock, account switch, watch-only add, fresh-session restore) now baselines the ack to the current receive frontier; only a same-session refresh keeps the persisted ack so an unacknowledged live celebration survives the refresh. Verified: with a stale persisted ack, a fresh import shows no celebration and re-baselines to the current frontier. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/app.js | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/app.js b/src/app.js index d704831..c77ee44 100644 --- a/src/app.js +++ b/src/app.js @@ -305,7 +305,7 @@ function addWatchOnly() { ui.watchXpub = ''; ui.watchLabel = ''; ui.fromWallet = false; - activateAccount(acc); + activateAccount(acc, { fresh: true }); } // ================================================================ HOW IT WORKS @@ -553,7 +553,7 @@ async function enterWallet(mnemonic, passphrase, opts = {}) { mnemonic: (mnemonic || '').trim().replace(/\s+/g, ' '), passphrase: passphrase || '', }); - await activateAccount(acc, opts); + await activateAccount(acc, { ...opts, fresh: true }); } // Load an account into the wallet and start scanning. Full-account seeds are @@ -597,13 +597,21 @@ async function activateAccount(acc, opts = {}) { // show instantly; a partial frontier-only refresh leaves the state stale and // would let the celebration baseline before a known payment is even counted. await wallet.scan({ silent: hadCache || hadNostr }); - // Persisted acknowledgement: first time, baseline to the current index so we - // don't celebrate historical payments. After that, any advance beyond the - // acknowledged index shows the celebration (and survives refreshes). - let ack = wallet.getReceiveAck(); - if (ack == null) { + // Celebration baseline. Opening/importing/switching a wallet (opts.fresh) + // baselines to the current frontier, so payments already received before + // opening never trigger the "payment received" screen. A same-session + // refresh keeps the persisted ack, so an unacknowledged celebration + // survives the refresh. + let ack; + if (opts.fresh) { ack = wallet.nextReceiveIndex; wallet.setReceiveAck(ack); + } else { + ack = wallet.getReceiveAck(); + if (ack == null) { + ack = wallet.nextReceiveIndex; + wallet.setReceiveAck(ack); + } } ui.receiveSeenIndex = ack; wallet.startRealtime(); @@ -670,7 +678,7 @@ function removeAccount(id) { if (acc.type === 'watch') saveWatchAccounts(); persistAccounts(); if (activeId === id) { - if (accounts.length) activateAccount(accounts[0]); + if (accounts.length) activateAccount(accounts[0], { fresh: true }); else lock(); } else { render(); @@ -679,7 +687,7 @@ function removeAccount(id) { function switchAccount(id) { const acc = accounts.find((a) => a.id === id); - if (acc) activateAccount(acc); + if (acc) activateAccount(acc, { fresh: true }); } // Restore accounts after a refresh (sessionStorage); on a fresh session, prompt @@ -698,7 +706,7 @@ function restoreAccountsState() { const watch = loadWatchAccounts(); if (watch.length) { accounts = watch.slice(); - activateAccount(accounts[0]); + activateAccount(accounts[0], { fresh: true }); return true; } return false; @@ -782,13 +790,13 @@ function unlockVault() { .concat(loadWatchAccounts()); ui.vaultPw = ''; ui.vaultError = ''; - activateAccount(accounts[0]); + activateAccount(accounts[0], { fresh: true }); } function skipVault() { ui.vaultPw = ''; ui.vaultError = ''; const watch = loadWatchAccounts(); - if (watch.length) { accounts = watch.slice(); activateAccount(accounts[0]); } + if (watch.length) { accounts = watch.slice(); activateAccount(accounts[0], { fresh: true }); } else { ui.screen = 'unlock'; render(); } } -- cgit v1.2.3