fix(history): keep the open detail row across paging and filtering

The inline detail panel was tracked by the filtered list index. The
display list is rebuilt and re-sorted on every data change, sort switch
or filter edit, so a stored index pointed at a different transaction
after the rebuild, and a page reset was triggered by any change in the
row count, bouncing the view back to page 1.

- The open row is now tracked by a stable content key (txid, display
  type, address and the amount's exact bits) so the right row stays open
  across rebuilds.
- The page resets and the open row collapses only when a filter input
  changes (type, sort or the full search text). A new or removed row is
  data and now just clamps the current page.
- The open row is also cleared on page navigation and when the list
  empties, so a wallet switch does not resurrect an old expansion.

Adds pure helpers rowKey/filterInputsChanged to
transactions_tab_helpers.h.

Tests: rowKey distinguishes each field and separates +0.0 from -0.0, and
filterInputsChanged latches each input change but not the first frame.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 09:24:51 -05:00
co-authored by Claude Opus 4.8
parent a5e4fd5c02
commit c3f981b52e
3 changed files with 83 additions and 13 deletions
+26
View File
@@ -10570,6 +10570,31 @@ void testHistoryTxidGroups()
}
}
// History row identity + filter-change detection (c196): a kept-open detail panel must survive list
// rebuilds (keyed by content, not index) and page/filter changes must reset and collapse.
void testHistoryFilterInputs()
{
using namespace dragonx::ui::history;
// rowKey distinguishes each field, and amount by exact bits (so +0.0 != -0.0).
EXPECT_TRUE(rowKey("tx","Sent","zaddr",1.0) == rowKey("tx","Sent","zaddr",1.0));
EXPECT_TRUE(rowKey("tx","Sent","zaddr",1.0) != rowKey("tx2","Sent","zaddr",1.0));
EXPECT_TRUE(rowKey("tx","Sent","zaddr",1.0) != rowKey("tx","Recv","zaddr",1.0));
EXPECT_TRUE(rowKey("tx","Sent","zaddr",1.0) != rowKey("tx","Sent","taddr",1.0));
EXPECT_TRUE(rowKey("tx","Sent","zaddr",1.0) != rowKey("tx","Sent","zaddr",2.0));
EXPECT_TRUE(rowKey("tx","Sent","zaddr",0.0) != rowKey("tx","Sent","zaddr",-0.0));
// filterInputsChanged: first call at defaults is not a change; each input change is, and latches.
FilterInputs prev;
EXPECT_TRUE(!filterInputsChanged(prev, 0, 0, "")); // defaults -> no change
EXPECT_TRUE(filterInputsChanged(prev, 1, 0, "")); // type changed
EXPECT_TRUE(!filterInputsChanged(prev, 1, 0, "")); // same -> latched
EXPECT_TRUE(filterInputsChanged(prev, 1, 2, "")); // sort changed
EXPECT_TRUE(filterInputsChanged(prev, 1, 2, "abc")); // search changed
EXPECT_TRUE(!filterInputsChanged(prev, 1, 2, "abc")); // same -> latched
EXPECT_TRUE(filterInputsChanged(prev, 1, 2, "ab")); // full-text change (prefix differs)
}
// tx_revision starts at >=1 (a UI cache's 0 sentinel never matches), each helper adds exactly 1, the helpers
// never touch last_tx_update, and clear() bumps without resetting, so no earlier revision ever recurs.
void testWalletStateTxRevision()
@@ -18060,6 +18085,7 @@ int main()
testLiteTransactionsAreNewestFirst();
testSameTransactionList();
testHistoryTxidGroups();
testHistoryFilterInputs();
testWalletStateTxRevision();
testTransactionRefreshRevision();
testLiteSendShowsRecipientFromOutgoing();