feat(ux): robust sync ETA, persist Explorer settings, gate export-all out of lite
- Sync ETA: a new pure util::SyncRateEstimator (App-owned) replaces the function-local statics that went stale after a rescan/restart (producing the "412h" ETA). It self-resets on a height drop and on connect/disconnect/rescan/ restart (via an atomic flag, since onConnected can run off-thread), formats with days, and uses the Warning() token. - Settings: the Explorer-tab URL fields + custom-fees/fetch-prices checkboxes now persist on edit — they were the only controls still relying on the About-only Save button that a user on this tab never sees. - Export-all-keys: hidden in the lite build (which has its own key export), instead of dead-ending on "Not connected to the daemon". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
//
|
||||
// A tiny, pure block-sync rate estimator for the status-bar / peers-card ETA. Pure (no ImGui, no App):
|
||||
// App owns one instance, feeds it the current height + a monotonic clock once per frame while syncing,
|
||||
// and resets it on connect / disconnect / rescan / restart so a height drop never produces a stale or
|
||||
// absurd ("412h") ETA.
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
|
||||
namespace dragonx {
|
||||
namespace util {
|
||||
|
||||
class SyncRateEstimator {
|
||||
public:
|
||||
void reset() { prevBlocks_ = -1; prevTime_ = 0.0; rate_ = 0.0; }
|
||||
|
||||
// Call once per frame while syncing. `now` is monotonic seconds (e.g. ImGui::GetTime()).
|
||||
void update(int blocks, double now) {
|
||||
// First sample, a rescan/restart height drop, or a clock reset → re-baseline, don't compute.
|
||||
if (prevBlocks_ < 0 || blocks < prevBlocks_ || now < prevTime_) {
|
||||
prevBlocks_ = blocks; prevTime_ = now; rate_ = 0.0; return;
|
||||
}
|
||||
const double dt = now - prevTime_;
|
||||
if (blocks > prevBlocks_) {
|
||||
if (dt > 0.5) {
|
||||
const double raw = (blocks - prevBlocks_) / dt;
|
||||
rate_ = rate_ > 0.0 ? rate_ * 0.7 + raw * 0.3 : raw; // smooth (same alpha as the old code)
|
||||
prevBlocks_ = blocks; prevTime_ = now;
|
||||
}
|
||||
} else if (dt > 10.0) { // stalled: decay and re-baseline the clock
|
||||
rate_ *= 0.5; prevTime_ = now;
|
||||
if (rate_ < 0.1) rate_ = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
double rate() const { return rate_; } // blocks/sec, 0 when unknown
|
||||
|
||||
// <= 0 when unknown. Capped at ~1 year so a tiny rate can't overflow.
|
||||
long long etaSeconds(int blocksLeft) const {
|
||||
if (rate_ <= 0.1 || blocksLeft <= 0) return -1;
|
||||
const double s = blocksLeft / rate_;
|
||||
return s > 3.15e7 ? 31500000LL : static_cast<long long>(s);
|
||||
}
|
||||
|
||||
private:
|
||||
int prevBlocks_ = -1; // -1 = unset (distinct from a legitimate height 0)
|
||||
double prevTime_ = 0.0;
|
||||
double rate_ = 0.0;
|
||||
};
|
||||
|
||||
// Human ETA with days, e.g. "3d 4h", "5h 12m", "7m 9s", "42s". English unit letters (as the old code).
|
||||
inline void formatEta(long long s, char* out, std::size_t n) {
|
||||
if (s >= 86400) std::snprintf(out, n, "%lldd %lldh", s / 86400, (s % 86400) / 3600);
|
||||
else if (s >= 3600) std::snprintf(out, n, "%lldh %lldm", s / 3600, (s % 3600) / 60);
|
||||
else if (s >= 60) std::snprintf(out, n, "%lldm %llds", s / 60, s % 60);
|
||||
else std::snprintf(out, n, "%llds", s);
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace dragonx
|
||||
Reference in New Issue
Block a user