Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 43 additions & 32 deletions src/components/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use crate::{
rfid_reader,
};

type SelectedRace = Option<Result<Race, Box<dyn std::error::Error>>>;

const LOADING: Asset = asset!("/assets/loading.webp");

#[derive(Debug)]
Expand All @@ -18,9 +20,45 @@ pub enum Action {
FinishByStartNumber(u32),
}

fn handle_rfid_event(selected_race: &mut Signal<SelectedRace>, event: rfid_reader::Event) {
match event {
rfid_reader::Event::Connected(device) => info!("RFID {device} connected"),
rfid_reader::Event::Disconnected { device, error } => {
info!("RFID {device} disconnected: {error:?}")
}
rfid_reader::Event::Tag(tag) => {
info!("Tag {tag}");
selected_race.with_mut(|maybe_race| {
if let Some(Ok(race)) = maybe_race {
race.tag_finished(&tag);
}
});
}
}
}

fn handle_action(selected_race: &mut Signal<SelectedRace>, action: Action) {
match action {
Action::Start(track) => {
selected_race.with_mut(|maybe_race| {
if let Some(Ok(race)) = maybe_race {
race.start(track);
}
});
}
Action::FinishByStartNumber(starting_number) => {
selected_race.with_mut(|maybe_race| {
if let Some(Ok(race)) = maybe_race {
race.finish_start_number(starting_number);
}
});
}
}
}

#[component]
pub fn App() -> Element {
let mut selected_race = use_signal(|| Option::<Result<Race, Box<dyn std::error::Error>>>::None);
let mut selected_race = use_signal(|| SelectedRace::None);

use_coroutine(
move |mut actions_rx: UnboundedReceiver<Action>| async move {
Expand All @@ -33,38 +71,11 @@ pub fn App() -> Element {
tokio::select! {
Ok(rfid_event) = rfid_rx.recv() => {
println!("{rfid_event:?}");
match rfid_event {
rfid_reader::Event::Connected(device) => info!("RFID {device} connected"),
rfid_reader::Event::Disconnected { device, error } => info!("RFID {device} disconnected: {error:?}"),
rfid_reader::Event::Tag(tag) => {
info!("Tag {tag}");
selected_race.with_mut(|maybe_race| {
if let Some(Ok(race)) = maybe_race {
race.tag_finished(&tag);
}
});
},
}
handle_rfid_event(&mut selected_race, rfid_event);
}

Some(msg) = actions_rx.next() => {
println!("{msg:?}");
match msg {
Action::Start(track) => {
selected_race.with_mut(|maybe_race| {
if let Some(Ok(race)) = maybe_race {
race.start(track);
}
});
}
Action::FinishByStartNumber(starting_number) => {
selected_race.with_mut(|maybe_race| {
if let Some(Ok(race)) = maybe_race {
race.finish_start_number(starting_number);
}
});
}
}
Some(action) = actions_rx.next() => {
println!("{action:?}");
handle_action(&mut selected_race, action);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod race;
pub mod racers;
pub mod races_list;
pub mod track_start;
pub mod upload_results;
8 changes: 6 additions & 2 deletions src/components/race.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use dioxus::prelude::*;

use crate::{
components::manual_start_number_input::ManualStartNumberInput, components::racers::Racers,
components::track_start::TrackStart, race::Race,
components::{
manual_start_number_input::ManualStartNumberInput, racers::Racers, track_start::TrackStart,
upload_results::UploadResults,
},
race::Race,
};

#[component]
Expand All @@ -12,6 +15,7 @@ pub fn RaceComponent(race: Race) -> Element {
for track in race.clone().tracks {
TrackStart { track: track.clone() }
}
UploadResults { race: race.clone() }
}
Racers { race: race.clone() }
ManualStartNumberInput {}
Expand Down
83 changes: 83 additions & 0 deletions src/components/upload_results.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use dioxus::prelude::*;
use tracing::{error, info};

use crate::{
race::{Race, Racer},
restclient::{RaceRestAPI, RacerResult},
};

enum SubmitState {
Idle,
Submitting,
Success,
Error,
}

fn racer_to_result(racer: &Racer) -> Option<RacerResult> {
match (racer.start, racer.finish) {
(Some(start_time), Some(finish_time)) => Some(RacerResult {
registration_id: racer.id,
start_time,
finish_time,
}),
(Some(_start_time), None) => {
error!("Skipping submit for {}, no finish time", racer.start_number);
None
}
(None, Some(_finish_time)) => {
error!("Skipping submit for {}, no start time", racer.start_number);
None
}
(None, None) => {
error!(
"Skipping submit for {}, no start and no finish time",
racer.start_number
);
None
}
}
}

fn upload_results(race: &Race, mut state: Signal<SubmitState>, api: RaceRestAPI) {
let results: Vec<_> = race.racers.iter().filter_map(racer_to_result).collect();
if results.is_empty() {
error!("There are no results!");
state.set(SubmitState::Error);
} else {
state.set(SubmitState::Submitting);
let id = race.id;
spawn(async move {
state.set(match api.results(id, results).await {
Ok(_) => {
info!("Results succesfully uploaded");
SubmitState::Success
}
Err(err) => {
error!("Uploading of results failed: {err:?}");
SubmitState::Error
}
})
});
}
}

#[component]
pub fn UploadResults(race: Race) -> Element {
let api = use_context::<RaceRestAPI>();
let upload_state = use_signal(|| SubmitState::Idle);

let classes = match *upload_state.read() {
SubmitState::Success => "btn-success",
SubmitState::Submitting => "btn-warning",
SubmitState::Error => "btn-danger",
_ => "",
};

rsx! {
button {
class: ["btn btn-primary ms-auto", classes].join(" "),
onclick: move |_evt| { upload_results(&race, upload_state, api.clone()) },
"Upload results"
}
}
}
2 changes: 2 additions & 0 deletions src/race.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct Race {

#[derive(Clone, PartialEq)]
pub struct Racer {
pub id: u32,
pub start_number: u32,
pub tag: String,
pub first_name: String,
Expand Down Expand Up @@ -67,6 +68,7 @@ impl Race {
let racers = api_result
.into_iter()
.map(|racer| Racer {
id: racer.id,
start_number: racer.start_number.unwrap_or(0),
tag: racer.tag_id.unwrap_or("".to_string()),
first_name: racer.first_name,
Expand Down
Loading