feat(settings): redesign the Import Key dialog

Rework the full-node Import Key dialog into a safer, Material-consistent, more
capable flow:

- Security: the key is masked by default (Password) with a reveal (eye) toggle,
  a DialogWarningHeader "only import a key you own" note, and the buffer is
  wiped with sodium_memzero on close and on successful import.
- Progress: import triggers a blocking full-chain rescan; the dialog now shows
  "Importing & rescanning — this can take several minutes" with LoadingDots and
  disables Import while it runs (no double-submit). Offline shows a "connect a
  running node" note and disables Import up front.
- Viewing keys: the classifier gains isViewingKey / isRecognizedImportKey;
  importPrivateKey auto-detects a shielded viewing key (zxviews…) and routes to
  z_importviewingkey (watch-only) vs z_importkey / importprivkey. The live type
  indicator shows Transparent / Shielded spending / Shielded viewing (watch-only).
- Result: on success the imported address is captured from the RPC result and
  shown via AddressCopyField.
- Material restyle (OverlayDialogSpec/BlurFloat + TactileButton + Esc) and full
  i18n (12 new keys x 8 languages; CJK subset rebuilt).

Verified: rendered on dark + light skins; a seeded zxviews… key shows the masked
field + "Shielded viewing key (watch-only)" indicator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-13 12:58:36 -05:00
parent 8293f02e36
commit 46f3001360
15 changed files with 274 additions and 88 deletions

View File

@@ -101,10 +101,18 @@ WalletSecurityController::KeyKind WalletSecurityController::classifyPrivateKey(c
// (The old `key[0]=='s'` test misrouted an uppercase "SK..." shielded key to the transparent RPC.)
if (key.rfind("secret-extended-key-", 0) == 0) return KeyKind::Shielded;
if (key.size() >= 2 && key[0] == 'S' && key[1] == 'K') return KeyKind::Shielded;
if (!key.empty() && key[0] == 's') return KeyKind::Shielded;
// A "z"-prefixed key is a shielded viewing key (zxview…); "s"-prefixed is a legacy Sprout key.
if (!key.empty() && (key[0] == 's' || key[0] == 'z')) return KeyKind::Shielded;
return KeyKind::Transparent;
}
bool WalletSecurityController::isViewingKey(const std::string& key)
{
// Sapling extended full viewing key (mainnet HRP "zxviews"; "zxview" also matches the prefix the
// lite backend recognizes). Watch-only: reveals the address's funds but cannot spend them.
return key.rfind("zxview", 0) == 0;
}
bool WalletSecurityController::isRecognizedPrivateKey(const std::string& key)
{
if (key.rfind("secret-extended-key-", 0) == 0) return true; // Sapling z spending key
@@ -115,6 +123,11 @@ bool WalletSecurityController::isRecognizedPrivateKey(const std::string& key)
return false;
}
bool WalletSecurityController::isRecognizedImportKey(const std::string& key)
{
return isRecognizedPrivateKey(key) || isViewingKey(key);
}
const char* WalletSecurityController::importSuccessMessage(KeyKind kind)
{
return kind == KeyKind::Shielded