From 56d3522f8c74ff774cb3ecdbf76b48dd216497f9 Mon Sep 17 00:00:00 2001 From: edouard Date: Fri, 3 Feb 2023 21:59:26 +0100 Subject: [PATCH] gui: change path signature details --- gui/src/app/view/spend/detail.rs | 157 +++++++++++++++++++------------ gui/src/app/view/spend/mod.rs | 8 +- gui/src/daemon/model.rs | 15 +-- 3 files changed, 109 insertions(+), 71 deletions(-) diff --git a/gui/src/app/view/spend/detail.rs b/gui/src/app/view/spend/detail.rs index defa4d51..2dc1473d 100644 --- a/gui/src/app/view/spend/detail.rs +++ b/gui/src/app/view/spend/detail.rs @@ -287,26 +287,54 @@ pub fn signatures<'a>( keys_aliases: &'a HashMap, ) -> Element<'a, Message> { Column::new() - .push(Collapse::new( + .push( + if let Some(sigs) = tx.path_ready() { + Container::new( + Scrollable::new( + Row::new() + .spacing(5) + .align_items(Alignment::Center) + .push(icon::circle_check_icon().style(color::SUCCESS)) + .push(text("Ready").bold().style(color::SUCCESS)) + .push(text(", signed by")) + .push( + sigs.signed_pubkeys + .keys() + .fold(Row::new().spacing(5), |row, value| { + row.push(if let Some(alias) = keys_aliases.get(value) { + Container::new( + tooltip::Tooltip::new( + Container::new(text(alias)) + .padding(3) + .style(badge::PillStyle::Simple), + value.to_string(), + tooltip::Position::Bottom, + ) + .style(card::SimpleCardStyle), + ) + } else { + Container::new(text(value.to_string())) + .padding(3) + .style(badge::PillStyle::Simple) + }) + }), + ) + ).horizontal_scroll(scrollable::Properties::new().width(2).scroller_width(2)) + ).padding(15) + } else{ + Container::new( + Collapse::new( move || { Button::new( Row::new() .align_items(Alignment::Center) - .push(if tx.is_ready() { - Row::new() - .spacing(5) - .align_items(Alignment::Center) - .push(icon::circle_check_icon().style(color::SUCCESS)) - .push(text("Ready").bold().style(color::SUCCESS)) - .width(Length::Fill) - } else { - Row::new() + .push(Row::new() .spacing(5) .align_items(Alignment::Center) .push(icon::circle_cross_icon()) .push(text("Not ready").bold()) .width(Length::Fill) - }) + ) .push(icon::collapse_icon()), ) .padding(15) @@ -317,21 +345,14 @@ pub fn signatures<'a>( Button::new( Row::new() .align_items(Alignment::Center) - .push(if tx.is_ready() { - Row::new() - .spacing(5) - .align_items(Alignment::Center) - .push(icon::circle_check_icon().style(color::SUCCESS)) - .push(text("Ready").bold().style(color::SUCCESS)) - .width(Length::Fill) - } else { + .push( Row::new() .spacing(5) .align_items(Alignment::Center) .push(icon::circle_cross_icon()) .push(text("Not ready").bold()) .width(Length::Fill) - }) + ) .push(icon::collapsed_icon()), ) .padding(15) @@ -344,6 +365,11 @@ pub fn signatures<'a>( Column::new() .padding(15) .spacing(10) + .push(text(if tx.sigs.recovery_path().is_some() { + "2 spending paths available. Finalizing this transaction requires either:" + } else { + "1 spending path available. Finalizing this transaction requires:" + })) .push(path_view( desc_info.primary_path(), tx.sigs.primary_path(), @@ -356,14 +382,14 @@ pub fn signatures<'a>( ), ) }, - )) + ))}) .push_maybe(if tx.status == SpendStatus::Pending { Some( Column::new().push(separation().width(Length::Fill)).push( Container::new( Row::new() .push(Space::with_width(Length::Fill)) - .push_maybe(if !tx.is_ready() { + .push_maybe(if tx.path_ready().is_some() { Some( button::primary(None, "Sign") .on_press(Message::Spend(SpendTxMessage::Sign)) @@ -394,29 +420,62 @@ pub fn path_view<'a>( key_aliases: &'a HashMap, ) -> Element<'a, Message> { let mut keys: Vec = path.thresh_fingerprints().1.into_iter().collect(); + let missing_signatures = if sigs.sigs_count >= sigs.threshold { + 0 + } else { + sigs.threshold - sigs.sigs_count + }; keys.sort(); Scrollable::new( Row::new() - .spacing(5) .align_items(Alignment::Center) - .push(if sigs.signed_pubkeys.len() >= sigs.threshold { + .push(if sigs.sigs_count >= sigs.threshold { icon::circle_check_icon().style(color::SUCCESS) } else { icon::circle_cross_icon() }) - .push( - Container::new(text(format!(" {} ", sigs.threshold))).style( - if sigs.signed_pubkeys.len() >= sigs.threshold { - badge::PillStyle::Success - } else { - badge::PillStyle::Simple - }, - ), - ) + .push(text(format!(" {}", missing_signatures)).bold()) .push(text(format!( - "signature{} out of", - if sigs.threshold > 1 { "s" } else { "" } + " more signature{}", + if missing_signatures > 1 { + "s from " + } else if missing_signatures == 0 { + "" + } else { + " from " + } ))) + .push_maybe(if keys.is_empty() { + None + } else { + Some(keys.iter().fold(Row::new().spacing(5), |row, &value| { + row.push_maybe(if !sigs.signed_pubkeys.contains_key(&value) { + Some(if let Some(alias) = key_aliases.get(&value) { + Container::new( + tooltip::Tooltip::new( + Container::new(text(alias)) + .padding(3) + .style(badge::PillStyle::Simple), + value.to_string(), + tooltip::Position::Bottom, + ) + .style(card::SimpleCardStyle), + ) + } else { + Container::new(text(value.to_string())) + .padding(3) + .style(badge::PillStyle::Simple) + }) + } else { + None + }) + })) + }) + .push_maybe(if sigs.signed_pubkeys.is_empty() { + None + } else { + Some(text(", already signed by ")) + }) .push( sigs.signed_pubkeys .keys() @@ -426,7 +485,7 @@ pub fn path_view<'a>( tooltip::Tooltip::new( Container::new(text(alias)) .padding(3) - .style(badge::PillStyle::Success), + .style(badge::PillStyle::Simple), value.to_string(), tooltip::Position::Bottom, ) @@ -435,32 +494,10 @@ pub fn path_view<'a>( } else { Container::new(text(value.to_string())) .padding(3) - .style(badge::PillStyle::Success) + .style(badge::PillStyle::Simple) }) }), - ) - .push(keys.iter().fold(Row::new().spacing(5), |row, &value| { - row.push_maybe(if !sigs.signed_pubkeys.contains_key(&value) { - Some(if let Some(alias) = key_aliases.get(&value) { - Container::new( - tooltip::Tooltip::new( - Container::new(text(alias)) - .padding(3) - .style(badge::PillStyle::Simple), - value.to_string(), - tooltip::Position::Bottom, - ) - .style(card::SimpleCardStyle), - ) - } else { - Container::new(text(value.to_string())) - .padding(3) - .style(badge::PillStyle::Simple) - }) - } else { - None - }) - })), + ), ) .horizontal_scroll(scrollable::Properties::new().width(2).scroller_width(2)) .into() diff --git a/gui/src/app/view/spend/mod.rs b/gui/src/app/view/spend/mod.rs index 4a98a927..6fd58bd2 100644 --- a/gui/src/app/view/spend/mod.rs +++ b/gui/src/app/view/spend/mod.rs @@ -121,8 +121,8 @@ fn spend_tx_list_view<'a>(i: usize, tx: &SpendTx) -> Element<'a, Message> { .align_items(Alignment::Center) .push(text(format!( "{}/{}", - if sigs.signed_pubkeys.len() <= sigs.threshold { - sigs.signed_pubkeys.len() + if sigs.sigs_count <= sigs.threshold { + sigs.sigs_count } else { sigs.threshold }, @@ -142,8 +142,8 @@ fn spend_tx_list_view<'a>(i: usize, tx: &SpendTx) -> Element<'a, Message> { .align_items(Alignment::Center) .push(text(format!( "{}/{}", - if sigs.signed_pubkeys.len() <= sigs.threshold { - sigs.signed_pubkeys.len() + if sigs.sigs_count <= sigs.threshold { + sigs.sigs_count } else { sigs.threshold }, diff --git a/gui/src/daemon/model.rs b/gui/src/daemon/model.rs index 9f38582f..69a42ae5 100644 --- a/gui/src/daemon/model.rs +++ b/gui/src/daemon/model.rs @@ -3,7 +3,7 @@ pub use liana::{ CreateSpendResult, GetAddressResult, GetInfoResult, ListCoinsEntry, ListCoinsResult, ListSpendEntry, ListSpendResult, ListTransactionsResult, TransactionInfo, }, - descriptors::PartialSpendInfo, + descriptors::{PartialSpendInfo, PathSpendInfo}, miniscript::bitcoin::{util::psbt::Psbt, Amount, Transaction}, }; @@ -78,17 +78,18 @@ impl SpendTx { } } - pub fn is_ready(&self) -> bool { + /// Returns the path ready if it exists. + pub fn path_ready(&self) -> Option<&PathSpendInfo> { let path = self.sigs.primary_path(); - if path.signed_pubkeys.len() >= path.threshold { - return true; + if path.sigs_count >= path.threshold { + return Some(path); } if let Some(path) = self.sigs.recovery_path() { - if path.signed_pubkeys.len() >= path.threshold { - return true; + if path.sigs_count >= path.threshold { + return Some(path); } } - false + None } }