// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "backup_wallet_dialog.h" #include "../../app.h" #include "../../rpc/rpc_client.h" #include "../../rpc/rpc_worker.h" #include "../../util/i18n.h" #include "../../util/platform.h" #include "../notifications.h" #include "../schema/ui_schema.h" #include "../material/draw_helpers.h" #include "../theme.h" #include "../effects/imgui_acrylic.h" #include "imgui.h" #include #include #include #include namespace dragonx { namespace ui { namespace fs = std::filesystem; using json = nlohmann::json; // Static state static bool s_open = false; static char s_destination[512] = ""; static std::string s_status; static bool s_backing_up = false; void BackupWalletDialog::show() { s_open = true; s_status.clear(); s_backing_up = false; // Generate default destination with timestamp std::time_t now = std::time(nullptr); char timebuf[32]; std::strftime(timebuf, sizeof(timebuf), "%Y%m%d_%H%M%S", std::localtime(&now)); // Default to home directory std::string home = util::Platform::getHomeDir(); snprintf(s_destination, sizeof(s_destination), "%s/wallet_backup_%s.dat", home.c_str(), timebuf); } bool BackupWalletDialog::isOpen() { return s_open; } void BackupWalletDialog::render(App* app) { if (!s_open) return; auto& S = schema::UI(); auto win = S.window("dialogs.backup-wallet"); auto backupBtn = S.button("dialogs.backup-wallet", "backup-button"); auto closeBtn = S.button("dialogs.backup-wallet", "close-button"); ImGui::SetNextWindowSize(ImVec2(win.width, win.height), ImGuiCond_FirstUseEver); ImVec2 center = ImGui::GetMainViewport()->GetCenter(); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowFocus(); const auto& acrylicTheme = GetCurrentAcrylicTheme(); ImGui::OpenPopup("Backup Wallet"); if (effects::ImGuiAcrylic::BeginAcrylicPopupModal("Backup Wallet", &s_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar, acrylicTheme.popup)) { ImGui::TextWrapped( "Create a backup of your wallet.dat file. This file contains all your " "private keys and transaction history. Store the backup in a secure location." ); ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); if (s_backing_up) { ImGui::BeginDisabled(); } // Destination path ImGui::Text("Backup destination:"); ImGui::SetNextItemWidth(-1); ImGui::InputText("##Destination", s_destination, sizeof(s_destination)); ImGui::Spacing(); // Show wallet.dat location std::string walletPath = util::Platform::getDataDir() + "/wallet.dat"; ImGui::TextDisabled("Source: %s", walletPath.c_str()); // Check if source exists bool sourceExists = fs::exists(walletPath); if (!sourceExists) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); ImGui::Text("Warning: wallet.dat not found at expected location"); ImGui::PopStyleColor(); } if (s_backing_up) { ImGui::EndDisabled(); } ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); // Backup button - use RPC backupwallet if (s_backing_up) { ImGui::BeginDisabled(); } if (material::StyledButton("Create Backup", ImVec2(backupBtn.width, 0), S.resolveFont(backupBtn.font))) { if (strlen(s_destination) == 0) { Notifications::instance().warning("Please enter a destination path"); } else if (!app->rpc() || !app->rpc()->isConnected()) { Notifications::instance().error("Not connected to daemon"); } else { s_backing_up = true; s_status = "Creating backup..."; // Run backup on worker thread to avoid freezing UI std::string dest(s_destination); if (app->worker()) { app->worker()->post([rpc = app->rpc(), dest]() -> rpc::RPCWorker::MainCb { bool success = false; std::string statusMsg; try { rpc->call("backupwallet", json::array({dest})); // Check if file was created if (fs::exists(dest)) { auto size = fs::file_size(dest); char sizebuf[32]; if (size > 1024 * 1024) { snprintf(sizebuf, sizeof(sizebuf), "%.2f MB", size / (1024.0 * 1024.0)); } else if (size > 1024) { snprintf(sizebuf, sizeof(sizebuf), "%.2f KB", size / 1024.0); } else { snprintf(sizebuf, sizeof(sizebuf), "%zu bytes", size); } statusMsg = std::string("Backup created successfully (") + sizebuf + ")"; success = true; } else { statusMsg = "Backup may have failed - file not found"; } } catch (const std::exception& e) { statusMsg = std::string("Backup failed: ") + e.what(); } return [success, statusMsg]() { s_status = statusMsg; s_backing_up = false; if (success) { Notifications::instance().success("Wallet backup created"); } else { Notifications::instance().warning(statusMsg); } }; }); } } } if (s_backing_up) { ImGui::EndDisabled(); ImGui::SameLine(); ImGui::TextDisabled("Backing up..."); } ImGui::SameLine(); if (material::StyledButton("Close", ImVec2(closeBtn.width, 0), S.resolveFont(closeBtn.font))) { s_open = false; } // Status if (!s_status.empty()) { ImGui::Spacing(); ImGui::TextWrapped("%s", s_status.c_str()); } ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); // Tips ImGui::TextDisabled("Tips:"); ImGui::BulletText("Store backups on external drives or cloud storage"); ImGui::BulletText("Create multiple backups in different locations"); ImGui::BulletText("Test restoring from backup periodically"); } effects::ImGuiAcrylic::EndAcrylicPopup(); } } // namespace ui } // namespace dragonx