// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "validate_address_dialog.h" #include "../../app.h" #include "../../rpc/rpc_client.h" #include "../../rpc/rpc_worker.h" #include "../../util/i18n.h" #include "../schema/ui_schema.h" #include "../material/draw_helpers.h" #include "../theme.h" #include "imgui.h" namespace dragonx { namespace ui { // Static member initialization bool ValidateAddressDialog::s_open = false; bool ValidateAddressDialog::s_validated = false; bool ValidateAddressDialog::s_validating = false; char ValidateAddressDialog::s_address_input[512] = ""; bool ValidateAddressDialog::s_is_valid = false; bool ValidateAddressDialog::s_is_mine = false; std::string ValidateAddressDialog::s_address_type; std::string ValidateAddressDialog::s_error_message; void ValidateAddressDialog::show() { s_open = true; s_validated = false; s_validating = false; s_address_input[0] = '\0'; s_is_valid = false; s_is_mine = false; s_address_type.clear(); s_error_message.clear(); } bool ValidateAddressDialog::isOpen() { return s_open; } void ValidateAddressDialog::render(App* app) { if (!s_open) return; auto& S = schema::UI(); auto win = S.window("dialogs.validate-address"); auto valBtn = S.button("dialogs.validate-address", "validate-button"); auto pasteBtn = S.button("dialogs.validate-address", "paste-button"); auto lbl = S.label("dialogs.validate-address", "label"); auto closeBtn = S.button("dialogs.validate-address", "close-button"); if (material::BeginOverlayDialog(TR("validate_title"), &s_open, win.width, 0.94f)) { ImGui::TextWrapped("%s", TR("validate_description")); ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); // Address input ImGui::Text("%s", TR("address_label")); ImGui::SetNextItemWidth(-1); bool enter_pressed = ImGui::InputText("##ValidateAddr", s_address_input, sizeof(s_address_input), ImGuiInputTextFlags_EnterReturnsTrue); ImGui::Spacing(); // Validate button bool can_validate = strlen(s_address_input) > 0 && !s_validating && app->isConnected(); if (!can_validate) { ImGui::BeginDisabled(); } if (material::StyledButton(TR("validate_btn"), ImVec2(valBtn.width, 0), S.resolveFont(valBtn.font)) || (enter_pressed && can_validate)) { s_validating = true; s_validated = false; s_error_message.clear(); std::string address(s_address_input); // Determine if z-address or t-address bool is_zaddr = !address.empty() && address[0] == 'z'; if (is_zaddr) { if (app->worker()) { app->worker()->post([rpc = app->rpc(), address]() -> rpc::RPCWorker::MainCb { bool valid = false, mine = false; std::string error; try { rpc::RPCClient::TraceScope trace("Receive tab / Validate address"); auto result = rpc->call("validateaddress", {address}); valid = result.value("isvalid", false); mine = result.value("ismine", false); } catch (const std::exception& e) { error = e.what(); } return [valid, mine, error]() { if (error.empty()) { s_is_valid = valid; s_is_mine = mine; s_address_type = TR("validate_shielded_type"); } else { s_error_message = error; s_is_valid = false; } s_validated = true; s_validating = false; }; }); } } else { if (app->worker()) { app->worker()->post([rpc = app->rpc(), address]() -> rpc::RPCWorker::MainCb { bool valid = false, mine = false; std::string error; try { rpc::RPCClient::TraceScope trace("Receive tab / Validate address"); auto result = rpc->call("validateaddress", {address}); valid = result.value("isvalid", false); mine = result.value("ismine", false); } catch (const std::exception& e) { error = e.what(); } return [valid, mine, error]() { if (error.empty()) { s_is_valid = valid; s_is_mine = mine; s_address_type = TR("validate_transparent_type"); } else { s_error_message = error; s_is_valid = false; } s_validated = true; s_validating = false; }; }); } } } if (!can_validate) { ImGui::EndDisabled(); } ImGui::SameLine(); if (material::StyledButton(TR("paste"), ImVec2(pasteBtn.width, 0), S.resolveFont(pasteBtn.font))) { const char* clipboard = ImGui::GetClipboardText(); if (clipboard) { strncpy(s_address_input, clipboard, sizeof(s_address_input) - 1); s_address_input[sizeof(s_address_input) - 1] = '\0'; s_validated = false; } } if (s_validating) { ImGui::SameLine(); ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "%s", TR("validating")); } ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); // Results if (s_validated) { ImGui::Text("%s", TR("validate_results")); ImGui::Spacing(); if (!s_error_message.empty()) { ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Error: %s", s_error_message.c_str()); } else { // Valid/Invalid indicator ImGui::Text("%s", TR("validate_status")); ImGui::SameLine(lbl.position); if (s_is_valid) { ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "%s", TR("validate_valid")); } else { ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", TR("validate_invalid")); } if (s_is_valid) { // Address type ImGui::Text("%s", TR("validate_type")); ImGui::SameLine(lbl.position); ImGui::Text("%s", s_address_type.c_str()); // Is mine? ImGui::Text("%s", TR("validate_ownership")); ImGui::SameLine(lbl.position); if (s_is_mine) { ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "%s", TR("validate_is_mine")); } else { ImGui::TextDisabled("%s", TR("validate_not_mine")); } } } } else if (!app->isConnected()) { ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), "%s", TR("not_connected")); } ImGui::Spacing(); // Close button at bottom float button_width = closeBtn.width; ImGui::SetCursorPosX((ImGui::GetWindowWidth() - button_width) / 2.0f); if (material::StyledButton(TR("close"), ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) { s_open = false; } material::EndOverlayDialog(); } } } // namespace ui } // namespace dragonx