use crate::alloc::string::ToString; use crate::card::model::Card; use crate::navigation::inputs::ButtonAction; use crate::navigation::inputs::Inputs; use crate::navigation::outputs::Outputs; use crate::navigation::state::NavigationState; use crate::peripherals::Peripherals; use crate::views::error_view::ErrorView; use crate::views::view::View; use alloc::boxed::Box; use alloc::string::String; use core::error; use embassy_futures::select::{Either, select}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_time::{Duration, Timer}; pub type Mutex = embassy_sync::mutex::Mutex; pub type Signal = embassy_sync::signal::Signal; pub static CARD_DATA: Mutex> = Mutex::new(None); const DEBOUNCE_DURATION_MILLIS: u64 = 100; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Action { Button(ButtonAction), Timer, } pub struct NewState { pub view: View, pub replace_view: bool, pub redraw: bool, } pub trait Navigable { fn display( &self, outputs: &mut Outputs, peripherals: &Peripherals, ) -> impl core::future::Future>> + Send; fn handle_input(&self, input: Action) -> impl core::future::Future + Send; } #[embassy_executor::task] pub async fn run(mut inputs: Inputs, mut outputs: Outputs, peripherals: &'static Peripherals) { async fn display_error( error: String, state: &mut NavigationState, outputs: &mut Outputs, peripherals: &Peripherals, ) -> () { let error_view = ErrorView { error: error.to_string(), }; state.screens.push(View::Error(error_view)); state .screens .last() // Unwrap: we just pushed the state .unwrap() .display(outputs, peripherals) .await // Unwrap: display will panic on error in error view .unwrap(); } log::info!("starting navigation"); let mut state = NavigationState::new(); if let Err(error) = state .screens .last() .unwrap() .display(&mut outputs, peripherals) .await { display_error(error.to_string(), &mut state, &mut outputs, &peripherals).await; }; loop { let selection = select( inputs.wait_for_press(), Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)), ) .await; let action = match selection { Either::First(button) => Action::Button(button), Either::Second(_) => Action::Timer, }; if Action::Timer != action { log::info!("Action: {:?}", action); } let new_state = if action == Action::Button(ButtonAction::Back) { if state.screens.len() > 1 { state.screens.pop(); } NewState { view: state.screens.last().expect("no screen on stack").clone(), replace_view: false, redraw: true, } } else { let new_state = state .screens .last() .expect("no screen on stack") .handle_input(action) .await; if action == Action::Timer && !new_state.redraw { continue; } new_state }; if new_state.replace_view { state.screens.pop(); } state.screens.push(new_state.view); if action != Action::Timer || new_state.redraw { if let Err(error) = state .screens .last() .unwrap() .display(&mut outputs, peripherals) .await { display_error(error.to_string(), &mut state, &mut outputs, &peripherals).await; } } Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)).await; } }