diff --git a/gui/src/app/mod.rs b/gui/src/app/mod.rs index 3aab6ded..37114dd7 100644 --- a/gui/src/app/mod.rs +++ b/gui/src/app/mod.rs @@ -71,9 +71,12 @@ impl App { .into(), menu::Menu::Receive => ReceivePanel::default().into(), menu::Menu::Spend => SpendPanel::new(self.config.clone(), &self.cache.spend_txs).into(), - menu::Menu::CreateSpendTx => { - CreateSpendPanel::new(self.config.clone(), &self.cache.coins).into() - } + menu::Menu::CreateSpendTx => CreateSpendPanel::new( + self.config.clone(), + &self.cache.coins, + self.daemon.config().main_descriptor.timelock_value(), + ) + .into(), }; self.state.load(self.daemon.clone()) } diff --git a/gui/src/app/state/spend/mod.rs b/gui/src/app/state/spend/mod.rs index e66da1a6..67afbab9 100644 --- a/gui/src/app/state/spend/mod.rs +++ b/gui/src/app/state/spend/mod.rs @@ -104,13 +104,13 @@ pub struct CreateSpendPanel { } impl CreateSpendPanel { - pub fn new(config: Config, coins: &[Coin]) -> Self { + pub fn new(config: Config, coins: &[Coin], timelock: u32) -> Self { Self { draft: step::TransactionDraft::default(), current: 0, steps: vec![ Box::new(step::ChooseRecipients::default()), - Box::new(step::ChooseCoins::new(coins.to_vec())), + Box::new(step::ChooseCoins::new(coins.to_vec(), timelock)), Box::new(step::ChooseFeerate::default()), Box::new(step::SaveSpend::new(config)), ], diff --git a/gui/src/app/state/spend/step.rs b/gui/src/app/state/spend/step.rs index e519a475..b2c7a0ed 100644 --- a/gui/src/app/state/spend/step.rs +++ b/gui/src/app/state/spend/step.rs @@ -244,6 +244,7 @@ impl Step for ChooseFeerate { #[derive(Default)] pub struct ChooseCoins { + timelock: u32, coins: Vec<(Coin, bool)>, /// draft output amount must be superior to total input amount. is_valid: bool, @@ -251,8 +252,9 @@ pub struct ChooseCoins { } impl ChooseCoins { - pub fn new(coins: Vec) -> Self { + pub fn new(coins: Vec, timelock: u32) -> Self { Self { + timelock, coins: coins .into_iter() .filter_map(|c| { @@ -315,8 +317,14 @@ impl Step for ChooseCoins { .collect(); } - fn view<'a>(&'a self, _cache: &'a Cache) -> Element<'a, view::Message> { - view::spend::step::choose_coins_view(&self.coins, self.total_needed.as_ref(), self.is_valid) + fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> { + view::spend::step::choose_coins_view( + cache, + self.timelock, + &self.coins, + self.total_needed.as_ref(), + self.is_valid, + ) } } diff --git a/gui/src/app/view/spend/step.rs b/gui/src/app/view/spend/step.rs index 57238dc9..36ea6c34 100644 --- a/gui/src/app/view/spend/step.rs +++ b/gui/src/app/view/spend/step.rs @@ -7,11 +7,13 @@ use liana::miniscript::bitcoin::Amount; use crate::{ app::{ + cache::Cache, error::Error, view::{message::*, modal}, }, daemon::model::Coin, ui::{ + color, component::{ badge, button, card, form, text::{text, Text}, @@ -126,6 +128,8 @@ pub fn choose_feerate_view<'a>( } pub fn choose_coins_view<'a>( + cache: &Cache, + timelock: u32, coins: &[(Coin, bool)], total_needed: Option<&Amount>, is_valid: bool, @@ -136,14 +140,20 @@ pub fn choose_coins_view<'a>( Column::new() .push(text("Choose coins").bold().size(50)) .push( - Column::new().spacing(10).push( - coins - .iter() - .enumerate() - .fold(Column::new().spacing(10), |col, (i, (coin, selected))| { - col.push(coin_list_view(i, coin, *selected)) - }), - ), + Column::new() + .spacing(10) + .push(coins.iter().enumerate().fold( + Column::new().spacing(10), + |col, (i, (coin, selected))| { + col.push(coin_list_view( + i, + coin, + timelock, + cache.blockheight as u32, + *selected, + )) + }, + )), ) .push_maybe(if is_valid { Some(Container::new( @@ -164,7 +174,13 @@ pub fn choose_coins_view<'a>( ) } -fn coin_list_view<'a>(i: usize, coin: &Coin, selected: bool) -> Element<'a, Message> { +fn coin_list_view<'a>( + i: usize, + coin: &Coin, + timelock: u32, + blockheight: u32, + selected: bool, +) -> Element<'a, Message> { Container::new( Button::new( Row::new() @@ -176,7 +192,32 @@ fn coin_list_view<'a>(i: usize, coin: &Coin, selected: bool) -> Element<'a, Mess icon::square_icon() }) .push(badge::coin()) - .push(text(format!("block: {}", coin.block_height.unwrap_or(0))).small()) + .push_maybe(if let Some(b) = coin.block_height { + if blockheight > b as u32 + timelock { + Some(Container::new( + Row::new() + .spacing(5) + .push(text(" 0").small().style(color::ALERT)) + .push( + icon::hourglass_done_icon().small().style(color::ALERT), + ) + .align_items(Alignment::Center), + )) + } else { + Some(Container::new( + Row::new() + .spacing(5) + .push( + text(format!(" {}", b as u32 + timelock - blockheight)) + .small(), + ) + .push(icon::hourglass_icon().small()) + .align_items(Alignment::Center), + )) + } + } else { + None + }) .spacing(10) .align_items(Alignment::Center) .width(Length::Fill),