When the wallet is connected but the balance has quietly stopped refreshing — a busy
daemon can fail z_gettotalbalance without dropping the whole connection (only *both*
core RPCs failing 3x triggers a disconnect) — the old number sits on screen while the
node-status banner stays hidden. The Total Balance card now shows a small pill on its
status line ("Updated 2m ago", amber, escalating to red past 3 min) so the stale value
isn't silently trusted; hovering explains it and points at the node connection.
No refresh-path changes: WalletState::last_balance_update is already stamped only on a
successful fetch (network_refresh_service.cpp), so the badge reads it and computes age
against the same std::time clock via util::formatTimeAgoShort. The decision is a pure,
unit-tested helper (ui/staleness_badge.h::evaluateStalenessBadge, 45s/180s thresholds)
gated on connected so it never contradicts the banner.
Closes P2 (5/5). Build-clean; ctest 1/1 (adds testStalenessBadge).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
49 lines
2.1 KiB
C++
49 lines
2.1 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
|
|
// Refresh-staleness badge (finding W6-2). The wallet stamps WalletState::last_balance_update only on
|
|
// a *successful* balance fetch (see services/network_refresh_service.cpp), so a busy daemon that fails
|
|
// z_gettotalbalance without dropping the whole connection leaves the old balance on screen with a
|
|
// frozen timestamp — and the node-status banner (which only fires on a full disconnect) stays hidden.
|
|
// This badge is the surface that reflects that "connected but the number may be out of date" state.
|
|
//
|
|
// The decision is a pure function of (last-success timestamp, now, connected) so it is unit-testable;
|
|
// balance_tab.cpp draws the pill. Both use the same std::time(nullptr) wall-clock the refresh path
|
|
// stamps with, so age = now - last_update is consistent.
|
|
namespace dragonx::ui {
|
|
|
|
enum class StalenessSeverity {
|
|
Warning, // amber — noticeably behind
|
|
Error, // red — very stale, something is likely wrong
|
|
};
|
|
|
|
struct StalenessBadge {
|
|
bool show = false;
|
|
StalenessSeverity severity = StalenessSeverity::Warning;
|
|
std::int64_t seconds_old = 0;
|
|
};
|
|
|
|
// Balance refreshes every ~2s on the Overview profile (and ~10s while syncing), so tens of seconds
|
|
// with no successful update means refreshes are failing, not merely slow.
|
|
inline constexpr std::int64_t kStaleAfterSeconds = 45;
|
|
inline constexpr std::int64_t kVeryStaleAfterSeconds = 180;
|
|
|
|
inline StalenessBadge evaluateStalenessBadge(std::int64_t last_update, std::int64_t now, bool connected) {
|
|
StalenessBadge b;
|
|
// Offline is the node-status banner's job; don't double up. A zero stamp means "never updated
|
|
// this session" (fresh start) or "reset on disconnect" — nothing to be stale about yet.
|
|
if (!connected || last_update <= 0) return b;
|
|
|
|
std::int64_t age = now - last_update;
|
|
if (age < 0) age = 0; // clock skew guard
|
|
if (age < kStaleAfterSeconds) return b;
|
|
|
|
b.show = true;
|
|
b.seconds_old = age;
|
|
b.severity = (age >= kVeryStaleAfterSeconds) ? StalenessSeverity::Error : StalenessSeverity::Warning;
|
|
return b;
|
|
}
|
|
|
|
} // namespace dragonx::ui
|