feat(keys): improve the key-export modal — auto-clear copy, inline QR, cleaner actions
- Auto-clear: the Copy button now routes through App::copySecretToClipboard, so a copied private/viewing key is wiped from the clipboard after ~45s (same protection as the seed) with a "auto-clears" notice — instead of the raw SetClipboardText that left it indefinitely. - QR: once the key is revealed, a Show/Hide QR toggle renders the key's QR inline (via the same GenerateQRTexture/RenderQRCode widget the Receive tab uses) for scanning into another wallet. The QR texture is cached, regenerated on key change, and freed on hide/close/dismiss; hiding the key also hides its QR. - Actions row tightened to Show/Hide · Copy · QR, and the key + QR texture are now cleared on any dismissal (Close button, scrim click, Esc), not just the Close button. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,9 @@
|
||||
#include "../../util/i18n.h"
|
||||
#include "../schema/ui_schema.h"
|
||||
#include "../material/draw_helpers.h"
|
||||
#include "../material/type.h"
|
||||
#include "../material/colors.h"
|
||||
#include "../widgets/qr_code.h"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace dragonx {
|
||||
@@ -18,16 +21,27 @@ namespace ui {
|
||||
bool KeyExportDialog::s_open = false;
|
||||
bool KeyExportDialog::s_fetching = false;
|
||||
bool KeyExportDialog::s_show_key = false;
|
||||
bool KeyExportDialog::s_show_qr = false;
|
||||
std::uintptr_t KeyExportDialog::s_qr_tex = 0;
|
||||
std::string KeyExportDialog::s_qr_cached;
|
||||
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::releaseQr()
|
||||
{
|
||||
if (s_qr_tex) { FreeQRTexture(s_qr_tex); s_qr_tex = 0; }
|
||||
s_qr_cached.clear();
|
||||
s_show_qr = false;
|
||||
}
|
||||
|
||||
void KeyExportDialog::show(const std::string& address, KeyType type)
|
||||
{
|
||||
s_open = true;
|
||||
s_fetching = false;
|
||||
s_show_key = false;
|
||||
releaseQr();
|
||||
s_key_type = type;
|
||||
s_address = address;
|
||||
s_key.clear();
|
||||
@@ -198,18 +212,43 @@ void KeyExportDialog::render(App* app)
|
||||
ImVec2(-1, keyDisplay.height > 0 ? keyDisplay.height : 80), ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
|
||||
// Show/Hide and Copy buttons
|
||||
// Action row: Show/Hide · Copy · QR
|
||||
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;
|
||||
if (!s_show_key) s_show_qr = false; // hiding the key also hides its QR
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
// Auto-clearing clipboard: the key (as sensitive as the seed) is wiped after ~45s.
|
||||
app->copySecretToClipboard(s_key);
|
||||
}
|
||||
|
||||
// QR (only once revealed) — for scanning the key into another wallet.
|
||||
if (s_show_key) {
|
||||
ImGui::SameLine();
|
||||
if (material::StyledButton(s_show_qr ? TR("hide_qr") : TR("show_qr"),
|
||||
ImVec2(copyBtn.width, 0), S.resolveFont(copyBtn.font))) {
|
||||
s_show_qr = !s_show_qr;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_show_qr && s_show_key && !s_key.empty()) {
|
||||
if (s_qr_cached != s_key) { // (re)generate the QR texture when the key changes
|
||||
if (s_qr_tex) { FreeQRTexture(s_qr_tex); s_qr_tex = 0; }
|
||||
int qw = 0, qh = 0;
|
||||
s_qr_tex = GenerateQRTexture(s_key.c_str(), &qw, &qh);
|
||||
s_qr_cached = s_key;
|
||||
}
|
||||
if (s_qr_tex) {
|
||||
ImGui::Spacing();
|
||||
const float qrSize = 180.0f;
|
||||
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - qrSize) * 0.5f);
|
||||
RenderQRCode(s_qr_tex, qrSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,10 +266,18 @@ void KeyExportDialog::render(App* app)
|
||||
// Clear sensitive data
|
||||
s_key.clear();
|
||||
s_show_key = false;
|
||||
releaseQr();
|
||||
}
|
||||
|
||||
material::EndOverlayDialog();
|
||||
}
|
||||
|
||||
// Dialog dismissed any other way (scrim click / Esc): drop the key + its QR texture.
|
||||
if (!s_open) {
|
||||
if (!s_key.empty()) s_key.clear();
|
||||
s_show_key = false;
|
||||
releaseQr();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace dragonx {
|
||||
@@ -47,10 +48,15 @@ private:
|
||||
static bool s_open;
|
||||
static bool s_fetching;
|
||||
static bool s_show_key;
|
||||
static bool s_show_qr; // inline QR of the revealed key (for scanning into another wallet)
|
||||
static std::uintptr_t s_qr_tex; // cached QR texture handle (0 = none)
|
||||
static std::string s_qr_cached; // key the cached QR was generated for
|
||||
static KeyType s_key_type;
|
||||
static std::string s_address;
|
||||
static std::string s_key;
|
||||
static std::string s_error;
|
||||
|
||||
static void releaseQr(); // free the QR texture + reset its state
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -673,6 +673,8 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["address_details"] = "Address Details";
|
||||
strings_["view_on_explorer"] = "View on Explorer";
|
||||
strings_["qr_code"] = "QR Code";
|
||||
strings_["show_qr"] = "Show QR";
|
||||
strings_["hide_qr"] = "Hide QR";
|
||||
strings_["request_payment"] = "Request Payment";
|
||||
|
||||
// Transactions Tab
|
||||
|
||||
Reference in New Issue
Block a user