diff --git a/gui/src/app/message.rs b/gui/src/app/message.rs index 6496df2e..0a0ae5d2 100644 --- a/gui/src/app/message.rs +++ b/gui/src/app/message.rs @@ -6,7 +6,7 @@ use liana::{ miniscript::bitcoin::{ bip32::{ChildNumber, Fingerprint}, psbt::Psbt, - Address, + Address, Txid, }, }; @@ -30,6 +30,7 @@ pub enum Message { Labels(Result, Error>), SpendTxs(Result, Error>), Psbt(Result), + RbfPsbt(Result), Recovery(Result), Signed(Fingerprint, Result), WalletRegistered(Result), diff --git a/gui/src/app/state/transactions.rs b/gui/src/app/state/transactions.rs index 0967a85b..3e03bb0d 100644 --- a/gui/src/app/state/transactions.rs +++ b/gui/src/app/state/transactions.rs @@ -65,11 +65,7 @@ impl State for TransactionsPanel { self.warning.as_ref(), ); if let Some(modal) = &self.create_rbf_modal { - Modal::new(content, modal.view()) - .on_blur(Some(view::Message::CreateRbf( - view::CreateRbfMessage::Cancel, - ))) - .into() + modal.view(content) } else { content } @@ -252,9 +248,11 @@ pub struct CreateRbfModal { feerate_val: form::Value, /// Parsed feerate. feerate_vb: Option, - warning: Option, /// Replacement transaction ID. replacement_txid: Option, + + processing: bool, + warning: Option, } impl CreateRbfModal { @@ -274,8 +272,9 @@ impl CreateRbfModal { } else { Some(min_feerate_vb) }, - warning: None, replacement_txid: None, + warning: None, + processing: false, } } @@ -301,43 +300,64 @@ impl CreateRbfModal { self.feerate_vb = None; } } + Message::RbfPsbt(res) => { + self.processing = false; + match res { + Ok(txid) => { + self.replacement_txid = Some(txid); + } + Err(e) => self.warning = Some(e), + } + } Message::View(view::Message::CreateRbf(view::CreateRbfMessage::Confirm)) => { self.warning = None; - - let psbt = match daemon.rbf_psbt(&self.txid, self.is_cancel, self.feerate_vb) { - Ok(res) => match res { - CreateSpendResult::Success { psbt, .. } => psbt, - CreateSpendResult::InsufficientFunds { missing } => { - self.warning = Some( - SpendCreationError::CoinSelection( - liana::spend::InsufficientFunds { missing }, - ) - .into(), - ); - return Command::none(); - } - }, - Err(e) => { - self.warning = Some(e.into()); - return Command::none(); - } - }; - if let Err(e) = daemon.update_spend_tx(&psbt) { - self.warning = Some(e.into()); - return Command::none(); - } - self.replacement_txid = Some(psbt.unsigned_tx.txid()); + self.processing = true; + return Command::perform( + rbf(daemon, self.txid, self.is_cancel, self.feerate_vb), + Message::RbfPsbt, + ); } _ => {} } Command::none() } - fn view(&self) -> Element { - view::transactions::create_rbf_modal( - self.is_cancel, - &self.feerate_val, - self.replacement_txid, - self.warning.as_ref(), - ) + fn view<'a>(&'a self, content: Element<'a, view::Message>) -> Element { + let modal = Modal::new( + content, + view::transactions::create_rbf_modal( + self.is_cancel, + &self.feerate_val, + self.replacement_txid, + self.warning.as_ref(), + ), + ); + if self.processing { + modal + } else { + modal.on_blur(Some(view::Message::CreateRbf( + view::CreateRbfMessage::Cancel, + ))) + } + .into() } } + +async fn rbf( + daemon: Arc, + txid: Txid, + is_cancel: bool, + feerate_vb: Option, +) -> Result { + let res = daemon.rbf_psbt(&txid, is_cancel, feerate_vb)?; + let psbt = match res { + CreateSpendResult::Success { psbt, .. } => psbt, + CreateSpendResult::InsufficientFunds { missing } => { + return Err( + SpendCreationError::CoinSelection(liana::spend::InsufficientFunds { missing }) + .into(), + ); + } + }; + daemon.update_spend_tx(&psbt)?; + Ok(psbt.unsigned_tx.txid()) +}