feat(market): loading spinner while the price chart fetches (pair switches)

Switching exchange pairs (or first load) briefly leaves the chart with no series
while the venue's candles fetch, which showed the bare "No price history
available" empty state. When a chart fetch is in flight — App::isMarketChartLoading()
(the CoinGecko aggregate OR the per-exchange fetch) — draw a spinner + animated
"Loading price history…" in the plot area instead; the "no history" text only
shows when genuinely empty and idle. New i18n key market_chart_loading across all
8 languages. Verified via a forced-state render (spinner + label centered).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 09:38:44 -05:00
parent d1ff58c374
commit a107fda9b8
13 changed files with 40 additions and 5 deletions

View File

@@ -1605,11 +1605,28 @@ static void mktDrawPriceChart(const MktCtx& cx)
}
} else {
// Empty state, centered in the plot area (the interval strip + refresh are already drawn).
const char* msg = TR("market_no_history");
ImVec2 ts = sub1->CalcTextSizeA(sub1->LegacySize, FLT_MAX, 0, msg);
dl->AddText(sub1, sub1->LegacySize,
ImVec2(chartMin.x + (availWidth - ts.x) * 0.5f, (plotTop + plotBottom) * 0.5f - ts.y * 0.5f),
OnSurfaceDisabled(), msg);
const float dp = Layout::dpiScale();
const float ecx = chartMin.x + availWidth * 0.5f;
const float ecy = (plotTop + plotBottom) * 0.5f;
if (app->isMarketChartLoading()) {
// A fetch is in flight — e.g. just switched exchange pairs. Show a spinner + animated label
// instead of "no history", since the data is on its way.
const float r = 10.0f * dp;
const float t = (float)ImGui::GetTime() * 4.5f; // rotate ~0.7 rev/s
dl->PathArcTo(ImVec2(ecx, ecy - 8.0f * dp), r, t, t + IM_PI * 1.5f, 24);
dl->PathStroke(OnSurfaceMedium(), false, 2.5f * dp);
char lb[80];
snprintf(lb, sizeof(lb), "%s%s", TR("market_chart_loading"), LoadingDots());
ImVec2 ts = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, lb);
dl->AddText(capFont, capFont->LegacySize,
ImVec2(ecx - ts.x * 0.5f, ecy + 10.0f * dp), OnSurfaceMedium(), lb);
} else {
const char* msg = TR("market_no_history");
ImVec2 ts = sub1->CalcTextSizeA(sub1->LegacySize, FLT_MAX, 0, msg);
dl->AddText(sub1, sub1->LegacySize,
ImVec2(chartMin.x + (availWidth - ts.x) * 0.5f, ecy - ts.y * 0.5f),
OnSurfaceDisabled(), msg);
}
}
ImGui::SetCursorScreenPos(ImVec2(chartMin.x, chartMin.y + chartH));