1
0
forked from GRIN/grim

goblin: add-identity sheet gets real Generate/Import buttons, Cancel beneath (Build 150)

This commit is contained in:
2ro
2026-07-06 03:27:56 -04:00
parent 355e606da7
commit c5ee854805
7 changed files with 102 additions and 86 deletions
+2 -3
View File
@@ -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:
+2 -3
View File
@@ -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:
+2 -3
View File
@@ -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 nest 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:
+2 -3
View File
@@ -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:
+2 -3
View File
@@ -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:
+2 -3
View File
@@ -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:
+90 -68
View File
@@ -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<Option<String>> = 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();
}
});
}