Add key alias to hardware wallets

This commit is contained in:
edouard 2023-04-04 12:21:17 +02:00
parent 3287488737
commit f1549e1e97
11 changed files with 604 additions and 324 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,12 +1,6 @@
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};
@ -17,87 +11,31 @@ pub fn hw_list_view(
processing: bool,
signed: 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,
registered,
..
} => Row::new()
.align_items(Alignment::Center)
.spacing(5)
.push(text(format!("fingerprint: {}", fingerprint)).small())
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
)
.push_maybe(registered.and_then(|registered|
if !registered {
Some(Row::new()
.spacing(5)
.align_items(Alignment::Center)
.push(text("unregistered").small())
.push(
tooltip::Tooltip::new(
icon::warning_icon(),
"Policy is not registered on the device.\n You can register it in the settings.",
tooltip::Position::Bottom,
).style(theme::Container::Card(theme::Card::Simple))))
} else {
None
})
),
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()),
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(if signed {
Some(
Row::new()
.align_items(Alignment::Center)
.spacing(5)
.push(icon::circle_check_icon().style(color::legacy::SUCCESS))
.push(text("Signed").style(color::legacy::SUCCESS)),
)
} else {
None
})
.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 {
@ -120,69 +58,31 @@ pub fn hw_list_view_for_registration(
processing: bool,
registered: 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(
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()),
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 {
None
})
.push_maybe(if registered {
Some(
Row::new()
.align_items(Alignment::Center)
.spacing(5)
.push(icon::circle_check_icon().style(color::legacy::SUCCESS))
.push(text("Registered").style(color::legacy::SUCCESS)),
)
} else {
None
})
.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 && hw.is_supported() {

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},
@ -21,11 +22,15 @@ pub enum HardwareWallet {
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();
@ -35,6 +40,7 @@ impl HardwareWallet {
fingerprint,
version,
registered: None,
alias: aliases.and_then(|aliases| aliases.get(&fingerprint).cloned()),
})
}
@ -80,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);
@ -98,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);
@ -115,13 +121,15 @@ pub async fn list_hardware_wallets(
let version = device.get_version().await.ok();
if ledger_version_supported(version.as_ref()) {
let mut registered = false;
if let Some((name, descriptor)) = wallet {
if let Some(cfg) = cfg.iter().find(|cfg| cfg.fingerprint == fingerprint) {
device
.load_wallet(name, descriptor, Some(cfg.token()))
.expect("Configuration must be correct");
registered = true;
}
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(),
@ -129,6 +137,7 @@ pub async fn list_hardware_wallets(
device: Arc::new(device),
version,
registered: Some(registered),
alias: wallet.keys_aliases.get(&fingerprint).cloned(),
});
} else {
hws.push(HardwareWallet::Unsupported {
@ -163,15 +172,15 @@ pub async fn list_hardware_wallets(
let version = device.get_version().await.ok();
if ledger_version_supported(version.as_ref()) {
let mut registered = false;
if let Some((name, descriptor)) = wallet {
if let Some(cfg) =
cfg.iter().find(|cfg| cfg.fingerprint == fingerprint)
{
device
.load_wallet(name, descriptor, Some(cfg.token()))
.expect("Configuration must be correct");
registered = true;
}
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(),
@ -179,6 +188,7 @@ pub async fn list_hardware_wallets(
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;