fix(history): keep shielded txs in date order (they were stuck at the top)

History looked unsorted because every merged "shield" row carried
confirmations=0, and the list sorts 0-conf (pending) transactions to the very
top. So long-confirmed shielding transactions floated above newer ones — and
when the type filter was switched off "All" they vanished (shield rows only
match the "Sent" filter), which read as "transactions disappear when sorting".

Root cause: the autoshield merge set the row's confirmations to
min(send, recv). Both legs are the SAME transaction (one real confirmation
count), but the send leg (parsed from z_viewtransaction) routinely arrives with
confirmations=0, so min() picked 0. Use max() to take the populated value.

Also give the sort a txid tiebreak so same-block transactions keep a stable
order instead of reshuffling every time a new block bumps confirmations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 08:52:44 -05:00
parent 3a597482da
commit 2470675746

View File

@@ -425,7 +425,12 @@ void RenderTransactionsTab(App* app)
dtx.is_shield = true;
dtx.amount = rtx.amount; // positive receive amount
dtx.timestamp = std::max(stx.timestamp, rtx.timestamp);
dtx.confirmations = std::min(stx.confirmations, rtx.confirmations);
// Both legs are the SAME transaction, so they share one real confirmation
// count — but the send leg (from z_viewtransaction) often comes through
// with confirmations=0. min() would then make a long-confirmed shield look
// pending (conf==0), which the sort floats to the very top, out of date
// order. Take the populated value.
dtx.confirmations = std::max(stx.confirmations, rtx.confirmations);
dtx.address = rtx.address; // shielded destination
dtx.from_address = stx.address.empty() ? stx.from_address : stx.address;
dtx.memo = rtx.memo.empty() ? stx.memo : rtx.memo;
@@ -456,13 +461,16 @@ void RenderTransactionsTab(App* app)
}
}
// Sort: pending (0-conf) transactions first, then by timestamp descending
// Sort: pending (0-conf) transactions first, then newest-first by timestamp, with txid as
// a final deterministic tiebreak so same-block transactions keep a stable order across
// rebuilds (otherwise equal timestamps reorder every time a new block bumps confirmations).
std::sort(display_txns.begin(), display_txns.end(),
[](const DisplayTx& a, const DisplayTx& b) {
bool aPending = (a.confirmations == 0);
bool bPending = (b.confirmations == 0);
if (aPending != bPending) return aPending;
return a.timestamp > b.timestamp;
if (a.timestamp != b.timestamp) return a.timestamp > b.timestamp;
return a.txid > b.txid;
});
s_display_cache_key = displayKey;