From ff77c4cedd759b87295c05ad213910f995ba7c32 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Thu, 28 Dec 2023 10:28:51 -0500 Subject: [PATCH] Save real data to the database. Load data on app start. --- fitnesstrax/app/src/app.rs | 33 ++++-- fitnesstrax/app/src/app_window.rs | 116 ++++++------------- fitnesstrax/app/src/components/day.rs | 6 +- fitnesstrax/app/src/main.rs | 6 +- fitnesstrax/app/src/types.rs | 47 ++++++++ fitnesstrax/app/src/views/historical_view.rs | 48 +------- fitnesstrax/app/src/views/mod.rs | 5 +- 7 files changed, 115 insertions(+), 146 deletions(-) create mode 100644 fitnesstrax/app/src/types.rs diff --git a/fitnesstrax/app/src/app.rs b/fitnesstrax/app/src/app.rs index 49500d6..f069638 100644 --- a/fitnesstrax/app/src/app.rs +++ b/fitnesstrax/app/src/app.rs @@ -14,7 +14,8 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with FitnessTrax. If not, see . */ -use emseries::{Record, RecordId, Series}; +use crate::types::DayInterval; +use emseries::{time_range, Record, RecordId, Series, Timestamp}; use ft_core::TraxRecord; use std::{ path::{Path, PathBuf}, @@ -29,7 +30,7 @@ pub enum AppInvocation { /// Request a set of records from the core. // Note: this will require a time range, but doesn't yet. - RequestRecords, + RequestRecords(DayInterval), UpdateRecord(Record), @@ -52,7 +53,7 @@ pub enum AppResponse { /// The database is open and here is a set of records. Typically, the set of records will be /// all of the records within a time frame, but this can actually be any set of records. - Records, + Records(Vec>), /// The database has been changed. This message is useful for telling the UI that a significant /// change has happened. Further, the UI needs to save PathBuf to settings, because the @@ -82,31 +83,39 @@ impl App { self.open_db(&db_path); AppResponse::DatabaseChanged(db_path) } - AppInvocation::RequestRecords => { - if self.database.read().unwrap().is_none() { - AppResponse::NoDatabase - } else { - AppResponse::Records + AppInvocation::RequestRecords(interval) => match &*self.database.read().unwrap() { + Some(database) => { + let records = database + .search(time_range( + Timestamp::Date(interval.start), + true, + Timestamp::Date(interval.end), + true, + )) + .map(|record| record.clone()) + .collect::>>(); + AppResponse::Records(records) } - } + None => AppResponse::NoDatabase, + }, AppInvocation::UpdateRecord(record) => match *self.database.write().unwrap() { Some(ref mut database) => { database.update(record).unwrap(); - AppResponse::Records + AppResponse::Records(vec![]) } None => AppResponse::NoDatabase, }, AppInvocation::PutRecord(record) => match *self.database.write().unwrap() { Some(ref mut database) => { database.put(record).unwrap(); - AppResponse::Records + AppResponse::Records(vec![]) } None => AppResponse::NoDatabase, }, AppInvocation::DeleteRecord(record_id) => match *self.database.write().unwrap() { Some(ref mut database) => { database.delete(&record_id).unwrap(); - AppResponse::Records + AppResponse::Records(vec![]) } None => AppResponse::NoDatabase, }, diff --git a/fitnesstrax/app/src/app_window.rs b/fitnesstrax/app/src/app_window.rs index b59afa7..6adb4f7 100644 --- a/fitnesstrax/app/src/app_window.rs +++ b/fitnesstrax/app/src/app_window.rs @@ -127,13 +127,13 @@ impl AppWindow { self.settings .set_string("series-path", db_path.to_str().unwrap()) .unwrap(); - self.change_view(ViewName::Historical); + self.change_view(ViewName::Historical(vec![])); } AppResponse::NoDatabase => { self.change_view(ViewName::Welcome); } - AppResponse::Records => { - self.change_view(ViewName::Historical); + AppResponse::Records(records) => { + self.change_view(ViewName::Historical(records)); } } } @@ -163,86 +163,36 @@ impl AppWindow { }) .upcast(), ), - ViewName::Historical => View::Historical( - HistoricalView::new( - vec![ - 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(); - Rc::new(move |date, records| { - let layout = gtk::Box::new(gtk::Orientation::Vertical, 0); - layout.append(&adw::HeaderBar::new()); - layout.append(&DayDetail::new( - date, - records, - { - let app_tx = s.app_tx.clone(); - move |record| { - let _ = - app_tx.send_blocking(AppInvocation::PutRecord(record)); - } - }, - { - let app_tx = s.app_tx.clone(); - move |record| { - let _ = app_tx - .send_blocking(AppInvocation::UpdateRecord(record)); - } - }, - )); - let page = &adw::NavigationPage::builder() - .title(date.format("%Y-%m-%d").to_string()) - .child(&layout) - .build(); - s.navigation.push(page); - }) - }, - ) + ViewName::Historical(records) => View::Historical( + HistoricalView::new(records, { + let s = self.clone(); + Rc::new(move |date: chrono::NaiveDate, records| { + let layout = gtk::Box::new(gtk::Orientation::Vertical, 0); + layout.append(&adw::HeaderBar::new()); + layout.append(&DayDetail::new( + date, + records, + { + let app_tx = s.app_tx.clone(); + move |record| { + let _ = app_tx.send_blocking(AppInvocation::PutRecord(record)); + } + }, + { + let app_tx = s.app_tx.clone(); + move |record| { + let _ = + app_tx.send_blocking(AppInvocation::UpdateRecord(record)); + } + }, + )); + let page = &adw::NavigationPage::builder() + .title(date.format("%Y-%m-%d").to_string()) + .child(&layout) + .build(); + s.navigation.push(page); + }) + }) .upcast(), ), } diff --git a/fitnesstrax/app/src/components/day.rs b/fitnesstrax/app/src/components/day.rs index 6af77a1..8529c04 100644 --- a/fitnesstrax/app/src/components/day.rs +++ b/fitnesstrax/app/src/components/day.rs @@ -320,8 +320,10 @@ impl WeightView { } fn blur(&self) { - if *self.imp().current.borrow() == *self.imp().edit.borrow() { - self.imp().on_edit_finished.borrow()(0. * si::KG); + let edit = self.imp().edit.borrow(); + if *self.imp().current.borrow() == *edit { + let w = edit.buffer().text().parse::().unwrap(); + self.imp().on_edit_finished.borrow()(w * si::KG); self.view(); } } diff --git a/fitnesstrax/app/src/main.rs b/fitnesstrax/app/src/main.rs index 6bcd8e7..05f6c1b 100644 --- a/fitnesstrax/app/src/main.rs +++ b/fitnesstrax/app/src/main.rs @@ -17,11 +17,13 @@ You should have received a copy of the GNU General Public License along with Fit mod app; mod app_window; mod components; +mod types; mod views; use adw::prelude::*; use app_window::AppWindow; use std::{env, path::PathBuf}; +use types::DayInterval; const APP_ID_DEV: &str = "com.luminescent-dreams.fitnesstrax.dev"; const APP_ID_PROD: &str = "com.luminescent-dreams.fitnesstrax"; @@ -86,7 +88,9 @@ fn main() { glib::spawn_future_local(async move { // The app requests data to start with. This kicks everything off. The response from // the app will cause the window to be updated shortly. - let _ = app_tx.send(app::AppInvocation::RequestRecords).await; + let _ = app_tx + .send(app::AppInvocation::RequestRecords(DayInterval::default())) + .await; while let Ok(response) = ui_rx.recv().await { window.process_response(response); diff --git a/fitnesstrax/app/src/types.rs b/fitnesstrax/app/src/types.rs new file mode 100644 index 0000000..319e845 --- /dev/null +++ b/fitnesstrax/app/src/types.rs @@ -0,0 +1,47 @@ +use chrono::{Duration, Local, NaiveDate}; + +// This interval doesn't feel right, either. The idea that I have a specific interval type for just +// NaiveDate is odd. This should be genericized, as should the iterator. Also, it shouldn't live +// here, but in utilities. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DayInterval { + pub start: NaiveDate, + pub end: NaiveDate, +} + +impl Default for DayInterval { + fn default() -> Self { + Self { + start: (Local::now() - Duration::days(7)).date_naive(), + end: Local::now().date_naive(), + } + } +} + +impl DayInterval { + pub fn days(&self) -> impl Iterator { + DayIterator { + current: self.start.clone(), + end: self.end.clone(), + } + } +} + +struct DayIterator { + current: NaiveDate, + end: NaiveDate, +} + +impl Iterator for DayIterator { + type Item = NaiveDate; + + fn next(&mut self) -> Option { + if self.current <= self.end { + let val = self.current.clone(); + self.current += Duration::days(1); + Some(val) + } else { + None + } + } +} diff --git a/fitnesstrax/app/src/views/historical_view.rs b/fitnesstrax/app/src/views/historical_view.rs index 938223d..ad68396 100644 --- a/fitnesstrax/app/src/views/historical_view.rs +++ b/fitnesstrax/app/src/views/historical_view.rs @@ -14,7 +14,7 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with FitnessTrax. If not, see . */ -use crate::components::DaySummary; +use crate::{components::DaySummary, types::DayInterval}; use chrono::{Duration, Local, NaiveDate}; use emseries::Record; use ft_core::TraxRecord; @@ -196,52 +196,6 @@ impl GroupedRecords { } } -// This interval doesn't feel right, either. The idea that I have a specific interval type for just -// NaiveDate is odd. This should be genericized, as should the iterator. Also, it shouldn't live -// here, but in utilities. -#[derive(Clone, Debug, PartialEq, Eq)] -struct DayInterval { - start: NaiveDate, - end: NaiveDate, -} - -impl Default for DayInterval { - fn default() -> Self { - Self { - start: (Local::now() - Duration::days(7)).date_naive(), - end: Local::now().date_naive(), - } - } -} - -impl DayInterval { - fn days(&self) -> impl Iterator { - DayIterator { - current: self.start.clone(), - end: self.end.clone(), - } - } -} - -struct DayIterator { - current: NaiveDate, - end: NaiveDate, -} - -impl Iterator for DayIterator { - type Item = NaiveDate; - - fn next(&mut self) -> Option { - if self.current <= self.end { - let val = self.current.clone(); - self.current += Duration::days(1); - Some(val) - } else { - None - } - } -} - #[cfg(test)] mod test { use super::GroupedRecords; diff --git a/fitnesstrax/app/src/views/mod.rs b/fitnesstrax/app/src/views/mod.rs index 123929f..4a86f2a 100644 --- a/fitnesstrax/app/src/views/mod.rs +++ b/fitnesstrax/app/src/views/mod.rs @@ -25,10 +25,13 @@ pub use placeholder_view::PlaceholderView; mod welcome_view; pub use welcome_view::WelcomeView; +use emseries::Record; +use ft_core::TraxRecord; + #[derive(Clone, Debug, PartialEq)] pub enum ViewName { Welcome, - Historical, + Historical(Vec>), } pub enum View {