use axum::extract::State; use axum::response::IntoResponse; use axum::routing::get; use axum::Router; use hemx_axum::{interactions, runtime_js, runtime_js_path, EffectResponse, InteractionRequest}; use hemx_workout_example::ui::workout; use hemx_workout_example::{self as workout_app, AppState}; use std::net::SocketAddr; use std::str::FromStr; #[tokio::main] async fn main() { let app = app(AppState::demo()); let addr = std::env::var("HEMX_WORKOUT_ADDR") .ok() .and_then(|value| SocketAddr::from_str(&value).ok()) .unwrap_or_else(|| SocketAddr::from(([127, 0, 0, 1], 3028))); let listener = tokio::net::TcpListener::bind(addr).await.expect( "bind workout example address; set HEMX_WORKOUT_ADDR=127.0.0.1:3030 if the default is busy", ); eprintln!("hemx workout example: http://{addr}"); axum::serve(listener, app).await.unwrap(); } fn app(state: AppState) -> Router { Router::new() .route("/", get(page).post(interact)) .route(runtime_js_path(), get(runtime)) .with_state(state) } async fn page(State(state): State) -> impl IntoResponse { axum::response::Html(state.with_workout(|workout| workout_app::render(workout).into_string())) } async fn interact( State(state): State, request: InteractionRequest, ) -> Result { request .dispatch_async( interactions(hemx_workout_example::ui::BUILD_FINGERPRINT) .on(workout::complete_set, { let state = state.clone(); move |_| workout_app::complete_set(&state) }) .on(workout::change_weight, { let state = state.clone(); move |form| { workout_app::change_weight(&state, form.parse::("kg").unwrap_or(0.0)) } }) .on(workout::skip_exercise, { let state = state.clone(); move |_| workout_app::skip_exercise(&state) }) .on(workout::record_note, { let state = state.clone(); move |form| { workout_app::record_note( &state, form.value("text").unwrap_or("").to_owned(), ) } }) .on(workout::export_log, { let state = state.clone(); move |_| workout_app::export_log(&state) }) .on(workout::record_share_result, { let state = state.clone(); move |_| workout_app::record_share_result(&state) }) .on(workout::request_native_haptic, { let state = state.clone(); move |_| workout_app::request_native_haptic(&state) }) .on(workout::record_native_haptic_ack, move |_| { workout_app::record_native_haptic_ack(&state) }), ) .await } async fn runtime() -> impl IntoResponse { runtime_js() } #[cfg(test)] mod tests { use super::*; #[test] fn routes_can_be_built_for_run_and_deploy_smoke() { let _app = app(AppState::demo()); } }