diff options
| author | Adam Soltys <[email protected]> | 2026-06-16 00:30:52 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-06-16 00:30:52 -0700 |
| commit | f42f5c3697497e777fa613519ac3b7d13dc9b410 (patch) | |
| tree | 0fc0be7cb3b8466b31e94abcedb6fb72a87e205b | |
| parent | ed396fd93afdb6abec956175b00a871153e8e6da (diff) | |
| download | coinosv3-f42f5c3697497e777fa613519ac3b7d13dc9b410.tar.gz coinosv3-f42f5c3697497e777fa613519ac3b7d13dc9b410.zip | |
Show pending txs in history with a clear unconfirmed indicator
WS-detected deposits now get added to history (previously the ingest advanced
past the address so the tx never entered history — balance showed but History
was empty). Pending txs render with a "⏳ pending" badge, an orange-tinted row,
and "awaiting confirmation" so they're clearly distinct from confirmed ones.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
| -rw-r--r-- | src/app.js | 14 | ||||
| -rw-r--r-- | src/style.css | 1 | ||||
| -rw-r--r-- | src/wallet.js | 13 |
3 files changed, 23 insertions, 5 deletions
@@ -1017,17 +1017,19 @@ function historyTab() { { class: 'list' }, wallet.txs.map((t) => { const incoming = t.net >= 0; - const display = incoming ? t.net : t.net; // net already signed return h( 'a', - { class: 'item', href: wallet.api.explorerTx(t.txid), target: '_blank', rel: 'noopener', style: 'color:inherit' }, + { class: 'item' + (t.confirmed ? '' : ' pendingtx'), href: wallet.api.explorerTx(t.txid), target: '_blank', rel: 'noopener', style: 'color:inherit' }, h('div', { class: `ico ${incoming ? 'in' : 'out'}` }, incoming ? '↓' : '↑'), h('div', { class: 'grow' }, - h('div', {}, incoming ? 'Received' : 'Sent'), - h('div', { class: 'small faint' }, t.confirmed ? timeAgo(t.blockTime) : 'unconfirmed') + h('div', { class: 'row gap6' }, + incoming ? 'Received' : 'Sent', + t.confirmed ? null : h('span', { class: 'tag pending' }, '⏳ pending') + ), + h('div', { class: 'small faint' }, t.confirmed ? timeAgo(t.blockTime) : 'awaiting confirmation') ), h('div', { style: 'text-align:right' }, - h('div', { class: incoming ? 'amount-pos' : 'amount-neg' }, (incoming ? '+' : '') + fmtAmount(display)), + h('div', { class: incoming ? 'amount-pos' : 'amount-neg' }, (incoming ? '+' : '') + fmtAmount(t.net)), !incoming && t.fee ? h('div', { class: 'small faint' }, `fee ${fmtAmount(t.fee)}`) : null ) ); @@ -1081,3 +1083,5 @@ async function importSnapshotFile(e) { // Restore a wallet left open in this tab; otherwise show the unlock screen. if (!restoreSession()) render(); + + diff --git a/src/style.css b/src/style.css index ed1d542..fbfd1aa 100644 --- a/src/style.css +++ b/src/style.css @@ -176,6 +176,7 @@ textarea { resize: vertical; min-height: 84px; font-family: var(--mono); font-si .amount { font-family: var(--mono); font-weight: 600; } .tag { font-size: 11px; font-weight: 700; padding: 2px 7px; border-radius: 6px; } .tag.pending { background: var(--accent-soft); color: var(--accent-ink); } +.item.pendingtx { background: var(--accent-soft); } .tag.conf { background: var(--green-soft); color: var(--green); } /* coin control rows */ diff --git a/src/wallet.js b/src/wallet.js index f47d2f1..ba22af2 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -686,11 +686,14 @@ export class Wallet { let changed = false; const have = new Set(this.utxos.map(utxoId)); + const mine = new Set(this.addrMap.keys()); for (const tx of txs) { if (!tx || !Array.isArray(tx.vout)) continue; + let relevant = false; tx.vout.forEach((vo, i) => { const a = vo && vo.scriptpubkey_address; if (!a || !this.addrMap.has(a)) return; + relevant = true; const id = `${tx.txid}:${i}`; if (have.has(id)) return; const p = this.addrMap.get(a); @@ -710,11 +713,21 @@ export class Wallet { else entry.pending = (entry.pending || 0) + vo.value; changed = true; }); + for (const vin of tx.vin || []) { + const a = vin.prevout && vin.prevout.scriptpubkey_address; + if (a && mine.has(a)) relevant = true; + } + // Record it in history (so a pending deposit shows up there too). + if (relevant && !this.txs.some((t) => t.txid === tx.txid)) { + this.txs.push(this._txSummary(tx)); + changed = true; + } } if (changed) { this.nextReceiveIndex = firstUnused(this.receive); this.nextChangeIndex = firstUnused(this.change); this._recomputeBalanceFromChains(); + this._sortTxs(); this.retrack(); // start watching the new fresh address this.emit(); } |
