Add spend txs to app cache

This commit is contained in:
edouard 2022-10-27 10:52:29 +02:00
parent ecd5962831
commit 95aa8a1529
15 changed files with 216 additions and 23 deletions

View File

@ -1,7 +1,8 @@
use crate::daemon::model::Coin;
use crate::daemon::model::{Coin, SpendTx};
#[derive(Default)]
pub struct Cache {
pub blockheight: i32,
pub coins: Vec<Coin>,
pub spend_txs: Vec<SpendTx>,
}

View File

@ -2,6 +2,7 @@
pub enum Menu {
Home,
Receive,
Spend,
Settings,
Coins,
}

View File

@ -65,6 +65,7 @@ impl App {
menu::Menu::Home => Home::new(&self.cache.coins).into(),
menu::Menu::Coins => CoinsPanel::new(&self.cache.coins).into(),
menu::Menu::Receive => ReceivePanel::default().into(),
menu::Menu::Spend => ReceivePanel::default().into(),
};
self.state.load(self.daemon.clone())
}

View File

@ -25,9 +25,10 @@ impl CoinsPanel {
}
impl State for CoinsPanel {
fn view<'a>(&'a self, _cache: &'a Cache) -> Element<'a, view::Message> {
fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> {
view::dashboard(
&Menu::Coins,
cache,
self.warning.as_ref(),
view::coins::coins_view(&self.coins),
)

View File

@ -42,8 +42,13 @@ impl Home {
}
impl State for Home {
fn view<'a>(&'a self, _cache: &'a Cache) -> Element<'a, view::Message> {
view::dashboard(&Menu::Home, None, view::home::home_view(&self.balance))
fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> {
view::dashboard(
&Menu::Home,
cache,
None,
view::home::home_view(&self.balance),
)
}
fn update(
@ -83,15 +88,16 @@ pub struct ReceivePanel {
}
impl State for ReceivePanel {
fn view<'a>(&'a self, _cache: &'a Cache) -> Element<'a, view::Message> {
fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> {
if let Some(address) = &self.address {
view::dashboard(
&Menu::Receive,
cache,
self.warning.as_ref(),
view::receive::receive(address, self.qr_code.as_ref().unwrap()),
)
} else {
view::dashboard(&Menu::Receive, self.warning.as_ref(), column())
view::dashboard(&Menu::Receive, cache, self.warning.as_ref(), column())
}
}
fn update(

View File

@ -103,6 +103,7 @@ impl State for SettingsState {
fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> {
let can_edit = self.current.is_none() && !self.daemon_is_external;
view::settings::list(
cache,
self.warning.as_ref(),
self.settings
.iter()

View File

@ -16,13 +16,14 @@ use iced::{
use crate::ui::{
color,
component::{button, separation, text::*},
icon::{coin_icon, home_icon, receive_icon, settings_icon},
component::{badge, button, separation, text::*},
icon::{coin_icon, home_icon, receive_icon, send_icon, settings_icon},
util::Collection,
};
use crate::app::{error::Error, menu::Menu};
use crate::app::{cache::Cache, error::Error, menu::Menu};
pub fn sidebar(menu: &Menu) -> widget::Container<Message> {
pub fn sidebar<'a>(menu: &Menu, cache: &'a Cache) -> widget::Container<'a, Message> {
let home_button = if *menu == Menu::Home {
button::primary(Some(home_icon()), "Home")
.on_press(Message::Reload)
@ -34,13 +35,131 @@ pub fn sidebar(menu: &Menu) -> widget::Container<Message> {
};
let coins_button = if *menu == Menu::Coins {
button::primary(Some(coin_icon()), "Coins")
.on_press(Message::Reload)
.width(iced::Length::Units(200))
iced::pure::widget::button::Button::new(
container(
row()
.push(
row()
.push(coin_icon())
.push(text("Coins"))
.spacing(10)
.width(iced::Length::Fill)
.align_items(iced::Alignment::Center),
)
.push(
container(text(&format!(" {} ", cache.coins.len())).small().bold())
.style(badge::PillStyle::InversePrimary),
)
.spacing(10)
.width(iced::Length::Fill)
.align_items(iced::Alignment::Center),
)
.width(iced::Length::Fill)
.padding(5)
.center_x(),
)
.style(button::Style::Primary)
.on_press(Message::Reload)
.width(iced::Length::Units(200))
} else {
button::transparent(Some(coin_icon()), "Coins")
.on_press(Message::Menu(Menu::Coins))
.width(iced::Length::Units(200))
iced::pure::widget::button::Button::new(
container(
row()
.push(
row()
.push(coin_icon())
.push(text("Coins"))
.spacing(10)
.width(iced::Length::Fill)
.align_items(iced::Alignment::Center),
)
.push(
container(text(&format!(" {} ", cache.coins.len())).small().bold())
.style(badge::PillStyle::Primary),
)
.spacing(10)
.width(iced::Length::Fill)
.align_items(iced::Alignment::Center),
)
.width(iced::Length::Fill)
.padding(5)
.center_x(),
)
.style(button::Style::Transparent)
.on_press(Message::Menu(Menu::Coins))
.width(iced::Length::Units(200))
};
let spend_button = if *menu == Menu::Spend {
iced::pure::widget::button::Button::new(
container(
row()
.push(
row()
.push(send_icon())
.push(text("Send"))
.spacing(10)
.width(iced::Length::Fill)
.align_items(iced::Alignment::Center),
)
.push_maybe(if cache.spend_txs.is_empty() {
None
} else {
Some(
container(
text(&format!(" {} ", cache.spend_txs.len()))
.small()
.bold(),
)
.style(badge::PillStyle::InversePrimary),
)
})
.spacing(10)
.width(iced::Length::Fill)
.align_items(iced::Alignment::Center),
)
.width(iced::Length::Fill)
.padding(5)
.center_x(),
)
.style(button::Style::Primary)
.on_press(Message::Reload)
.width(iced::Length::Units(200))
} else {
iced::pure::widget::button::Button::new(
container(
row()
.push(
row()
.push(coin_icon())
.push(text("Send"))
.spacing(10)
.width(iced::Length::Fill)
.align_items(iced::Alignment::Center),
)
.push_maybe(if cache.spend_txs.is_empty() {
None
} else {
Some(
container(
text(&format!(" {} ", cache.spend_txs.len()))
.small()
.bold(),
)
.style(badge::PillStyle::Primary),
)
})
.spacing(10)
.width(iced::Length::Fill)
.align_items(iced::Alignment::Center),
)
.width(iced::Length::Fill)
.padding(5)
.center_x(),
)
.style(button::Style::Transparent)
.on_press(Message::Menu(Menu::Spend))
.width(iced::Length::Units(200))
};
let receive_button = if *menu == Menu::Receive {
@ -76,6 +195,7 @@ pub fn sidebar(menu: &Menu) -> widget::Container<Message> {
)
.push(home_button)
.push(coins_button)
.push(spend_button)
.push(receive_button)
.spacing(15)
.height(Length::Fill),
@ -99,11 +219,16 @@ impl widget::container::StyleSheet for SidebarStyle {
pub fn dashboard<'a, T: Into<Element<'a, Message>>>(
menu: &'a Menu,
cache: &'a Cache,
warning: Option<&Error>,
content: T,
) -> Element<'a, Message> {
row()
.push(sidebar(menu).width(Length::Shrink).height(Length::Fill))
.push(
sidebar(menu, cache)
.width(Length::Shrink)
.height(Length::Fill),
)
.push(
column().push(warn(warning)).push(
main_section(container(scrollable(content)))

View File

@ -12,7 +12,7 @@ use super::{
};
use crate::{
app::{error::Error, menu::Menu},
app::{cache::Cache, error::Error, menu::Menu},
ui::{
color,
component::{badge, button, card, form, separation, text::*},
@ -21,11 +21,13 @@ use crate::{
};
pub fn list<'a>(
cache: &'a Cache,
warning: Option<&Error>,
settings: Vec<Element<'a, Message>>,
) -> Element<'a, Message> {
dashboard(
&Menu::Settings,
cache,
warning,
widget::Column::with_children(settings).spacing(20),
)

View File

@ -70,6 +70,10 @@ impl<C: Client + Debug> Daemon for Minisafed<C> {
fn list_coins(&self) -> Result<ListCoinsResult, DaemonError> {
self.call("listcoins", Option::<Request>::None)
}
fn list_spend_txs(&self) -> Result<ListSpendResult, DaemonError> {
self.call("listspend", Option::<Request>::None)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]

View File

@ -91,4 +91,15 @@ impl Daemon for EmbeddedDaemon {
.control
.list_coins())
}
fn list_spend_txs(&self) -> Result<ListSpendResult, DaemonError> {
Ok(self
.handle
.as_ref()
.ok_or(DaemonError::NoAnswer)?
.lock()
.unwrap()
.control
.list_spend())
}
}

View File

@ -49,4 +49,6 @@ pub trait Daemon: Debug {
fn get_new_address(&self) -> Result<model::GetAddressResult, DaemonError>;
fn list_coins(&self) -> Result<model::ListCoinsResult, DaemonError>;
fn list_spend_txs(&self) -> Result<model::ListSpendResult, DaemonError>;
}

View File

@ -1,3 +1,7 @@
pub use minisafe::commands::{GetAddressResult, GetInfoResult, ListCoinsEntry, ListCoinsResult};
pub use minisafe::commands::{
GetAddressResult, GetInfoResult, ListCoinsEntry, ListCoinsResult, ListSpendEntry,
ListSpendResult,
};
pub type Coin = ListCoinsEntry;
pub type SpendTx = ListSpendEntry;

View File

@ -42,7 +42,12 @@ enum Step {
pub enum Message {
Event(iced_native::Event),
Syncing(Result<GetInfoResult, DaemonError>),
Synced(GetInfoResult, Vec<Coin>, Arc<dyn Daemon + Sync + Send>),
Synced(
GetInfoResult,
Vec<Coin>,
Vec<SpendTx>,
Arc<dyn Daemon + Sync + Send>,
),
Started(Result<Arc<dyn Daemon + Sync + Send>, Error>),
Loaded(Result<Arc<dyn Daemon + Sync + Send>, Error>),
Failure(DaemonError),
@ -127,9 +132,13 @@ impl Loader {
.list_coins()
.map(|res| res.coins)
.unwrap_or_else(|_| Vec::new());
(info, coins, daemon)
let spend_txs = daemon
.list_spend_txs()
.map(|res| res.spend_txs)
.unwrap_or_else(|_| Vec::new());
(info, coins, spend_txs, daemon)
},
|res| Message::Synced(res.0, res.1, res.2),
|res| Message::Synced(res.0, res.1, res.2, res.3),
);
} else {
*progress = info.sync

View File

@ -161,10 +161,11 @@ impl Application for GUI {
}
}
(State::Loader(loader), Message::Load(msg)) => {
if let loader::Message::Synced(info, coins, minisafed) = *msg {
if let loader::Message::Synced(info, coins, spend_txs, minisafed) = *msg {
let cache = Cache {
blockheight: info.blockheight,
coins,
spend_txs,
};
let (app, command) = App::new(cache, loader.gui_config.clone(), minisafed);

View File

@ -117,3 +117,27 @@ pub fn coin<T>() -> widget::container::Container<'static, T> {
.center_x()
.center_y()
}
pub enum PillStyle {
InversePrimary,
Primary,
}
impl widget::container::StyleSheet for PillStyle {
fn style(&self) -> widget::container::Style {
match self {
Self::Primary => widget::container::Style {
background: color::PRIMARY.into(),
border_radius: 10.0,
text_color: iced::Color::WHITE.into(),
..widget::container::Style::default()
},
Self::InversePrimary => widget::container::Style {
background: color::FOREGROUND.into(),
border_radius: 10.0,
text_color: color::PRIMARY.into(),
..widget::container::Style::default()
},
}
}
}