Compare commits

..

No commits in common. "39acfe7950014f31fcf899109a9a8670340b0a47" and "3a716ee546913efb3e4d7e3198e4d466e24aa1a8" have entirely different histories.

5 changed files with 35 additions and 140 deletions

View File

@ -120,7 +120,7 @@ pub trait Recordable {
/// Uniquely identifies a record. /// Uniquely identifies a record.
/// ///
/// This is a wrapper around a basic uuid with some extra convenience methods. /// This is a wrapper around a basic uuid with some extra convenience methods.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
pub struct RecordId(Uuid); pub struct RecordId(Uuid);
impl Default for RecordId { impl Default for RecordId {

View File

@ -23,13 +23,13 @@ use crate::{
}, },
view_models::DayDetailViewModel, view_models::DayDetailViewModel,
}; };
use emseries::Record; use dimensioned::si;
use ft_core::{RecordType, TraxRecord}; use ft_core::{RecordType, TraxRecord};
use glib::Object; use glib::Object;
use gtk::{prelude::*, subclass::prelude::*}; use gtk::{prelude::*, subclass::prelude::*};
use std::{cell::RefCell, rc::Rc}; use std::cell::RefCell;
use super::{time_distance::TimeDistanceEdit, time_distance_detail}; use super::time_distance_detail;
pub struct DaySummaryPrivate { pub struct DaySummaryPrivate {
date: gtk::Label, date: gtk::Label,
@ -198,19 +198,12 @@ impl DayDetail {
pub struct DayEditPrivate { pub struct DayEditPrivate {
on_finished: RefCell<Box<dyn Fn()>>, on_finished: RefCell<Box<dyn Fn()>>,
workout_rows: RefCell<gtk::Box>,
} }
impl Default for DayEditPrivate { impl Default for DayEditPrivate {
fn default() -> Self { fn default() -> Self {
Self { Self {
on_finished: RefCell::new(Box::new(|| {})), on_finished: RefCell::new(Box::new(|| {})),
workout_rows: RefCell::new(
gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)
.hexpand(true)
.build(),
),
} }
} }
} }
@ -240,15 +233,9 @@ impl DayEdit {
s.set_hexpand(true); s.set_hexpand(true);
*s.imp().on_finished.borrow_mut() = Box::new(on_finished); *s.imp().on_finished.borrow_mut() = Box::new(on_finished);
let workout_buttons = workout_buttons(view_model.clone(), {
let s = s.clone();
move |workout| s.add_row(workout)
});
s.append(&control_buttons(&s, &view_model)); s.append(&control_buttons(&s, &view_model));
s.append(&weight_and_steps_row(&view_model)); s.append(&weight_and_steps_row(&view_model));
s.append(&*s.imp().workout_rows.borrow()); s.append(&workout_buttons());
s.append(&workout_buttons);
s s
} }
@ -256,24 +243,6 @@ impl DayEdit {
fn finish(&self) { fn finish(&self) {
(self.imp().on_finished.borrow())() (self.imp().on_finished.borrow())()
} }
fn add_row(&self, workout: Record<TraxRecord>) {
println!("add_row: {:?}", workout);
let workout_rows = self.imp().workout_rows.borrow();
let workout_type = workout.data.workout_type();
match workout.data {
TraxRecord::BikeRide(w)
| TraxRecord::Row(w)
| TraxRecord::Swim(w)
| TraxRecord::Run(w)
| TraxRecord::Walk(w) => {
workout_rows.append(&TimeDistanceEdit::new(workout_type, w, |_, _| {}))
}
_ => {}
}
}
} }
fn control_buttons(s: &DayEdit, view_model: &DayDetailViewModel) -> ActionGroup { fn control_buttons(s: &DayEdit, view_model: &DayDetailViewModel) -> ActionGroup {
@ -322,37 +291,24 @@ fn weight_and_steps_row(view_model: &DayDetailViewModel) -> gtk::Box {
row row
} }
fn workout_buttons<AddRow>(view_model: DayDetailViewModel, add_row: AddRow) -> gtk::Box fn workout_buttons() -> gtk::Box {
where let sunrise_button = gtk::Button::builder()
AddRow: Fn(Record<TraxRecord>) + 'static, .icon_name("daytime-sunrise-symbolic")
{ .width_request(64)
let add_row = Rc::new(add_row); .height_request(64)
.build();
let walking_button = gtk::Button::builder() let walking_button = gtk::Button::builder()
.icon_name("walking2-symbolic") .icon_name("walking2-symbolic")
.width_request(64) .width_request(64)
.height_request(64) .height_request(64)
.build(); .build();
walking_button.connect_clicked({
let view_model = view_model.clone();
let add_row = add_row.clone();
move |_| {
let workout = view_model.new_record(RecordType::Walk);
&add_row(workout);
}
});
let running_button = gtk::Button::builder() let running_button = gtk::Button::builder()
.icon_name("running-symbolic") .icon_name("running-symbolic")
.width_request(64) .width_request(64)
.height_request(64) .height_request(64)
.build(); .build();
running_button.connect_clicked({
let view_model = view_model.clone();
move |_| {
let workout = view_model.new_record(RecordType::Walk);
add_row(workout);
}
});
let layout = gtk::Box::builder() let layout = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical) .orientation(gtk::Orientation::Vertical)
@ -360,6 +316,7 @@ where
let row = gtk::Box::builder() let row = gtk::Box::builder()
.orientation(gtk::Orientation::Horizontal) .orientation(gtk::Orientation::Horizontal)
.build(); .build();
row.append(&sunrise_button);
row.append(&walking_button); row.append(&walking_button);
row.append(&running_button); row.append(&running_button);
layout.append(&row); layout.append(&row);

View File

@ -132,7 +132,6 @@ impl Default for TimeDistanceEdit {
fn default() -> Self { fn default() -> Self {
let s: Self = Object::builder().build(); let s: Self = Object::builder().build();
s.set_orientation(gtk::Orientation::Horizontal); s.set_orientation(gtk::Orientation::Horizontal);
s.set_hexpand(true);
s.set_css_classes(&["time-distance-edit"]); s.set_css_classes(&["time-distance-edit"]);
s s
@ -140,17 +139,12 @@ impl Default for TimeDistanceEdit {
} }
impl TimeDistanceEdit { impl TimeDistanceEdit {
pub fn new<OnUpdate>(type_: RecordType, record: TimeDistance, on_update: OnUpdate) -> Self fn empty<OnUpdate>(on_update: OnUpdate) -> Self
where where
OnUpdate: Fn(&ft_core::RecordType, &ft_core::TimeDistance), OnUpdate: Fn(&ft_core::RecordType, &ft_core::TimeDistance),
{ {
println!("new TimeDistanceEdit");
let s = Self::default(); let s = Self::default();
s.append(&gtk::Label::new(Some(
record.datetime.format("%H:%M").to_string().as_ref(),
)));
s s
} }

View File

@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with Fit
use crate::app::App; use crate::app::App;
use dimensioned::si; use dimensioned::si;
use emseries::{Record, RecordId, Recordable}; use emseries::{Record, RecordId, Recordable};
use ft_core::{RecordType, TimeDistance, TraxRecord}; use ft_core::{TimeDistance, TraxRecord};
use std::{ use std::{
collections::HashMap, collections::HashMap,
ops::Deref, ops::Deref,
@ -27,7 +27,7 @@ use std::{
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum RecordState<T: Clone + Recordable> { enum RecordState<T: Clone + Recordable> {
Original(Record<T>), Original(Record<T>),
New(Record<T>), New(T),
Updated(Record<T>), Updated(Record<T>),
#[allow(unused)] #[allow(unused)]
Deleted(Record<T>), Deleted(Record<T>),
@ -53,21 +53,13 @@ impl<T: Clone + emseries::Recordable> RecordState<T> {
} }
} }
fn set_value(&mut self, value: T) { fn with_value(self, value: T) -> RecordState<T> {
*self = match self { match self {
RecordState::Original(r) => RecordState::Updated(Record { data: value, ..*r }), RecordState::Original(r) => RecordState::Updated(Record { data: value, ..r }),
RecordState::New(_) => RecordState::New(Record { RecordState::New(_) => RecordState::New(value),
id: RecordId::default(), RecordState::Updated(r) => RecordState::Updated(Record { data: value, ..r }),
data: value, RecordState::Deleted(r) => RecordState::Updated(Record { data: value, ..r }),
}), }
RecordState::Updated(r) => RecordState::Updated(Record { data: value, ..*r }),
RecordState::Deleted(r) => RecordState::Updated(Record { data: value, ..*r }),
};
}
fn with_value(mut self, value: T) -> RecordState<T> {
self.set_value(value);
self
} }
#[allow(unused)] #[allow(unused)]
@ -86,7 +78,7 @@ impl<T: Clone + emseries::Recordable> Deref for RecordState<T> {
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
match self { match self {
RecordState::Original(ref r) => &r.data, RecordState::Original(ref r) => &r.data,
RecordState::New(ref r) => &r.data, RecordState::New(ref r) => r,
RecordState::Updated(ref r) => &r.data, RecordState::Updated(ref r) => &r.data,
RecordState::Deleted(ref r) => &r.data, RecordState::Deleted(ref r) => &r.data,
} }
@ -134,12 +126,9 @@ impl DayDetailViewModel {
date: self.date, date: self.date,
weight: new_weight, weight: new_weight,
}), }),
None => RecordState::New(Record { None => RecordState::New(ft_core::Weight {
id: RecordId::default(), date: self.date,
data: ft_core::Weight { weight: new_weight,
date: self.date,
weight: new_weight,
},
}), }),
}; };
*record = Some(new_record); *record = Some(new_record);
@ -156,12 +145,9 @@ impl DayDetailViewModel {
date: self.date, date: self.date,
count: new_count, count: new_count,
}), }),
None => RecordState::New(Record { None => RecordState::New(ft_core::Steps {
id: RecordId::default(), date: self.date,
data: ft_core::Steps { count: new_count,
date: self.date,
count: new_count,
},
}), }),
}; };
*record = Some(new_record); *record = Some(new_record);
@ -191,25 +177,6 @@ impl DayDetailViewModel {
) )
} }
pub fn new_record(&self, type_: RecordType) -> Record<TraxRecord> {
let new_record = Record {
id: RecordId::default(),
data: ft_core::TraxRecord::new(type_, chrono::Local::now().into()),
};
self.records
.write()
.unwrap()
.insert(new_record.id.clone(), RecordState::New(new_record.clone()));
new_record
}
pub fn update_record(&self, update: Record<TraxRecord>) {
let mut records = self.records.write().unwrap();
records
.entry(update.id)
.and_modify(|mut record| record.set_value(update.data));
}
pub fn records(&self) -> Vec<Record<TraxRecord>> { pub fn records(&self) -> Vec<Record<TraxRecord>> {
let read_lock = self.records.read().unwrap(); let read_lock = self.records.read().unwrap();
read_lock read_lock
@ -227,8 +194,8 @@ impl DayDetailViewModel {
if let Some(app) = s.app { if let Some(app) = s.app {
let weight_record = s.weight.read().unwrap().clone(); let weight_record = s.weight.read().unwrap().clone();
match weight_record { match weight_record {
Some(RecordState::New(Record { data, .. })) => { Some(RecordState::New(weight)) => {
let _ = app.put_record(TraxRecord::Weight(data)).await; let _ = app.put_record(TraxRecord::Weight(weight)).await;
} }
Some(RecordState::Original(_)) => {} Some(RecordState::Original(_)) => {}
Some(RecordState::Updated(weight)) => { Some(RecordState::Updated(weight)) => {
@ -245,8 +212,8 @@ impl DayDetailViewModel {
let steps_record = s.steps.read().unwrap().clone(); let steps_record = s.steps.read().unwrap().clone();
match steps_record { match steps_record {
Some(RecordState::New(Record { data, .. })) => { Some(RecordState::New(steps)) => {
let _ = app.put_record(TraxRecord::Steps(data)).await; let _ = app.put_record(TraxRecord::Steps(steps)).await;
} }
Some(RecordState::Original(_)) => {} Some(RecordState::Original(_)) => {}
Some(RecordState::Updated(steps)) => { Some(RecordState::Updated(steps)) => {
@ -271,7 +238,7 @@ impl DayDetailViewModel {
for record in records { for record in records {
match record { match record {
RecordState::New(Record { data, .. }) => { RecordState::New(data) => {
let _ = app.put_record(data).await; let _ = app.put_record(data).await;
} }
RecordState::Original(_) => {} RecordState::Original(_) => {}

View File

@ -57,17 +57,6 @@ pub struct TimeDistance {
pub comments: Option<String>, pub comments: Option<String>,
} }
impl TimeDistance {
pub fn new(time: DateTime<FixedOffset>) -> Self {
Self {
datetime: time,
distance: None,
duration: None,
comments: None,
}
}
}
/// A singular daily weight measurement. Weight changes slowly enough that it seems unlikely to /// A singular daily weight measurement. Weight changes slowly enough that it seems unlikely to
/// need to track more than a single weight in a day. /// need to track more than a single weight in a day.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -110,18 +99,6 @@ pub enum TraxRecord {
} }
impl TraxRecord { impl TraxRecord {
pub fn new(type_: RecordType, time: DateTime<FixedOffset>) -> TraxRecord {
match type_ {
RecordType::BikeRide => TraxRecord::BikeRide(TimeDistance::new(time)),
RecordType::Row => TraxRecord::Row(TimeDistance::new(time)),
RecordType::Run => TraxRecord::Run(TimeDistance::new(time)),
RecordType::Steps => unimplemented!(),
RecordType::Swim => unimplemented!(),
RecordType::Walk => TraxRecord::Walk(TimeDistance::new(time)),
RecordType::Weight => unimplemented!(),
}
}
pub fn workout_type(&self) -> RecordType { pub fn workout_type(&self) -> RecordType {
match self { match self {
TraxRecord::BikeRide(_) => RecordType::BikeRide, TraxRecord::BikeRide(_) => RecordType::BikeRide,