// 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 "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"); if (material::BeginOverlayDialog(TR("backup_title"), &s_open, win.width, 0.94f)) { ImGui::TextWrapped("%s", TR("backup_description")); ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); if (s_backing_up) { ImGui::BeginDisabled(); } // Destination path ImGui::Text("%s", TR("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(TR("backup_source"), 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("%s", TR("backup_wallet_not_found")); 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(TR("backup_create"), 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(TR("backup_created")); } else { Notifications::instance().warning(statusMsg); } }; }); } } } if (s_backing_up) { ImGui::EndDisabled(); ImGui::SameLine(); ImGui::TextDisabled("%s", TR("backup_backing_up")); } ImGui::SameLine(); if (material::StyledButton(TR("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("%s", TR("backup_tips")); ImGui::BulletText("%s", TR("backup_tip_external")); ImGui::BulletText("%s", TR("backup_tip_multiple")); ImGui::BulletText("%s", TR("backup_tip_test")); material::EndOverlayDialog(); } } } // namespace ui } // namespace dragonx