From ac49f44f84df944207a48477bacd869f0394b3ba Mon Sep 17 00:00:00 2001 From: DanS Date: Mon, 10 Aug 2026 12:28:48 -0500 Subject: [PATCH] fix(node): show recovery actions in the daemon-error overlay (not a separate dialog) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- src/app.cpp | 64 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index a504d23..195ea6c 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -5679,26 +5679,50 @@ void App::renderLoadingOverlay(float contentH) IM_COL32(255, 90, 90, 255), errTitle); curY += ts.y + gap * 0.5f; - // Error details (wrapped) — show 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; + // Wallet auto-recovery/salvage takes over the error card: a concise message + prominent one-click + // actions, RIGHT HERE in the overlay the user is looking at (the separate dialog can be occluded + // by this full-frame overlay while the node is down). Skip the verbose daemon dump so the buttons + // stay on-screen. Same handlers as the dialog. + if (wallet_auto_recovered_) { + const char* msg = TR("wallet_recovered_warn"); + float wrapW = ws.x * 0.8f; if (wrapW > 640.0f) wrapW = 640.0f; + ImVec2 ms = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, wrapW, msg); + dl->AddText(capFont, capFont->LegacySize, ImVec2(wp.x + cx - wrapW * 0.5f, curY), + IM_COL32(230, 210, 210, 235), msg, nullptr, wrapW); + curY += ms.y + gap; + const float dpi = ui::Layout::dpiScale(); + const float bw = 340.0f * dpi; + auto placeBtn = [&](const char* label) -> bool { + ImGui::SetCursorScreenPos(ImVec2(wp.x + cx - bw * 0.5f, curY)); + const bool clicked = ui::material::TactileButton(label, ImVec2(bw, 0)); + curY = ImGui::GetItemRectMax().y + gap * 0.4f; + return clicked; + }; + 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; + } } }