Save new time/distance records
This sets up a bunch of callbacks. We're starting to get into Callback Hell, where there are things that need knowledge that I really don't want them to have. However, edit fields for TimeDistanceEdit now propogate data back into the view model, which is then able to save the results.
This commit is contained in:
parent
4fdf390ecf
commit
6e26923629
|
@ -22,7 +22,7 @@ use crate::{
|
|||
},
|
||||
view_models::DayDetailViewModel,
|
||||
};
|
||||
use emseries::Record;
|
||||
use emseries::{Record, RecordId};
|
||||
use ft_core::{RecordType, TraxRecord};
|
||||
use glib::Object;
|
||||
use gtk::{prelude::*, subclass::prelude::*};
|
||||
|
@ -198,6 +198,7 @@ impl DayDetail {
|
|||
pub struct DayEditPrivate {
|
||||
on_finished: RefCell<Box<dyn Fn()>>,
|
||||
workout_rows: RefCell<gtk::Box>,
|
||||
view_model: RefCell<DayDetailViewModel>,
|
||||
}
|
||||
|
||||
impl Default for DayEditPrivate {
|
||||
|
@ -210,6 +211,7 @@ impl Default for DayEditPrivate {
|
|||
.hexpand(true)
|
||||
.build(),
|
||||
),
|
||||
view_model: RefCell::new(DayDetailViewModel::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -238,6 +240,7 @@ impl DayEdit {
|
|||
s.set_orientation(gtk::Orientation::Vertical);
|
||||
s.set_hexpand(true);
|
||||
*s.imp().on_finished.borrow_mut() = Box::new(on_finished);
|
||||
*s.imp().view_model.borrow_mut() = view_model.clone();
|
||||
|
||||
let workout_buttons = workout_buttons(view_model.clone(), {
|
||||
let s = s.clone();
|
||||
|
@ -257,22 +260,43 @@ impl DayEdit {
|
|||
}
|
||||
|
||||
fn add_row(&self, workout: Record<TraxRecord>) {
|
||||
println!("add_row: {:?}", workout);
|
||||
println!("adding a row for {:?}", workout);
|
||||
let workout_rows = self.imp().workout_rows.borrow();
|
||||
|
||||
let workout_id = workout.id;
|
||||
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, |_, _| {}))
|
||||
TraxRecord::BikeRide(ref w)
|
||||
| TraxRecord::Row(ref w)
|
||||
| TraxRecord::Swim(ref w)
|
||||
| TraxRecord::Run(ref w)
|
||||
| TraxRecord::Walk(ref w) => {
|
||||
workout_rows.append(&TimeDistanceEdit::new(workout_type, w.clone(), {
|
||||
let s = self.clone();
|
||||
move |type_, data| {
|
||||
println!("update workout callback on workout: {:?}", workout_id);
|
||||
s.update_workout(workout_id, type_, data)
|
||||
}
|
||||
}));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_workout(&self, id: RecordId, type_: RecordType, data: ft_core::TimeDistance) {
|
||||
println!("update workout");
|
||||
let data = match type_ {
|
||||
RecordType::BikeRide => TraxRecord::BikeRide(data),
|
||||
RecordType::Row => TraxRecord::Row(data),
|
||||
RecordType::Swim => TraxRecord::Swim(data),
|
||||
RecordType::Run => TraxRecord::Run(data),
|
||||
RecordType::Walk => TraxRecord::Walk(data),
|
||||
_ => panic!("Record type {:?} is not a Time/Distance record", type_),
|
||||
};
|
||||
let record = Record { id, data };
|
||||
self.imp().view_model.borrow().update_record(record);
|
||||
}
|
||||
}
|
||||
|
||||
fn control_buttons(s: &DayEdit, view_model: &DayDetailViewModel) -> ActionGroup {
|
||||
|
|
|
@ -50,7 +50,7 @@ where
|
|||
"0",
|
||||
value,
|
||||
|v| format!("{}", v),
|
||||
move |v| v.parse::<u32>().map_err(|_| ParseError),
|
||||
|v| v.parse::<u32>().map_err(|_| ParseError),
|
||||
on_update,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ use dimensioned::si;
|
|||
use gtk::prelude::*;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
type Parser<T> = dyn Fn(&str) -> Result<T, ParseError>;
|
||||
type OnUpdate<T> = dyn Fn(Option<T>);
|
||||
pub type Parser<T> = dyn Fn(&str) -> Result<T, ParseError>;
|
||||
pub type OnUpdate<T> = dyn Fn(Option<T>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TextEntry<T: Clone + std::fmt::Debug> {
|
||||
|
|
|
@ -17,12 +17,15 @@ 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 crate::components::distance_field;
|
||||
use crate::{
|
||||
components::{distance_field, duration_field, time_field},
|
||||
types::{DistanceFormatter, DurationFormatter, TimeFormatter},
|
||||
};
|
||||
use dimensioned::si;
|
||||
use ft_core::{RecordType, TimeDistance};
|
||||
use glib::Object;
|
||||
use gtk::{prelude::*, subclass::prelude::*};
|
||||
use std::cell::RefCell;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
pub fn time_distance_summary(
|
||||
distance: si::Meter<f64>,
|
||||
|
@ -107,10 +110,20 @@ pub fn time_distance_detail(type_: ft_core::RecordType, record: ft_core::TimeDis
|
|||
layout
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TimeDistanceEditPrivate {
|
||||
type_: RefCell<Option<ft_core::RecordType>>,
|
||||
record: RefCell<Option<ft_core::RecordType>>,
|
||||
type_: RefCell<RecordType>,
|
||||
workout: RefCell<TimeDistance>,
|
||||
on_update: Rc<RefCell<Box<dyn Fn(RecordType, TimeDistance)>>>,
|
||||
}
|
||||
|
||||
impl Default for TimeDistanceEditPrivate {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
type_: RefCell::new(RecordType::BikeRide),
|
||||
workout: RefCell::new(TimeDistance::new(chrono::Utc::now().into())),
|
||||
on_update: Rc::new(RefCell::new(Box::new(|_, _| {}))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -131,7 +144,7 @@ glib::wrapper! {
|
|||
impl Default for TimeDistanceEdit {
|
||||
fn default() -> Self {
|
||||
let s: Self = Object::builder().build();
|
||||
s.set_orientation(gtk::Orientation::Horizontal);
|
||||
s.set_orientation(gtk::Orientation::Vertical);
|
||||
s.set_hexpand(true);
|
||||
s.set_css_classes(&["time-distance-edit"]);
|
||||
|
||||
|
@ -140,23 +153,65 @@ impl Default for TimeDistanceEdit {
|
|||
}
|
||||
|
||||
impl TimeDistanceEdit {
|
||||
pub fn new<OnUpdate>(type_: RecordType, record: TimeDistance, on_update: OnUpdate) -> Self
|
||||
pub fn new<OnUpdate>(type_: RecordType, workout: TimeDistance, on_update: OnUpdate) -> Self
|
||||
where
|
||||
OnUpdate: Fn(&ft_core::RecordType, &ft_core::TimeDistance),
|
||||
OnUpdate: Fn(ft_core::RecordType, ft_core::TimeDistance) + 'static,
|
||||
{
|
||||
println!("new TimeDistanceEdit");
|
||||
let s = Self::default();
|
||||
|
||||
s.append(>k::Label::new(Some(
|
||||
record.datetime.format("%H:%M").to_string().as_ref(),
|
||||
)));
|
||||
*s.imp().type_.borrow_mut() = type_;
|
||||
*s.imp().workout.borrow_mut() = workout.clone();
|
||||
*s.imp().on_update.borrow_mut() = Box::new(on_update);
|
||||
|
||||
let details_row = gtk::Box::builder()
|
||||
.orientation(gtk::Orientation::Horizontal)
|
||||
.build();
|
||||
|
||||
details_row.append(
|
||||
&time_field(
|
||||
Some(TimeFormatter::from(workout.datetime.naive_local().time())),
|
||||
{
|
||||
let s = s.clone();
|
||||
move |t| s.update_time(t)
|
||||
},
|
||||
)
|
||||
.widget(),
|
||||
);
|
||||
details_row.append(
|
||||
&distance_field(workout.distance.map(DistanceFormatter::from), {
|
||||
let s = s.clone();
|
||||
move |d| s.update_distance(d)
|
||||
})
|
||||
.widget(),
|
||||
);
|
||||
details_row.append(
|
||||
&duration_field(workout.duration.map(DurationFormatter::from), {
|
||||
let s = s.clone();
|
||||
move |d| s.update_duration(d)
|
||||
})
|
||||
.widget(),
|
||||
);
|
||||
s.append(&details_row);
|
||||
s.append(>k::Entry::new());
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
/*
|
||||
fn with_record<OnUpdate>(type_: ft_core::RecordType, record: ft_core::TimeDistance, on_update: OnUpdate) -> Self
|
||||
where OnUpdate: Fn(&ft_core::RecordType, &ft_core::TimeDistance) {
|
||||
fn update_time(&self, time: Option<TimeFormatter>) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn update_distance(&self, distance: Option<DistanceFormatter>) {
|
||||
println!("update distance");
|
||||
let mut workout = self.imp().workout.borrow_mut();
|
||||
workout.distance = distance.map(|d| *d);
|
||||
(self.imp().on_update.borrow())(self.imp().type_.borrow().clone(), workout.clone());
|
||||
}
|
||||
|
||||
fn update_duration(&self, duration: Option<DurationFormatter>) {
|
||||
let mut workout = self.imp().workout.borrow_mut();
|
||||
workout.duration = duration.map(|d| *d);
|
||||
(self.imp().on_update.borrow())(self.imp().type_.borrow().clone(), workout.clone());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -56,10 +56,7 @@ impl<T: Clone + emseries::Recordable> RecordState<T> {
|
|||
fn set_value(&mut self, value: T) {
|
||||
*self = match self {
|
||||
RecordState::Original(r) => RecordState::Updated(Record { data: value, ..*r }),
|
||||
RecordState::New(_) => RecordState::New(Record {
|
||||
id: RecordId::default(),
|
||||
data: value,
|
||||
}),
|
||||
RecordState::New(r) => RecordState::New(Record { data: value, ..*r }),
|
||||
RecordState::Updated(r) => RecordState::Updated(Record { data: value, ..*r }),
|
||||
RecordState::Deleted(r) => RecordState::Updated(Record { data: value, ..*r }),
|
||||
};
|
||||
|
@ -202,14 +199,20 @@ impl DayDetailViewModel {
|
|||
.write()
|
||||
.unwrap()
|
||||
.insert(new_record.id.clone(), RecordState::New(new_record.clone()));
|
||||
println!(
|
||||
"record added: {:?}",
|
||||
self.records.read().unwrap().get(&new_record.id)
|
||||
);
|
||||
new_record
|
||||
}
|
||||
|
||||
pub fn update_record(&self, update: Record<TraxRecord>) {
|
||||
println!("updating a record: {:?}", update);
|
||||
let mut records = self.records.write().unwrap();
|
||||
records
|
||||
.entry(update.id)
|
||||
.and_modify(|mut record| record.set_value(update.data));
|
||||
println!("record updated: {:?}", records.get(&update.id));
|
||||
}
|
||||
|
||||
pub fn records(&self) -> Vec<Record<TraxRecord>> {
|
||||
|
@ -272,6 +275,7 @@ impl DayDetailViewModel {
|
|||
.collect::<Vec<RecordState<TraxRecord>>>();
|
||||
|
||||
for record in records {
|
||||
println!("saving record: {:?}", record);
|
||||
match record {
|
||||
RecordState::New(Record { data, .. }) => {
|
||||
let _ = app.put_record(data).await;
|
||||
|
|
Loading…
Reference in New Issue