// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "export_transactions_dialog.h" #include "../../app.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 #include namespace dragonx { namespace ui { // Static state static bool s_open = false; static char s_filename[256] = ""; static std::string s_status; // Helper to escape CSV field static std::string escapeCSV(const std::string& field) { if (field.find(',') != std::string::npos || field.find('"') != std::string::npos || field.find('\n') != std::string::npos) { // Escape quotes and wrap in quotes std::string escaped; escaped.reserve(field.size() + 4); escaped += '"'; for (char c : field) { if (c == '"') escaped += "\"\""; else escaped += c; } escaped += '"'; return escaped; } return field; } void ExportTransactionsDialog::show() { s_open = true; s_status.clear(); // Generate default filename 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)); snprintf(s_filename, sizeof(s_filename), "dragonx_transactions_%s.csv", timebuf); } bool ExportTransactionsDialog::isOpen() { return s_open; } void ExportTransactionsDialog::render(App* app) { if (!s_open) return; auto& S = schema::UI(); auto win = S.window("dialogs.export-transactions"); auto exportBtn = S.button("dialogs.export-transactions", "export-button"); auto closeBtn = S.button("dialogs.export-transactions", "close-button"); if (material::BeginOverlayDialog(TR("export_tx_title"), &s_open, win.width, 0.94f)) { const auto& state = app->getWalletState(); ImGui::Text(TR("export_tx_count"), state.transactions.size()); ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); // Filename ImGui::Text("%s", TR("output_filename")); ImGui::SetNextItemWidth(-1); ImGui::InputText("##Filename", s_filename, sizeof(s_filename)); ImGui::Spacing(); ImGui::TextDisabled("%s", TR("file_save_location")); ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); // Export button if (material::StyledButton(TR("export"), ImVec2(exportBtn.width, 0), S.resolveFont(exportBtn.font))) { if (state.transactions.empty()) { Notifications::instance().warning(TR("export_tx_none")); } else { std::string configDir = util::Platform::getConfigDir(); std::string filepath = configDir + "/" + s_filename; std::ofstream file(filepath); if (!file.is_open()) { s_status = "Failed to create file"; Notifications::instance().error(TR("export_tx_file_fail")); } else { // Write CSV header file << "Date,Type,Amount,Address,TXID,Confirmations,Memo\n"; // Write transactions for (const auto& tx : state.transactions) { // Date std::time_t t = static_cast(tx.timestamp); char datebuf[32]; std::strftime(datebuf, sizeof(datebuf), "%Y-%m-%d %H:%M:%S", std::localtime(&t)); file << datebuf << ","; // Type file << escapeCSV(tx.type) << ","; // Amount std::ostringstream amt; amt << std::fixed << std::setprecision(8) << tx.amount; file << amt.str() << ","; // Address file << escapeCSV(tx.address) << ","; // TXID file << escapeCSV(tx.txid) << ","; // Confirmations file << tx.confirmations << ","; // Memo file << escapeCSV(tx.memo) << "\n"; } file.close(); s_status = "Exported " + std::to_string(state.transactions.size()) + " transactions to: " + filepath; Notifications::instance().success(TR("export_tx_success")); } } } 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()); } material::EndOverlayDialog(); } } } // namespace ui } // namespace dragonx