1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-04-20 17:19:01 +00:00

fixup! feat(eckhart): add FIDO2 support FidoCredential component, confirm_fido flow and icons

This commit is contained in:
Lukas Bielesch 2025-04-13 00:05:35 +02:00
parent f4fd6a2f0b
commit 80174621db
3 changed files with 57 additions and 35 deletions

View File

@ -8,22 +8,25 @@ use crate::{
paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs},
TextStyle,
},
Component, Event, EventCtx, LineBreaking,
Component, Event, EventCtx, LineBreaking, Never,
},
geometry::{Insets, LinearPlacement, Offset, Rect},
geometry::{LinearPlacement, Rect},
shape::Renderer,
},
};
use super::super::{firmware::fido_icons::get_fido_icon_data, theme};
pub struct FidoCredential<F: Fn() -> TString<'static>> {
pub trait FidoAccountName: Fn() -> TString<'static> {}
impl<T: Fn() -> TString<'static>> FidoAccountName for T {}
pub struct FidoCredential<F: FidoAccountName> {
app_icon: Option<Image>,
text: Paragraphs<ParagraphVecShort<'static>>,
get_account: F,
}
impl<F: Fn() -> TString<'static>> FidoCredential<F> {
impl<F: FidoAccountName> FidoCredential<F> {
const ICON_SIZE: i16 = 32;
const SPACING: i16 = 24;
@ -32,6 +35,7 @@ impl<F: Fn() -> TString<'static>> FidoCredential<F> {
app_name: TString<'static>,
get_account: F,
) -> Self {
// Text style without line-breaking hyphens
const STYLE: TextStyle =
theme::TEXT_REGULAR.with_line_breaking(LineBreaking::BreakWordsNoHyphen);
let app_icon = get_fido_icon_data(icon_name).map(Image::new);
@ -50,21 +54,19 @@ impl<F: Fn() -> TString<'static>> FidoCredential<F> {
}
}
impl<F: Fn() -> TString<'static>> Component for FidoCredential<F> {
type Msg = ();
impl<F: FidoAccountName> Component for FidoCredential<F> {
type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect {
let icon_size = self.app_icon.map_or(Offset::zero(), |i| i.toif.size());
let (icon_area, text_area) = bounds.split_top(icon_size.y);
let text_area = text_area.inset(Insets::top(Self::SPACING));
self.text.place(text_area);
// let text_height = self.text.area().height();
// let vertical_space = bounds.height() - icon_size.y - Self::SPACING -
// text_height; let off = Offset::y(vertical_space / 2);
let text_area = if let Some(app_icon) = &mut self.app_icon {
let icon_size = app_icon.toif.size();
let (icon_area, text_area) = bounds.split_top(icon_size.y + Self::SPACING);
app_icon.place(icon_area.with_width(icon_size.x).with_height(icon_size.y));
text_area
} else {
bounds
};
// let icon_area = icon_area.with_width(icon_size.x).translate(off);
// let text_area = text_area.with_height(text_height).translate(off);
self.app_icon.place(icon_area);
self.text.place(text_area);
bounds
}
@ -85,11 +87,15 @@ impl<F: Fn() -> TString<'static>> Component for FidoCredential<F> {
}
}
impl<F: Fn() -> TString<'static>> SinglePage for FidoCredential<F> {}
impl<F: FidoAccountName> SinglePage for FidoCredential<F> {}
#[cfg(feature = "ui_debug")]
impl<F: Fn() -> TString<'static>> crate::trace::Trace for FidoCredential<F> {
impl<F: FidoAccountName> crate::trace::Trace for FidoCredential<F> {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("FidoCredential");
if let Some(app_icon) = self.app_icon.as_ref() {
t.child("app_icon", app_icon);
}
t.child("paragraphs", &self.text);
}
}

View File

@ -22,7 +22,7 @@ pub use action_bar::{ActionBar, ActionBarMsg};
pub use brightness_screen::SetBrightnessScreen;
pub use confirm_homescreen::{ConfirmHomescreen, ConfirmHomescreenMsg};
pub use device_menu_screen::{DeviceMenuMsg, DeviceMenuScreen};
pub use fido::FidoCredential;
pub use fido::{FidoAccountName, FidoCredential};
pub use header::{Header, HeaderMsg};
pub use hint::Hint;
pub use hold_to_confirm::HoldToConfirmAnim;

