Merge #26: Add coins panel
b849f9bb3d8577f6a0c52e6903842ab72d602583 Update minisafe master (edouard) 86eddb28c625a96a0e9b3db7cc80dbb3eb0813f5 add coins panel to gui (edouard) Pull request description: based on #22 ACKs for top commit: edouardparis: Self-ACK b849f9bb3d8577f6a0c52e6903842ab72d602583 Tree-SHA512: 9959ecd84594ce94c3c9ae383344615b533a1dc968268cb8a8336840750a9c6ee82ab29a7f45999e5d27ab79a296dd2396a0d9e56890f7063c38717e0f0f6391
This commit is contained in:
commit
382962a47b
3
gui/Cargo.lock
generated
3
gui/Cargo.lock
generated
@ -1390,9 +1390,10 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
[[package]]
|
||||
name = "minisafe"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/revault/minisafe?branch=master#4a802890634f60d77e4da5a56f9189024ef99500"
|
||||
source = "git+https://github.com/revault/minisafe?branch=master#9bb20303e71cd95528b39e0aa7c8d44f3e61d1c2"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"base64",
|
||||
"dirs",
|
||||
"fern",
|
||||
"jsonrpc",
|
||||
|
||||
@ -3,4 +3,5 @@ pub enum Menu {
|
||||
Home,
|
||||
Receive,
|
||||
Settings,
|
||||
Coins,
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ pub use minisafe::config::Config as DaemonConfig;
|
||||
pub use config::Config;
|
||||
pub use message::Message;
|
||||
|
||||
use state::{Home, ReceivePanel, State};
|
||||
use state::{CoinsPanel, Home, ReceivePanel, State};
|
||||
|
||||
use crate::{
|
||||
app::{cache::Cache, error::Error, menu::Menu},
|
||||
@ -63,6 +63,7 @@ impl App {
|
||||
.into()
|
||||
}
|
||||
menu::Menu::Home => Home::new(&self.cache.coins).into(),
|
||||
menu::Menu::Coins => CoinsPanel::new(&self.cache.coins).into(),
|
||||
menu::Menu::Receive => ReceivePanel::default().into(),
|
||||
};
|
||||
self.state.load(self.daemon.clone())
|
||||
|
||||
79
gui/src/app/state/coins.rs
Normal file
79
gui/src/app/state/coins.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use iced::pure::Element;
|
||||
use iced::Command;
|
||||
|
||||
use crate::{
|
||||
app::{cache::Cache, error::Error, menu::Menu, message::Message, state::State, view},
|
||||
daemon::{model::Coin, Daemon},
|
||||
};
|
||||
|
||||
pub struct CoinsPanel {
|
||||
coins: Vec<Coin>,
|
||||
selected_coin: Option<usize>,
|
||||
warning: Option<Error>,
|
||||
}
|
||||
|
||||
impl CoinsPanel {
|
||||
pub fn new(coins: &[Coin]) -> Self {
|
||||
Self {
|
||||
coins: coins.to_owned(),
|
||||
selected_coin: None,
|
||||
warning: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State for CoinsPanel {
|
||||
fn view<'a>(&'a self, _cache: &'a Cache) -> Element<'a, view::Message> {
|
||||
view::dashboard(
|
||||
&Menu::Coins,
|
||||
self.warning.as_ref(),
|
||||
view::coins::coins_view(&self.coins),
|
||||
)
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
_daemon: Arc<dyn Daemon + Sync + Send>,
|
||||
_cache: &Cache,
|
||||
message: Message,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::Coins(res) => match res {
|
||||
Err(e) => self.warning = Some(e),
|
||||
Ok(coins) => {
|
||||
self.warning = None;
|
||||
self.coins = coins;
|
||||
}
|
||||
},
|
||||
Message::View(view::Message::Close) => {
|
||||
self.selected_coin = None;
|
||||
}
|
||||
Message::View(view::Message::Select(i)) => {
|
||||
self.selected_coin = Some(i);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn load(&self, daemon: Arc<dyn Daemon + Sync + Send>) -> Command<Message> {
|
||||
let daemon = daemon.clone();
|
||||
Command::perform(
|
||||
async move {
|
||||
daemon
|
||||
.list_coins()
|
||||
.map(|res| res.coins)
|
||||
.map_err(|e| e.into())
|
||||
},
|
||||
Message::Coins,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CoinsPanel> for Box<dyn State> {
|
||||
fn from(s: CoinsPanel) -> Box<dyn State> {
|
||||
Box::new(s)
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
mod coins;
|
||||
mod settings;
|
||||
|
||||
use std::sync::Arc;
|
||||
@ -8,6 +9,8 @@ use iced::{widget::qr_code, Command, Subscription};
|
||||
|
||||
use super::{cache::Cache, error::Error, menu::Menu, message::Message, view};
|
||||
use crate::daemon::{model::Coin, Daemon};
|
||||
|
||||
pub use coins::CoinsPanel;
|
||||
pub use settings::SettingsState;
|
||||
|
||||
pub trait State {
|
||||
|
||||
61
gui/src/app/view/coins.rs
Normal file
61
gui/src/app/view/coins.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use iced::{
|
||||
pure::{button, column, container, row, Element},
|
||||
Alignment, Length,
|
||||
};
|
||||
|
||||
use crate::ui::component::{badge, button::Style, card, text::*};
|
||||
|
||||
use crate::{app::view::message::Message, daemon::model::Coin};
|
||||
|
||||
pub fn coins_view<'a>(coins: &[Coin]) -> Element<'a, Message> {
|
||||
column()
|
||||
.push(
|
||||
container(
|
||||
row()
|
||||
.push(text(&format!(" {}", coins.len())).bold())
|
||||
.push(text(" coins")),
|
||||
)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.push(
|
||||
column().spacing(10).push(
|
||||
coins
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(column().spacing(10), |col, (i, coin)| {
|
||||
col.push(coin_list_view(i, coin))
|
||||
}),
|
||||
),
|
||||
)
|
||||
.align_items(Alignment::Center)
|
||||
.spacing(20)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn coin_list_view<'a>(i: usize, coin: &Coin) -> Element<'a, Message> {
|
||||
container(
|
||||
button(
|
||||
row()
|
||||
.push(
|
||||
row()
|
||||
.push(badge::coin())
|
||||
.push(text(&format!("block: {}", coin.block_height.unwrap_or(0))).small())
|
||||
.spacing(10)
|
||||
.align_items(Alignment::Center)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.push(
|
||||
text(&format!("{} BTC", coin.amount.as_btc()))
|
||||
.bold()
|
||||
.width(Length::Shrink),
|
||||
)
|
||||
.align_items(Alignment::Center)
|
||||
.spacing(20),
|
||||
)
|
||||
.padding(10)
|
||||
.on_press(Message::Select(i))
|
||||
.style(Style::TransparentBorder),
|
||||
)
|
||||
.style(card::SimpleCardStyle)
|
||||
.into()
|
||||
}
|
||||
@ -5,6 +5,8 @@ pub enum Message {
|
||||
Reload,
|
||||
Clipboard(String),
|
||||
Menu(Menu),
|
||||
Close,
|
||||
Select(usize),
|
||||
Settings(usize, SettingsMessage),
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
mod message;
|
||||
mod warning;
|
||||
|
||||
pub mod coins;
|
||||
pub mod home;
|
||||
pub mod receive;
|
||||
pub mod settings;
|
||||
@ -16,7 +17,7 @@ use iced::{
|
||||
use crate::ui::{
|
||||
color,
|
||||
component::{button, separation, text::*},
|
||||
icon::{home_icon, receive_icon, settings_icon},
|
||||
icon::{coin_icon, home_icon, receive_icon, settings_icon},
|
||||
};
|
||||
|
||||
use crate::app::{error::Error, menu::Menu};
|
||||
@ -32,6 +33,16 @@ pub fn sidebar(menu: &Menu) -> widget::Container<Message> {
|
||||
.width(iced::Length::Units(200))
|
||||
};
|
||||
|
||||
let coins_button = if *menu == Menu::Coins {
|
||||
button::primary(Some(coin_icon()), "Coins")
|
||||
.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))
|
||||
};
|
||||
|
||||
let receive_button = if *menu == Menu::Receive {
|
||||
button::primary(Some(receive_icon()), "Receive")
|
||||
.on_press(Message::Reload)
|
||||
@ -64,8 +75,9 @@ pub fn sidebar(menu: &Menu) -> widget::Container<Message> {
|
||||
.spacing(10),
|
||||
)
|
||||
.push(home_button)
|
||||
.push(coins_button)
|
||||
.push(receive_button)
|
||||
.spacing(20)
|
||||
.spacing(15)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.push(container(settings_button).height(Length::Shrink)),
|
||||
|
||||
@ -3,7 +3,7 @@ use std::convert::TryFrom;
|
||||
use bitcoin::Network;
|
||||
use minisafe::{
|
||||
config::{BitcoinConfig, BitcoindConfig, Config as MinisafeConfig},
|
||||
miniscript::{Descriptor, DescriptorPublicKey},
|
||||
descriptors::InheritanceDescriptor,
|
||||
};
|
||||
|
||||
use serde::Serialize;
|
||||
@ -14,7 +14,7 @@ use std::{net::SocketAddr, path::PathBuf, time::Duration};
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Config {
|
||||
#[serde(serialize_with = "serialize_option_to_string")]
|
||||
pub main_descriptor: Option<Descriptor<DescriptorPublicKey>>,
|
||||
pub main_descriptor: Option<InheritanceDescriptor>,
|
||||
pub bitcoin_config: BitcoinConfig,
|
||||
/// Everything we need to know to talk to bitcoind
|
||||
pub bitcoind_config: BitcoindConfig,
|
||||
|
||||
@ -3,7 +3,7 @@ use std::str::FromStr;
|
||||
|
||||
use iced::pure::Element;
|
||||
use minisafe::{
|
||||
descriptors::inheritance_descriptor,
|
||||
descriptors::InheritanceDescriptor,
|
||||
miniscript::descriptor::{Descriptor, DescriptorPublicKey},
|
||||
};
|
||||
|
||||
@ -153,9 +153,7 @@ impl Step for DefineDescriptor {
|
||||
}
|
||||
false
|
||||
} else if !self.imported_descriptor.value.is_empty() {
|
||||
if let Ok(desc) =
|
||||
Descriptor::<DescriptorPublicKey>::from_str(&self.imported_descriptor.value)
|
||||
{
|
||||
if let Ok(desc) = InheritanceDescriptor::from_str(&self.imported_descriptor.value) {
|
||||
config.main_descriptor = Some(desc);
|
||||
true
|
||||
} else {
|
||||
@ -176,7 +174,11 @@ impl Step for DefineDescriptor {
|
||||
return false;
|
||||
}
|
||||
|
||||
match inheritance_descriptor(user_key.unwrap(), heir_key.unwrap(), sequence.unwrap()) {
|
||||
match InheritanceDescriptor::new(
|
||||
user_key.unwrap(),
|
||||
heir_key.unwrap(),
|
||||
sequence.unwrap(),
|
||||
) {
|
||||
Ok(desc) => {
|
||||
config.main_descriptor = Some(desc);
|
||||
true
|
||||
|
||||
@ -3,7 +3,7 @@ use iced::{
|
||||
Length,
|
||||
};
|
||||
|
||||
use crate::ui::color;
|
||||
use crate::ui::{color, icon};
|
||||
|
||||
pub enum Style {
|
||||
Standard,
|
||||
@ -68,3 +68,52 @@ impl<'a, Message: 'a, S: 'a + widget::container::StyleSheet> From<Badge<S>>
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReceiveStyle;
|
||||
impl widget::container::StyleSheet for ReceiveStyle {
|
||||
fn style(&self) -> widget::container::Style {
|
||||
widget::container::Style {
|
||||
border_radius: 40.0,
|
||||
background: color::BACKGROUND.into(),
|
||||
..widget::container::Style::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive<T>() -> widget::container::Container<'static, T> {
|
||||
container(icon::receive_icon().width(Length::Units(20)))
|
||||
.width(Length::Units(40))
|
||||
.height(Length::Units(40))
|
||||
.style(ReceiveStyle)
|
||||
.center_x()
|
||||
.center_y()
|
||||
}
|
||||
|
||||
pub struct SpendStyle;
|
||||
impl widget::container::StyleSheet for SpendStyle {
|
||||
fn style(&self) -> widget::container::Style {
|
||||
widget::container::Style {
|
||||
border_radius: 40.0,
|
||||
background: color::BACKGROUND.into(),
|
||||
..widget::container::Style::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spend<T>() -> widget::container::Container<'static, T> {
|
||||
container(icon::send_icon().width(Length::Units(20)))
|
||||
.width(Length::Units(40))
|
||||
.height(Length::Units(40))
|
||||
.style(ReceiveStyle)
|
||||
.center_x()
|
||||
.center_y()
|
||||
}
|
||||
|
||||
pub fn coin<T>() -> widget::container::Container<'static, T> {
|
||||
container(icon::coin_icon().width(Length::Units(20)))
|
||||
.width(Length::Units(40))
|
||||
.height(Length::Units(40))
|
||||
.style(ReceiveStyle)
|
||||
.center_x()
|
||||
.center_y()
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ impl button::StyleSheet for Style {
|
||||
border_radius: 10.0,
|
||||
border_width: 0.0,
|
||||
border_color: Color::TRANSPARENT,
|
||||
text_color: color::DARK_GREY,
|
||||
text_color: Color::BLACK,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -77,18 +77,18 @@ impl button::StyleSheet for Style {
|
||||
},
|
||||
Style::Transparent => button::Style {
|
||||
shadow_offset: Vector::default(),
|
||||
background: color::FOREGROUND.into(),
|
||||
background: color::BACKGROUND.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 0.0,
|
||||
border_color: Color::TRANSPARENT,
|
||||
text_color: color::DARK_GREY,
|
||||
text_color: Color::BLACK,
|
||||
},
|
||||
Style::TransparentBorder => button::Style {
|
||||
shadow_offset: Vector::default(),
|
||||
background: Color::TRANSPARENT.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 2.0,
|
||||
border_color: Color::TRANSPARENT,
|
||||
border_width: 1.0,
|
||||
border_color: Color::BLACK,
|
||||
text_color: Color::BLACK,
|
||||
},
|
||||
}
|
||||
|
||||
@ -66,6 +66,10 @@ pub fn vaults_icon() -> Text {
|
||||
icon('\u{F1C7}')
|
||||
}
|
||||
|
||||
pub fn coin_icon() -> Text {
|
||||
icon('\u{F567}')
|
||||
}
|
||||
|
||||
pub fn settings_icon() -> Text {
|
||||
icon('\u{F3E5}')
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user