feat(lite): ObsidianDragonLite Network tab — server browser

A lite-wallet-only "Network" tab (full-node keeps the Peers tab; exactly one shows per variant)
to manage lightwalletd servers, replacing the basic selector that was in Settings.

- Card list of servers with per-server latency + status dot, DNS host + resolved IP, and an
  Official/Custom pill. Official DragonX servers get a glowing outline.
- Pick a server (Sticky) by clicking its card, or toggle "use a random server" (Random mode);
  selection applies immediately (App::rebuildLiteWallet(force=true) tears down + rebuilds the
  controller against the new server and resyncs — its dtor detaches the uninterruptible sync
  thread, so this doesn't block).
- Add custom servers; hide/unhide servers (persisted set, revealed by a "Show hidden" toggle).
- Latency/IP come from a new background probe (util/LiteServerProbe): libcurl CONNECT_ONLY does
  the TCP+TLS handshake (works for gRPC lightwalletd, no HTTP response needed), recording
  APPCONNECT_TIME as latency and CURLINFO_PRIMARY_IP. Auto-runs on tab open + a Refresh button.

Wiring: WalletUiSurface::LiteNetwork (gated !fullNodePagesAvailable) + NavPage::LiteNetwork in
the sidebar + app.cpp dispatch; settings gains a hidden-servers set; isOfficialLiteServer() added
to lite_connection_service. The Settings page lite-server selector + its plumbing are removed
(single source of truth = the tab).

Reuses the existing server model (LiteServerPreference, Sticky/Random, selectLiteServer) and UI
primitives (DrawGlassPanel, ThemeEffects glow, peers-tab ping-dot idiom). Unit-tested
(liteServerHost, isOfficialLiteServer) + an env-gated live probe (verified vs lite.dragonx.is:
online, latency, IP). Both variants + lite-backend build; suite passes; hygiene clean; GUI
smoke-launched without crash.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 11:09:27 -05:00
parent afd612be7e
commit 732d892d4d
16 changed files with 589 additions and 172 deletions

View File

@@ -229,6 +229,11 @@ bool Settings::load(const std::string& path)
if (lite.contains("install_id") && lite["install_id"].is_string()) {
lite_install_id_ = lite["install_id"].get<std::string>();
}
if (lite.contains("hidden_servers") && lite["hidden_servers"].is_array()) {
lite_hidden_servers_.clear();
for (const auto& u : lite["hidden_servers"])
if (u.is_string()) lite_hidden_servers_.insert(u.get<std::string>());
}
}
if (j.contains("verbose_logging")) verbose_logging_ = j["verbose_logging"].get<bool>();
if (j.contains("debug_categories") && j["debug_categories"].is_array()) {
@@ -365,6 +370,8 @@ bool Settings::save(const std::string& path)
}
lite["rollout_override"] = lite_rollout_override_;
lite["install_id"] = lite_install_id_;
lite["hidden_servers"] = json::array();
for (const auto& u : lite_hidden_servers_) lite["hidden_servers"].push_back(u);
j["lite_wallet"] = lite;
}
j["verbose_logging"] = verbose_logging_;

View File

@@ -244,6 +244,12 @@ public:
const std::vector<LiteServerPreference>& getLiteServers() const { return lite_servers_; }
void setLiteServers(const std::vector<LiteServerPreference>& servers) { lite_servers_ = servers; }
// Lite servers the user has hidden from the Network tab (kept by URL, shown via a toggle).
const std::set<std::string>& getLiteHiddenServers() const { return lite_hidden_servers_; }
bool isLiteServerHidden(const std::string& url) const { return lite_hidden_servers_.count(url) > 0; }
void hideLiteServer(const std::string& url) { lite_hidden_servers_.insert(url); }
void unhideLiteServer(const std::string& url) { lite_hidden_servers_.erase(url); }
// Lite wallet rollout / kill-switch (see wallet/lite_rollout_policy.h).
// Override: "auto" (honor rollout manifest), "force_on", or "force_off".
std::string getLiteRolloutOverride() const { return lite_rollout_override_; }
@@ -415,6 +421,7 @@ private:
{"https://lite4.dragonx.is", "DragonX Lite 4", true},
{"https://lite5.dragonx.is", "DragonX Lite 5", true}
};
std::set<std::string> lite_hidden_servers_; // server URLs hidden from the Network tab
bool verbose_logging_ = false;
std::set<std::string> debug_categories_;