Merge #403: Add unregistered policy warning on hw list

f1549e1e976a30105bc6f36488a7e90beac700e5 Add key alias to hardware wallets (edouard)
328748873768dc44a092cb61297471bcf6236447 Add unregistered policy warning on hw list (edouard)

Pull request description:

  close #369
  close #339

ACKs for top commit:
  edouardparis:
    Self-ACK f1549e1e976a30105bc6f36488a7e90beac700e5

Tree-SHA512: 8a1ebb91e299bc162b0e2593c73b3ac6afb47d77445bf3798633fa7ca2fdd4e6f33d6113f715cb1ffe19c49a03e9177576210ce53774dec4aada7d4bc838691b
This commit is contained in:
edouard 2023-04-04 17:45:32 +02:00
commit a402f85101
No known key found for this signature in database
GPG Key ID: E65F7A089C20DC8F
13 changed files with 647 additions and 268 deletions

View File

@ -284,8 +284,9 @@ impl RegisterWalletModal {
}
fn load(&self, _daemon: Arc<dyn Daemon + Sync + Send>) -> Command<Message> {
let wallet = self.wallet.clone();
Command::perform(
list_hws(self.wallet.clone()),
async move { list_hardware_wallets(&wallet).await },
Message::ConnectedHardwareWallets,
)
}
@ -359,11 +360,3 @@ async fn update_keys_aliases(
Ok(())
}
async fn list_hws(wallet: Arc<Wallet>) -> Vec<HardwareWallet> {
list_hardware_wallets(
&wallet.hardware_wallets,
Some((&wallet.name, &wallet.main_descriptor.to_string())),
)
.await
}

View File

@ -292,8 +292,9 @@ impl Action for SignAction {
}
fn load(&self, _daemon: Arc<dyn Daemon + Sync + Send>) -> Command<Message> {
let wallet = self.wallet.clone();
Command::perform(
list_hws(self.wallet.clone()),
async move { list_hardware_wallets(&wallet).await },
Message::ConnectedHardwareWallets,
)
}
@ -385,14 +386,6 @@ impl Action for SignAction {
}
}
async fn list_hws(wallet: Arc<Wallet>) -> Vec<HardwareWallet> {
list_hardware_wallets(
&wallet.hardware_wallets,
Some((&wallet.name, &wallet.main_descriptor.to_string())),
)
.await
}
async fn sign_psbt_with_hot_signer(
wallet: Arc<Wallet>,
psbt: Psbt,

View File

@ -1,81 +1,88 @@
use iced::{widget::tooltip, Alignment, Length};
use iced::Length;
use liana_ui::{
color,
component::text::{text, Text},
icon, theme,
util::Collection,
widget::*,
};
use liana_ui::{component::hw, theme, widget::*};
use crate::{app::view::message::*, hw::HardwareWallet};
pub fn hw_list_view<'a>(
pub fn hw_list_view(
i: usize,
hw: &'a HardwareWallet,
hw: &HardwareWallet,
chosen: bool,
processing: bool,
status: Option<&'a str>,
) -> Element<'a, Message> {
let mut bttn = Button::new(
Row::new()
.push(
Column::new()
.push(text(format!("{}", hw.kind())).bold())
.push(match hw {
HardwareWallet::Supported {
fingerprint,
version,
..
} => Row::new()
.spacing(5)
.push(text(format!("fingerprint: {}", fingerprint)).small())
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
),
HardwareWallet::Unsupported {
version, message, ..
} => Row::new()
.spacing(5)
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
)
.push(
tooltip::Tooltip::new(
icon::warning_icon(),
message,
tooltip::Position::Bottom,
)
.style(theme::Container::Card(theme::Card::Simple)),
),
})
.spacing(5)
.width(Length::Fill),
)
.push_maybe(if chosen && processing {
Some(
Column::new()
.push(text("Processing..."))
.push(text("Please check your device").small()),
signed: bool,
) -> Element<Message> {
let mut bttn = Button::new(match hw {
HardwareWallet::Supported {
kind,
version,
fingerprint,
alias,
..
} => {
if chosen && processing {
hw::processing_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
} else if signed {
hw::sign_success_hardware_wallet(
kind,
version.as_ref(),
fingerprint,
alias.as_ref(),
)
} else {
None
})
.push_maybe(status.map(|v| {
Row::new()
.align_items(Alignment::Center)
.spacing(5)
.push(icon::circle_check_icon().style(color::legacy::SUCCESS))
.push(text(v).style(color::legacy::SUCCESS))
}))
.align_items(Alignment::Center)
.width(Length::Fill),
)
.padding(10)
hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
}
}
HardwareWallet::Unsupported { version, kind, .. } => {
hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref())
}
})
.style(theme::Button::Secondary)
.width(Length::Fill);
if !processing {
if let HardwareWallet::Supported { registered, .. } = hw {
if *registered != Some(false) {
bttn = bttn.on_press(Message::SelectHardwareWallet(i));
}
}
}
Container::new(bttn)
.width(Length::Fill)
.style(theme::Container::Card(theme::Card::Simple))
.into()
}
pub fn hw_list_view_for_registration(
i: usize,
hw: &HardwareWallet,
chosen: bool,
processing: bool,
registered: bool,
) -> Element<Message> {
let mut bttn = Button::new(match hw {
HardwareWallet::Supported {
kind,
version,
fingerprint,
alias,
..
} => {
if chosen && processing {
hw::processing_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
} else if registered {
hw::registration_success_hardware_wallet(
kind,
version.as_ref(),
fingerprint,
alias.as_ref(),
)
} else {
hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
}
}
HardwareWallet::Unsupported { version, kind, .. } => {
hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref())
}
})
.style(theme::Button::Secondary)
.width(Length::Fill);
if !processing && hw.is_supported() {

View File

@ -644,18 +644,14 @@ pub fn register_wallet_modal<'a>(
.push(hws.iter().enumerate().fold(
Column::new().spacing(10),
|col, (i, hw)| {
col.push(hw::hw_list_view(
col.push(hw::hw_list_view_for_registration(
i,
hw,
Some(i) == chosen_hw,
processing,
hw.fingerprint().and_then(|f| {
if registered.contains(&f) {
Some("Registered")
} else {
None
}
}),
hw.fingerprint()
.map(|f| registered.contains(&f))
.unwrap_or(false),
))
},
))

View File

@ -739,13 +739,9 @@ pub fn sign_action<'a>(
hw,
Some(i) == chosen_hw,
processing,
hw.fingerprint().and_then(|f| {
if signed.contains(&f) {
Some("Signed")
} else {
None
}
}),
hw.fingerprint()
.map(|f| signed.contains(&f))
.unwrap_or(false),
))
},
))

