fix(node): show recovery actions in the daemon-error overlay (not a separate dialog)

Reported with a screenshot: on a salvage-then-abort, the status correctly read
"Wallet needs recovery — see the prompt" but no prompt appeared — the separate
BeginOverlayDialog is occluded by the full-frame loading/daemon-error overlay
that's drawn every frame while the node is down.

Render the recovery actions directly IN the daemon-error overlay when a salvage is
detected: a concise message + prominent one-click "Rebuild wallet database" /
"Restore original" / "Open data folder" buttons (same handlers as the dialog),
placed right after the title and skipping the verbose daemon-output dump so they
stay on-screen. The verbose diagnostics + crash-count hint still show for
non-recovery errors. Build clean, suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-10 12:28:48 -05:00
parent 3216debc7d
commit ac49f44f84

View File

@@ -5679,26 +5679,50 @@ void App::renderLoadingOverlay(float contentH)
IM_COL32(255, 90, 90, 255), errTitle); IM_COL32(255, 90, 90, 255), errTitle);
curY += ts.y + gap * 0.5f; curY += ts.y + gap * 0.5f;
// Error details (wrapped) — show full diagnostic info // Wallet auto-recovery/salvage takes over the error card: a concise message + prominent one-click
const std::string& errDetail = daemon_controller_->lastError(); // actions, RIGHT HERE in the overlay the user is looking at (the separate dialog can be occluded
if (!errDetail.empty()) { // by this full-frame overlay while the node is down). Skip the verbose daemon dump so the buttons
float wrapW = ws.x * 0.8f; // stay on-screen. Same handlers as the dialog.
if (wrapW > 700.0f) wrapW = 700.0f; if (wallet_auto_recovered_) {
ImVec2 es = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, wrapW, errDetail.c_str()); const char* msg = TR("wallet_recovered_warn");
dl->AddText(capFont, capFont->LegacySize, float wrapW = ws.x * 0.8f; if (wrapW > 640.0f) wrapW = 640.0f;
ImVec2(wp.x + cx - wrapW * 0.5f, curY), ImVec2 ms = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, wrapW, msg);
IM_COL32(255, 180, 180, 220), errDetail.c_str(), nullptr, wrapW); dl->AddText(capFont, capFont->LegacySize, ImVec2(wp.x + cx - wrapW * 0.5f, curY),
curY += es.y + gap; IM_COL32(230, 210, 210, 235), msg, nullptr, wrapW);
} curY += ms.y + gap;
const float dpi = ui::Layout::dpiScale();
// Crash count hint const float bw = 340.0f * dpi;
if (daemon_controller_->crashCount() >= 3) { auto placeBtn = [&](const char* label) -> bool {
const char* hint = "Use Settings > Restart Daemon to try again"; ImGui::SetCursorScreenPos(ImVec2(wp.x + cx - bw * 0.5f, curY));
ImVec2 hs2 = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0.0f, hint); const bool clicked = ui::material::TactileButton(label, ImVec2(bw, 0));
dl->AddText(capFont, capFont->LegacySize, curY = ImGui::GetItemRectMax().y + gap * 0.4f;
ImVec2(wp.x + cx - hs2.x * 0.5f, curY), return clicked;
IM_COL32(200, 200, 200, 180), hint); };
curY += hs2.y + gap; if (walletRebuildAvailable() && placeBtn(TR("wallet_recovered_rebuild"))) rebuildWalletDatabase();
if (placeBtn(TR("wallet_recovered_restore"))) restoreOriginalWallet();
if (placeBtn(TR("wallet_recovered_open_folder")))
util::Platform::openFolder(util::Platform::getDragonXDataDir());
} else {
// Error details (wrapped) — full diagnostic info.
const std::string& errDetail = daemon_controller_->lastError();
if (!errDetail.empty()) {
float wrapW = ws.x * 0.8f;
if (wrapW > 700.0f) wrapW = 700.0f;
ImVec2 es = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, wrapW, errDetail.c_str());
dl->AddText(capFont, capFont->LegacySize,
ImVec2(wp.x + cx - wrapW * 0.5f, curY),
IM_COL32(255, 180, 180, 220), errDetail.c_str(), nullptr, wrapW);
curY += es.y + gap;
}
// Crash count hint
if (daemon_controller_->crashCount() >= 3) {
const char* hint = "Use Settings > Restart Daemon to try again";
ImVec2 hs2 = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0.0f, hint);
dl->AddText(capFont, capFont->LegacySize,
ImVec2(wp.x + cx - hs2.x * 0.5f, curY),
IM_COL32(200, 200, 200, 180), hint);
curY += hs2.y + gap;
}
} }
} }