fix(startup): surface a "taking too long" notice when the daemon won't come up
F3: the daemon connect loop retried forever with only an animated spinner when the daemon was reachable-but-never-ready (stuck in RPC warmup / -28, or an external daemon that never finishes init) -- no error, no guidance, no escape. It now stamps connect_stall_since_ the moment the daemon first goes "reachable but not ready" (the warmup branch + applyDaemonInitStatus) and clears it on connect / disconnect / warmup-complete. A pure, unit-testable util::connectHasStalled() helper (new util/connect_stall.h, 45s default from ui.toml [screens.loading].stall-timeout-sec) drives a "Taking longer than expected" notice in renderLoadingOverlay(): a title, a reassuring body with elapsed seconds, and a full-node hint to Settings > Restart Daemon or the Console. The background retry keeps running underneath, so the notice self-clears the instant it connects. Guarded off while the daemon is in State::Error (that case is owned by the existing crash-count hint). The overlay is a pure draw-list layer with no interactive widgets, so this follows the existing crash-hint idiom (guidance text, not injected buttons); the stalled state is computed locally in the overlay, so the only new App member is connect_stall_since_. Adds testConnectHasStalled to test_phase4.cpp and three i18n keys to i18n.cpp (English source of truth; the res/lang/*.json back-fill is deferred to a single add_missing_translations.py run at the end of the batch). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -396,6 +396,7 @@ void App::tryConnect()
|
||||
// fail until warmup completes. Set the warmup state so
|
||||
// the UI shows status instead of a blocking overlay.
|
||||
state_.warming_up = true;
|
||||
if (connect_stall_since_ <= 0.0) connect_stall_since_ = ImGui::GetTime(); // start the "taking too long" clock
|
||||
auto wt = translateWarmup(warmupStatus);
|
||||
state_.warmup_status = wt.title;
|
||||
state_.warmup_description = wt.description;
|
||||
@@ -537,6 +538,7 @@ void App::onConnected()
|
||||
}
|
||||
state_.daemon_initializing = false; // RPC is answering now; clear the "initializing" overlay
|
||||
daemon_wait_attempts_ = 0; // re-arm the port-busy / start-failure notifications
|
||||
connect_stall_since_ = 0.0; // connected — clear the "taking too long" clock
|
||||
daemon_start_error_shown_ = false;
|
||||
daemon_last_seen_crashes_ = 0; // (onConnected resets the daemon's crash count too)
|
||||
connection_status_ = TR("connected");
|
||||
@@ -617,6 +619,7 @@ void App::onDisconnected(const std::string& reason)
|
||||
state_.connected = false;
|
||||
state_.warming_up = false;
|
||||
state_.warmup_status.clear();
|
||||
connect_stall_since_ = 0.0; // reset the "taking too long" clock (App member, untouched by state_.clear())
|
||||
state_.clear();
|
||||
connection_status_ = reason;
|
||||
|
||||
@@ -671,6 +674,7 @@ void App::onDisconnected(const std::string& reason)
|
||||
std::string App::applyDaemonInitStatus(bool reachableButBusy)
|
||||
{
|
||||
state_.daemon_initializing = true;
|
||||
if (connect_stall_since_ <= 0.0) connect_stall_since_ = ImGui::GetTime(); // start the "taking too long" clock
|
||||
|
||||
// Find the most recent console line that names an init phase, so we can tell the user exactly
|
||||
// what the node is doing (loading the block index, verifying, activating best chain, …).
|
||||
@@ -1499,6 +1503,7 @@ void App::refreshCoreData()
|
||||
state_.warming_up = false;
|
||||
state_.warmup_status.clear();
|
||||
state_.warmup_description.clear();
|
||||
connect_stall_since_ = 0.0; // warmup finished — clear the "taking too long" clock
|
||||
connection_status_ = TR("connected");
|
||||
VERBOSE_LOGF("[warmup] Daemon ready, warmup complete\n");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user