View File

@ -1,5 +1,6 @@
use std::sync::Arc;
use std::{collections::HashMap, sync::Arc};
use crate::app::wallet::Wallet;
use async_hwi::{ledger, specter, DeviceKind, Error as HWIError, Version, HWI};
use liana::miniscript::bitcoin::{
hashes::hex::{FromHex, ToHex},
@ -20,11 +21,16 @@ pub enum HardwareWallet {
kind: DeviceKind,
fingerprint: Fingerprint,
version: Option<Version>,
registered: Option<bool>,
alias: Option<String>,
},
}
impl HardwareWallet {
async fn new(device: Arc<dyn HWI + Send + Sync>) -> Result<Self, HWIError> {
async fn new(
device: Arc<dyn HWI + Send + Sync>,
aliases: Option<&HashMap<Fingerprint, String>>,
) -> Result<Self, HWIError> {
let kind = device.device_kind();
let fingerprint = device.get_master_fingerprint().await?;
let version = device.get_version().await.ok();
@ -33,6 +39,8 @@ impl HardwareWallet {
kind,
fingerprint,
version,
registered: None,
alias: aliases.and_then(|aliases| aliases.get(&fingerprint).cloned()),
})
}
@ -78,13 +86,12 @@ impl HardwareWalletConfig {
}
}
pub async fn list_hardware_wallets(
cfg: &[HardwareWalletConfig],
wallet: Option<(&str, &str)>,
) -> Vec<HardwareWallet> {
pub async fn list_hardware_wallets(wallet: &Wallet) -> Vec<HardwareWallet> {
let descriptor = wallet.main_descriptor.to_string();
let mut hws: Vec<HardwareWallet> = Vec::new();
match specter::SpecterSimulator::try_connect().await {
Ok(device) => match HardwareWallet::new(Arc::new(device)).await {
Ok(device) => match HardwareWallet::new(Arc::new(device), Some(&wallet.keys_aliases)).await
{
Ok(hw) => hws.push(hw),
Err(e) => {
debug!("{}", e);
@ -96,7 +103,8 @@ pub async fn list_hardware_wallets(
}
}
match specter::Specter::try_connect_serial().await {
Ok(device) => match HardwareWallet::new(Arc::new(device)).await {
Ok(device) => match HardwareWallet::new(Arc::new(device), Some(&wallet.keys_aliases)).await
{
Ok(hw) => hws.push(hw),
Err(e) => {
debug!("{}", e);
@ -110,25 +118,26 @@ pub async fn list_hardware_wallets(
match ledger::LedgerSimulator::try_connect().await {
Ok(mut device) => match device.get_master_fingerprint().await {
Ok(fingerprint) => {
if let Some((name, descriptor)) = wallet {
device
.load_wallet(
name,
descriptor,
cfg.iter()
.find(|cfg| cfg.fingerprint == fingerprint)
.map(|cfg| cfg.token()),
)
.expect("Configuration must be correct");
}
let version = device.get_version().await.ok();
if ledger_version_supported(version.as_ref()) {
let mut registered = false;
if let Some(cfg) = wallet
.hardware_wallets
.iter()
.find(|cfg| cfg.fingerprint == fingerprint)
{
device
.load_wallet(&wallet.name, &descriptor, Some(cfg.token()))
.expect("Configuration must be correct");
registered = true;
}
hws.push(HardwareWallet::Supported {
kind: device.device_kind(),
fingerprint,
device: Arc::new(device),
version,
registered: Some(registered),
alias: wallet.keys_aliases.get(&fingerprint).cloned(),
});
} else {
hws.push(HardwareWallet::Unsupported {
@ -160,25 +169,26 @@ pub async fn list_hardware_wallets(
match ledger::Ledger::<ledger::TransportHID>::connect(&api, detected) {
Ok(mut device) => match device.get_master_fingerprint().await {
Ok(fingerprint) => {
if let Some((name, descriptor)) = wallet {
device
.load_wallet(
name,
descriptor,
cfg.iter()
.find(|cfg| cfg.fingerprint == fingerprint)
.map(|cfg| cfg.token()),
)
.expect("Configuration must be correct");
}
let version = device.get_version().await.ok();
if ledger_version_supported(version.as_ref()) {
let mut registered = false;
if let Some(cfg) = wallet
.hardware_wallets
.iter()
.find(|cfg| cfg.fingerprint == fingerprint)
{
device
.load_wallet(&wallet.name, &descriptor, Some(cfg.token()))
.expect("Configuration must be correct");
registered = true;
}
hws.push(HardwareWallet::Supported {
kind: device.device_kind(),
fingerprint,
device: Arc::new(device),
version,
registered: Some(registered),
alias: wallet.keys_aliases.get(&fingerprint).cloned(),
});
} else {
hws.push(HardwareWallet::Unsupported {
@ -222,3 +232,112 @@ fn ledger_version_supported(version: Option<&Version>) -> bool {
false
}
}
pub async fn list_unregistered_hardware_wallets(
aliases: Option<&HashMap<Fingerprint, String>>,
) -> Vec<HardwareWallet> {
let mut hws: Vec<HardwareWallet> = Vec::new();
match specter::SpecterSimulator::try_connect().await {
Ok(device) => match HardwareWallet::new(Arc::new(device), aliases).await {
Ok(hw) => hws.push(hw),
Err(e) => {
debug!("{}", e);
}
},
Err(HWIError::DeviceNotFound) => {}
Err(e) => {
debug!("{}", e);
}
}
match specter::Specter::try_connect_serial().await {
Ok(device) => match HardwareWallet::new(Arc::new(device), aliases).await {
Ok(hw) => hws.push(hw),
Err(e) => {
debug!("{}", e);
}
},
Err(HWIError::DeviceNotFound) => {}
Err(e) => {
debug!("{}", e);
}
}
match ledger::LedgerSimulator::try_connect().await {
Ok(device) => match device.get_master_fingerprint().await {
Ok(fingerprint) => {
let version = device.get_version().await.ok();
if ledger_version_supported(version.as_ref()) {
hws.push(HardwareWallet::Supported {
kind: device.device_kind(),
fingerprint,
device: Arc::new(device),
version,
registered: None,
alias: aliases.and_then(|aliases| aliases.get(&fingerprint).cloned()),
});
} else {
hws.push(HardwareWallet::Unsupported {
kind: device.device_kind(),
version,
message: "Minimal supported app version is 2.1.0".to_string(),
});
}
}
Err(_) => {
hws.push(HardwareWallet::Unsupported {
kind: device.device_kind(),
version: None,
message: "Minimal supported app version is 2.1.0".to_string(),
});
}
},
Err(HWIError::DeviceNotFound) => {}
Err(e) => {
debug!("{}", e);
}
}
match ledger::HidApi::new() {
Err(e) => {
debug!("{}", e);
}
Ok(api) => {
for detected in ledger::Ledger::<ledger::TransportHID>::enumerate(&api) {
match ledger::Ledger::<ledger::TransportHID>::connect(&api, detected) {
Ok(device) => match device.get_master_fingerprint().await {
Ok(fingerprint) => {
let version = device.get_version().await.ok();
if ledger_version_supported(version.as_ref()) {
hws.push(HardwareWallet::Supported {
kind: device.device_kind(),
fingerprint,
device: Arc::new(device),
version,
registered: None,
alias: aliases
.and_then(|aliases| aliases.get(&fingerprint).cloned()),
});
} else {
hws.push(HardwareWallet::Unsupported {
kind: device.device_kind(),
version,
message: "Minimal supported app version is 2.1.0".to_string(),
});
}
}
Err(_) => {
hws.push(HardwareWallet::Unsupported {
kind: device.device_kind(),
version: None,
message: "Minimal supported app version is 2.1.0".to_string(),
});
}
},
Err(HWIError::DeviceNotFound) => {}
Err(e) => {
debug!("{}", e);
}
}
}
}
}
hws
}

View File

@ -26,7 +26,7 @@ use async_hwi::DeviceKind;
use crate::{
app::settings::KeySetting,
hw::{list_hardware_wallets, HardwareWallet},
hw::{list_unregistered_hardware_wallets, HardwareWallet},
installer::{
message::{self, Message},
step::{Context, Step},
@ -754,8 +754,9 @@ impl EditXpubModal {
}
}
fn load(&self) -> Command<Message> {
let keys_aliases = self.keys_aliases.clone();
Command::perform(
list_hardware_wallets(&[], None),
async move { list_unregistered_hardware_wallets(Some(&keys_aliases)).await },
Message::ConnectedHardwareWallets,
)
}
@ -1174,7 +1175,7 @@ impl Step for ParticipateXpub {
fn load(&self) -> Command<Message> {
Command::perform(
list_hardware_wallets(&[], None),
list_unregistered_hardware_wallets(None),
Message::ConnectedHardwareWallets,
)
}
@ -1301,6 +1302,7 @@ impl From<ImportDescriptor> for Box<dyn Step> {
#[derive(Default)]
pub struct RegisterDescriptor {
descriptor: Option<LianaDescriptor>,
keys_aliases: HashMap<Fingerprint, String>,
processing: bool,
chosen_hw: Option<usize>,
hws: Vec<HardwareWallet>,
@ -1313,6 +1315,11 @@ pub struct RegisterDescriptor {
impl Step for RegisterDescriptor {
fn load_context(&mut self, ctx: &Context) {
self.descriptor = ctx.descriptor.clone();
let mut map = HashMap::new();
for key in ctx.keys.iter().filter(|k| !k.name.is_empty()) {
map.insert(key.master_fingerprint, key.name.clone());
}
self.keys_aliases = map;
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
@ -1373,8 +1380,9 @@ impl Step for RegisterDescriptor {
true
}
fn load(&self) -> Command<Message> {
let keys_aliases = self.keys_aliases.clone();
Command::perform(
list_hardware_wallets(&[], None),
async move { list_unregistered_hardware_wallets(Some(&keys_aliases)).await },
Message::ConnectedHardwareWallets,
)
}

View File

@ -9,7 +9,7 @@ use liana::miniscript::bitcoin;
use liana_ui::{
color,
component::{
button, card, collapse, form, separation,
button, card, collapse, form, hw, separation,
text::{text, Text},
tooltip,
},
@ -473,67 +473,32 @@ pub fn hardware_wallet_xpubs<'a>(
processing: bool,
error: Option<&Error>,
) -> Element<'a, Message> {
let mut bttn = Button::new(
Row::new()
.align_items(Alignment::Center)
.push(
Column::new()
.push(text(format!("{}", hw.kind())).bold())
.push(match hw {
HardwareWallet::Supported {
fingerprint,
version,
..
} => Row::new()
.spacing(5)
.push(text(format!("fingerprint: {}", fingerprint)).small())
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
),
HardwareWallet::Unsupported {
version, message, ..
} => Row::new()
.spacing(5)
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
)
.push(
iced::widget::tooltip::Tooltip::new(
icon::warning_icon(),
message,
iced::widget::tooltip::Position::Bottom,
)
.style(theme::Container::Card(theme::Card::Simple)),
),
})
.spacing(5)
.width(Length::Fill),
)
.push_maybe(error.map(|e| {
iced::widget::tooltip(
Row::new()
.spacing(5)
.align_items(Alignment::Center)
.push(icon::warning_icon().style(color::legacy::ALERT))
.push(text("An error occured").style(color::legacy::ALERT)),
e,
iced::widget::tooltip::Position::Bottom,
)
.style(theme::Container::Card(theme::Card::Error))
})),
)
.padding(10)
.style(theme::Button::TransparentBorder)
let mut bttn = Button::new(match hw {
HardwareWallet::Supported {
kind,
version,
fingerprint,
alias,
..
} => {
if processing {
hw::processing_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
} else {
hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
}
}
HardwareWallet::Unsupported { version, kind, .. } => {
hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref())
}
})
.style(theme::Button::Secondary)
.width(Length::Fill);
if !processing && hw.is_supported() {
bttn = bttn.on_press(Message::Select(i));
}
Container::new(
Column::new()
.push_maybe(error.map(|e| card::warning(e.to_string()).width(Length::Fill)))
.push(bttn)
.push_maybe(if xpubs.is_empty() {
None
@ -1468,66 +1433,29 @@ fn hw_list_view(
hw: &HardwareWallet,
chosen: bool,
processing: bool,
registered: bool,
selected: bool,
) -> Element<Message> {
let mut bttn = Button::new(
Row::new()
.push(
Column::new()
.push(text(format!("{}", hw.kind())).bold())
.push(match hw {
HardwareWallet::Supported {
fingerprint,
version,
..
} => Row::new()
.spacing(5)
.push(text(format!("fingerprint: {}", fingerprint)).small())
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
),
HardwareWallet::Unsupported {
version, message, ..
} => Row::new()
.spacing(5)
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
)
.push(
iced::widget::tooltip::Tooltip::new(
icon::warning_icon(),
message,
iced::widget::tooltip::Position::Bottom,
)
.style(theme::Container::Card(theme::Card::Simple)),
),
})
.spacing(5)
.width(Length::Fill),
)
.push_maybe(if chosen && processing {
Some(
Column::new()
.push(text("Processing..."))
.push(text("Please check your device").small()),
)
let mut bttn = Button::new(match hw {
HardwareWallet::Supported {
kind,
version,
fingerprint,
alias,
..
} => {
if chosen && processing {
hw::processing_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
} else if selected {
hw::selected_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
} else {
None
})
.push_maybe(if registered {
Some(Column::new().push(icon::circle_check_icon().style(color::legacy::SUCCESS)))
} else {
None
})
.align_items(Alignment::Center)
.width(Length::Fill),
)
.padding(10)
.style(theme::Button::TransparentBorder)
hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
}
}
HardwareWallet::Unsupported { version, kind, .. } => {
hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref())
}
})
.style(theme::Button::Secondary)
.width(Length::Fill);
if !processing && hw.is_supported() {
bttn = bttn.on_press(Message::Select(i));

View File

@ -10,3 +10,5 @@ iced = "0.7"
iced_native = "0.8"
web-sys = "0.3.61"
liana_ui = { path = "../.." }
[workspace]

View File

@ -49,6 +49,7 @@ impl Application for DesignSystem {
Box::new(section::Overview {}),
Box::new(section::Colors {}),
Box::new(section::Buttons {}),
Box::new(section::HardwareWallets {}),
],
current: 0,
};
@ -111,21 +112,23 @@ impl Application for DesignSystem {
fn view(&self) -> Element<Message> {
let sidebar = container(
column![
[ThemeType::Light, ThemeType::Dark].iter().fold(
column![text("Choose a theme:")].spacing(10),
|column, theme| {
column.push(radio(
format!("{theme:?}"),
*theme,
Some(match self.theme {
theme::Theme::Light => ThemeType::Light,
theme::Theme::Dark => ThemeType::Dark,
theme::Theme::Legacy => ThemeType::Legacy,
}),
Message::ThemeChanged,
))
},
),
[ThemeType::Light, ThemeType::Dark, ThemeType::Legacy]
.iter()
.fold(
column![text("Choose a theme:")].spacing(10),
|column, theme| {
column.push(radio(
format!("{theme:?}"),
*theme,
Some(match self.theme {
theme::Theme::Light => ThemeType::Light,
theme::Theme::Dark => ThemeType::Dark,
theme::Theme::Legacy => ThemeType::Legacy,
}),
Message::ThemeChanged,
))
},
),
Space::with_height(Length::Units(100)),
self.sections.iter().enumerate().fold(
Column::new().spacing(10),

View File

@ -3,7 +3,12 @@ use iced::{
widget::{button, column, container, row, Space},
Alignment, Length,
};
use liana_ui::{color, component::text::*, theme, widget::Element};
use liana_ui::{
color,
component::{hw, text::*},
theme,
widget::Element,
};
use super::{Message, Section};
@ -102,3 +107,94 @@ fn button_row(style: theme::Button, label: &'static str) -> Element<Message> {
.on_press(Message::Ignore)
.into()
}
pub struct HardwareWallets {}
impl Section for HardwareWallets {
fn title(&self) -> &'static str {
"Hardware wallets"
}
fn view(&self) -> Element<Message> {
column![
text(self.title()).bold().size(50),
column![
button(
hw::supported_hardware_wallet(
"ledger",
Some("v2.1.0"),
"f123de",
None::<String>
)
.width(Length::Units(500))
)
.on_press(Message::Ignore)
.style(theme::Button::Secondary),
button(
hw::supported_hardware_wallet(
"ledger",
Some("v2.1.0"),
"f123de",
Some("Edouard key")
)
.width(Length::Units(500))
)
.on_press(Message::Ignore)
.style(theme::Button::Secondary),
button(
hw::unregistered_hardware_wallet(
"ledger",
Some("v2.1.0"),
"f123de",
Some("Edouard key")
)
.width(Length::Units(500))
)
.on_press(Message::Ignore)
.style(theme::Button::Secondary),
button(
hw::unsupported_hardware_wallet("ledger", Some("v2.1.0"))
.width(Length::Units(500))
)
.on_press(Message::Ignore)
.style(theme::Button::Secondary),
button(
hw::processing_hardware_wallet(
"ledger",
Some("v2.1.0"),
"f123de",
Some("Edouard key")
)
.width(Length::Units(500))
)
.on_press(Message::Ignore)
.style(theme::Button::Secondary),
button(
hw::sign_success_hardware_wallet(
"ledger",
Some("v2.1.0"),
"f123de",
Some("Edouard key")
)
.width(Length::Units(500))
)
.on_press(Message::Ignore)
.style(theme::Button::Secondary),
button(
hw::registration_success_hardware_wallet(
"ledger",
Some("v2.1.0"),
"f123de",
Some("Edouard key")
)
.width(Length::Units(500))
)
.on_press(Message::Ignore)
.style(theme::Button::Secondary),
]
.spacing(20)
]
.spacing(100)
.into()
}
}

237
gui/ui/src/component/hw.rs Normal file
View File

@ -0,0 +1,237 @@
use crate::{color, component::text::*, icon, theme, util::*, widget::*};
use iced::{
widget::{column, container, row, tooltip},
Alignment, Length,
};
use std::borrow::Cow;
use std::fmt::Display;
pub fn supported_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
kind: K,
version: Option<V>,
fingerprint: F,
alias: Option<impl Into<Cow<'a, str>>>,
) -> Container<'a, T> {
Container::new(
column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text(a).bold()))
.push(text(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text(kind.to_string()).small())
.push_maybe(version.map(|v| text(v.to_string()).small()))
.into(),
])
.width(Length::Fill),
)
.padding(10)
}
pub fn unregistered_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
kind: K,
version: Option<V>,
fingerprint: F,
alias: Option<impl Into<Cow<'a, str>>>,
) -> Container<'a, T> {
container(
row(vec![
column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text(a).bold()))
.push(text(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text(kind.to_string()).small())
.push_maybe(version.map(|v| text(v.to_string()).small()))
.into(),
])
.width(Length::Fill)
.into(),
column(vec![tooltip::Tooltip::new(
icon::warning_icon(),
"The wallet descriptor is not registered on the device.\n You can register it in the settings.",
tooltip::Position::Bottom,
)
.style(theme::Container::Card(theme::Card::Simple))
.into()])
.into(),
])
.align_items(Alignment::Center),
)
.padding(10)
}
pub fn processing_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
kind: K,
version: Option<V>,
fingerprint: F,
alias: Option<impl Into<Cow<'a, str>>>,
) -> Container<'a, T> {
container(
row(vec![
column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text(a).bold()))
.push(text(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text(kind.to_string()).small())
.push_maybe(version.map(|v| text(v.to_string()).small()))
.into(),
])
.width(Length::Fill)
.into(),
column(vec![
text("Processing...").into(),
text("Please check your device").small().into(),
])
.into(),
])
.align_items(Alignment::Center),
)
.padding(10)
}
pub fn selected_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
kind: K,
version: Option<V>,
fingerprint: F,
alias: Option<impl Into<Cow<'a, str>>>,
) -> Container<'a, T> {
container(
row(vec![
column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text(a).bold()))
.push(text(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text(kind.to_string()).small())
.push_maybe(version.map(|v| text(v.to_string()).small()))
.into(),
])
.width(Length::Fill)
.into(),
icon::circle_check_icon()
.style(color::legacy::SUCCESS)
.into(),
])
.align_items(Alignment::Center),
)
.padding(10)
}
pub fn sign_success_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
kind: K,
version: Option<V>,
fingerprint: F,
alias: Option<impl Into<Cow<'a, str>>>,
) -> Container<'a, T> {
container(
row(vec![
column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text(a).bold()))
.push(text(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text(kind.to_string()).small())
.push_maybe(version.map(|v| text(v.to_string()).small()))
.into(),
])
.width(Length::Fill)
.into(),
row(vec![
icon::circle_check_icon()
.style(color::legacy::SUCCESS)
.into(),
text("Signed").style(color::legacy::SUCCESS).into(),
])
.align_items(Alignment::Center)
.spacing(5)
.into(),
])
.align_items(Alignment::Center),
)
.padding(10)
}
pub fn registration_success_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
kind: K,
version: Option<V>,
fingerprint: F,
alias: Option<impl Into<Cow<'a, str>>>,
) -> Container<'a, T> {
container(
row(vec![
column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text(a).bold()))
.push(text(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text(kind.to_string()).small())
.push_maybe(version.map(|v| text(v.to_string()).small()))
.into(),
])
.width(Length::Fill)
.into(),
row(vec![
icon::circle_check_icon()
.style(color::legacy::SUCCESS)
.into(),
text("Registered").style(color::legacy::SUCCESS).into(),
])
.align_items(Alignment::Center)
.spacing(5)
.into(),
])
.align_items(Alignment::Center),
)
.padding(10)
}
pub fn unsupported_hardware_wallet<'a, T: 'a, K: Display, V: Display>(
kind: K,
version: Option<V>,
) -> Container<'a, T> {
container(
row(vec![
column(vec![
Row::new()
.spacing(5)
.push(text("Unsupported version").bold())
.into(),
Row::new()
.spacing(5)
.push(text(kind.to_string()).small())
.push_maybe(version.map(|v| text(v.to_string()).small()))
.into(),
])
.width(Length::Fill)
.into(),
tooltip::Tooltip::new(
icon::warning_icon(),
"Please update the application on your device",
tooltip::Position::Bottom,
)
.style(theme::Container::Card(theme::Card::Simple))
.into(),
])
.align_items(Alignment::Center),
)
.padding(10)
}

View File

@ -3,6 +3,7 @@ pub mod button;
pub mod card;
pub mod collapse;
pub mod form;
pub mod hw;
pub mod modal;
pub mod notification;
pub mod text;