diff --git a/gui/src/app/state/mod.rs b/gui/src/app/state/mod.rs index 2b024a06..59a90fda 100644 --- a/gui/src/app/state/mod.rs +++ b/gui/src/app/state/mod.rs @@ -74,7 +74,7 @@ impl State for Home { return view::modal( false, self.warning.as_ref(), - view::home::event_view(&self.events[i]), + view::home::event_view(cache, &self.events[i]), ); } view::dashboard( diff --git a/gui/src/app/view/home.rs b/gui/src/app/view/home.rs index 5869eb93..41f77a01 100644 --- a/gui/src/app/view/home.rs +++ b/gui/src/app/view/home.rs @@ -8,11 +8,15 @@ use iced::{ use crate::ui::{ component::{badge, button::Style, card, text::*}, + icon, util::Collection, }; use liana::miniscript::bitcoin; -use crate::{app::view::message::Message, daemon::model::HistoryTransaction}; +use crate::{ + app::{cache::Cache, view::message::Message}, + daemon::model::HistoryTransaction, +}; pub const HISTORY_EVENT_PAGE_SIZE: u64 = 20; @@ -123,7 +127,7 @@ fn event_list_view<'a>(i: usize, event: &HistoryTransaction) -> Element<'a, Mess .into() } -pub fn event_view<'a>(event: &HistoryTransaction) -> Element<'a, Message> { +pub fn event_view<'a>(cache: &Cache, event: &'a HistoryTransaction) -> Element<'a, Message> { Column::new() .push( Row::new() @@ -164,14 +168,37 @@ pub fn event_view<'a>(event: &HistoryTransaction) -> Element<'a, Message> { .push( Row::new() .width(Length::Fill) + .align_items(Alignment::Center) .push(Container::new(text("Txid:").bold()).width(Length::Fill)) .push( - Container::new(text(format!("{}", event.tx.txid()))) + Row::new() + .align_items(Alignment::Center) + .push(Container::new(text(format!("{}", event.tx.txid())).small())) + .push( + Button::new(icon::clipboard_icon()) + .on_press(Message::Clipboard(event.tx.txid().to_string())) + .style(Style::TransparentBorder.into()), + ) .width(Length::Shrink), ), ) .spacing(5), )) + .push(super::spend::detail::inputs_and_outputs_view( + &event.coins, + &event.tx, + cache.network, + if event.is_external() { + None + } else { + Some(event.change_indexes.clone()) + }, + if event.is_external() { + Some(event.change_indexes.clone()) + } else { + None + }, + )) .align_items(Alignment::Center) .spacing(20) .max_width(750) diff --git a/gui/src/app/view/spend/detail.rs b/gui/src/app/view/spend/detail.rs index dd3717f8..1646c6b9 100644 --- a/gui/src/app/view/spend/detail.rs +++ b/gui/src/app/view/spend/detail.rs @@ -3,10 +3,7 @@ use iced::{ Alignment, Element, Length, }; -use liana::miniscript::bitcoin::{ - util::{bip32::Fingerprint, psbt::Psbt}, - Address, Amount, Network, -}; +use liana::miniscript::bitcoin::{util::bip32::Fingerprint, Address, Amount, Network, Transaction}; use crate::{ app::{ @@ -18,7 +15,9 @@ use crate::{ ui::{ color, component::{ - badge, button, card, container, separation, + badge, button, card, + collapse::Collapse, + container, separation, text::{text, Text}, }, icon, @@ -28,7 +27,7 @@ use crate::{ pub fn spend_view<'a, T: Into>>( warning: Option<&Error>, - tx: &SpendTx, + tx: &'a SpendTx, action: T, show_delete: bool, network: Network, @@ -44,9 +43,10 @@ pub fn spend_view<'a, T: Into>>( .push(spend_overview_view(tx)) .push(inputs_and_outputs_view( &tx.coins, - &tx.psbt, + &tx.psbt.unsigned_tx, network, - tx.change_index, + tx.change_index.map(|i| vec![i]), + None, )), ) } @@ -252,81 +252,212 @@ fn spend_overview_view<'a>(tx: &SpendTx) -> Element<'a, Message> { .into() } -fn inputs_and_outputs_view<'a>( - coins: &[Coin], - psbt: &Psbt, +pub fn inputs_and_outputs_view<'a>( + coins: &'a [Coin], + tx: &'a Transaction, network: Network, - change_index: Option, + change_indexes: Option>, + receive_indexes: Option>, ) -> Element<'a, Message> { Column::new() .push( - Row::new() + Column::new() .spacing(10) - .push( - Column::new() - .spacing(10) - .push(text("Spent coins:").bold()) - .push(coins.iter().fold(Column::new().spacing(10), |col, coin| { - col.push( - card::simple( - Column::new() - .width(Length::Fill) - .push(text(format!("{} BTC", coin.amount.to_btc())).bold()) - .push(text(format!("{}", coin.outpoint)).small()), + .push_maybe(if !coins.is_empty() { + Some( + Container::new(Collapse::new( + move || { + Button::new( + Row::new() + .align_items(Alignment::Center) + .push( + text(format!( + "{} spent coin{}", + coins.len(), + if coins.len() == 1 { "" } else { "s" } + )) + .bold() + .width(Length::Fill), + ) + .push(icon::collapse_icon()), ) - .width(Length::Fill), - ) - })) - .width(Length::FillPortion(1)), - ) + .padding(15) + .width(Length::Fill) + .style(button::Style::TransparentBorder.into()) + }, + move || { + Button::new( + Row::new() + .align_items(Alignment::Center) + .push( + text(format!( + "{} spent coin{}", + coins.len(), + if coins.len() == 1 { "" } else { "s" } + )) + .bold() + .width(Length::Fill), + ) + .push(icon::collapsed_icon()), + ) + .padding(15) + .width(Length::Fill) + .style(button::Style::TransparentBorder.into()) + }, + move || { + coins + .iter() + .fold(Column::new(), |col: Column<'a, Message>, coin| { + col.push(separation().width(Length::Fill)).push( + Row::new() + .padding(15) + .align_items(Alignment::Center) + .width(Length::Fill) + .push( + Row::new() + .width(Length::Fill) + .align_items(Alignment::Center) + .push( + text(coin.outpoint.to_string()) + .small() + ) + .push( + Button::new(icon::clipboard_icon()) + .on_press(Message::Clipboard( + coin.outpoint.to_string(), + )) + .style( + button::Style::TransparentBorder.into(), + ), + ), + ) + .push( + text(format!("{} BTC", coin.amount.to_btc())) + .bold(), + ), + ) + }) + .into() + }, + )) + .style(card::SimpleCardStyle), + ) + } else { + None + }) .push( - Column::new() - .spacing(10) - .push(text("Recipients:").bold()) - .push(psbt.unsigned_tx.output.iter().enumerate().fold( - Column::new().spacing(10), - |col, (i, output)| { - col.push( - card::simple( + Container::new(Collapse::new( + move || { + Button::new( + Row::new() + .align_items(Alignment::Center) + .push( + text(format!( + "{} recipient{}", + tx.output.len(), + if tx.output.len() == 1 { "" } else { "s" } + )) + .bold() + .width(Length::Fill), + ) + .push(icon::collapse_icon()), + ) + .padding(15) + .width(Length::Fill) + .style(button::Style::TransparentBorder.into()) + }, + move || { + Button::new( + Row::new() + .align_items(Alignment::Center) + .push( + text(format!( + "{} recipient{}", + tx.output.len(), + if tx.output.len() == 1 { "" } else { "s" } + )) + .bold() + .width(Length::Fill), + ) + .push(icon::collapsed_icon()), + ) + .padding(15) + .width(Length::Fill) + .style(button::Style::TransparentBorder.into()) + }, + move || { + tx.output + .iter() + .enumerate() + .fold(Column::new(), |col: Column<'a, Message>, (i, output)| { + let addr = Address::from_script(&output.script_pubkey, network).unwrap(); + col.push(separation().width(Length::Fill)).push( Column::new() + .padding(15) + .width(Length::Fill) .spacing(10) .push( - Column::new() + Row::new() .width(Length::Fill) + .push( + Row::new() + .align_items(Alignment::Center) + .width(Length::Fill) + .push(text(addr.to_string()).small()) + .push( + Button::new(icon::clipboard_icon()) + .on_press(Message::Clipboard( + addr.to_string(), + )) + .style( + button::Style::TransparentBorder.into(), + ), + ), + ) .push( text(format!( "{} BTC", Amount::from_sat(output.value).to_btc() )) .bold(), - ) - .push( - text(format!( - "{}", - Address::from_script( - &output.script_pubkey, - network - ) - .unwrap() - )) - .small(), ), ) - .push_maybe(if Some(i) == change_index { - Some( - Container::new(text("Change")) - .padding(5) - .style(badge::PillStyle::Success), - ) - } else { - None - }), + .push_maybe( + if let Some(indexes) = change_indexes.as_ref() { + if indexes.contains(&i) { + Some( + Container::new(text("Change")) + .padding(5) + .style(badge::PillStyle::Success), + ) + } else { + None + } + } else { + None + }, + ) + .push_maybe( + if let Some(indexes) = receive_indexes.as_ref() { + if indexes.contains(&i) { + Some( + Container::new(text("Deposit")) + .padding(5) + .style(badge::PillStyle::Success), + ) + } else { + None + } + } else { + None + }, + ), ) - .width(Length::Fill), - ) - }, - )) - .width(Length::FillPortion(1)), + }) + .into() + }, + )) + .style(card::SimpleCardStyle), ), ) .into() diff --git a/gui/src/ui/icon.rs b/gui/src/ui/icon.rs index f740f911..7f3b9163 100644 --- a/gui/src/ui/icon.rs +++ b/gui/src/ui/icon.rs @@ -122,7 +122,7 @@ pub fn dot_icon() -> Text<'static> { } pub fn clipboard_icon() -> Text<'static> { - icon('\u{F28E}') + icon('\u{F3C2}') } pub fn shield_icon() -> Text<'static> {