#pragma once #include // 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