feat(settings): confirmation modals for rescan and restart-daemon

The Rescan blockchain and Restart daemon buttons fired immediately on click —
both are disruptive (long offline rescan / connection drop) and easy to hit by
accident. Route them through confirmation modals, matching the existing
delete-blockchain / clear-ztx confirmations: the button now sets a confirm flag
and an overlay dialog performs the action only on explicit confirm. New i18n
strings added with English defaults.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 11:52:44 -05:00
parent c71c3c3378
commit 101c835c46
2 changed files with 68 additions and 3 deletions

View File

@@ -145,6 +145,8 @@ struct SettingsPageState {
bool tools_expanded = false; bool tools_expanded = false;
bool confirm_clear_ztx = false; bool confirm_clear_ztx = false;
bool confirm_delete_blockchain = false; bool confirm_delete_blockchain = false;
bool confirm_rescan = false;
bool confirm_restart_daemon = false;
effects::ScrollFadeShader fade_shader; effects::ScrollFadeShader fade_shader;
}; };
@@ -1993,7 +1995,7 @@ void RenderSettingsPage(App* app) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("%s", TR("tt_test_conn")); if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("%s", TR("tt_test_conn"));
ImGui::SameLine(0, Layout::spacingMd()); ImGui::SameLine(0, Layout::spacingMd());
if (TactileButton(TR("rescan"), ImVec2(nodeBtnW, 0), btnFont)) { if (TactileButton(TR("rescan"), ImVec2(nodeBtnW, 0), btnFont)) {
app->rescanBlockchain(); s_settingsState.confirm_rescan = true;
} }
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("%s", TR("tt_rescan")); if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("%s", TR("tt_rescan"));
ImGui::EndDisabled(); ImGui::EndDisabled();
@@ -2451,8 +2453,7 @@ void RenderSettingsPage(App* app) {
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::SameLine(); ImGui::SameLine();
if (TactileButton(TR("settings_restart_daemon"), ImVec2(0, 0), S.resolveFont("button"))) { if (TactileButton(TR("settings_restart_daemon"), ImVec2(0, 0), S.resolveFont("button"))) {
s_settingsState.debug_cats_dirty = false; s_settingsState.confirm_restart_daemon = true;
app->restartDaemon();
} }
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_restart_daemon")); if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("tt_restart_daemon"));
} }
@@ -2580,6 +2581,65 @@ void RenderSettingsPage(App* app) {
} }
} }
// Confirm: rescan blockchain (restarts the daemon, re-scans the whole chain — long but safe)
if (s_settingsState.confirm_rescan) {
if (BeginOverlayDialog(TR("confirm_rescan_title"), &s_settingsState.confirm_rescan, 500.0f, 0.94f)) {
ImGui::PushFont(Type().iconLarge());
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
ImGui::PopFont();
ImGui::SameLine();
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
ImGui::Spacing();
ImGui::TextWrapped("%s", TR("confirm_rescan_msg"));
ImGui::Spacing();
ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "%s", TR("confirm_rescan_safe"));
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
if (ImGui::Button(TrId("cancel", "rescan_cancel").c_str(), ImVec2(btnW, 40))) {
s_settingsState.confirm_rescan = false;
}
ImGui::SameLine();
if (ImGui::Button(TrId("rescan", "rescan_confirm").c_str(), ImVec2(btnW, 40))) {
app->rescanBlockchain();
s_settingsState.confirm_rescan = false;
}
EndOverlayDialog();
}
}
// Confirm: restart daemon (briefly drops the connection to apply changed options)
if (s_settingsState.confirm_restart_daemon) {
if (BeginOverlayDialog(TR("confirm_restart_daemon_title"), &s_settingsState.confirm_restart_daemon, 500.0f, 0.94f)) {
ImGui::PushFont(Type().iconLarge());
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
ImGui::PopFont();
ImGui::SameLine();
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
ImGui::Spacing();
ImGui::TextWrapped("%s", TR("confirm_restart_daemon_msg"));
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
if (ImGui::Button(TrId("cancel", "restartd_cancel").c_str(), ImVec2(btnW, 40))) {
s_settingsState.confirm_restart_daemon = false;
}
ImGui::SameLine();
if (ImGui::Button(TrId("settings_restart_daemon", "restartd_confirm").c_str(), ImVec2(btnW, 40))) {
s_settingsState.debug_cats_dirty = false;
app->restartDaemon();
s_settingsState.confirm_restart_daemon = false;
}
EndOverlayDialog();
}
}
} }
} // namespace ui } // namespace ui

View File

@@ -413,6 +413,11 @@ void I18n::loadBuiltinEnglish()
strings_["confirm_delete_blockchain_title"] = "Delete Blockchain Data"; strings_["confirm_delete_blockchain_title"] = "Delete Blockchain Data";
strings_["confirm_delete_blockchain_msg"] = "This will stop the daemon, delete all blockchain data (blocks, chainstate, peers), and start a fresh sync from scratch. This can take several hours to complete."; strings_["confirm_delete_blockchain_msg"] = "This will stop the daemon, delete all blockchain data (blocks, chainstate, peers), and start a fresh sync from scratch. This can take several hours to complete.";
strings_["confirm_delete_blockchain_safe"] = "Your wallet.dat, config, and transaction history are safe and will not be deleted."; strings_["confirm_delete_blockchain_safe"] = "Your wallet.dat, config, and transaction history are safe and will not be deleted.";
strings_["confirm_rescan_title"] = "Rescan Blockchain";
strings_["confirm_rescan_msg"] = "This restarts the daemon and re-scans the entire blockchain for your wallet's transactions. It can take a long time and the wallet stays offline until it finishes.";
strings_["confirm_rescan_safe"] = "Your wallet.dat and blockchain data are not deleted — only re-scanned.";
strings_["confirm_restart_daemon_title"] = "Restart Daemon";
strings_["confirm_restart_daemon_msg"] = "This stops and restarts the daemon to apply the changed options. The wallet will briefly disconnect and reconnect.";
strings_["tt_encrypt"] = "Encrypt wallet.dat with a passphrase"; strings_["tt_encrypt"] = "Encrypt wallet.dat with a passphrase";
strings_["tt_change_pass"] = "Change the wallet encryption passphrase"; strings_["tt_change_pass"] = "Change the wallet encryption passphrase";
strings_["tt_lock"] = "Lock the wallet immediately"; strings_["tt_lock"] = "Lock the wallet immediately";