diff --git a/src/gui/views/goblin/send.rs b/src/gui/views/goblin/send.rs index a80c286f..a82fa939 100644 --- a/src/gui/views/goblin/send.rs +++ b/src/gui/views/goblin/send.rs @@ -138,6 +138,9 @@ pub struct SendFlow { /// Set when the success screen's "Receipt" button is tapped: the host view /// opens the receipt for the latest tx with this npub after the flow closes. pub receipt_npub: Option, + /// Atomic amount (nanogrin) we last asked the wallet to price, so the review + /// page dispatches one `CalculateFee` per amount instead of every frame. + fee_requested_for: Option, } impl Default for SendFlow { @@ -162,6 +165,7 @@ impl Default for SendFlow { scan_open: false, request: false, receipt_npub: None, + fee_requested_for: None, } } } @@ -1115,11 +1119,24 @@ impl SendFlow { &t!("goblin.send.row_delivery_val"), ); } else { - w::info_row( - ui, - &t!("goblin.send.row_network_fee"), - &t!("goblin.send.row_network_fee_val"), - ); + // Live network fee for this exact amount, priced by the wallet (one + // async CalculateFee per amount, like GRIM's send modal). Until the + // first result lands we show an ellipsis rather than a wrong number. + let amount_nano = amount_from_hr_string(&amount).unwrap_or(0); + if amount_nano > 0 && self.fee_requested_for != Some(amount_nano) { + self.fee_requested_for = Some(amount_nano); + wallet.task(WalletTask::CalculateFee(amount_nano, 0)); + } + let fee_val = match wallet.calculated_fee(amount_nano) { + Some(fee) => format!("{} {}", w::amount_str(fee), w::TSU), + None => { + // Result lands on a worker thread; poll until it does. + ui.ctx() + .request_repaint_after(std::time::Duration::from_millis(120)); + "…".to_string() + } + }; + w::info_row(ui, &t!("goblin.send.row_network_fee"), &fee_val); w::info_row( ui, &t!("goblin.send.row_privacy"), diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index 172554c2..3cd739a5 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -126,6 +126,9 @@ pub struct Wallet { /// Amount requests to calculate fee. fee_calculating: Arc, + /// Last calculated network fee as `(amount, fee)` so a screen can read the + /// fee for the amount it is showing without owning the task-result slot. + last_fee: Arc>>, /// Flag to check if sending request is creating. send_creating: Arc, @@ -176,6 +179,7 @@ impl Wallet { message_opening: Arc::new(AtomicBool::from(false)), send_creating: Arc::new(AtomicBool::new(false)), fee_calculating: Arc::new(AtomicU8::new(0)), + last_fee: Arc::new(RwLock::new(None)), invoice_creating: Arc::new(AtomicBool::new(false)), proof_verifying: Arc::new(AtomicBool::new(false)), tasks_sender: Arc::new(RwLock::new(None)), @@ -1389,6 +1393,14 @@ impl Wallet { self.fee_calculating.load(Ordering::Relaxed) > 0 } + /// Last calculated network fee for `amount`, if one is cached. Returns + /// `None` until a `CalculateFee` task for that exact amount has completed. + pub fn calculated_fee(&self, amount: u64) -> Option { + self.last_fee + .read() + .and_then(|(a, f)| if a == amount { Some(f) } else { None }) + } + /// Initialize a transaction to send amount. fn send(&self, a: u64, dest: Option) -> Result { let config = self.get_config(); @@ -2154,6 +2166,7 @@ async fn handle_task(w: &Wallet, t: WalletTask) { } // Calculate fee for provided amount. if let Ok(fee) = w.calculate_fee(*a) { + *w.last_fee.write() = Some((*a, fee)); w.on_task_result(None, &WalletTask::CalculateFee(*a, fee)) } let calculating = w.fee_calculating.load(Ordering::Relaxed);