refactor(market): resolve exchange/pair selection through one helper

The chart fetch and the Market tab both resolved the saved exchange and
pair names into the registry, with their own near-identical scans and
slightly different fallbacks. Keeping two copies risked the chart and
the chips disagreeing about which pair was selected.

- A single resolver maps the saved names to a registry index, matching
  the exchange name (any exchange when none is saved) then the pair
  display name, and falling back to the registry's first pair, the UI
  default. The chart fetch and the Market tab both call it.
- The Market tab now derives its selection indices from the saved names
  against the current registry every frame, so the indices stay correct
  when the live exchange list replaces the fallback; a steady-state fast
  path skips the scan once the indices already name the saved selection.

Tests: the resolver returns the right index for a matched exchange/pair,
an empty-exchange match, and the first-pair fallback on misses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 16:00:10 -05:00
co-authored by Claude Opus 4.8
parent ce00621a4a
commit 3f82af99c4
4 changed files with 74 additions and 25 deletions
+26
View File
@@ -12,6 +12,7 @@
#include <sys/wait.h>
#include <unistd.h>
#endif
#include "data/exchange_info.h"
#include "util/text_format.h"
#include "chat/chat_crypto.h"
#include "chat/chat_identity.h"
@@ -17958,6 +17959,30 @@ void testLoadLanguageFromLooseFiles()
EXPECT_EQ(std::string(i18n.translate("overview")), std::string("Overview"));
}
void testResolveExchangeSelection()
{
using dragonx::data::ExchangeInfo;
using dragonx::data::ExchangePair;
auto mk = [](const char* n, std::initializer_list<const char*> pairs) {
ExchangeInfo e; e.name = n;
for (auto* d : pairs) { ExchangePair p; p.displayName = d; e.pairs.push_back(p); }
return e;
};
std::vector<ExchangeInfo> reg = { mk("A", {"X/BTC", "X/USDT"}), mk("B", {"X/BTC", "Y/BTC"}) };
auto r = dragonx::data::resolveExchangeSelection(reg, "B", "Y/BTC");
EXPECT_TRUE(r.found); EXPECT_EQ(r.exchangeIdx, 1); EXPECT_EQ(r.pairIdx, 1);
r = dragonx::data::resolveExchangeSelection(reg, "B", "X/BTC");
EXPECT_TRUE(r.found); EXPECT_EQ(r.exchangeIdx, 1); EXPECT_EQ(r.pairIdx, 0);
r = dragonx::data::resolveExchangeSelection(reg, "", "Y/BTC"); // empty exchange => any
EXPECT_TRUE(r.found); EXPECT_EQ(r.exchangeIdx, 1); EXPECT_EQ(r.pairIdx, 1);
r = dragonx::data::resolveExchangeSelection(reg, "A", "Y/BTC"); // pair not on that exchange
EXPECT_TRUE(!r.found); EXPECT_EQ(r.exchangeIdx, 0); EXPECT_EQ(r.pairIdx, 0);
r = dragonx::data::resolveExchangeSelection(reg, "Z", "X/BTC"); // unknown exchange
EXPECT_TRUE(!r.found); EXPECT_EQ(r.exchangeIdx, 0); EXPECT_EQ(r.pairIdx, 0);
r = dragonx::data::resolveExchangeSelection({}, "A", "X/BTC");
EXPECT_TRUE(!r.found); EXPECT_EQ(r.exchangeIdx, 0);
}
void testCaptureExtent()
{
using namespace dragonx::ui::effects;
@@ -18406,6 +18431,7 @@ int main()
testLoadLanguageFromLooseFiles();
testWriteFileIfDifferent();
testCaptureExtent();
testResolveExchangeSelection();
if (g_failures != 0) {
std::cerr << g_failures << " test assertion(s) failed\n";