diff --git a/emseries/src/types.rs b/emseries/src/types.rs index 80f215d..018a99b 100644 --- a/emseries/src/types.rs +++ b/emseries/src/types.rs @@ -178,7 +178,7 @@ impl fmt::Display for RecordId { /// directly, as the database will create them. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct Record { - pub(crate) id: RecordId, + pub id: RecordId, pub data: T, } diff --git a/fitnesstrax/app/src/app_window.rs b/fitnesstrax/app/src/app_window.rs index 7b01f7a..d97017c 100644 --- a/fitnesstrax/app/src/app_window.rs +++ b/fitnesstrax/app/src/app_window.rs @@ -23,6 +23,7 @@ use adw::prelude::*; use async_channel::Sender; use chrono::{FixedOffset, NaiveDate, TimeZone}; use dimensioned::si::{KG, M, S}; +use emseries::{Record, RecordId}; use ft_core::{Steps, TimeDistance, TraxRecord, Weight}; use gio::resources_lookup_data; use gtk::STYLE_PROVIDER_PRIORITY_USER; @@ -165,36 +166,51 @@ impl AppWindow { ViewName::Historical => View::Historical( HistoricalView::new( vec![ - TraxRecord::Steps(Steps { - date: NaiveDate::from_ymd_opt(2023, 10, 13).unwrap(), - count: 1500, - }), - TraxRecord::Weight(Weight { - date: NaiveDate::from_ymd_opt(2023, 10, 13).unwrap(), - weight: 85. * KG, - }), - TraxRecord::Weight(Weight { - date: NaiveDate::from_ymd_opt(2023, 10, 14).unwrap(), - weight: 86. * KG, - }), - TraxRecord::BikeRide(TimeDistance { - datetime: FixedOffset::west_opt(10 * 60 * 60) - .unwrap() - .with_ymd_and_hms(2019, 6, 15, 12, 0, 0) - .unwrap(), - distance: Some(1000. * M), - duration: Some(150. * S), - comments: Some("Test Comments".to_owned()), - }), - TraxRecord::BikeRide(TimeDistance { - datetime: FixedOffset::west_opt(10 * 60 * 60) - .unwrap() - .with_ymd_and_hms(2019, 6, 15, 23, 0, 0) - .unwrap(), - distance: Some(1000. * M), - duration: Some(150. * S), - comments: Some("Test Comments".to_owned()), - }), + Record { + id: RecordId::default(), + data: TraxRecord::Steps(Steps { + date: NaiveDate::from_ymd_opt(2023, 10, 13).unwrap(), + count: 1500, + }), + }, + Record { + id: RecordId::default(), + data: TraxRecord::Weight(Weight { + date: NaiveDate::from_ymd_opt(2023, 10, 13).unwrap(), + weight: 85. * KG, + }), + }, + Record { + id: RecordId::default(), + data: TraxRecord::Weight(Weight { + date: NaiveDate::from_ymd_opt(2023, 10, 14).unwrap(), + weight: 86. * KG, + }), + }, + Record { + id: RecordId::default(), + data: TraxRecord::BikeRide(TimeDistance { + datetime: FixedOffset::west_opt(10 * 60 * 60) + .unwrap() + .with_ymd_and_hms(2019, 6, 15, 12, 0, 0) + .unwrap(), + distance: Some(1000. * M), + duration: Some(150. * S), + comments: Some("Test Comments".to_owned()), + }), + }, + Record { + id: RecordId::default(), + data: TraxRecord::BikeRide(TimeDistance { + datetime: FixedOffset::west_opt(10 * 60 * 60) + .unwrap() + .with_ymd_and_hms(2019, 6, 15, 23, 0, 0) + .unwrap(), + distance: Some(1000. * M), + duration: Some(150. * S), + comments: Some("Test Comments".to_owned()), + }), + }, ], { let s = self.clone(); diff --git a/fitnesstrax/app/src/components/day.rs b/fitnesstrax/app/src/components/day.rs index 23da6c2..f6f8b98 100644 --- a/fitnesstrax/app/src/components/day.rs +++ b/fitnesstrax/app/src/components/day.rs @@ -17,10 +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 dimensioned::si; +use emseries::Record; use ft_core::{RecordType, TimeDistance, TraxRecord, Weight}; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; -use std::cell::RefCell; +use std::{cell::RefCell, rc::Rc}; pub struct DaySummaryPrivate { date: gtk::Label, @@ -66,7 +67,7 @@ impl DaySummary { s } - pub fn set_data(&self, date: chrono::NaiveDate, records: Vec) { + pub fn set_data(&self, date: chrono::NaiveDate, records: Vec>) { self.imp() .date .set_text(&date.format("%Y-%m-%d").to_string()); @@ -75,8 +76,10 @@ impl DaySummary { self.remove(weight_label); } - if let Some(TraxRecord::Weight(weight_record)) = - records.iter().filter(|f| f.is_weight()).next() + if let Some(Record { + data: TraxRecord::Weight(weight_record), + .. + }) = records.iter().filter(|f| f.data.is_weight()).next() { let label = gtk::Label::builder() .halign(gtk::Align::Start) @@ -128,37 +131,71 @@ glib::wrapper! { } impl DayDetail { - pub fn new(date: chrono::NaiveDate, records: Vec) -> Self { + pub fn new(date: chrono::NaiveDate, records: Vec>) -> Self { let s: Self = Object::builder().build(); s.set_orientation(gtk::Orientation::Vertical); + let click_controller = gtk::GestureClick::new(); + click_controller.connect_released({ + let s = s.clone(); + move |_, _, _, _| { + println!("clicked outside of focusable entity"); + if let Some(widget) = s.focus_child().and_downcast_ref::() { + println!("focused child is the weight view"); + widget.blur(); + } + } + }); + s.add_controller(click_controller); + let weight_record = records.iter().find_map(|record| match record { - TraxRecord::Weight(record) => Some(record.clone()), + Record { + id, + data: TraxRecord::Weight(record), + } => Some((id.clone(), record.clone())), _ => None, }); - let weight_view = WeightView::new(weight_record, |weight| { - println!("on_blur on the weight view: {:?}", weight) - }); + let weight_view = match weight_record { + Some((unique_id, record)) => WeightView::new(Some(record), move |weight| { + println!( + "on_blur on the weight view. Need to record {:?}, {:?}", + unique_id, weight + ); + }), + None => WeightView::new(None, |weight| { + println!( + "on_blur on the weight view. Need to create a new record for {:?}", + weight + ); + }), + }; s.append(&weight_view); records.into_iter().for_each(|record| { let record_view = match record { - TraxRecord::BikeRide(record) => Some( + Record { + data: TraxRecord::BikeRide(record), + .. + } => Some( TimeDistanceView::new(RecordType::BikeRide, record).upcast::(), ), - TraxRecord::Row(record) => { - Some(TimeDistanceView::new(RecordType::Row, record).upcast::()) - } - TraxRecord::Run(record) => { - Some(TimeDistanceView::new(RecordType::Row, record).upcast::()) - } - TraxRecord::Swim(record) => { - Some(TimeDistanceView::new(RecordType::Row, record).upcast::()) - } - TraxRecord::Walk(record) => { - Some(TimeDistanceView::new(RecordType::Row, record).upcast::()) - } + Record { + data: TraxRecord::Row(record), + .. + } => Some(TimeDistanceView::new(RecordType::Row, record).upcast::()), + Record { + data: TraxRecord::Run(record), + .. + } => Some(TimeDistanceView::new(RecordType::Row, record).upcast::()), + Record { + data: TraxRecord::Swim(record), + .. + } => Some(TimeDistanceView::new(RecordType::Row, record).upcast::()), + Record { + data: TraxRecord::Walk(record), + .. + } => Some(TimeDistanceView::new(RecordType::Row, record).upcast::()), _ => None, }; @@ -179,6 +216,7 @@ pub struct WeightViewPrivate { view: RefCell, edit: RefCell, current: RefCell, + on_edit_finished: RefCell)>>, } impl Default for WeightViewPrivate { @@ -197,6 +235,7 @@ impl Default for WeightViewPrivate { view: RefCell::new(view), edit: RefCell::new(edit), current: RefCell::new(current.upcast()), + on_edit_finished: RefCell::new(Box::new(|_| {})), } } } @@ -217,12 +256,14 @@ glib::wrapper! { } impl WeightView { - pub fn new(weight: Option, on_blur: OnBlur) -> Self + pub fn new(weight: Option, on_edit_finished: OnEditFinished) -> Self where - OnBlur: Fn(si::Kilogram), + OnEditFinished: Fn(si::Kilogram) + 'static, { let s: Self = Object::builder().build(); + *s.imp().on_edit_finished.borrow_mut() = Box::new(on_edit_finished); + *s.imp().record.borrow_mut() = weight; s.view(); @@ -234,16 +275,7 @@ impl WeightView { } }); - let edit_click_controller = gtk::GestureClick::new(); - edit_click_controller.connect_released({ - let s = s.clone(); - move |_, _, _, _| { - s.view(); - } - }); - s.imp().view.borrow().add_controller(view_click_controller); - s.imp().edit.borrow().add_controller(edit_click_controller); s } @@ -278,6 +310,13 @@ impl WeightView { self.append(&new_view); *current = new_view; } + + fn blur(&self) { + if *self.imp().current.borrow() == *self.imp().edit.borrow() { + self.imp().on_edit_finished.borrow()(0. * si::KG); + self.view(); + } + } } #[derive(Default)] diff --git a/fitnesstrax/app/src/views/historical_view.rs b/fitnesstrax/app/src/views/historical_view.rs index d1ef170..79c85dd 100644 --- a/fitnesstrax/app/src/views/historical_view.rs +++ b/fitnesstrax/app/src/views/historical_view.rs @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with Fit */ use crate::components::DaySummary; -use emseries::{Recordable, Timestamp}; +use emseries::{Record, Recordable, Timestamp}; use ft_core::TraxRecord; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; @@ -46,9 +46,9 @@ glib::wrapper! { } impl HistoricalView { - pub fn new(records: Vec, on_select_day: Rc) -> Self + pub fn new(records: Vec>, on_select_day: Rc) -> Self where - SelectFn: Fn(chrono::NaiveDate, Vec) + 'static, + SelectFn: Fn(chrono::NaiveDate, Vec>) + 'static, { let s: Self = Object::builder().build(); s.set_orientation(gtk::Orientation::Vertical); @@ -111,7 +111,7 @@ impl HistoricalView { #[derive(Default)] pub struct DayRecordsPrivate { date: RefCell, - records: RefCell>, + records: RefCell>>, } #[glib::object_subclass] @@ -127,7 +127,7 @@ glib::wrapper! { } impl DayRecords { - pub fn new(date: chrono::NaiveDate, records: Vec) -> Self { + pub fn new(date: chrono::NaiveDate, records: Vec>) -> Self { let s: Self = Object::builder().build(); *s.imp().date.borrow_mut() = date; @@ -140,30 +140,26 @@ impl DayRecords { self.imp().date.borrow().clone() } - pub fn records(&self) -> Vec { + pub fn records(&self) -> Vec> { self.imp().records.borrow().clone() } - pub fn add_record(&self, record: TraxRecord) { + pub fn add_record(&self, record: Record) { self.imp().records.borrow_mut().push(record); } } struct GroupedRecords(Vec); -impl From> for GroupedRecords { - fn from(records: Vec) -> GroupedRecords { +impl From>> for GroupedRecords { + fn from(records: Vec>) -> GroupedRecords { GroupedRecords( records .into_iter() .fold(HashMap::new(), |mut acc, rec| { - let date = match rec.timestamp() { - Timestamp::DateTime(dtz) => dtz.date_naive(), - Timestamp::Date(date) => date, - }; - acc.entry(date) + acc.entry(rec.date()) .and_modify(|entry: &mut DayRecords| (*entry).add_record(rec.clone())) - .or_insert(DayRecords::new(date, vec![rec])); + .or_insert(DayRecords::new(rec.date(), vec![rec])); acc }) .values()