View File

@ -19,7 +19,7 @@ use crate::{
use super::super::{
component::Button,
firmware::{
ActionBar, FidoCredential, Header, TextScreen, TextScreenMsg, VerticalMenu,
ActionBar, FidoCredential, Header, HeaderMsg, TextScreen, TextScreenMsg, VerticalMenu,
VerticalMenuScreen, VerticalMenuScreenMsg,
},
theme,
@ -57,7 +57,13 @@ impl FlowController for ConfirmFido {
Self::Authenticate.goto()
}
(_, FlowMsg::Info) => Self::Menu.goto(),
(Self::Authenticate, FlowMsg::Cancelled) => Self::ChooseCredential.goto(),
(Self::Authenticate, FlowMsg::Cancelled) => {
if single_cred() {
self.return_msg(FlowMsg::Cancelled)
} else {
Self::ChooseCredential.goto()
}
}
(Self::Authenticate, FlowMsg::Confirmed) => {
self.return_msg(FlowMsg::Choice(CRED_SELECTED.load(Ordering::Relaxed)))
}
@ -88,6 +94,7 @@ pub fn new_confirm_fido(
SINGLE_CRED.store(num_accounts <= 1, Ordering::Relaxed);
CRED_SELECTED.store(0, Ordering::Relaxed);
// Intro screen
let content_intro = TextScreen::new(
Paragraph::new::<TString>(&theme::TEXT_REGULAR, TR::fido__select_intro.into())
.into_paragraphs()
@ -103,54 +110,63 @@ pub fn new_confirm_fido(
_ => None,
});
// Choose credential screen
let mut credentials = VerticalMenu::empty();
for i in 0..num_accounts {
let account = unwrap!(accounts.get(i));
let label = account
.try_into()
.unwrap_or_else(|_| TString::from_str("-"));
credentials = credentials.item(Button::with_text(label));
credentials = credentials.item(Button::new_menu_item(label, theme::menu_item_title()));
}
let content_choose_credential = VerticalMenuScreen::new(credentials)
.with_header(Header::new(TR::fido__title_select_credential.into()))
.map(|msg| match msg {
VerticalMenuScreenMsg::Selected(i) => Some(FlowMsg::Choice(i)),
VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
VerticalMenuScreenMsg::Menu => Some(FlowMsg::Info),
_ => None,
});
// Authenticate screen
let get_account = move || {
let current = CRED_SELECTED.load(Ordering::Relaxed);
let account = unwrap!(accounts.get(current));
account.try_into().unwrap_or_else(|_| TString::from_str(""))
};
let mut auth_header = Header::new(TR::fido__title_credential_details.into());
auth_header = if single_cred() {
auth_header.with_menu_button()
let auth_header = if single_cred() {
Header::new(title)
} else {
auth_header.with_close_button()
Header::new(TR::fido__title_credential_details.into()).with_close_button()
};
let auth_action_bar = if single_cred() {
ActionBar::new_cancel_confirm()
} else {
ActionBar::new_single(
Button::with_text(TR::words__authenticate.into()).styled(theme::button_confirm()),
)
};
let content_authenticate =
TextScreen::new(FidoCredential::new(icon_name, app_name, get_account))
.with_header(auth_header)
.with_action_bar(ActionBar::new_single(
Button::with_text(TR::words__authenticate.into()).styled(theme::button_confirm()),
))
.with_action_bar(auth_action_bar)
.map(|msg| match msg {
TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
TextScreenMsg::Menu => Some(FlowMsg::Info),
_ => None,
});
let content_menu = VerticalMenuScreen::new(VerticalMenu::empty().item(
Button::with_text(TR::buttons__cancel.into()).styled(theme::menu_item_title_orange()),
))
.with_header(Header::new(TR::fido__title_authenticate.into()).with_close_button())
// Menu screen
let content_menu = VerticalMenuScreen::new(VerticalMenu::empty().item(Button::new_menu_item(
TR::buttons__cancel.into(),
theme::menu_item_title_orange(),
)))
.with_header(Header::new(title).with_close_button())
.map(|msg| match msg {
VerticalMenuScreenMsg::Selected(0) => Some(FlowMsg::Choice(0)),
VerticalMenuScreenMsg::Menu => Some(FlowMsg::Info),
VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
_ => None,
});