feat(settings): redesign encrypt & change-passphrase modals onto reference design

Migrate the Encrypt Wallet (3-phase) and Change Passphrase dialogs in
renderEncryptWalletDialog onto the settings-modal reference design —
presentation-only, no change to the crypto/state-machine/secret-wipe logic:

- struct-form BeginOverlayDialog(OverlayDialogSpec{BlurFloat, cardWidth, idSuffix})
  replacing the legacy positional overload (same visual style, dp-scaled width)
- amber TextWrapped warning -> material::DialogWarningHeader(wiz_encrypt_warning)
- drop redundant Separator dividers
- every hardcoded English string -> TR() (13 new enc_*/change_pass_* keys added to
  the English source + all 8 res/lang/*.json additively; CJK subset rebuilt)

The strength-meter math, 3-phase transitions, "cannot close while encrypting"
guard, memset/SecureVault buffer wipes, and the encrypt/changePassphrase/vault
async paths are byte-for-byte unchanged. Verified: build + ctest + hygiene clean,
wiz_strength %s format-string safe across all languages, and a 3-lens adversarial
review (secret-hygiene / presentation-only / ui-i18n) returned zero findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-14 11:13:44 -05:00
parent 5dd68dc9bd
commit 625df8abe6
11 changed files with 157 additions and 41 deletions

Binary file not shown.

View File

@@ -116,6 +116,10 @@
"bootstrap_wallet_protected": "(wallet.dat ist geschützt)", "bootstrap_wallet_protected": "(wallet.dat ist geschützt)",
"bootstrap_warning": "Vorhandene Blockdaten (blocks, chainstate, notarizations) werden gelöscht und ersetzt. Ihre wallet.dat wird NICHT verändert oder gelöscht.", "bootstrap_warning": "Vorhandene Blockdaten (blocks, chainstate, notarizations) werden gelöscht und ersetzt. Ihre wallet.dat wird NICHT verändert oder gelöscht.",
"cancel": "Abbrechen", "cancel": "Abbrechen",
"change_pass_confirm": "Neue bestätigen:",
"change_pass_current": "Aktuelle Passphrase:",
"change_pass_new": "Neue Passphrase:",
"change_pass_title": "Passphrase ändern",
"characters": "Zeichen", "characters": "Zeichen",
"chat": "Chat", "chat": "Chat",
"chat_cancel": "Abbrechen", "chat_cancel": "Abbrechen",
@@ -341,6 +345,15 @@
"download_bootstrap": "Bootstrap herunterladen", "download_bootstrap": "Bootstrap herunterladen",
"dragonx_green": "DragonX (Grün)", "dragonx_green": "DragonX (Grün)",
"edit": "Bearbeiten", "edit": "Bearbeiten",
"enc_confirm": "Bestätigen:",
"enc_desc": "Die Verschlüsselung Ihrer Wallet schützt Ihre privaten Schlüssel mit einer Passphrase. Nach der Verschlüsselung wird der Daemon neu gestartet.",
"enc_encrypting": "Wallet wird verschlüsselt...",
"enc_pin_desc": "Mit einer 4-8-stelligen PIN entsperren Sie Ihre Wallet, ohne jedes Mal die vollständige Passphrase einzugeben.",
"enc_pin_set_ok": "PIN erfolgreich festgelegt",
"enc_pin_skipped": "PIN übersprungen. Sie können später in den Einstellungen eine festlegen.",
"enc_pin_vault_fail": "PIN-Tresor konnte nicht erstellt werden",
"enc_success": "Wallet erfolgreich verschlüsselt!",
"enc_wait": "Bitte warten, schließen Sie die Anwendung nicht.",
"error": "Fehler", "error": "Fehler",
"error_format": "Fehler: %s", "error_format": "Fehler: %s",
"est_time_to_block": "Gesch. Zeit bis Block", "est_time_to_block": "Gesch. Zeit bis Block",

View File

@@ -116,6 +116,10 @@
"bootstrap_wallet_protected": "(wallet.dat está protegido)", "bootstrap_wallet_protected": "(wallet.dat está protegido)",
"bootstrap_warning": "Los datos de bloques existentes (blocks, chainstate, notarizations) se eliminarán y reemplazarán. Su wallet.dat NO será modificado ni eliminado.", "bootstrap_warning": "Los datos de bloques existentes (blocks, chainstate, notarizations) se eliminarán y reemplazarán. Su wallet.dat NO será modificado ni eliminado.",
"cancel": "Cancelar", "cancel": "Cancelar",
"change_pass_confirm": "Confirmar nueva:",
"change_pass_current": "Frase de contraseña actual:",
"change_pass_new": "Nueva frase de contraseña:",
"change_pass_title": "Cambiar frase de contraseña",
"characters": "caracteres", "characters": "caracteres",
"chat": "Chat", "chat": "Chat",
"chat_cancel": "Cancelar", "chat_cancel": "Cancelar",
@@ -341,6 +345,15 @@
"download_bootstrap": "Descargar Bootstrap", "download_bootstrap": "Descargar Bootstrap",
"dragonx_green": "DragonX (Verde)", "dragonx_green": "DragonX (Verde)",
"edit": "Editar", "edit": "Editar",
"enc_confirm": "Confirmar:",
"enc_desc": "Cifrar tu monedero protege tus claves privadas con una frase de contraseña. Tras el cifrado, el daemon se reiniciará.",
"enc_encrypting": "Cifrando el monedero...",
"enc_pin_desc": "Un PIN de 4-8 dígitos te permite desbloquear tu monedero sin escribir la frase de contraseña completa cada vez.",
"enc_pin_set_ok": "PIN configurado correctamente",
"enc_pin_skipped": "PIN omitido. Puedes configurar uno más tarde en Ajustes.",
"enc_pin_vault_fail": "No se pudo crear la bóveda del PIN",
"enc_success": "¡Monedero cifrado correctamente!",
"enc_wait": "Espera, no cierres la aplicación.",
"error": "Error", "error": "Error",
"error_format": "Error: %s", "error_format": "Error: %s",
"est_time_to_block": "Tiempo Est. al Bloque", "est_time_to_block": "Tiempo Est. al Bloque",

View File

@@ -116,6 +116,10 @@
"bootstrap_wallet_protected": "(wallet.dat est protégé)", "bootstrap_wallet_protected": "(wallet.dat est protégé)",
"bootstrap_warning": "Les données de blocs existantes (blocks, chainstate, notarizations) seront supprimées et remplacées. Votre wallet.dat ne sera PAS modifié ni supprimé.", "bootstrap_warning": "Les données de blocs existantes (blocks, chainstate, notarizations) seront supprimées et remplacées. Votre wallet.dat ne sera PAS modifié ni supprimé.",
"cancel": "Annuler", "cancel": "Annuler",
"change_pass_confirm": "Confirmer la nouvelle :",
"change_pass_current": "Phrase secrète actuelle :",
"change_pass_new": "Nouvelle phrase secrète :",
"change_pass_title": "Changer la phrase secrète",
"characters": "caractères", "characters": "caractères",
"chat": "Discussion", "chat": "Discussion",
"chat_cancel": "Annuler", "chat_cancel": "Annuler",
@@ -341,6 +345,15 @@
"download_bootstrap": "Télécharger Bootstrap", "download_bootstrap": "Télécharger Bootstrap",
"dragonx_green": "DragonX (Vert)", "dragonx_green": "DragonX (Vert)",
"edit": "Modifier", "edit": "Modifier",
"enc_confirm": "Confirmer :",
"enc_desc": "Chiffrer votre portefeuille protège vos clés privées avec une phrase secrète. Après le chiffrement, le daemon redémarrera.",
"enc_encrypting": "Chiffrement du portefeuille...",
"enc_pin_desc": "Un code PIN de 4 à 8 chiffres vous permet de déverrouiller votre portefeuille sans saisir la phrase secrète complète à chaque fois.",
"enc_pin_set_ok": "Code PIN défini avec succès",
"enc_pin_skipped": "Code PIN ignoré. Vous pourrez en définir un plus tard dans les Paramètres.",
"enc_pin_vault_fail": "Échec de la création du coffre du code PIN",
"enc_success": "Portefeuille chiffré avec succès !",
"enc_wait": "Veuillez patienter, ne fermez pas l'application.",
"error": "Erreur", "error": "Erreur",
"error_format": "Erreur : %s", "error_format": "Erreur : %s",
"est_time_to_block": "Temps est. par bloc", "est_time_to_block": "Temps est. par bloc",

View File

@@ -116,6 +116,10 @@
"bootstrap_wallet_protected": "(wallet.dat は保護されています)", "bootstrap_wallet_protected": "(wallet.dat は保護されています)",
"bootstrap_warning": "既存のブロックデータblocks、chainstate、notarizationsは削除され置き換えられます。wallet.dat は変更・削除されません。", "bootstrap_warning": "既存のブロックデータblocks、chainstate、notarizationsは削除され置き換えられます。wallet.dat は変更・削除されません。",
"cancel": "キャンセル", "cancel": "キャンセル",
"change_pass_confirm": "新しいパスフレーズ(確認):",
"change_pass_current": "現在のパスフレーズ:",
"change_pass_new": "新しいパスフレーズ:",
"change_pass_title": "パスフレーズを変更",
"characters": "文字", "characters": "文字",
"chat": "チャット", "chat": "チャット",
"chat_cancel": "キャンセル", "chat_cancel": "キャンセル",
@@ -341,6 +345,15 @@
"download_bootstrap": "ブートストラップをダウンロード", "download_bootstrap": "ブートストラップをダウンロード",
"dragonx_green": "DragonXグリーン", "dragonx_green": "DragonXグリーン",
"edit": "編集", "edit": "編集",
"enc_confirm": "確認:",
"enc_desc": "ウォレットを暗号化すると、パスフレーズで秘密鍵が保護されます。暗号化後、デーモンが再起動します。",
"enc_encrypting": "ウォレットを暗号化しています...",
"enc_pin_desc": "4〜8桁のPINを使うと、毎回パスフレーズ全体を入力せずにウォレットのロックを解除できます。",
"enc_pin_set_ok": "PINを設定しました",
"enc_pin_skipped": "PINをスキップしました。後で設定で設定できます。",
"enc_pin_vault_fail": "PINボルトの作成に失敗しました",
"enc_success": "ウォレットの暗号化に成功しました!",
"enc_wait": "お待ちください。アプリケーションを閉じないでください。",
"error": "エラー", "error": "エラー",
"error_format": "エラー:%s", "error_format": "エラー:%s",
"est_time_to_block": "予測ブロック時間", "est_time_to_block": "予測ブロック時間",

View File

@@ -116,6 +116,10 @@
"bootstrap_wallet_protected": "(wallet.dat 보호됨)", "bootstrap_wallet_protected": "(wallet.dat 보호됨)",
"bootstrap_warning": "기존 블록 데이터(blocks, chainstate, notarizations)가 삭제되고 교체됩니다. wallet.dat는 수정되거나 삭제되지 않습니다.", "bootstrap_warning": "기존 블록 데이터(blocks, chainstate, notarizations)가 삭제되고 교체됩니다. wallet.dat는 수정되거나 삭제되지 않습니다.",
"cancel": "취소", "cancel": "취소",
"change_pass_confirm": "새 암호 확인:",
"change_pass_current": "현재 암호:",
"change_pass_new": "새 암호:",
"change_pass_title": "암호 변경",
"characters": "문자", "characters": "문자",
"chat": "채팅", "chat": "채팅",
"chat_cancel": "취소", "chat_cancel": "취소",
@@ -341,6 +345,15 @@
"download_bootstrap": "부트스트랩 다운로드", "download_bootstrap": "부트스트랩 다운로드",
"dragonx_green": "DragonX(그린)", "dragonx_green": "DragonX(그린)",
"edit": "편집", "edit": "편집",
"enc_confirm": "확인:",
"enc_desc": "지갑을 암호화하면 암호로 개인 키를 보호합니다. 암호화 후 데몬이 다시 시작됩니다.",
"enc_encrypting": "지갑을 암호화하는 중...",
"enc_pin_desc": "4~8자리 PIN으로 매번 전체 암호를 입력하지 않고도 지갑 잠금을 해제할 수 있습니다.",
"enc_pin_set_ok": "PIN이 설정되었습니다",
"enc_pin_skipped": "PIN을 건너뛰었습니다. 나중에 설정에서 만들 수 있습니다.",
"enc_pin_vault_fail": "PIN 보관소를 만들지 못했습니다",
"enc_success": "지갑이 성공적으로 암호화되었습니다!",
"enc_wait": "잠시 기다려 주세요. 애플리케이션을 닫지 마세요.",
"error": "오류", "error": "오류",
"error_format": "오류: %s", "error_format": "오류: %s",
"est_time_to_block": "예상 블록 시간", "est_time_to_block": "예상 블록 시간",

View File

@@ -116,6 +116,10 @@
"bootstrap_wallet_protected": "(wallet.dat está protegido)", "bootstrap_wallet_protected": "(wallet.dat está protegido)",
"bootstrap_warning": "Os dados de blocos existentes (blocks, chainstate, notarizations) serão excluídos e substituídos. Seu wallet.dat NÃO será modificado ou excluído.", "bootstrap_warning": "Os dados de blocos existentes (blocks, chainstate, notarizations) serão excluídos e substituídos. Seu wallet.dat NÃO será modificado ou excluído.",
"cancel": "Cancelar", "cancel": "Cancelar",
"change_pass_confirm": "Confirmar nova:",
"change_pass_current": "Senha atual:",
"change_pass_new": "Nova senha:",
"change_pass_title": "Alterar senha",
"characters": "caracteres", "characters": "caracteres",
"chat": "Chat", "chat": "Chat",
"chat_cancel": "Cancelar", "chat_cancel": "Cancelar",
@@ -341,6 +345,15 @@
"download_bootstrap": "Baixar Bootstrap", "download_bootstrap": "Baixar Bootstrap",
"dragonx_green": "DragonX (Verde)", "dragonx_green": "DragonX (Verde)",
"edit": "Editar", "edit": "Editar",
"enc_confirm": "Confirmar:",
"enc_desc": "Criptografar sua carteira protege suas chaves privadas com uma senha. Após a criptografia, o daemon será reiniciado.",
"enc_encrypting": "Criptografando a carteira...",
"enc_pin_desc": "Um PIN de 4 a 8 dígitos permite desbloquear sua carteira sem digitar a senha completa toda vez.",
"enc_pin_set_ok": "PIN definido com sucesso",
"enc_pin_skipped": "PIN ignorado. Você pode definir um depois em Configurações.",
"enc_pin_vault_fail": "Falha ao criar o cofre do PIN",
"enc_success": "Carteira criptografada com sucesso!",
"enc_wait": "Aguarde, não feche o aplicativo.",
"error": "Erro", "error": "Erro",
"error_format": "Erro: %s", "error_format": "Erro: %s",
"est_time_to_block": "Tempo Est. por Bloco", "est_time_to_block": "Tempo Est. por Bloco",

View File

@@ -116,6 +116,10 @@
"bootstrap_wallet_protected": "(wallet.dat защищён)", "bootstrap_wallet_protected": "(wallet.dat защищён)",
"bootstrap_warning": "Существующие данные блоков (blocks, chainstate, notarizations) будут удалены и заменены. Ваш wallet.dat НЕ будет изменён или удалён.", "bootstrap_warning": "Существующие данные блоков (blocks, chainstate, notarizations) будут удалены и заменены. Ваш wallet.dat НЕ будет изменён или удалён.",
"cancel": "Отмена", "cancel": "Отмена",
"change_pass_confirm": "Подтвердите новый:",
"change_pass_current": "Текущий пароль:",
"change_pass_new": "Новый пароль:",
"change_pass_title": "Сменить пароль",
"characters": "символов", "characters": "символов",
"chat": "Чат", "chat": "Чат",
"chat_cancel": "Отмена", "chat_cancel": "Отмена",
@@ -341,6 +345,15 @@
"download_bootstrap": "Скачать бутстрап", "download_bootstrap": "Скачать бутстрап",
"dragonx_green": "DragonX (Зелёная)", "dragonx_green": "DragonX (Зелёная)",
"edit": "Редактировать", "edit": "Редактировать",
"enc_confirm": "Подтвердите:",
"enc_desc": "Шифрование кошелька защищает ваши приватные ключи паролем. После шифрования демон перезапустится.",
"enc_encrypting": "Шифрование кошелька...",
"enc_pin_desc": "PIN-код из 48 цифр позволяет разблокировать кошелёк, не вводя полный пароль каждый раз.",
"enc_pin_set_ok": "PIN-код успешно установлен",
"enc_pin_skipped": "PIN-код пропущен. Вы можете задать его позже в настройках.",
"enc_pin_vault_fail": "Не удалось создать хранилище PIN-кода",
"enc_success": "Кошелёк успешно зашифрован!",
"enc_wait": "Пожалуйста, подождите, не закрывайте приложение.",
"error": "Ошибка", "error": "Ошибка",
"error_format": "Ошибка: %s", "error_format": "Ошибка: %s",
"est_time_to_block": "Расч. время до блока", "est_time_to_block": "Расч. время до блока",

View File

@@ -116,6 +116,10 @@
"bootstrap_wallet_protected": "(wallet.dat 已受保护)", "bootstrap_wallet_protected": "(wallet.dat 已受保护)",
"bootstrap_warning": "现有区块数据blocks、chainstate、notarizations将被删除并替换。您的 wallet.dat 不会被修改或删除。", "bootstrap_warning": "现有区块数据blocks、chainstate、notarizations将被删除并替换。您的 wallet.dat 不会被修改或删除。",
"cancel": "取消", "cancel": "取消",
"change_pass_confirm": "确认新密码:",
"change_pass_current": "当前密码短语:",
"change_pass_new": "新密码短语:",
"change_pass_title": "更改密码短语",
"characters": "字符", "characters": "字符",
"chat": "聊天", "chat": "聊天",
"chat_cancel": "取消", "chat_cancel": "取消",
@@ -341,6 +345,15 @@
"download_bootstrap": "下载引导程序", "download_bootstrap": "下载引导程序",
"dragonx_green": "DragonX绿色", "dragonx_green": "DragonX绿色",
"edit": "编辑", "edit": "编辑",
"enc_confirm": "确认:",
"enc_desc": "加密钱包会用密码短语保护您的私钥。加密后,守护进程将重新启动。",
"enc_encrypting": "正在加密钱包...",
"enc_pin_desc": "4-8 位 PIN 码可让您无需每次输入完整密码短语即可解锁钱包。",
"enc_pin_set_ok": "PIN 码设置成功",
"enc_pin_skipped": "已跳过 PIN。您可以稍后在设置中设置。",
"enc_pin_vault_fail": "无法创建 PIN 保险库",
"enc_success": "钱包加密成功!",
"enc_wait": "请稍候,不要关闭应用程序。",
"error": "错误", "error": "错误",
"error_format": "错误:%s", "error_format": "错误:%s",
"est_time_to_block": "预计出块时间", "est_time_to_block": "预计出块时间",

View File

@@ -1125,35 +1125,33 @@ void App::renderEncryptWalletDialog() {
// Encrypt wallet dialog — multi-phase: passphrase → encrypting → PIN setup // Encrypt wallet dialog — multi-phase: passphrase → encrypting → PIN setup
if (show_encrypt_dialog_) { if (show_encrypt_dialog_) {
const char* dlgTitle = (encrypt_dialog_phase_ == EncryptDialogPhase::PinSetup) const char* dlgTitle = (encrypt_dialog_phase_ == EncryptDialogPhase::PinSetup)
? "Quick-Unlock PIN" : "Encrypt Wallet"; ? TR("wiz_pin_title") : TR("settings_encrypt_wallet");
// Prevent closing via X button while encrypting // Prevent closing via X button while encrypting
bool canClose = (encrypt_dialog_phase_ != EncryptDialogPhase::Encrypting); bool canClose = (encrypt_dialog_phase_ != EncryptDialogPhase::Encrypting);
bool* pOpen = canClose ? &show_encrypt_dialog_ : nullptr; bool* pOpen = canClose ? &show_encrypt_dialog_ : nullptr;
if (BeginOverlayDialog(dlgTitle, pOpen, 460.0f, 0.94f)) { OverlayDialogSpec ov;
ov.title = dlgTitle; ov.p_open = pOpen;
ov.style = OverlayStyle::BlurFloat;
ov.cardWidth = 480.0f; ov.idSuffix = "encrypt";
if (BeginOverlayDialog(ov)) {
// ---- Phase 1: Passphrase entry ---- // ---- Phase 1: Passphrase entry ----
if (encrypt_dialog_phase_ == EncryptDialogPhase::PassphraseEntry) { if (encrypt_dialog_phase_ == EncryptDialogPhase::PassphraseEntry) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 0.7f, 0.3f, 1)); DialogWarningHeader(TR("wiz_encrypt_warning"));
ImGui::TextWrapped(ICON_MD_WARNING
" If you lose your passphrase, you lose access to your funds.");
ImGui::PopStyleColor();
ImGui::Spacing(); ImGui::Spacing();
ImGui::TextWrapped("Encrypting your wallet protects your private keys " ImGui::TextWrapped("%s", TR("enc_desc"));
"with a passphrase. After encryption, the daemon will restart.");
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing(); ImGui::Spacing();
ImGui::Text("Passphrase:"); ImGui::TextUnformatted(TR("wiz_passphrase"));
ImGui::PushItemWidth(-1); ImGui::PushItemWidth(-1);
ImGui::InputText("##enc_pass", encrypt_pass_buf_, sizeof(encrypt_pass_buf_), ImGui::InputText("##enc_pass", encrypt_pass_buf_, sizeof(encrypt_pass_buf_),
ImGuiInputTextFlags_Password); ImGuiInputTextFlags_Password);
ImGui::PopItemWidth(); ImGui::PopItemWidth();
ImGui::Text("Confirm:"); ImGui::TextUnformatted(TR("enc_confirm"));
ImGui::PushItemWidth(-1); ImGui::PushItemWidth(-1);
ImGui::InputText("##enc_confirm", encrypt_confirm_buf_, sizeof(encrypt_confirm_buf_), ImGui::InputText("##enc_confirm", encrypt_confirm_buf_, sizeof(encrypt_confirm_buf_),
ImGuiInputTextFlags_Password); ImGuiInputTextFlags_Password);
@@ -1174,7 +1172,7 @@ void App::renderEncryptWalletDialog() {
} }
int classes = (int)hasDigit + (int)hasLower + (int)hasUpper + (int)hasSymbol; int classes = (int)hasDigit + (int)hasLower + (int)hasUpper + (int)hasSymbol;
const char* strengthLabel = "Weak"; const char* strengthLabel = TR("wiz_strength_weak");
ImVec4 strengthCol(0.9f, 0.2f, 0.2f, 1.0f); ImVec4 strengthCol(0.9f, 0.2f, 0.2f, 1.0f);
float strengthPct = 0.25f; float strengthPct = 0.25f;
int tier = 0; // 0=Weak, 1=Fair, 2=Good, 3=Strong int tier = 0; // 0=Weak, 1=Fair, 2=Good, 3=Strong
@@ -1183,9 +1181,9 @@ void App::renderEncryptWalletDialog() {
else if (len >= 8) tier = 1; else if (len >= 8) tier = 1;
// Downgrade one tier when only a single character class is used. // Downgrade one tier when only a single character class is used.
if (classes <= 1 && tier > 0) tier -= 1; if (classes <= 1 && tier > 0) tier -= 1;
if (tier == 3) { strengthLabel = "Strong"; strengthCol = ImVec4(0.3f,0.9f,0.5f,1); strengthPct = 1.0f; } if (tier == 3) { strengthLabel = TR("wiz_strength_strong"); strengthCol = ImVec4(0.3f,0.9f,0.5f,1); strengthPct = 1.0f; }
else if (tier == 2) { strengthLabel = "Good"; strengthCol = ImVec4(0.3f,0.9f,0.5f,1); strengthPct = 0.75f; } else if (tier == 2) { strengthLabel = TR("wiz_strength_good"); strengthCol = ImVec4(0.3f,0.9f,0.5f,1); strengthPct = 0.75f; }
else if (tier == 1) { strengthLabel = "Fair"; strengthCol = ImVec4(1,0.7f,0.3f,1); strengthPct = 0.5f; } else if (tier == 1) { strengthLabel = TR("wiz_strength_fair"); strengthCol = ImVec4(1,0.7f,0.3f,1); strengthPct = 0.5f; }
float barW = ImGui::GetContentRegionAvail().x; float barW = ImGui::GetContentRegionAvail().x;
float barH = 4.0f; float barH = 4.0f;
@@ -1197,7 +1195,7 @@ void App::renderEncryptWalletDialog() {
dl->AddRectFilled(p, ImVec2(p.x + barW * strengthPct, p.y + barH), dl->AddRectFilled(p, ImVec2(p.x + barW * strengthPct, p.y + barH),
ImGui::ColorConvertFloat4ToU32(strengthCol), 2.0f); ImGui::ColorConvertFloat4ToU32(strengthCol), 2.0f);
ImGui::Dummy(ImVec2(barW, barH)); ImGui::Dummy(ImVec2(barW, barH));
ImGui::Text("Strength: %s", strengthLabel); ImGui::Text(TR("wiz_strength"), strengthLabel);
} }
if (!encrypt_status_.empty()) { if (!encrypt_status_.empty()) {
@@ -1210,7 +1208,7 @@ void App::renderEncryptWalletDialog() {
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
ImGui::BeginDisabled(!valid || encrypt_in_progress_); ImGui::BeginDisabled(!valid || encrypt_in_progress_);
if (ui::material::TactileButton("Encrypt Wallet", ImVec2(btnW, 40))) { if (ui::material::TactileButton(TR("settings_encrypt_wallet"), ImVec2(btnW, 40))) {
std::string pass(encrypt_pass_buf_); std::string pass(encrypt_pass_buf_);
enc_dlg_saved_passphrase_ = pass; enc_dlg_saved_passphrase_ = pass;
memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_)); memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_));
@@ -1222,7 +1220,7 @@ void App::renderEncryptWalletDialog() {
ImGui::EndDisabled(); ImGui::EndDisabled();
ImGui::SameLine(); ImGui::SameLine();
if (ui::material::TactileButton("Cancel", ImVec2(btnW, 40))) { if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40))) {
memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_)); memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_));
memset(encrypt_confirm_buf_, 0, sizeof(encrypt_confirm_buf_)); memset(encrypt_confirm_buf_, 0, sizeof(encrypt_confirm_buf_));
show_encrypt_dialog_ = false; show_encrypt_dialog_ = false;
@@ -1231,7 +1229,7 @@ void App::renderEncryptWalletDialog() {
// ---- Phase 2: Encrypting in progress ---- // ---- Phase 2: Encrypting in progress ----
} else if (encrypt_dialog_phase_ == EncryptDialogPhase::Encrypting) { } else if (encrypt_dialog_phase_ == EncryptDialogPhase::Encrypting) {
const char* statusTitle = encrypt_in_progress_ const char* statusTitle = encrypt_in_progress_
? "Encrypting wallet..." : encrypt_status_.c_str(); ? TR("enc_encrypting") : encrypt_status_.c_str();
ImGui::Text("%s", statusTitle); ImGui::Text("%s", statusTitle);
ImGui::Spacing(); ImGui::Spacing();
@@ -1258,7 +1256,7 @@ void App::renderEncryptWalletDialog() {
} }
ImGui::Spacing(); ImGui::Spacing();
ImGui::TextColored(ImVec4(1,1,1,0.4f), "Please wait, do not close the application."); ImGui::TextColored(ImVec4(1,1,1,0.4f), "%s", TR("enc_wait"));
// Transition to PIN phase when encryption finishes successfully // Transition to PIN phase when encryption finishes successfully
if (!encrypt_in_progress_ && encrypt_dialog_phase_ == EncryptDialogPhase::Encrypting) { if (!encrypt_in_progress_ && encrypt_dialog_phase_ == EncryptDialogPhase::Encrypting) {
@@ -1268,23 +1266,20 @@ void App::renderEncryptWalletDialog() {
// ---- Phase 3: PIN setup (after successful encryption) ---- // ---- Phase 3: PIN setup (after successful encryption) ----
} else if (encrypt_dialog_phase_ == EncryptDialogPhase::PinSetup) { } else if (encrypt_dialog_phase_ == EncryptDialogPhase::PinSetup) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.3f, 0.9f, 0.5f, 1)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.3f, 0.9f, 0.5f, 1));
ImGui::Text(ICON_MD_CHECK_CIRCLE " Wallet encrypted successfully!"); ImGui::Text(ICON_MD_CHECK_CIRCLE " %s", TR("enc_success"));
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::Spacing(); ImGui::Spacing();
ImGui::TextWrapped("A 4-8 digit PIN lets you unlock your wallet " ImGui::TextWrapped("%s", TR("enc_pin_desc"));
"without typing the full passphrase every time.");
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing(); ImGui::Spacing();
ImGui::Text("PIN (4-8 digits):"); ImGui::TextUnformatted(TR("wiz_pin_label"));
ImGui::PushItemWidth(-1); ImGui::PushItemWidth(-1);
ImGui::InputText("##enc_dlg_pin", enc_dlg_pin_buf_, sizeof(enc_dlg_pin_buf_), ImGui::InputText("##enc_dlg_pin", enc_dlg_pin_buf_, sizeof(enc_dlg_pin_buf_),
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal); ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
ImGui::PopItemWidth(); ImGui::PopItemWidth();
ImGui::Text("Confirm PIN:"); ImGui::TextUnformatted(TR("wiz_pin_confirm"));
ImGui::PushItemWidth(-1); ImGui::PushItemWidth(-1);
ImGui::InputText("##enc_dlg_pin_confirm", enc_dlg_pin_confirm_buf_, ImGui::InputText("##enc_dlg_pin_confirm", enc_dlg_pin_confirm_buf_,
sizeof(enc_dlg_pin_confirm_buf_), sizeof(enc_dlg_pin_confirm_buf_),
@@ -1304,7 +1299,7 @@ void App::renderEncryptWalletDialog() {
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
ImGui::BeginDisabled(!pinValid || !hasPassphrase || pin_in_progress_); ImGui::BeginDisabled(!pinValid || !hasPassphrase || pin_in_progress_);
if (ui::material::TactileButton("Set PIN", ImVec2(btnW, 40))) { if (ui::material::TactileButton(TR("settings_set_pin"), ImVec2(btnW, 40))) {
pin_in_progress_ = true; pin_in_progress_ = true;
enc_dlg_pin_status_.clear(); enc_dlg_pin_status_.clear();
std::string savedPass = enc_dlg_saved_passphrase_; std::string savedPass = enc_dlg_saved_passphrase_;
@@ -1317,7 +1312,7 @@ void App::renderEncryptWalletDialog() {
settings_->setPinEnabled(true); settings_->setPinEnabled(true);
settings_->save(); settings_->save();
pin_in_progress_ = false; pin_in_progress_ = false;
ui::Notifications::instance().info("PIN set successfully"); ui::Notifications::instance().info(TR("enc_pin_set_ok"));
// Clean up // Clean up
if (!enc_dlg_saved_passphrase_.empty()) { if (!enc_dlg_saved_passphrase_.empty()) {
util::SecureVault::secureZero(&enc_dlg_saved_passphrase_[0], util::SecureVault::secureZero(&enc_dlg_saved_passphrase_[0],
@@ -1328,20 +1323,20 @@ void App::renderEncryptWalletDialog() {
memset(enc_dlg_pin_confirm_buf_, 0, sizeof(enc_dlg_pin_confirm_buf_)); memset(enc_dlg_pin_confirm_buf_, 0, sizeof(enc_dlg_pin_confirm_buf_));
show_encrypt_dialog_ = false; show_encrypt_dialog_ = false;
} else { } else {
enc_dlg_pin_status_ = "Failed to create PIN vault"; enc_dlg_pin_status_ = TR("enc_pin_vault_fail");
pin_in_progress_ = false; pin_in_progress_ = false;
} }
}; };
}); });
} else { } else {
enc_dlg_pin_status_ = "Failed to create PIN vault"; enc_dlg_pin_status_ = TR("enc_pin_vault_fail");
pin_in_progress_ = false; pin_in_progress_ = false;
} }
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
ImGui::SameLine(); ImGui::SameLine();
if (ui::material::TactileButton("Skip", ImVec2(btnW, 40))) { if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(btnW, 40))) {
if (!enc_dlg_saved_passphrase_.empty()) { if (!enc_dlg_saved_passphrase_.empty()) {
util::SecureVault::secureZero(&enc_dlg_saved_passphrase_[0], util::SecureVault::secureZero(&enc_dlg_saved_passphrase_[0],
enc_dlg_saved_passphrase_.size()); enc_dlg_saved_passphrase_.size());
@@ -1350,8 +1345,7 @@ void App::renderEncryptWalletDialog() {
memset(enc_dlg_pin_buf_, 0, sizeof(enc_dlg_pin_buf_)); memset(enc_dlg_pin_buf_, 0, sizeof(enc_dlg_pin_buf_));
memset(enc_dlg_pin_confirm_buf_, 0, sizeof(enc_dlg_pin_confirm_buf_)); memset(enc_dlg_pin_confirm_buf_, 0, sizeof(enc_dlg_pin_confirm_buf_));
show_encrypt_dialog_ = false; show_encrypt_dialog_ = false;
ui::Notifications::instance().info( ui::Notifications::instance().info(TR("enc_pin_skipped"));
"PIN skipped. You can set one later in Settings.");
} }
} }
EndOverlayDialog(); EndOverlayDialog();
@@ -1367,21 +1361,25 @@ void App::renderEncryptWalletDialog() {
// Change passphrase dialog // Change passphrase dialog
if (show_change_passphrase_) { if (show_change_passphrase_) {
if (BeginOverlayDialog("Change Passphrase", &show_change_passphrase_, 440.0f, 0.94f)) { OverlayDialogSpec ov;
ov.title = TR("change_pass_title"); ov.p_open = &show_change_passphrase_;
ov.style = OverlayStyle::BlurFloat;
ov.cardWidth = 460.0f; ov.idSuffix = "changepass";
if (BeginOverlayDialog(ov)) {
ImGui::Text("Current Passphrase:"); ImGui::TextUnformatted(TR("change_pass_current"));
ImGui::PushItemWidth(-1); ImGui::PushItemWidth(-1);
ImGui::InputText("##chg_old", change_old_pass_buf_, sizeof(change_old_pass_buf_), ImGui::InputText("##chg_old", change_old_pass_buf_, sizeof(change_old_pass_buf_),
ImGuiInputTextFlags_Password); ImGuiInputTextFlags_Password);
ImGui::PopItemWidth(); ImGui::PopItemWidth();
ImGui::Text("New Passphrase:"); ImGui::TextUnformatted(TR("change_pass_new"));
ImGui::PushItemWidth(-1); ImGui::PushItemWidth(-1);
ImGui::InputText("##chg_new", change_new_pass_buf_, sizeof(change_new_pass_buf_), ImGui::InputText("##chg_new", change_new_pass_buf_, sizeof(change_new_pass_buf_),
ImGuiInputTextFlags_Password); ImGuiInputTextFlags_Password);
ImGui::PopItemWidth(); ImGui::PopItemWidth();
ImGui::Text("Confirm New:"); ImGui::TextUnformatted(TR("change_pass_confirm"));
ImGui::PushItemWidth(-1); ImGui::PushItemWidth(-1);
ImGui::InputText("##chg_confirm", change_confirm_buf_, sizeof(change_confirm_buf_), ImGui::InputText("##chg_confirm", change_confirm_buf_, sizeof(change_confirm_buf_),
ImGuiInputTextFlags_Password); ImGuiInputTextFlags_Password);
@@ -1396,7 +1394,7 @@ void App::renderEncryptWalletDialog() {
strlen(change_new_pass_buf_) >= 8 && strlen(change_new_pass_buf_) >= 8 &&
strcmp(change_new_pass_buf_, change_confirm_buf_) == 0; strcmp(change_new_pass_buf_, change_confirm_buf_) == 0;
ImGui::BeginDisabled(!valid || encrypt_in_progress_); ImGui::BeginDisabled(!valid || encrypt_in_progress_);
if (ui::material::TactileButton("Change Passphrase", ImVec2(-1, 40))) { if (ui::material::TactileButton(TR("change_pass_title"), ImVec2(-1, 40))) {
changePassphrase(std::string(change_old_pass_buf_), changePassphrase(std::string(change_old_pass_buf_),
std::string(change_new_pass_buf_)); std::string(change_new_pass_buf_));
} }

View File

@@ -508,6 +508,20 @@ void I18n::loadBuiltinEnglish()
strings_["wiz_encrypt_desc"] = "Encrypt your wallet to protect private keys with a passphrase."; strings_["wiz_encrypt_desc"] = "Encrypt your wallet to protect private keys with a passphrase.";
strings_["wiz_encrypt_warning"] = "If you lose your passphrase, you lose access to your funds."; strings_["wiz_encrypt_warning"] = "If you lose your passphrase, you lose access to your funds.";
strings_["wiz_passphrase"] = "Passphrase:"; strings_["wiz_passphrase"] = "Passphrase:";
// --- Encrypt / change-passphrase dialogs (settings) ---
strings_["enc_desc"] = "Encrypting your wallet protects your private keys with a passphrase. After encryption, the daemon will restart.";
strings_["enc_confirm"] = "Confirm:";
strings_["enc_encrypting"] = "Encrypting wallet...";
strings_["enc_wait"] = "Please wait, do not close the application.";
strings_["enc_success"] = "Wallet encrypted successfully!";
strings_["enc_pin_desc"] = "A 4-8 digit PIN lets you unlock your wallet without typing the full passphrase every time.";
strings_["enc_pin_set_ok"] = "PIN set successfully";
strings_["enc_pin_vault_fail"] = "Failed to create PIN vault";
strings_["enc_pin_skipped"] = "PIN skipped. You can set one later in Settings.";
strings_["change_pass_title"] = "Change Passphrase";
strings_["change_pass_current"] = "Current Passphrase:";
strings_["change_pass_new"] = "New Passphrase:";
strings_["change_pass_confirm"] = "Confirm New:";
strings_["wiz_confirm"] = "Confirm:"; strings_["wiz_confirm"] = "Confirm:";
strings_["wiz_strength_weak"] = "Weak"; strings_["wiz_strength_weak"] = "Weak";
strings_["wiz_strength_fair"] = "Fair"; strings_["wiz_strength_fair"] = "Fair";