diff --git a/fitnesstrax/app/src/components/day.rs b/fitnesstrax/app/src/components/day.rs index b4b130d..5ba2d8d 100644 --- a/fitnesstrax/app/src/components/day.rs +++ b/fitnesstrax/app/src/components/day.rs @@ -17,9 +17,11 @@ You should have received a copy of the GNU General Public License along with Fit // use chrono::NaiveDate; // use ft_core::TraxRecord; use crate::{ - components::{steps_editor, weight_editor, ActionGroup, Steps, Weight}, + components::{steps_editor, time_distance_summary, weight_editor, ActionGroup, Steps, Weight}, view_models::DayDetailViewModel, }; +use dimensioned::si; +use ft_core::{RecordType, TraxRecord}; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; use std::cell::RefCell; @@ -96,8 +98,10 @@ impl DaySummary { label.set_label(&format!("{} steps", s.to_string())); } row.append(&label); - self.append(&row); + + let biking_summary = view_model.biking_summary(); + time_distance_summary(biking_summary.0, biking_summary.1).map(|label| self.append(&label)); } } diff --git a/fitnesstrax/app/src/components/mod.rs b/fitnesstrax/app/src/components/mod.rs index 880664e..7d65146 100644 --- a/fitnesstrax/app/src/components/mod.rs +++ b/fitnesstrax/app/src/components/mod.rs @@ -30,7 +30,7 @@ mod text_entry; pub use text_entry::{ParseError, TextEntry}; mod time_distance; -pub use time_distance::TimeDistanceView; +pub use time_distance::{time_distance_summary, TimeDistanceView}; mod weight; pub use weight::{weight_editor, Weight}; diff --git a/fitnesstrax/app/src/components/time_distance.rs b/fitnesstrax/app/src/components/time_distance.rs index ac1ad9c..53c0881 100644 --- a/fitnesstrax/app/src/components/time_distance.rs +++ b/fitnesstrax/app/src/components/time_distance.rs @@ -17,11 +17,33 @@ You should have received a copy of the GNU General Public License along with Fit // use crate::components::{EditView, ParseError, TextEntry}; // use chrono::{Local, NaiveDate}; // use dimensioned::si; +use dimensioned::si; use ft_core::{RecordType, TimeDistance}; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; use std::cell::RefCell; +pub fn time_distance_summary( + distance: si::Meter, + duration: si::Second, +) -> Option { + let text = match (distance > si::M, duration > si::S) { + (true, true) => Some(format!( + "{} kilometers of biking in {} minutes", + distance.value_unsafe / 1000., + duration.value_unsafe / 60. + )), + (true, false) => Some(format!( + "{} kilometers of biking", + distance.value_unsafe / 1000. + )), + (false, true) => Some(format!("{} seconds of biking", duration.value_unsafe / 60.)), + (false, false) => None, + }; + + text.map(|text| gtk::Label::new(Some(&text))) +} + #[derive(Default)] pub struct TimeDistanceViewPrivate { #[allow(unused)] diff --git a/fitnesstrax/app/src/view_models/day_detail.rs b/fitnesstrax/app/src/view_models/day_detail.rs index f32e75f..8f7769a 100644 --- a/fitnesstrax/app/src/view_models/day_detail.rs +++ b/fitnesstrax/app/src/view_models/day_detail.rs @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with Fit use crate::app::App; use dimensioned::si; use emseries::{Record, RecordId, Recordable}; -use ft_core::TraxRecord; +use ft_core::{TimeDistance, TraxRecord}; use std::{ collections::HashMap, ops::Deref, @@ -44,6 +44,15 @@ impl RecordState { } } + fn data(&self) -> Option<&Record> { + match self { + RecordState::Original(ref r) => Some(&r), + RecordState::New(ref r) => None, + RecordState::Updated(ref r) => Some(&r), + RecordState::Deleted(ref r) => Some(&r), + } + } + fn with_value(self, value: T) -> RecordState { match self { RecordState::Original(r) => RecordState::Updated(Record { data: value, ..r }), @@ -163,6 +172,40 @@ impl DayDetailViewModel { *record = Some(new_record); } + pub fn biking_summary(&self) -> (si::Meter, si::Second) { + self.records.read().unwrap().iter().fold( + (0. * si::M, 0. * si::S), + |(acc_distance, acc_duration), (_, record)| match record.data() { + Some(Record { + data: + TraxRecord::BikeRide(TimeDistance { + distance, duration, .. + }), + .. + }) => ( + distance + .map(|distance| acc_distance + distance) + .unwrap_or(acc_distance), + (duration + .map(|duration| acc_duration + duration) + .unwrap_or(acc_duration)), + ), + + _ => (acc_distance, acc_duration), + }, + ) + } + + pub fn records(&self) -> Vec> { + let read_lock = self.records.read().unwrap(); + read_lock + .iter() + .map(|(_, record_state)| record_state.data()) + .filter_map(|r| r) + .cloned() + .collect::>>() + } + pub fn save(&self) { glib::spawn_future({ let s = self.clone(); @@ -233,3 +276,31 @@ impl DayDetailViewModel { unimplemented!(); } } + +/* +struct SavedRecordIterator<'a> { + read_lock: RwLockReadGuard<'a, HashMap>>, + iter: Box> + 'a>, +} + +impl<'a> SavedRecordIterator<'a> { + fn new(records: Arc>>>) -> Self { + let read_lock = records.read().unwrap(); + let iter = read_lock + .iter() + .map(|(_, record_state)| record_state.data()) + .filter_map(|r| r); + Self { + read_lock, + iter: Box::new(iter), + } + } +} + +impl<'a> Iterator for SavedRecordIterator<'a> { + type Item = &'a Record; + fn next(&mut self) -> Option { + None + } +} +*/ diff --git a/fitnesstrax/core/src/types.rs b/fitnesstrax/core/src/types.rs index b0959f7..e61959e 100644 --- a/fitnesstrax/core/src/types.rs +++ b/fitnesstrax/core/src/types.rs @@ -118,6 +118,14 @@ impl TraxRecord { pub fn is_steps(&self) -> bool { matches!(self, TraxRecord::Steps(_)) } + + pub fn is_bike_ride(&self) -> bool { + matches!(self, TraxRecord::BikeRide(_)) + } + + pub fn is_run(&self) -> bool { + matches!(self, TraxRecord::Run(_)) + } } impl Recordable for TraxRecord {