// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "request_payment_dialog.h" #include "../../app.h" #include "../../util/i18n.h" #include "../../util/payment_uri.h" #include "../notifications.h" #include "../schema/ui_schema.h" #include "../widgets/qr_code.h" #include "../material/draw_helpers.h" #include "imgui.h" #include #include #include namespace dragonx { namespace ui { // Static state static bool s_open = false; static char s_address[512] = ""; static double s_amount = 0.0; static char s_memo[512] = ""; static char s_label[128] = ""; static std::string s_payment_uri; static uintptr_t s_qr_texture = 0; static int s_qr_width = 0; static int s_qr_height = 0; static bool s_uri_dirty = true; // Helper to build payment URI static std::string buildPaymentUri() { // Route through the shared builder so this dialog and the receive tab can't diverge on scheme/encoding. return dragonx::util::buildPaymentUri(s_address, s_amount, s_label, s_memo); } void RequestPaymentDialog::show(const std::string& address) { s_open = true; s_amount = 0.0; s_memo[0] = '\0'; s_label[0] = '\0'; s_uri_dirty = true; if (!address.empty()) { strncpy(s_address, address.c_str(), sizeof(s_address) - 1); } else { s_address[0] = '\0'; } // Free old QR texture if (s_qr_texture != 0) { FreeQRTexture(s_qr_texture); s_qr_texture = 0; } } void RequestPaymentDialog::render(App* app) { if (!s_open) return; auto& S = schema::UI(); auto win = S.window("dialogs.request-payment"); auto zAddrLbl = S.label("dialogs.request-payment", "z-addr-label"); auto zAddrFrontLbl = S.label("dialogs.request-payment", "z-addr-front-label"); auto zAddrBackLbl = S.label("dialogs.request-payment", "z-addr-back-label"); auto tAddrLbl = S.label("dialogs.request-payment", "t-addr-label"); auto tAddrFrontLbl = S.label("dialogs.request-payment", "t-addr-front-label"); auto tAddrBackLbl = S.label("dialogs.request-payment", "t-addr-back-label"); auto amountInput = S.input("dialogs.request-payment", "amount-input"); auto memoInput = S.input("dialogs.request-payment", "memo-input"); auto qr = S.drawElement("dialogs.request-payment", "qr-code"); auto actionBtn = S.button("dialogs.request-payment", "action-button"); material::OverlayDialogSpec ov; ov.title = TR("request_title"); ov.p_open = &s_open; ov.style = material::OverlayStyle::BlurFloat; ov.cardWidth = win.width; ov.idSuffix = "requestpay"; if (material::BeginOverlayDialog(ov)) { const auto& state = app->getWalletState(); ImGui::TextWrapped("%s", TR("request_description")); ImGui::Spacing(); // Address selection ImGui::Text("%s", TR("request_receive_address")); std::string addr_display = s_address[0] ? s_address : TR("request_select_address"); if (addr_display.length() > static_cast(zAddrLbl.truncate)) { addr_display = addr_display.substr(0, zAddrFrontLbl.truncate) + "..." + addr_display.substr(addr_display.length() - zAddrBackLbl.truncate); } ImGui::SetNextItemWidth(-1); if (ImGui::BeginCombo("##Address", addr_display.c_str())) { // Z-addresses if (!state.z_addresses.empty()) { ImGui::TextDisabled("%s", TR("request_shielded_addrs")); for (size_t i = 0; i < state.z_addresses.size(); i++) { const auto& addr = state.z_addresses[i]; std::string label = addr.address; if (label.length() > static_cast(zAddrLbl.truncate)) { label = label.substr(0, zAddrFrontLbl.truncate) + "..." + label.substr(label.length() - zAddrBackLbl.truncate); } if (ImGui::Selectable(label.c_str(), addr.address == s_address)) { strncpy(s_address, addr.address.c_str(), sizeof(s_address) - 1); s_uri_dirty = true; } } } // T-addresses if (!state.t_addresses.empty()) { ImGui::TextDisabled("%s", TR("request_transparent_addrs")); for (size_t i = 0; i < state.t_addresses.size(); i++) { const auto& addr = state.t_addresses[i]; std::string label = addr.address; if (label.length() > static_cast(tAddrLbl.truncate)) { label = label.substr(0, tAddrFrontLbl.truncate) + "..." + label.substr(label.length() - tAddrBackLbl.truncate); } if (ImGui::Selectable(label.c_str(), addr.address == s_address)) { strncpy(s_address, addr.address.c_str(), sizeof(s_address) - 1); s_uri_dirty = true; } } } ImGui::EndCombo(); } ImGui::Spacing(); // Amount (optional) ImGui::Text("%s", TR("request_amount")); ImGui::SetNextItemWidth(amountInput.width); if (ImGui::InputDouble("##Amount", &s_amount, 0.1, 1.0, "%.8f")) { s_uri_dirty = true; } ImGui::SameLine(); ImGui::TextDisabled("DRGX"); ImGui::Spacing(); // Label (optional) if (material::LabeledInput(TR("request_label"), "##Label", s_label, sizeof(s_label))) { s_uri_dirty = true; } ImGui::Spacing(); // Memo (optional, only for z-addr) bool is_zaddr = (s_address[0] == 'z'); if (is_zaddr) { if (material::LabeledInputMultiline(TR("request_memo"), "##Memo", s_memo, sizeof(s_memo), ImVec2(-1, memoInput.height > 0 ? memoInput.height : 60))) { s_uri_dirty = true; } } else { s_memo[0] = '\0'; // Clear memo for t-addr } ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); // Build and display payment URI if (s_uri_dirty && s_address[0] != '\0') { s_payment_uri = buildPaymentUri(); // Generate new QR code if (s_qr_texture != 0) { FreeQRTexture(s_qr_texture); } s_qr_texture = GenerateQRTexture(s_payment_uri.c_str(), &s_qr_width, &s_qr_height); s_uri_dirty = false; } // QR Code display if (s_qr_texture != 0) { RenderQRCode(s_qr_texture, qr.size > 0 ? qr.size : 200); } ImGui::Spacing(); // Payment URI display if (!s_payment_uri.empty()) { // Use a selectable text area for the URI char uri_buf[1024]; strncpy(uri_buf, s_payment_uri.c_str(), sizeof(uri_buf) - 1); material::LabeledInput(TR("request_payment_uri"), "##URI", uri_buf, sizeof(uri_buf), -1.0f, nullptr, ImGuiInputTextFlags_ReadOnly); ImGui::Spacing(); // Copy button if (material::TactileButton(TR("request_copy_uri"), ImVec2(actionBtn.width, 0), S.resolveFont(actionBtn.font))) { ImGui::SetClipboardText(s_payment_uri.c_str()); Notifications::instance().success(TR("request_uri_copied")); } ImGui::SameLine(); if (material::TactileButton(TR("copy_address"), ImVec2(actionBtn.width, 0), S.resolveFont(actionBtn.font))) { ImGui::SetClipboardText(s_address); Notifications::instance().success(TR("address_copied")); } } ImGui::Spacing(); // Close button if (material::TactileButton(TR("close"), ImVec2(actionBtn.width, 0), S.resolveFont(actionBtn.font))) { s_open = false; } material::EndOverlayDialog(); } // Cleanup on close if (!s_open && s_qr_texture != 0) { FreeQRTexture(s_qr_texture); s_qr_texture = 0; } } } // namespace ui } // namespace dragonx