- Replace all hardcoded English strings with TR() translation keys across every tab, dialog, and component (~20 UI files) - Expand all 8 language files (de, es, fr, ja, ko, pt, ru, zh) with complete translations (~37k lines added) - Improve i18n loader with exe-relative path fallback and English base fallback for missing keys - Add pool-side hashrate polling via pool stats API in xmrig_manager - Introduce Layout::beginFrame() per-frame caching and refresh balance layout config only on schema generation change - Offload daemon output parsing to worker thread - Add CJK subset fallback font for Chinese/Japanese/Korean glyphs
236 lines
9.4 KiB
C++
236 lines
9.4 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
|
|
#include "key_export_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 "imgui.h"
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
// Static member initialization
|
|
bool KeyExportDialog::s_open = false;
|
|
bool KeyExportDialog::s_fetching = false;
|
|
bool KeyExportDialog::s_show_key = false;
|
|
KeyExportDialog::KeyType KeyExportDialog::s_key_type = KeyExportDialog::KeyType::Private;
|
|
std::string KeyExportDialog::s_address;
|
|
std::string KeyExportDialog::s_key;
|
|
std::string KeyExportDialog::s_error;
|
|
|
|
void KeyExportDialog::show(const std::string& address, KeyType type)
|
|
{
|
|
s_open = true;
|
|
s_fetching = false;
|
|
s_show_key = false;
|
|
s_key_type = type;
|
|
s_address = address;
|
|
s_key.clear();
|
|
s_error.clear();
|
|
}
|
|
|
|
bool KeyExportDialog::isOpen()
|
|
{
|
|
return s_open;
|
|
}
|
|
|
|
void KeyExportDialog::render(App* app)
|
|
{
|
|
if (!s_open) return;
|
|
|
|
const char* title = (s_key_type == KeyType::Private) ?
|
|
TR("export_private_key") : TR("export_viewing_key");
|
|
|
|
auto& S = schema::UI();
|
|
auto win = S.window("dialogs.key-export");
|
|
auto warningBox = S.drawElement("dialogs.key-export", "warning-box");
|
|
auto addrInput = S.input("dialogs.key-export", "address-input");
|
|
auto revealBtn = S.button("dialogs.key-export", "reveal-button");
|
|
auto keyDisplay = S.drawElement("dialogs.key-export", "key-display");
|
|
auto toggleBtn = S.button("dialogs.key-export", "toggle-button");
|
|
auto copyBtn = S.button("dialogs.key-export", "copy-button");
|
|
auto closeBtn = S.button("dialogs.key-export", "close-button");
|
|
|
|
if (material::BeginOverlayDialog(title, &s_open, win.width, 0.94f)) {
|
|
ImGui::Spacing();
|
|
|
|
// Warning section with colored background
|
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.6f, 0.2f, 0.2f, 0.3f));
|
|
ImGui::BeginChild("WarningBox", ImVec2(-1, warningBox.height > 0 ? warningBox.height : 80), true);
|
|
|
|
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), " %s", TR("warning_upper"));
|
|
ImGui::Spacing();
|
|
|
|
if (s_key_type == KeyType::Private) {
|
|
ImGui::TextWrapped(" %s", TR("key_export_private_warning"));
|
|
} else {
|
|
ImGui::TextWrapped(" %s", TR("key_export_viewing_warning"));
|
|
}
|
|
|
|
ImGui::EndChild();
|
|
ImGui::PopStyleColor();
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
// Address display
|
|
ImGui::Text("%s", TR("address_label"));
|
|
|
|
// Determine if it's a z-address (longer) or t-address
|
|
bool is_zaddr = s_address.length() > 50;
|
|
|
|
if (is_zaddr) {
|
|
// Use multiline for z-addresses
|
|
char addr_buf[512];
|
|
strncpy(addr_buf, s_address.c_str(), sizeof(addr_buf) - 1);
|
|
addr_buf[sizeof(addr_buf) - 1] = '\0';
|
|
ImGui::InputTextMultiline("##Address", addr_buf, sizeof(addr_buf),
|
|
ImVec2(-1, addrInput.height > 0 ? addrInput.height : 60), ImGuiInputTextFlags_ReadOnly);
|
|
} else {
|
|
char addr_buf[128];
|
|
strncpy(addr_buf, s_address.c_str(), sizeof(addr_buf) - 1);
|
|
addr_buf[sizeof(addr_buf) - 1] = '\0';
|
|
ImGui::SetNextItemWidth(-1);
|
|
ImGui::InputText("##Address", addr_buf, sizeof(addr_buf), ImGuiInputTextFlags_ReadOnly);
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
|
|
// Key display section
|
|
const char* key_label = (s_key_type == KeyType::Private) ? TR("key_export_private_key") : TR("key_export_viewing_key");
|
|
ImGui::Text("%s", key_label);
|
|
|
|
if (s_fetching) {
|
|
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "%s", TR("key_export_fetching"));
|
|
} else if (!s_error.empty()) {
|
|
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), TR("error_format"), s_error.c_str());
|
|
} else if (s_key.empty()) {
|
|
// Show button to fetch key
|
|
if (material::StyledButton(TR("key_export_reveal"), ImVec2(revealBtn.width, 0), S.resolveFont(revealBtn.font))) {
|
|
s_fetching = true;
|
|
|
|
// Check if z-address or t-address
|
|
bool is_zaddress = (s_address.length() > 50 || s_address[0] == 'z');
|
|
|
|
if (s_key_type == KeyType::Private) {
|
|
// Export private key
|
|
std::string addr = s_address;
|
|
std::string method = is_zaddress ? "z_exportkey" : "dumpprivkey";
|
|
if (app->worker()) {
|
|
app->worker()->post([rpc = app->rpc(), addr, method]() -> rpc::RPCWorker::MainCb {
|
|
std::string key;
|
|
std::string error;
|
|
try {
|
|
auto result = rpc->call(method, {addr});
|
|
key = result.get<std::string>();
|
|
} catch (const std::exception& e) {
|
|
error = e.what();
|
|
}
|
|
return [key, error]() {
|
|
if (error.empty()) {
|
|
s_key = key;
|
|
s_show_key = false; // Don't show by default
|
|
} else {
|
|
s_error = error;
|
|
}
|
|
s_fetching = false;
|
|
};
|
|
});
|
|
}
|
|
} else {
|
|
// Export viewing key (only for z-addresses)
|
|
if (is_zaddress) {
|
|
std::string addr = s_address;
|
|
if (app->worker()) {
|
|
app->worker()->post([rpc = app->rpc(), addr]() -> rpc::RPCWorker::MainCb {
|
|
std::string key;
|
|
std::string error;
|
|
try {
|
|
auto result = rpc->call("z_exportviewingkey", {addr});
|
|
key = result.get<std::string>();
|
|
} catch (const std::exception& e) {
|
|
error = e.what();
|
|
}
|
|
return [key, error]() {
|
|
if (error.empty()) {
|
|
s_key = key;
|
|
s_show_key = true; // Viewing keys are less sensitive
|
|
} else {
|
|
s_error = error;
|
|
}
|
|
s_fetching = false;
|
|
};
|
|
});
|
|
}
|
|
} else {
|
|
s_error = TR("key_export_viewing_keys_zonly");
|
|
s_fetching = false;
|
|
}
|
|
}
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("%s", TR("key_export_click_retrieve"));
|
|
} else {
|
|
// Key has been fetched - display it
|
|
|
|
if (s_show_key) {
|
|
// Show the actual key
|
|
char key_buf[1024];
|
|
strncpy(key_buf, s_key.c_str(), sizeof(key_buf) - 1);
|
|
key_buf[sizeof(key_buf) - 1] = '\0';
|
|
ImGui::InputTextMultiline("##Key", key_buf, sizeof(key_buf),
|
|
ImVec2(-1, keyDisplay.height > 0 ? keyDisplay.height : 80), ImGuiInputTextFlags_ReadOnly);
|
|
} else {
|
|
// Show masked
|
|
std::string masked(s_key.length(), '*');
|
|
char masked_buf[1024];
|
|
strncpy(masked_buf, masked.c_str(), sizeof(masked_buf) - 1);
|
|
masked_buf[sizeof(masked_buf) - 1] = '\0';
|
|
ImGui::InputTextMultiline("##Key", masked_buf, sizeof(masked_buf),
|
|
ImVec2(-1, keyDisplay.height > 0 ? keyDisplay.height : 80), ImGuiInputTextFlags_ReadOnly);
|
|
}
|
|
|
|
// Show/Hide and Copy buttons
|
|
ImGui::Spacing();
|
|
|
|
if (material::StyledButton(s_show_key ? TR("hide") : TR("show"), ImVec2(toggleBtn.width, 0), S.resolveFont(toggleBtn.font))) {
|
|
s_show_key = !s_show_key;
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
|
|
if (material::StyledButton(TR("copy_to_clipboard"), ImVec2(copyBtn.width, 0), S.resolveFont(copyBtn.font))) {
|
|
ImGui::SetClipboardText(s_key.c_str());
|
|
// Could add a notification here
|
|
}
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
// Close button
|
|
float button_width = closeBtn.width;
|
|
float avail_width = ImGui::GetContentRegionAvail().x;
|
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (avail_width - button_width) / 2.0f);
|
|
|
|
if (material::StyledButton(TR("close"), ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) {
|
|
s_open = false;
|
|
// Clear sensitive data
|
|
s_key.clear();
|
|
s_show_key = false;
|
|
}
|
|
|
|
material::EndOverlayDialog();
|
|
}
|
|
}
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|