From 4b0071fa9169c3e18d32a7624888cbc7300f6141 Mon Sep 17 00:00:00 2001 From: 2ro <17595647+2ro@users.noreply.github.com> Date: Mon, 6 Jul 2026 03:27:56 -0400 Subject: [PATCH] goblin: add-identity sheet gets real Generate/Import buttons, Cancel beneath (Build 150) --- locales/de.yml | 5 +- locales/en.yml | 5 +- locales/fr.yml | 5 +- locales/ru.yml | 5 +- locales/tr.yml | 5 +- locales/zh-CN.yml | 5 +- src/gui/views/goblin/mod.rs | 158 ++++++++++++++++++++---------------- 7 files changed, 102 insertions(+), 86 deletions(-) diff --git a/locales/de.yml b/locales/de.yml index 82b5d4eb..640fb05e 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -702,8 +702,8 @@ goblin: add_title: "Eine Identität hinzufügen" nsec_hint: "Nsec einfügen" generate_note: "Ein neuer, anonymer Schlüssel wird für diese Wallet erstellt." - import_instead: "Stattdessen eine bestehende Identität importieren" - generate_instead: "Stattdessen einen neuen Schlüssel erstellen" + generate: "Erstellen" + import: "Importieren" choose_backup: "Eine .backup-Datei wählen" backup_selected: "Backup-Datei ausgewählt" delete_short: "Identität löschen" @@ -715,7 +715,6 @@ goblin: tag_note: "Gib ihr einen privaten Namen. Nur du siehst ihn — er wird nie geteilt." tag_hint: "Privater Name" tag_save: "Speichern" - add_confirm: "Hinzufügen" pass_prompt: "Dein Wallet-Passwort verschlüsselt und entsperrt jede Identität. Gib es ein, um fortzufahren." working: "Arbeite…" onboarding: diff --git a/locales/en.yml b/locales/en.yml index b39d994b..14f8b84c 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -702,8 +702,8 @@ goblin: add_title: "Add an identity" nsec_hint: "Paste an nsec" generate_note: "A fresh, anonymous key will be created for this wallet." - import_instead: "Import an existing identity instead" - generate_instead: "Create a new key instead" + generate: "Generate" + import: "Import" choose_backup: "Choose a .backup file" backup_selected: "Backup file selected" delete_short: "Delete identity" @@ -715,7 +715,6 @@ goblin: tag_note: "Give it a private name. Only you see it — it is never shared." tag_hint: "Private name" tag_save: "Save" - add_confirm: "Add" pass_prompt: "Your wallet password encrypts and unlocks each identity. Enter it to continue." working: "Working…" onboarding: diff --git a/locales/fr.yml b/locales/fr.yml index 4cd7091e..deaf99c2 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -702,8 +702,8 @@ goblin: add_title: "Ajouter une identité" nsec_hint: "Coller un nsec" generate_note: "Une nouvelle clé anonyme sera créée pour ce portefeuille." - import_instead: "Importer plutôt une identité existante" - generate_instead: "Créer plutôt une nouvelle clé" + generate: "Créer" + import: "Importer" choose_backup: "Choisir un fichier .backup" backup_selected: "Fichier de sauvegarde sélectionné" delete_short: "Supprimer l'identité" @@ -715,7 +715,6 @@ goblin: tag_note: "Donnez-lui un nom privé. Vous seul le voyez — il n’est jamais partagé." tag_hint: "Nom privé" tag_save: "Enregistrer" - add_confirm: "Ajouter" pass_prompt: "Le mot de passe de votre portefeuille chiffre et déverrouille chaque identité. Saisissez-le pour continuer." working: "En cours…" onboarding: diff --git a/locales/ru.yml b/locales/ru.yml index 827d98af..3ec33304 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -702,8 +702,8 @@ goblin: add_title: "Добавить личность" nsec_hint: "Вставьте nsec" generate_note: "Для этого кошелька будет создан новый анонимный ключ." - import_instead: "Вместо этого импортировать существующую личность" - generate_instead: "Вместо этого создать новый ключ" + generate: "Создать" + import: "Импортировать" choose_backup: "Выбрать файл .backup" backup_selected: "Файл резервной копии выбран" delete_short: "Удалить личность" @@ -715,7 +715,6 @@ goblin: tag_note: "Дайте ей приватное имя. Его видите только вы — оно никогда не передаётся." tag_hint: "Приватное имя" tag_save: "Сохранить" - add_confirm: "Добавить" pass_prompt: "Пароль кошелька шифрует и разблокирует каждую личность. Введите его, чтобы продолжить." working: "Обработка…" onboarding: diff --git a/locales/tr.yml b/locales/tr.yml index 2cb6eb62..98db99fa 100644 --- a/locales/tr.yml +++ b/locales/tr.yml @@ -702,8 +702,8 @@ goblin: add_title: "Bir kimlik ekle" nsec_hint: "Bir nsec yapıştır" generate_note: "Bu cüzdan için yeni, anonim bir anahtar oluşturulacak." - import_instead: "Bunun yerine mevcut bir kimliği içe aktar" - generate_instead: "Bunun yerine yeni bir anahtar oluştur" + generate: "Oluştur" + import: "İçe aktar" choose_backup: "Bir .backup dosyası seç" backup_selected: "Yedek dosyası seçildi" delete_short: "Kimliği sil" @@ -715,7 +715,6 @@ goblin: tag_note: "Ona özel bir ad ver. Sadece sen görürsün — asla paylaşılmaz." tag_hint: "Özel ad" tag_save: "Kaydet" - add_confirm: "Ekle" pass_prompt: "Cüzdan parolan her kimliği şifreler ve açar. Devam etmek için gir." working: "Çalışıyor…" onboarding: diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index cbdb174f..38a3d384 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -702,8 +702,8 @@ goblin: add_title: "添加一个身份" nsec_hint: "粘贴 nsec" generate_note: "将为此钱包创建一个全新的匿名密钥。" - import_instead: "改为导入现有身份" - generate_instead: "改为创建新密钥" + generate: "创建" + import: "导入" choose_backup: "选择 .backup 文件" backup_selected: "已选择备份文件" delete_short: "删除身份" @@ -715,7 +715,6 @@ goblin: tag_note: "给它起一个私密名称。只有你能看到——绝不会被分享。" tag_hint: "私密名称" tag_save: "保存" - add_confirm: "添加" pass_prompt: "钱包密码用于加密和解锁每个身份。输入以继续。" working: "处理中…" onboarding: diff --git a/src/gui/views/goblin/mod.rs b/src/gui/views/goblin/mod.rs index 7283081f..4fe617cb 100644 --- a/src/gui/views/goblin/mod.rs +++ b/src/gui/views/goblin/mod.rs @@ -5432,40 +5432,96 @@ impl GoblinWalletView { }); } ui.add_space(10.0); - // ONE mode toggle instead of the old Generate/Import pair (whose - // "Generate new" half was the already-active default and read as - // a dead button). Tapping switches the sheet's mode, revealing or - // hiding the import inputs; the label always names the OTHER mode. - let toggle_label = if self.identity_switch.import { - t!("goblin.identities.generate_instead") - } else { - t!("goblin.identities.import_instead") - }; - if w::big_action_on_card(ui, &toggle_label).clicked() { - self.identity_switch.import = !self.identity_switch.import; - self.identity_switch.error.clear(); - } - ui.add_space(10.0); let import = self.identity_switch.import; - // Generate is always ready; import needs either a selected - // .backup or a pasted nsec. The password is entered in the modal. + let busy = self.identity_switch.busy; + // Two real actions instead of a mode toggle: Generate is always + // ready; Import first reveals the .backup/nsec inputs, then + // confirms once one of them is filled. The password is entered + // in the modal. let has_import = !self.identity_switch.backup_input.trim().is_empty() || self.identity_switch.nsec.trim().starts_with("nsec1"); - let armed = (!import || has_import) && !self.identity_switch.busy; + let import_armed = (!import || has_import) && !busy; + // The password-gated add to launch this frame: `Some(None)` for + // a fresh key, `Some(Some(blob))` for an import. + let mut open_add: Option> = None; ui.horizontal(|ui| { let half = (ui.available_width() - 10.0) / 2.0; + // Generate on the LEFT, the positive (green) action: a + // fresh anonymous key via the existing password step. + ui.scope_builder( + egui::UiBuilder::new().max_rect(egui::Rect::from_min_size( + ui.cursor().min, + Vec2::new(half, 44.0), + )), + |ui| { + ui.add_enabled_ui(!busy, |ui| { + if w::big_action_on_card_ink( + ui, + &t!("goblin.identities.generate"), + if busy { t.surface_text_mute } else { t.pos }, + ) + .clicked() + { + open_add = Some(None); + } + }); + }, + ); + ui.add_space(10.0); + // Import on the RIGHT, neutral: reveal-then-confirm. + ui.scope_builder( + egui::UiBuilder::new().max_rect(egui::Rect::from_min_size( + ui.cursor().min, + Vec2::new(half, 44.0), + )), + |ui| { + ui.add_enabled_ui(import_armed, |ui| { + if w::big_action_on_card_ink( + ui, + &t!("goblin.identities.import"), + if import_armed { + t.surface_text + } else { + t.surface_text_mute + }, + ) + .clicked() + { + if !import { + // First tap: reveal the import inputs. + self.identity_switch.import = true; + self.identity_switch.error.clear(); + } else { + // Confirm: the selected .backup wins over + // a pasted nsec. + let b = self.identity_switch.backup_input.trim(); + let blob = if b.is_empty() { + self.identity_switch.nsec.trim().to_string() + } else { + b.to_string() + }; + open_add = Some(Some(blob)); + } + } + }); + }, + ); + }); + // Cancel centered BENEATH the pair: red, same uniform size. + ui.add_space(10.0); + ui.horizontal(|ui| { + let half = (ui.available_width() - 10.0) / 2.0; + ui.add_space((ui.available_width() - half) / 2.0); ui.scope_builder( egui::UiBuilder::new().max_rect(egui::Rect::from_min_size( ui.cursor().min, Vec2::new(half, 44.0), )), |ui| { - // Uniform paired buttons (the Generate/Import toggle - // pattern): same widget, size and shape for both halves. if w::big_action_on_card_ink( ui, &t!("goblin.settings.cancel"), - t.surface_text, + t.neg, ) .clicked() { @@ -5477,55 +5533,21 @@ impl GoblinWalletView { } }, ); - ui.add_space(10.0); - ui.scope_builder( - egui::UiBuilder::new().max_rect(egui::Rect::from_min_size( - ui.cursor().min, - Vec2::new(half, 44.0), - )), - |ui| { - ui.add_enabled_ui(armed, |ui| { - if w::big_action_on_card_ink( - ui, - &t!("goblin.identities.add_confirm"), - if armed { t.pos } else { t.surface_text_mute }, - ) - .clicked() - { - // Open the password modal to encrypt+store the - // new identity. It is added WITHOUT switching; - // the user activates it later by tapping it. The - // import blob is the selected .backup if any, - // else the pasted nsec. - let import_blob = if import { - let b = self.identity_switch.backup_input.trim(); - if b.is_empty() { - Some( - self.identity_switch - .nsec - .trim() - .to_string(), - ) - } else { - Some(b.to_string()) - } - } else { - None - }; - self.identity_switch.error.clear(); - self.identity_switch.pass.clear(); - self.identity_switch.wrong_pass = false; - self.identity_switch.pending = - Some(PendingPassAction::Add(import_blob)); - Modal::new(IDENTITY_PASS_MODAL) - .position(ModalPosition::CenterTop) - .title(t!("goblin.identities.add_title")) - .show(); - } - }); - }, - ); }); + if let Some(import_blob) = open_add { + // Open the password modal to encrypt+store the new + // identity. It is added WITHOUT switching; the user + // activates it later by tapping it. + self.identity_switch.error.clear(); + self.identity_switch.pass.clear(); + self.identity_switch.wrong_pass = false; + self.identity_switch.pending = + Some(PendingPassAction::Add(import_blob)); + Modal::new(IDENTITY_PASS_MODAL) + .position(ModalPosition::CenterTop) + .title(t!("goblin.identities.add_title")) + .show(); + } }); }