From 80174621db6536b10f1df6944d053871df3f04d3 Mon Sep 17 00:00:00 2001 From: Lukas Bielesch Date: Sun, 13 Apr 2025 00:05:35 +0200 Subject: [PATCH] fixup! feat(eckhart): add FIDO2 support FidoCredential component, confirm_fido flow and icons --- .../src/ui/layout_eckhart/firmware/fido.rs | 42 +++++++++------- .../src/ui/layout_eckhart/firmware/mod.rs | 2 +- .../ui/layout_eckhart/flow/confirm_fido.rs | 48 ++++++++++++------- 3 files changed, 57 insertions(+), 35 deletions(-) diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/fido.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/fido.rs index af08fec383..f93c97d3ca 100644 --- a/core/embed/rust/src/ui/layout_eckhart/firmware/fido.rs +++ b/core/embed/rust/src/ui/layout_eckhart/firmware/fido.rs @@ -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 TString<'static>> { +pub trait FidoAccountName: Fn() -> TString<'static> {} +impl TString<'static>> FidoAccountName for T {} + +pub struct FidoCredential { app_icon: Option, text: Paragraphs>, get_account: F, } -impl TString<'static>> FidoCredential { +impl FidoCredential { const ICON_SIZE: i16 = 32; const SPACING: i16 = 24; @@ -32,6 +35,7 @@ impl TString<'static>> FidoCredential { 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 TString<'static>> FidoCredential { } } -impl TString<'static>> Component for FidoCredential { - type Msg = (); +impl Component for FidoCredential { + 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 TString<'static>> Component for FidoCredential { } } -impl TString<'static>> SinglePage for FidoCredential {} +impl SinglePage for FidoCredential {} #[cfg(feature = "ui_debug")] -impl TString<'static>> crate::trace::Trace for FidoCredential { +impl crate::trace::Trace for FidoCredential { 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); } } diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/mod.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/mod.rs index ac5d30d48f..939398802e 100644 --- a/core/embed/rust/src/ui/layout_eckhart/firmware/mod.rs +++ b/core/embed/rust/src/ui/layout_eckhart/firmware/mod.rs @@ -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; diff --git a/core/embed/rust/src/ui/layout_eckhart/flow/confirm_fido.rs b/core/embed/rust/src/ui/layout_eckhart/flow/confirm_fido.rs index 86ce96d803..791d94edc7 100644 --- a/core/embed/rust/src/ui/layout_eckhart/flow/confirm_fido.rs +++ b/core/embed/rust/src/ui/layout_eckhart/flow/confirm_fido.rs @@ -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::(&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, });