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:
commit
a402f85101
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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),
|
||||
))
|
||||
},
|
||||
))
|
||||
|
||||
@ -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),
|
||||
))
|
||||
},
|
||||
))
|
||||
|
||||
183
gui/src/hw.rs
183
gui/src/hw.rs
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -10,3 +10,5 @@ iced = "0.7"
|
||||
iced_native = "0.8"
|
||||
web-sys = "0.3.61"
|
||||
liana_ui = { path = "../.." }
|
||||
|
||||
[workspace]
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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
237
gui/ui/src/component/hw.rs
Normal 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)
|
||||
}
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user