// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #pragma once // Pure helpers behind the History tab (no ImGui), so they can be unit-tested. #include "../../data/wallet_state.h" #include #include #include #include #include #include #include #include namespace dragonx { namespace ui { namespace history { // Rows of one txid chained in ascending index order. first[i] = lowest index sharing tx i's txid; // next[i] = next higher index with the same txid, or -1. Holds indices only (no string copies, no // pointers into the list), so it stays valid for as long as the list is unchanged (same tx_revision). struct TxidGroups { std::vector first; std::vector next; bool hasSiblings(std::size_t i) const { return next[static_cast(first[i])] >= 0; } }; inline TxidGroups groupByTxid(const std::vector& txs) { TxidGroups g; const std::size_t n = txs.size(); g.first.assign(n, -1); g.next.assign(n, -1); std::unordered_map last; // txid (view into txs) -> last index seen last.reserve(n); for (std::size_t i = 0; i < n; ++i) { auto ins = last.try_emplace(std::string_view(txs[i].txid), static_cast(i)); if (ins.second) { g.first[i] = static_cast(i); } else { g.first[i] = g.first[static_cast(ins.first->second)]; g.next[static_cast(ins.first->second)] = static_cast(i); ins.first->second = static_cast(i); } } return g; } // Stable identity of a display row, used to keep an inline detail panel open across list rebuilds. // txid alone is not unique (a tx can show several rows: multiple receive outputs, unmerged legs), so // add the display type, address and the amount's exact bits. Two rows identical in all four are the same // logical payment and expanding both is acceptable. inline std::string rowKey(const std::string& txid, const std::string& displayType, const std::string& address, double amount) { std::uint64_t bits = 0; std::memcpy(&bits, &amount, sizeof bits); char hex[17]; std::snprintf(hex, sizeof hex, "%016llx", static_cast(bits)); std::string k; k.reserve(txid.size() + displayType.size() + address.size() + 20); k.append(txid).push_back('|'); k.append(displayType).push_back('|'); k.append(address).push_back('|'); k.append(hex); return k; } // The inputs that decide which rows the History list shows and in what order. struct FilterInputs { int type = 0; int sort = 0; std::string search; }; // True (and `prev` updated) when type, sort or the FULL search text changed since last call. The caller // then resets to page 1 and collapses the open row. Starts at the defaults (0, 0, "") so the first frame // does not count as a change. inline bool filterInputsChanged(FilterInputs& prev, int type, int sort, const std::string& search) { if (prev.type == type && prev.sort == sort && prev.search == search) return false; prev.type = type; prev.sort = sort; prev.search = search; return true; } } // namespace history } // namespace ui } // namespace dragonx