ui: add a widget to let user select custom derivation path of an xpub

This commit is contained in:
pythcoiner 2025-05-14 17:27:18 +02:00
parent 0ba12eb689
commit 8efc55bcf5
No known key found for this signature in database
GPG Key ID: C1048AEEDF303B88
2 changed files with 135 additions and 31 deletions

View File

@ -1684,13 +1684,21 @@ pub fn hw_list_view<'a>(
} else 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(), {
if not_tapminiscript {
Some("Device firmware version does not support taproot miniscript")
} else {
None
}
})
hw::selected_hardware_wallet(
kind,
version.as_ref(),
fingerprint,
alias.as_ref(),
{
if not_tapminiscript {
Some("Device firmware version does not support taproot miniscript")
} else {
None
}
},
None,
true,
)
} else if not_tapminiscript {
hw::warning_hardware_wallet(
kind,
@ -1758,6 +1766,8 @@ pub fn key_list_view<'a>(
} else {
None
},
None,
true,
)
} else if device_must_support_taproot
&& kind.map(|kind| is_compatible_with_tapminiscript(kind, version)) == Some(false)

View File

@ -1,7 +1,8 @@
use crate::{color, component::text, icon, image, theme, widget::*};
use bitcoin::bip32::{ChildNumber, Fingerprint};
use iced::{
alignment::Vertical,
widget::{column, container, row, tooltip, Space},
widget::{column, container, pick_list, row, tooltip, Space},
Alignment, Length,
};
use std::borrow::Cow;
@ -59,6 +60,88 @@ pub fn supported_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
.padding(10)
}
#[derive(Debug, Clone, PartialEq)]
pub struct Account {
pub index: ChildNumber,
pub fingerprint: Fingerprint,
}
impl Account {
pub fn new(index: ChildNumber, fingerprint: Fingerprint) -> Self {
Self { index, fingerprint }
}
}
impl Display for Account {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let index = self.index.to_string();
let index = index.replace("'", "");
write!(f, "Account #{}", index)
}
}
pub fn supported_hardware_wallet_with_account<
'a,
M: 'static + From<(Fingerprint, ChildNumber)> + Clone,
K: Display,
V: Display,
>(
kind: K,
version: Option<V>,
fingerprint: Fingerprint,
alias: Option<impl Into<Cow<'a, str>> + Display>,
account: Option<ChildNumber>,
edit_account: bool,
) -> Container<'a, M> {
let accounts: Vec<_> = (0..10)
.map(|i| {
Account::new(
ChildNumber::from_hardened_idx(i).expect("hardcoded"),
fingerprint,
)
})
.collect();
let account = Some(account.unwrap_or(ChildNumber::Hardened { index: 0 }));
let account = account.map(|i| Account::new(i, fingerprint));
let pick_account = pick_list(accounts, account.clone(), |a| {
(a.fingerprint, a.index).into()
});
let pick_account = if edit_account {
Some(pick_account)
} else {
None
};
let display_account = account.and_then(|a| {
if !edit_account {
Some(text::p1_bold(a))
} else {
None
}
});
let key = column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text::p1_bold(a)))
.push(text::p1_regular(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text::caption(kind.to_string()))
.push_maybe(version.map(|v| text::caption(v.to_string())))
.into(),
])
.width(Length::Fill);
Container::new(
Row::new()
.push(key)
.push(Space::with_width(Length::Fill))
.push_maybe(pick_account)
.push_maybe(display_account.map(|a| column![Space::with_height(8), a])),
)
.align_y(Alignment::Center)
.padding(10)
}
pub fn warning_hardware_wallet<'a, T: 'static, K: Display, V: Display, F: Display>(
kind: K,
version: Option<V>,
@ -208,39 +291,50 @@ pub fn selected_hardware_wallet<'a, T: 'static, K: Display, V: Display, F: Displ
fingerprint: F,
alias: Option<impl Into<Cow<'a, str>> + Display>,
warning: Option<&'static str>,
account: Option<ChildNumber>,
display_account: bool,
) -> Container<'a, T> {
container(
row(vec![
column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(text::p1_bold))
.push(text::p1_regular(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text::caption(kind.to_string()))
.push_maybe(version.map(|v| text::caption(v)))
.into(),
])
.width(Length::Fill)
let account = account.unwrap_or(ChildNumber::from_hardened_idx(0).expect("hardcoded"));
let index = match account {
ChildNumber::Hardened { index } => index,
ChildNumber::Normal { .. } => unreachable!(),
};
let account = if display_account {
Some(format!("Account #{index}"))
} else {
None
};
let key = column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text::p1_bold(a)))
.push(text::p1_regular(format!("#{}", fingerprint)))
.into(),
if let Some(w) = warning {
Row::new()
.spacing(5)
.push(text::caption(kind.to_string()))
.push_maybe(version.map(|v| text::caption(v.to_string())))
.into(),
]);
container(
Row::new()
.push(key)
.push(Space::with_width(Length::Fill))
.push_maybe(account.map(|a| column![Space::with_height(8), text::p1_bold(a)]))
.push(Space::with_width(10))
.push_maybe(warning.map(|w| {
tooltip::Tooltip::new(
icon::warning_icon(),
iced::widget::text!("{}", w),
tooltip::Position::Bottom,
)
.style(theme::card::simple)
.into()
} else {
Row::new().into()
},
image::success_mark_icon().width(Length::Fixed(50.0)).into(),
])
.align_y(Alignment::Center),
}))
.push(image::success_mark_icon().width(Length::Fixed(50.0))),
)
.padding(10)
.align_y(Alignment::Center)
}
pub fn sign_success_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(