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:
Binary file not shown.
@@ -534,6 +534,7 @@
|
||||
"market_btc_price": "BTC PREIS",
|
||||
"market_cap": "Marktkapitalisierung",
|
||||
"market_cap_short": "Kap.",
|
||||
"market_chart_loading": "Preisverlauf wird geladen",
|
||||
"market_iv_1d": "1T",
|
||||
"market_iv_1h": "1S",
|
||||
"market_iv_1m": "1M",
|
||||
|
||||
@@ -534,6 +534,7 @@
|
||||
"market_btc_price": "PRECIO BTC",
|
||||
"market_cap": "Cap. de Mercado",
|
||||
"market_cap_short": "Cap.",
|
||||
"market_chart_loading": "Cargando historial de precios",
|
||||
"market_iv_1d": "1D",
|
||||
"market_iv_1h": "1H",
|
||||
"market_iv_1m": "1M",
|
||||
|
||||
@@ -534,6 +534,7 @@
|
||||
"market_btc_price": "PRIX BTC",
|
||||
"market_cap": "Capitalisation",
|
||||
"market_cap_short": "Cap.",
|
||||
"market_chart_loading": "Chargement de l'historique des prix",
|
||||
"market_iv_1d": "1J",
|
||||
"market_iv_1h": "1H",
|
||||
"market_iv_1m": "1M",
|
||||
|
||||
@@ -534,6 +534,7 @@
|
||||
"market_btc_price": "BTC価格",
|
||||
"market_cap": "時価総額",
|
||||
"market_cap_short": "時価総額",
|
||||
"market_chart_loading": "価格履歴を読み込み中",
|
||||
"market_iv_1d": "1日",
|
||||
"market_iv_1h": "1時間",
|
||||
"market_iv_1m": "1ヶ月",
|
||||
|
||||
@@ -534,6 +534,7 @@
|
||||
"market_btc_price": "BTC 가격",
|
||||
"market_cap": "시가총액",
|
||||
"market_cap_short": "시총",
|
||||
"market_chart_loading": "가격 기록 불러오는 중",
|
||||
"market_iv_1d": "1일",
|
||||
"market_iv_1h": "1시간",
|
||||
"market_iv_1m": "1개월",
|
||||
|
||||
@@ -534,6 +534,7 @@
|
||||
"market_btc_price": "PREÇO BTC",
|
||||
"market_cap": "Capitalização",
|
||||
"market_cap_short": "Cap.",
|
||||
"market_chart_loading": "Carregando histórico de preços",
|
||||
"market_iv_1d": "1D",
|
||||
"market_iv_1h": "1H",
|
||||
"market_iv_1m": "1M",
|
||||
|
||||
@@ -534,6 +534,7 @@
|
||||
"market_btc_price": "ЦЕНА BTC",
|
||||
"market_cap": "Рыночная капитализация",
|
||||
"market_cap_short": "Кап.",
|
||||
"market_chart_loading": "Загрузка истории цен",
|
||||
"market_iv_1d": "1Д",
|
||||
"market_iv_1h": "1Ч",
|
||||
"market_iv_1m": "1М",
|
||||
|
||||
@@ -534,6 +534,7 @@
|
||||
"market_btc_price": "BTC 价格",
|
||||
"market_cap": "市值",
|
||||
"market_cap_short": "市值",
|
||||
"market_chart_loading": "正在加载价格历史",
|
||||
"market_iv_1d": "1天",
|
||||
"market_iv_1h": "1时",
|
||||
"market_iv_1m": "1M",
|
||||
|
||||
@@ -2254,6 +2254,12 @@ TRANSLATIONS = {
|
||||
"ja": "フォルダーをスキャンして他のウォレットを探す",
|
||||
"ko": "폴더를 스캔하여 다른 지갑 찾기"
|
||||
},
|
||||
"market_chart_loading": {
|
||||
"es": "Cargando historial de precios", "de": "Preisverlauf wird geladen",
|
||||
"fr": "Chargement de l'historique des prix", "pt": "Carregando histórico de preços",
|
||||
"ru": "Загрузка истории цен", "zh": "正在加载价格历史",
|
||||
"ja": "価格履歴を読み込み中", "ko": "가격 기록 불러오는 중"
|
||||
},
|
||||
}
|
||||
|
||||
def main():
|
||||
|
||||
@@ -339,6 +339,9 @@ public:
|
||||
// Market chart shows the real per-exchange price. Re-fetches on pair change; falls back to the
|
||||
// CoinGecko aggregate for unmapped venues / failed fetches. Safe to call every frame.
|
||||
void refreshExchangeChart();
|
||||
// True while a market price-history fetch is in flight (CoinGecko aggregate OR per-exchange). The
|
||||
// Market chart shows a loading indicator instead of the empty state during pair switches.
|
||||
bool isMarketChartLoading() const { return chart_fetch_in_flight_ || exchange_chart_fetch_in_flight_; }
|
||||
|
||||
/// @brief Per-category refresh intervals, adjusted by active tab
|
||||
using RefreshIntervals = services::NetworkRefreshService::Intervals;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -1347,6 +1347,7 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["market_attribution"] = "Price data from CoinGecko";
|
||||
strings_["market_btc_price"] = "BTC PRICE";
|
||||
strings_["market_no_history"] = "No price history available";
|
||||
strings_["market_chart_loading"] = "Loading price history";
|
||||
strings_["market_no_price"] = "No price data";
|
||||
strings_["market_now"] = "Now";
|
||||
strings_["market_pct_shielded"] = "%.0f%% Shielded";
|
||||
|
||||
Reference in New Issue
Block a user