Show a summary of the day's biking stats when there is one
This commit is contained in:
parent
7ec48ded5d
commit
9c27610cb9
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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<f64>,
|
||||
duration: si::Second<f64>,
|
||||
) -> Option<gtk::Label> {
|
||||
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)]
|
||||
|
|
|
@ -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<T: Clone + emseries::Recordable> RecordState<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn data(&self) -> Option<&Record<T>> {
|
||||
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<T> {
|
||||
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<f64>, si::Second<f64>) {
|
||||
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<Record<TraxRecord>> {
|
||||
let read_lock = self.records.read().unwrap();
|
||||
read_lock
|
||||
.iter()
|
||||
.map(|(_, record_state)| record_state.data())
|
||||
.filter_map(|r| r)
|
||||
.cloned()
|
||||
.collect::<Vec<Record<TraxRecord>>>()
|
||||
}
|
||||
|
||||
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<RecordId, RecordState<TraxRecord>>>,
|
||||
iter: Box<dyn Iterator<Item = &'a Record<TraxRecord>> + 'a>,
|
||||
}
|
||||
|
||||
impl<'a> SavedRecordIterator<'a> {
|
||||
fn new(records: Arc<RwLock<HashMap<RecordId, RecordState<TraxRecord>>>>) -> 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<TraxRecord>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
None
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue