diff --git a/src/ui/windows/balance_tab.cpp b/src/ui/windows/balance_tab.cpp index 18c7eae..2b17fcd 100644 --- a/src/ui/windows/balance_tab.cpp +++ b/src/ui/windows/balance_tab.cpp @@ -205,6 +205,39 @@ void RenderBalanceTab(App* app) } } + // Shared "still catching up" strip, above whichever layout renders, so a syncing node's 0 / stale + // balance is never mistaken for lost funds (the documented "my coins vanished after an update" + // trap). Scoped to the NORMAL catching-up case — connected, a network tip is known, still verifying + // blocks, and not in a rescan / witness rebuild. Offline, no-peers, a busy node and rescans are + // already surfaced by the node-status banner, so this never stacks a second banner on them. + { + namespace m = material; + const auto& st = app->state(); + if (st.connected && st.sync.tip_known && st.sync.syncing && + !st.sync.building_witnesses && !st.sync.rescanning) { + ImFont* ico = m::Type().iconSmall(); + ImFont* txt = m::Type().body2(); + const float padX = ui::Layout::spacingMd(); + const float padY = ui::Layout::spacingSm(); + const float availW = ImGui::GetContentRegionAvail().x; + char line[192]; + snprintf(line, sizeof(line), TR("balance_catchup_notice"), + static_cast(st.sync.verification_progress) * 100.0f); + const float iconW = ico->LegacySize + padX * 0.5f; + const float wrapW = std::max(1.0f, availW - padX * 2.0f - iconW); + ImVec2 tsz = txt->CalcTextSizeA(txt->LegacySize, FLT_MAX, wrapW, line); + const float h = std::max(ico->LegacySize, tsz.y) + padY * 2.0f; + ImVec2 p0 = ImGui::GetCursorScreenPos(); + ImDrawList* dl = ImGui::GetWindowDrawList(); + dl->AddRectFilled(p0, ImVec2(p0.x + availW, p0.y + h), + m::WithAlphaF(m::Warning(), 0.13f), ui::Layout::glassRounding()); + dl->AddText(ico, ico->LegacySize, ImVec2(p0.x + padX, p0.y + padY), m::Warning(), ICON_MD_SYNC); + dl->AddText(txt, txt->LegacySize, ImVec2(p0.x + padX + iconW, p0.y + padY), + m::OnSurfaceMedium(), line, nullptr, wrapW); + ImGui::Dummy(ImVec2(availW, h + ui::Layout::spacingSm())); + } + } + // Dispatch by string ID for (const auto& entry : s_layoutDispatch) { if (layoutId == entry.id) { diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index 556504d..d3075f0 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -1237,6 +1237,7 @@ void I18n::loadBuiltinEnglish() strings_["balance_shielded_fmt"] = "Shielded: %.8f"; strings_["total_balance_label"] = "Total Balance"; strings_["balance_syncing_pct"] = "Syncing %.1f%%"; + strings_["balance_catchup_notice"] = "Catching up to the network (%.0f%%) — your balance will finish updating when sync completes. Your funds are safe."; strings_["balance_mining_rate"] = "Mining %s"; strings_["balance_layout_switched"] = "Layout: %s"; strings_["byte_count_fmt"] = "%zu / %zu bytes";