From 24706757463d1f116cfbf65f599db67805215c14 Mon Sep 17 00:00:00 2001 From: DanS Date: Sat, 13 Jun 2026 08:52:44 -0500 Subject: [PATCH] fix(history): keep shielded txs in date order (they were stuck at the top) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/ui/windows/transactions_tab.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ui/windows/transactions_tab.cpp b/src/ui/windows/transactions_tab.cpp index d0e5368..0f73805 100644 --- a/src/ui/windows/transactions_tab.cpp +++ b/src/ui/windows/transactions_tab.cpp @@ -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;