138 lines
4.0 KiB
Rust
138 lines
4.0 KiB
Rust
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<T> = embassy_sync::mutex::Mutex<CriticalSectionRawMutex, T>;
|
|
pub type Signal<T> = embassy_sync::signal::Signal<CriticalSectionRawMutex, T>;
|
|
pub static CARD_DATA: Mutex<Option<Card>> = 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<Output = Result<(), Box<dyn error::Error>>> + Send;
|
|
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + 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;
|
|
}
|
|
}
|