feat(lite): show connection + sync status in the Network tab

Add a status panel at the top of the Network tab driven by the live WalletState:
- Connection: a colored dot + Connected / Syncing / Not connected, with the in-use server host
  (or "Random server") and its latency on the right.
- Sync: "<pct>%  ·  <walletHeight> / <chainHeight>" while syncing (with a thin progress bar),
  "Synced · block N" when complete, or "No wallet open" when disconnected.

Reads app->state().sync (populated by the lite refresh: progress / wallet+chain height / complete)
and state().connected (= walletOpen). Advances with a Dummy so the bounds grow correctly.

Both variants build; suite passes; hygiene clean; lite GUI smoke OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 11:41:55 -05:00
parent 8ba4233b9b
commit a6921bca60
2 changed files with 73 additions and 0 deletions

View File

@@ -6,6 +6,7 @@
#include "../../app.h"
#include "../../config/settings.h"
#include "../../data/wallet_state.h"
#include "../../util/i18n.h"
#include "../../util/lite_server_probe.h"
#include "../../wallet/lite_connection_service.h"
@@ -18,6 +19,7 @@
#include "../../embedded/IconsMaterialDesign.h"
#include "imgui.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
@@ -84,6 +86,70 @@ void RenderLiteNetworkTab(App* app)
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("lite_net_intro"));
ImGui::Spacing();
// ── Connection + sync status panel ─────────────────────────────────────────
{
const WalletState& ws = app->state();
const SyncInfo& sync = ws.sync;
const bool connected = ws.connected;
ImDrawList* sdl = ImGui::GetWindowDrawList();
const float panelH = 56.0f * dp;
const float panelW = ImGui::GetContentRegionAvail().x;
const float pad2 = 12.0f * dp;
ImVec2 pMin = ImGui::GetCursorScreenPos();
ImVec2 pMax(pMin.x + panelW, pMin.y + panelH);
GlassPanelSpec sspec; sspec.rounding = 8.0f * dp;
DrawGlassPanel(sdl, pMin, pMax, sspec);
// Top row: status dot + connection label (left); in-use server + latency (right).
ImU32 dotCol; const char* connLabel;
if (!connected) { dotCol = OnSurfaceDisabled(); connLabel = TR("lite_net_disconnected"); }
else if (sync.syncing) { dotCol = Warning(); connLabel = TR("lite_net_syncing"); }
else { dotCol = Success(); connLabel = TR("lite_net_synced"); }
const float dotR = 5.0f * dp;
sdl->AddCircleFilled(ImVec2(pMin.x + pad2 + dotR, pMin.y + 17.0f * dp), dotR, dotCol);
sdl->AddText(sub2, sub2->LegacySize, ImVec2(pMin.x + pad2 + dotR * 2 + 8.0f * dp, pMin.y + 9.0f * dp),
OnSurface(), connLabel);
std::string inUse = randomMode ? std::string(TR("lite_net_random_server"))
: (sticky.empty() ? std::string("") : util::liteServerHost(sticky));
if (!randomMode && !sticky.empty()) {
const auto it = probe.find(sticky);
if (it != probe.end() && it->second.online)
inUse += " · " + std::to_string(it->second.latencyMs) + "ms";
}
const ImVec2 inUseSz = cap->CalcTextSizeA(cap->LegacySize, FLT_MAX, 0, inUse.c_str());
sdl->AddText(cap, cap->LegacySize, ImVec2(pMax.x - pad2 - inUseSz.x, pMin.y + 12.0f * dp),
OnSurfaceMedium(), inUse.c_str());
// Bottom row: sync detail + (while syncing) a thin progress bar.
char syncBuf[96];
if (!connected)
snprintf(syncBuf, sizeof(syncBuf), "%s: %s", TR("lite_net_sync_label"), TR("lite_net_no_wallet"));
else if (sync.syncing)
snprintf(syncBuf, sizeof(syncBuf), "%s: %.1f%% · %d / %d", TR("lite_net_sync_label"),
sync.verification_progress * 100.0, sync.blocks, sync.headers);
else
snprintf(syncBuf, sizeof(syncBuf), "%s: %s · block %d", TR("lite_net_sync_label"),
TR("lite_net_synced"), sync.blocks);
sdl->AddText(cap, cap->LegacySize, ImVec2(pMin.x + pad2, pMin.y + 32.0f * dp),
OnSurfaceMedium(), syncBuf);
if (connected && sync.syncing) {
const float barY = pMin.y + panelH - 6.0f * dp;
const float barX = pMin.x + pad2;
const float barW = panelW - pad2 * 2;
const float barH = 2.5f * dp;
const float pct = static_cast<float>(std::max(0.0, std::min(1.0, sync.verification_progress)));
sdl->AddRectFilled(ImVec2(barX, barY), ImVec2(barX + barW, barY + barH),
WithAlpha(OnSurface(), 25), barH * 0.5f);
if (pct > 0)
sdl->AddRectFilled(ImVec2(barX, barY), ImVec2(barX + barW * pct, barY + barH),
Primary(), barH * 0.5f);
}
ImGui::Dummy(ImVec2(panelW, panelH)); // reserve the panel (proper boundary growth)
ImGui::Spacing();
}
bool useRandom = randomMode;
if (ImGui::Checkbox(TR("lite_net_use_random"), &useRandom)) {
st->setLiteServerSelectionMode(useRandom ? LiteMode::Random : LiteMode::Sticky);

View File

@@ -1080,6 +1080,13 @@ void I18n::loadBuiltinEnglish()
strings_["lite_net_unhide"] = "Unhide";
strings_["lite_net_show_hidden"] = "Show hidden servers";
strings_["lite_net_hidden_section"] = "Hidden servers";
strings_["lite_net_connected"] = "Connected";
strings_["lite_net_disconnected"] = "Not connected";
strings_["lite_net_syncing"] = "Syncing";
strings_["lite_net_synced"] = "Synced";
strings_["lite_net_random_server"] = "Random server";
strings_["lite_net_sync_label"] = "Sync";
strings_["lite_net_no_wallet"] = "No wallet open";
// --- Peers Tab ---
strings_["peers_avg_ping"] = "Avg Ping";