From 0c1a1b8d6d6ead20fdb1af659cace229f814d9fb Mon Sep 17 00:00:00 2001 From: edouardparis Date: Tue, 5 Mar 2024 18:15:16 +0100 Subject: [PATCH] gui: add redirection to selected transaction panel --- gui/src/app/menu.rs | 1 + gui/src/app/mod.rs | 13 ++++++ gui/src/app/state/transactions.rs | 75 ++++++++++++++++--------------- gui/src/app/view/home.rs | 27 +---------- gui/src/daemon/mod.rs | 38 ++++++++++++++++ 5 files changed, 93 insertions(+), 61 deletions(-) diff --git a/gui/src/app/menu.rs b/gui/src/app/menu.rs index 07d40f248..c78cd34b7 100644 --- a/gui/src/app/menu.rs +++ b/gui/src/app/menu.rs @@ -5,6 +5,7 @@ pub enum Menu { Receive, PSBTs, Transactions, + TransactionPreSelected(Txid), Settings, Coins, CreateSpendTx, diff --git a/gui/src/app/mod.rs b/gui/src/app/mod.rs index 0586cec48..2a1a4fdee 100644 --- a/gui/src/app/mod.rs +++ b/gui/src/app/mod.rs @@ -84,6 +84,7 @@ impl Panels { Menu::Receive => &self.receive, Menu::PSBTs => &self.psbts, Menu::Transactions => &self.transactions, + Menu::TransactionPreSelected(_) => &self.transactions, Menu::Settings => &self.settings, Menu::Coins => &self.coins, Menu::CreateSpendTx => &self.create_spend, @@ -99,6 +100,7 @@ impl Panels { Menu::Receive => &mut self.receive, Menu::PSBTs => &mut self.psbts, Menu::Transactions => &mut self.transactions, + Menu::TransactionPreSelected(_) => &mut self.transactions, Menu::Settings => &mut self.settings, Menu::Coins => &mut self.coins, Menu::CreateSpendTx => &mut self.create_spend, @@ -152,6 +154,17 @@ impl App { fn set_current_panel(&mut self, menu: Menu) -> Command { match &menu { + menu::Menu::TransactionPreSelected(txid) => { + if let Ok(Some(tx)) = self + .daemon + .get_history_txs(&[*txid]) + .map(|txs| txs.first().cloned()) + { + self.panels.transactions.preselect(tx); + self.panels.current = menu; + return Command::none(); + }; + } menu::Menu::PsbtPreSelected(txid) => { // Get preselected spend from DB in case it's not yet in the cache. // We only need this single spend as we will go straight to its view and not show the PSBTs list. diff --git a/gui/src/app/state/transactions.rs b/gui/src/app/state/transactions.rs index e0da07400..903defadf 100644 --- a/gui/src/app/state/transactions.rs +++ b/gui/src/app/state/transactions.rs @@ -36,7 +36,7 @@ pub struct TransactionsPanel { pending_txs: Vec, txs: Vec, labels_edited: LabelsEdited, - selected_tx: Option, + selected_tx: Option, warning: Option, create_rbf_modal: Option, } @@ -52,16 +52,17 @@ impl TransactionsPanel { create_rbf_modal: None, } } + + pub fn preselect(&mut self, tx: HistoryTransaction) { + self.selected_tx = Some(tx); + self.warning = None; + self.create_rbf_modal = None; + } } impl State for TransactionsPanel { fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> { - if let Some(i) = self.selected_tx { - let tx = if i < self.pending_txs.len() { - &self.pending_txs[i] - } else { - &self.txs[i - self.pending_txs.len()] - }; + if let Some(tx) = self.selected_tx.as_ref() { let content = view::transactions::tx_view( cache, tx, @@ -121,40 +122,42 @@ impl State for TransactionsPanel { return self.reload(daemon); } Message::View(view::Message::Select(i)) => { - self.selected_tx = Some(i); + self.selected_tx = if i < self.pending_txs.len() { + self.pending_txs.get(i).cloned() + } else { + self.txs.get(i - self.pending_txs.len()).cloned() + }; } Message::View(view::Message::CreateRbf(view::CreateRbfMessage::Cancel)) => { self.create_rbf_modal = None; } Message::View(view::Message::CreateRbf(view::CreateRbfMessage::New(is_cancel))) => { - if let Some(idx) = self.selected_tx { - if let Some(tx) = self.pending_txs.get(idx) { - if tx.fee_amount.is_some() { - let tx = tx.clone(); - let txid = tx.tx.txid(); - return Command::perform( - async move { - daemon - // TODO: filter for spending coins when this is possible: - // https://github.com/wizardsardine/liana/issues/677 - .list_coins() - .map(|res| { - res.coins - .iter() - .filter_map(|c| { - if c.outpoint.txid == txid { - c.spend_info.map(|info| info.txid) - } else { - None - } - }) - .collect() - }) - .map_err(|e| e.into()) - }, - move |res| Message::RbfModal(tx, is_cancel, res), - ); - } + if let Some(tx) = &self.selected_tx { + if tx.fee_amount.is_some() { + let tx = tx.clone(); + let txid = tx.tx.txid(); + return Command::perform( + async move { + daemon + // TODO: filter for spending coins when this is possible: + // https://github.com/wizardsardine/liana/issues/677 + .list_coins() + .map(|res| { + res.coins + .iter() + .filter_map(|c| { + if c.outpoint.txid == txid { + c.spend_info.map(|info| info.txid) + } else { + None + } + }) + .collect() + }) + .map_err(|e| e.into()) + }, + move |res| Message::RbfModal(tx, is_cancel, res), + ); } } } diff --git a/gui/src/app/view/home.rs b/gui/src/app/view/home.rs index b26ea223b..df01753df 100644 --- a/gui/src/app/view/home.rs +++ b/gui/src/app/view/home.rs @@ -315,31 +315,8 @@ pub fn payment_view<'a>( .spacing(5), )) .push( - Column::new() - .spacing(20) - // We do not need to display inputs for external incoming transactions - .push_maybe(if tx.is_external() { - None - } else { - Some(super::psbt::inputs_view( - &tx.coins, - &tx.tx, - &tx.labels, - labels_editing, - )) - }) - .push(super::psbt::outputs_view( - &tx.tx, - cache.network, - if tx.is_external() { - None - } else { - Some(tx.change_indexes.clone()) - }, - &tx.labels, - labels_editing, - tx.is_single_payment().is_some(), - )), + button::primary(None, "See transaction details") + .on_press(Message::Menu(Menu::TransactionPreSelected(tx.tx.txid()))), ) .spacing(20), ) diff --git a/gui/src/daemon/mod.rs b/gui/src/daemon/mod.rs index 1964c9817..0b7492a7b 100644 --- a/gui/src/daemon/mod.rs +++ b/gui/src/daemon/mod.rs @@ -184,6 +184,44 @@ pub trait Daemon: Debug { Ok(txs) } + fn get_history_txs( + &self, + txids: &[Txid], + ) -> Result, DaemonError> { + let info = self.get_info()?; + let coins = self.list_coins()?.coins; + let txs = self.list_txs(txids)?.transactions; + let mut txs = txs + .into_iter() + .map(|tx| { + let mut tx_coins = Vec::new(); + let mut change_indexes = Vec::new(); + for coin in &coins { + if coin.outpoint.txid == tx.tx.txid() { + change_indexes.push(coin.outpoint.vout as usize) + } else if tx + .tx + .input + .iter() + .any(|input| input.previous_output == coin.outpoint) + { + tx_coins.push(coin.clone()); + } + } + model::HistoryTransaction::new( + tx.tx, + tx.height, + tx.time, + tx_coins, + change_indexes, + info.network, + ) + }) + .collect(); + load_labels(self, &mut txs)?; + Ok(txs) + } + fn list_pending_txs(&self) -> Result, DaemonError> { let info = self.get_info()?; let coins = self.list_coins()?.coins;