1
0
forked from GRIN/grim

goblin: min-confirmations setting + integrated node moved to Advanced (148)

MIN CONFIRMATIONS (GRIM parity): the wallet group in Settings gains a
"Minimum confirmations" row just below the Node row, showing the current value
(default 10) and opening a numeric edit modal ported from GRIM's
min_conf_modal_ui (same prompt/error strings, Cancel/Save, CenterTop). The
fork already carried the full backend — WalletConfig::min_confirmations with
MIN_CONFIRMATIONS_DEFAULT=10 and Wallet::update_min_confirmations — and the
value already feeds the spendable/send logic (retrieve_summary_info, send and
receive params) plus the GUI confirmation counts via
data.info.minimum_confirmations, so this only exposes the existing knob.

INTEGRATED NODE -> ADVANCED: the integrated-node tabs (info, metrics, mining
with stratum, node settings) leave main Settings — Goblin is the lighter
client. Their ONE home is now Settings -> Advanced, as a second action on the
"Run your own node" card (live sync status in the label when the node runs),
with back returning to Advanced. The duplicate entry on the node-connection
page is removed too, per the single-home rule; all functionality is kept.
This commit is contained in:
2ro
2026-07-06 00:07:21 -04:00
parent c50918eef7
commit f216b62cc9
7 changed files with 126 additions and 26 deletions
+1
View File
@@ -470,6 +470,7 @@ goblin:
relays: "Relays"
nostr_relays: "Nostr-Relays"
node: "Node"
min_conf: "Mindestbestätigungen"
integrated_node: "Einstellungen des integrierten Nodes"
node_advanced: "Erweitert"
slatepacks: "Slatepacks"
+1
View File
@@ -470,6 +470,7 @@ goblin:
relays: "Relays"
nostr_relays: "Nostr Relays"
node: "Node"
min_conf: "Minimum confirmations"
integrated_node: "Integrated node settings"
node_advanced: "Advanced"
slatepacks: "Slatepacks"
+1
View File
@@ -470,6 +470,7 @@ goblin:
relays: "Relais"
nostr_relays: "Relais Nostr"
node: "Nœud"
min_conf: "Confirmations minimales"
integrated_node: "Paramètres du nœud intégré"
node_advanced: "Avancé"
slatepacks: "Slatepacks"
+1
View File
@@ -470,6 +470,7 @@ goblin:
relays: "Реле"
nostr_relays: "Реле Nostr"
node: "Узел"
min_conf: "Минимум подтверждений"
integrated_node: "Настройки встроенного узла"
node_advanced: "Дополнительно"
slatepacks: "Slatepacks"
+1
View File
@@ -470,6 +470,7 @@ goblin:
relays: "Relaylar"
nostr_relays: "Nostr Relayları"
node: "Düğüm"
min_conf: "Asgari onay sayısı"
integrated_node: "Tümleşik düğüm ayarları"
node_advanced: "Gelişmiş"
slatepacks: "Slatepackler"
+1
View File
@@ -470,6 +470,7 @@ goblin:
relays: "中继"
nostr_relays: "Nostr 中继"
node: "节点"
min_conf: "最少确认数"
integrated_node: "集成节点设置"
node_advanced: "高级"
slatepacks: "Slatepack"
+120 -26
View File
@@ -130,6 +130,8 @@ pub struct GoblinWalletView {
/// "Wipe payment history" tap-twice confirm: armed after the first tap,
/// wipes on the second (cleared once fired).
wipe_confirm: bool,
/// Minimum-confirmations value being edited in its modal (GRIM parity).
min_conf_edit: String,
}
/// Whether the per-identity cue is drawn on activity rows (owner-approved). The
@@ -259,6 +261,7 @@ impl Default for GoblinWalletView {
cancel_msg: None,
copy_flash: None,
wipe_confirm: false,
min_conf_edit: String::new(),
}
}
}
@@ -330,6 +333,10 @@ const IDENTITY_MANAGE_MODAL: &str = "goblin_identity_manage_modal";
/// wallet-password modal executes the delete). Also background-locking.
const IDENTITY_DELETE_MODAL: &str = "goblin_identity_delete_modal";
/// Id of the minimum-confirmations edit modal (GRIM parity: numeric input,
/// Cancel/Save), opened from the Settings wallet group.
const MIN_CONF_MODAL: &str = "goblin_min_conf_modal";
/// A password-gated identity action the modal executes. Switching no longer uses
/// the modal (it is instant and local); only these need the wallet password.
#[derive(Clone)]
@@ -2608,6 +2615,13 @@ impl GoblinWalletView {
SettingsPage::Identities => return self.identities_ui(ui, wallet, cb),
SettingsPage::Main => {}
}
// Minimum-confirmations edit modal (GRIM parity), opened from the
// wallet group below.
if Modal::opened() == Some(MIN_CONF_MODAL) {
Modal::ui(ui.ctx(), cb, |ui, modal, cb| {
self.min_conf_modal_content(ui, wallet, modal, cb);
});
}
ui.add_space(8.0);
ui.label(
RichText::new(t!("goblin.settings.title"))
@@ -2902,22 +2916,26 @@ impl GoblinWalletView {
ui.add_space(16.0);
let mut open_node = false;
let mut open_integrated = false;
let mut open_slatepack = false;
settings_group(ui, &t!("goblin.settings.wallet"), |ui| {
if settings_row_nav(ui, &t!("goblin.settings.node"), &node_summary(wallet)) {
open_node = true;
}
// GRIM's integrated-node tabs (info, metrics, mining, node
// settings), shown in Goblin chrome. Live sync status when
// the node runs, like the Node row above.
let node_value = if crate::node::Node::is_running() {
crate::node::Node::get_sync_status_text()
} else {
String::new()
};
if settings_row_nav(ui, &t!("goblin.settings.integrated_node"), &node_value) {
open_integrated = true;
// Minimum confirmations before received funds are spendable
// (GRIM parity, default 10). Prominent, just below the node
// row; tapping opens the numeric edit modal. The value feeds
// the wallet's spendable/send logic via
// WalletConfig::min_confirmations.
if settings_row_nav(
ui,
&t!("goblin.settings.min_conf"),
&wallet.get_config().min_confirmations.to_string(),
) {
self.min_conf_edit = wallet.get_config().min_confirmations.to_string();
Modal::new(MIN_CONF_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("goblin.settings.min_conf"))
.show();
}
// GRIM's native by-hand slatepack exchange, for when a payment
// can't go through a username.
@@ -2948,11 +2966,6 @@ impl GoblinWalletView {
self.node_secret_input.clear();
self.settings_page = SettingsPage::Node;
}
if open_integrated {
self.node_tab = Box::new(crate::gui::views::network::NetworkNode);
self.node_tab_back = SettingsPage::Main;
self.settings_page = SettingsPage::IntegratedNode;
}
ui.add_space(16.0);
let mut open_pairing = false;
@@ -3369,6 +3382,7 @@ impl GoblinWalletView {
let progress = wallet.repairing_progress();
let mut leave = false;
let mut open_node = false;
let mut open_integrated = false;
{
let adv = &mut self.advanced;
ScrollArea::vertical()
@@ -3405,6 +3419,23 @@ impl GoblinWalletView {
if w::big_action_on_card(ui, &t!("goblin.advanced.manage_node")).clicked() {
open_node = true;
}
ui.add_space(10.0);
// GRIM's integrated-node tabs (info, metrics, mining with
// stratum, node settings) in Goblin chrome. Their ONE home
// (single-home rule): Goblin is the lighter client, so they
// live here under Advanced, not in main Settings.
let node_label = if crate::node::Node::is_running() {
format!(
"{} · {}",
t!("goblin.settings.integrated_node"),
crate::node::Node::get_sync_status_text()
)
} else {
t!("goblin.settings.integrated_node").to_string()
};
if w::big_action_on_card(ui, &node_label).clicked() {
open_integrated = true;
}
});
ui.add_space(12.0);
@@ -3660,12 +3691,18 @@ impl GoblinWalletView {
self.settings_page = SettingsPage::Main;
}
if open_node {
// Advanced → "Manage node connection" opens Goblin's own Node screen
// (its Advanced button reaches the integrated-node tabs from there).
// Advanced → "Manage node connection" opens Goblin's own Node screen.
self.node_url_input.clear();
self.node_secret_input.clear();
self.settings_page = SettingsPage::Node;
}
if open_integrated {
// Advanced is the one home of the integrated-node tabs; back
// returns here.
self.node_tab = Box::new(crate::gui::views::network::NetworkNode);
self.node_tab_back = SettingsPage::Advanced;
self.settings_page = SettingsPage::IntegratedNode;
}
}
/// GRIM's four integrated-node tabs (Info / Metrics / Mining / Settings)
@@ -3855,14 +3892,8 @@ impl GoblinWalletView {
self.node_url_input.clear();
self.node_secret_input.clear();
}
ui.add_space(10.0);
// Advanced: GRIM's integrated-node tabs (info, metrics, mining
// with stratum, node settings) inside Goblin chrome.
if w::big_action(ui, &t!("goblin.settings.node_advanced"), true).clicked() {
self.node_tab = Box::new(crate::gui::views::network::NetworkNode);
self.node_tab_back = SettingsPage::Node;
self.settings_page = SettingsPage::IntegratedNode;
}
// (The integrated-node tabs' single home is Settings → Advanced;
// no duplicate entry here.)
ui.add_space(16.0);
});
}
@@ -5561,6 +5592,69 @@ impl GoblinWalletView {
}
}
/// Content of the minimum-confirmations edit modal — a direct port of GRIM's
/// min_conf_modal_ui (numeric input, invalid-value error, Cancel/Save). The
/// saved value persists in WalletConfig::min_confirmations and feeds the
/// wallet's spendable/send logic on the next balance refresh.
fn min_conf_modal_content(
&mut self,
ui: &mut egui::Ui,
wallet: &Wallet,
modal: &Modal,
cb: &dyn PlatformCallbacks,
) {
let on_save = |s: &mut GoblinWalletView| {
if let Ok(min_conf) = s.min_conf_edit.parse::<u64>() {
wallet.update_min_confirmations(min_conf);
Modal::close();
}
};
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(
RichText::new(t!("wallets.min_tx_conf_count"))
.size(17.0)
.color(Colors::gray()),
);
ui.add_space(8.0);
let mut edit = TextEdit::new(egui::Id::from(modal.id)).h_center().numeric();
edit.ui(ui, &mut self.min_conf_edit, cb);
if edit.enter_pressed {
on_save(self);
}
if self.min_conf_edit.parse::<u64>().is_err() {
ui.add_space(12.0);
ui.label(
RichText::new(t!("network_settings.not_valid_value"))
.size(17.0)
.color(Colors::red()),
);
}
ui.add_space(12.0);
});
ui.scope(|ui| {
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(
ui,
t!("modal.cancel"),
Colors::white_or_black(false),
|| {
Modal::close();
},
);
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
on_save(self);
});
});
});
ui.add_space(6.0);
});
}
/// Inline username-claim widget (availability check + registration).
fn claim_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) {
let t = theme::tokens();