gui: add refresh coins buttons

close #192
close #371
This commit is contained in:
edouard 2023-05-05 16:58:55 +02:00 committed by Antoine Poinsot
parent 032cf801d5
commit 79984718bc
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
10 changed files with 126 additions and 54 deletions

View File

@ -1,3 +1,4 @@
use liana::miniscript::bitcoin::OutPoint;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Menu {
Home,
@ -8,4 +9,5 @@ pub enum Menu {
Coins,
CreateSpendTx,
Recovery,
RefreshCoins(Vec<OutPoint>),
}

View File

@ -92,6 +92,13 @@ impl App {
self.cache.blockheight as u32,
)
.into(),
menu::Menu::RefreshCoins(preselected) => CreateSpendPanel::new_self_send(
self.wallet.clone(),
&self.cache.coins,
self.cache.blockheight as u32,
preselected,
)
.into(),
};
self.state.load(self.daemon.clone())
}

View File

@ -11,7 +11,7 @@ use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use iced::{widget::qr_code, Command, Subscription};
use liana::miniscript::bitcoin::{Address, Amount};
use liana::miniscript::bitcoin::{Address, Amount, OutPoint};
use liana_ui::widget::*;
use super::{cache::Cache, error::Error, menu::Menu, message::Message, view, wallet::Wallet};
@ -50,7 +50,7 @@ pub struct Home {
balance: Amount,
unconfirmed_balance: Amount,
remaining_sequence: Option<u32>,
number_of_expiring_coins: usize,
expiring_coins: Vec<OutPoint>,
pending_events: Vec<HistoryTransaction>,
events: Vec<HistoryTransaction>,
selected_event: Option<(usize, usize)>,
@ -76,7 +76,7 @@ impl Home {
balance,
unconfirmed_balance,
remaining_sequence: None,
number_of_expiring_coins: 0,
expiring_coins: Vec::new(),
selected_event: None,
events: Vec::new(),
pending_events: Vec::new(),
@ -103,7 +103,7 @@ impl State for Home {
&self.balance,
&self.unconfirmed_balance,
&self.remaining_sequence,
self.number_of_expiring_coins,
&self.expiring_coins,
&self.pending_events,
&self.events,
),
@ -125,7 +125,7 @@ impl State for Home {
self.balance = Amount::from_sat(0);
self.unconfirmed_balance = Amount::from_sat(0);
self.remaining_sequence = None;
self.number_of_expiring_coins = 0;
self.expiring_coins = Vec::new();
for coin in coins {
if coin.spend_info.is_none() {
if coin.block_height.is_some() {
@ -135,7 +135,7 @@ impl State for Home {
remaining_sequence(&coin, cache.blockheight as u32, timelock);
// number of block in a day
if seq <= 144 {
self.number_of_expiring_coins += 1;
self.expiring_coins.push(coin.outpoint);
}
if let Some(last) = &mut self.remaining_sequence {
if seq < *last {

View File

@ -3,6 +3,7 @@ use std::sync::Arc;
use iced::Command;
use liana::miniscript::bitcoin::OutPoint;
use liana_ui::widget::Element;
use super::{redirect, State};
@ -25,12 +26,33 @@ impl CreateSpendPanel {
draft: step::TransactionDraft::default(),
current: 0,
steps: vec![
Box::new(step::DefineSpend::new(
descriptor,
coins.to_vec(),
timelock,
blockheight,
)),
Box::new(
step::DefineSpend::new(descriptor, coins, timelock)
.with_coins_sorted(blockheight),
),
Box::new(step::SaveSpend::new(wallet)),
],
}
}
pub fn new_self_send(
wallet: Arc<Wallet>,
coins: &[Coin],
blockheight: u32,
preselected_coins: &[OutPoint],
) -> Self {
let descriptor = wallet.main_descriptor.clone();
let timelock = descriptor.first_timelock_value();
Self {
draft: step::TransactionDraft::default(),
current: 0,
steps: vec![
Box::new(
step::DefineSpend::new(descriptor, coins, timelock)
.with_preselected_coins(preselected_coins)
.with_coins_sorted(blockheight)
.self_send(),
),
Box::new(step::SaveSpend::new(wallet)),
],
}

View File

@ -60,12 +60,7 @@ pub struct DefineSpend {
}
impl DefineSpend {
pub fn new(
descriptor: LianaDescriptor,
coins: Vec<Coin>,
timelock: u16,
blockheight: u32,
) -> Self {
pub fn new(descriptor: LianaDescriptor, coins: &[Coin], timelock: u16) -> Self {
let balance_available = coins
.iter()
.filter_map(|coin| {
@ -76,27 +71,17 @@ impl DefineSpend {
}
})
.sum();
let mut coins: Vec<(Coin, bool)> = coins
.into_iter()
let coins: Vec<(Coin, bool)> = coins
.iter()
.filter_map(|c| {
if c.spend_info.is_none() {
Some((c, false))
Some((*c, false))
} else {
None
}
})
.collect();
coins.sort_by(|(a, _), (b, _)| {
if remaining_sequence(a, blockheight, timelock)
== remaining_sequence(b, blockheight, timelock)
{
// bigger amount first
b.amount.cmp(&a.amount)
} else {
// smallest blockheight (remaining_sequence) first
a.block_height.cmp(&b.block_height)
}
});
Self {
balance_available,
descriptor,
@ -111,6 +96,37 @@ impl DefineSpend {
warning: None,
}
}
pub fn with_preselected_coins(mut self, preselected_coins: &[OutPoint]) -> Self {
for (coin, selected) in &mut self.coins {
*selected = preselected_coins.contains(&coin.outpoint);
}
self
}
pub fn with_coins_sorted(mut self, blockheight: u32) -> Self {
let timelock = self.timelock;
self.coins.sort_by(|(a, a_selected), (b, b_selected)| {
if *a_selected && !b_selected || !a_selected && *b_selected {
b_selected.cmp(a_selected)
} else if remaining_sequence(a, blockheight, timelock)
== remaining_sequence(b, blockheight, timelock)
{
// bigger amount first
b.amount.cmp(&a.amount)
} else {
// smallest blockheight (remaining_sequence) first
a.block_height.cmp(&b.block_height)
}
});
self
}
pub fn self_send(mut self) -> Self {
self.recipients = Vec::new();
self
}
fn check_valid(&mut self) {
self.is_valid = self.feerate.valid && !self.feerate.value.is_empty();
self.is_duplicate = false;

View File

@ -1,8 +1,8 @@
use iced::{Alignment, Length};
use iced::{widget::Space, Alignment, Length};
use liana_ui::{
color,
component::{amount::*, badge, text::*},
component::{amount::*, badge, button, text::*},
icon,
image::*,
theme,
@ -11,7 +11,7 @@ use liana_ui::{
};
use crate::{
app::{cache::Cache, view::message::Message},
app::{cache::Cache, menu::Menu, view::message::Message},
daemon::model::{remaining_sequence, Coin},
};
@ -136,7 +136,7 @@ fn coin_list_view(
.spacing(5)
})),
)
.push_maybe(coin.spend_info.map(|info| {
.push(if let Some(info) = coin.spend_info {
Column::new()
.push(
Row::new()
@ -159,7 +159,16 @@ fn coin_list_view(
)
})
.spacing(5)
})),
} else {
Column::new().push(
Row::new().push(Space::with_width(Length::Fill)).push(
button::primary(Some(icon::arrow_repeat()), "Refresh coin")
.on_press(Message::Menu(Menu::RefreshCoins(vec![
coin.outpoint,
]))),
),
)
}),
)
} else {
None

View File

@ -5,7 +5,7 @@ use iced::{alignment, Alignment, Length};
use liana::miniscript::bitcoin;
use liana_ui::{
color,
component::{amount::*, card, event, text::*},
component::{amount::*, button, card, event, text::*},
icon, theme,
util::Collection,
widget::*,
@ -27,7 +27,7 @@ pub fn home_view<'a>(
balance: &'a bitcoin::Amount,
unconfirmed_balance: &'a bitcoin::Amount,
remaining_sequence: &Option<u32>,
number_of_expiring_coins: usize,
expiring_coins: &Vec<bitcoin::OutPoint>,
pending_events: &[HistoryTransaction],
events: &Vec<HistoryTransaction>,
) -> Element<'a, Message> {
@ -48,7 +48,7 @@ pub fn home_view<'a>(
None
}),
)
.push_maybe(if number_of_expiring_coins == 0 {
.push_maybe(if expiring_coins.is_empty() {
remaining_sequence.map(|sequence| {
Container::new(
Row::new()
@ -75,13 +75,21 @@ pub fn home_view<'a>(
} else {
Some(
Container::new(
Row::new().spacing(15).align_items(Alignment::Center).push(
h4_regular(format!(
"You have {} coins that are already or about to be expired",
number_of_expiring_coins
))
.width(Length::Fill),
),
Row::new()
.spacing(15)
.align_items(Alignment::Center)
.push(
h4_regular(format!(
"You have {} coins that are already or about to be expired",
expiring_coins.len(),
))
.width(Length::Fill),
)
.push(
button::primary(Some(icon::arrow_repeat()), "Refresh coins").on_press(
Message::Menu(Menu::RefreshCoins(expiring_coins.clone())),
),
),
)
.padding(25)
.style(theme::Card::Invalid),

View File

@ -177,7 +177,7 @@ pub fn sidebar<'a>(menu: &Menu, cache: &'a Cache) -> Container<'a, Message> {
.center_x(),
)
.style(theme::Button::Menu(true))
.on_press(Message::Reload)
.on_press(Message::Menu(Menu::CreateSpendTx))
.width(iced::Length::Fill),
menu_green_bar()
)

View File

@ -117,12 +117,16 @@ pub fn create_spend_tx<'a>(
None
})
.push(Space::with_width(Length::Fill))
.push(
button::secondary(Some(icon::plus_icon()), "Add recipient")
.on_press(Message::CreateSpend(
CreateSpendMessage::AddRecipient,
)),
),
.push_maybe(if is_self_send {
None
} else {
Some(
button::secondary(Some(icon::plus_icon()), "Add recipient")
.on_press(Message::CreateSpend(
CreateSpendMessage::AddRecipient,
)),
)
}),
)
.spacing(20),
)

View File

@ -22,6 +22,10 @@ pub fn arrow_right() -> Text<'static> {
icon('\u{F138}')
}
pub fn arrow_repeat() -> Text<'static> {
icon('\u{F130}')
}
pub fn arrow_return_right() -> Text<'static> {
icon('\u{F132}')
}