Start setting up the day detail view model
I've created the view model and added a getter function for the weight. I'm passing the view model now to the DayDetailView, DayDetail, and DayEdit. I'm starting to set up the Save function for the view model, draining all of the updated records and saving them. None of the components yet save any updates to the view model, so updated_records is always going to be empty until I figure that out.
This commit is contained in:
parent
1c2c4982a1
commit
18e7e4fe2f
|
@ -16,7 +16,9 @@ You should have received a copy of the GNU General Public License along with Fit
|
|||
|
||||
use crate::{
|
||||
app::App,
|
||||
views::{DayDetailView, HistoricalView, PlaceholderView, View, WelcomeView},
|
||||
views::{
|
||||
DayDetailView, DayDetailViewModel, HistoricalView, PlaceholderView, View, WelcomeView,
|
||||
},
|
||||
};
|
||||
use adw::prelude::*;
|
||||
use chrono::{Duration, Local};
|
||||
|
@ -137,7 +139,12 @@ impl AppWindow {
|
|||
Rc::new(move |date, records| {
|
||||
let layout = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||
layout.append(&adw::HeaderBar::new());
|
||||
layout.append(&DayDetailView::new(date, records, s.app.clone()));
|
||||
// layout.append(&DayDetailView::new(date, records, s.app.clone()));
|
||||
layout.append(&DayDetailView::new(DayDetailViewModel::new(
|
||||
date,
|
||||
records,
|
||||
s.app.clone(),
|
||||
)));
|
||||
let page = &adw::NavigationPage::builder()
|
||||
.title(date.format("%Y-%m-%d").to_string())
|
||||
.child(&layout)
|
||||
|
|
|
@ -16,7 +16,10 @@ 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::{ActionGroup, TimeDistanceView, Weight};
|
||||
use crate::{
|
||||
components::{ActionGroup, TimeDistanceView, Weight},
|
||||
views::DayDetailViewModel,
|
||||
};
|
||||
use emseries::Record;
|
||||
use glib::Object;
|
||||
use gtk::{prelude::*, subclass::prelude::*};
|
||||
|
@ -102,27 +105,14 @@ impl DaySummary {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct DayDetailPrivate {
|
||||
date: gtk::Label,
|
||||
weight: RefCell<Option<gtk::Label>>,
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct DayDetailPrivate {}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for DayDetailPrivate {
|
||||
const NAME: &'static str = "DayDetail";
|
||||
type Type = DayDetail;
|
||||
type ParentType = gtk::Box;
|
||||
|
||||
fn new() -> Self {
|
||||
let date = gtk::Label::builder()
|
||||
.css_classes(["daysummary-date"])
|
||||
.halign(gtk::Align::Start)
|
||||
.build();
|
||||
Self {
|
||||
date,
|
||||
weight: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for DayDetailPrivate {}
|
||||
|
@ -134,11 +124,7 @@ glib::wrapper! {
|
|||
}
|
||||
|
||||
impl DayDetail {
|
||||
pub fn new<OnEdit>(
|
||||
date: chrono::NaiveDate,
|
||||
records: Vec<Record<ft_core::TraxRecord>>,
|
||||
on_edit: OnEdit,
|
||||
) -> Self
|
||||
pub fn new<OnEdit>(view_model: DayDetailViewModel, on_edit: OnEdit) -> Self
|
||||
where
|
||||
OnEdit: Fn() + 'static,
|
||||
{
|
||||
|
@ -148,7 +134,7 @@ impl DayDetail {
|
|||
|
||||
s.append(
|
||||
&ActionGroup::builder()
|
||||
.primary_action("Edit", Box::new(on_edit))
|
||||
.primary_action("Edit", Box::new(move || on_edit()))
|
||||
.build(),
|
||||
);
|
||||
|
||||
|
@ -167,6 +153,7 @@ impl DayDetail {
|
|||
s.add_controller(click_controller);
|
||||
*/
|
||||
|
||||
/*
|
||||
let weight_record = records.iter().find_map(|record| match record {
|
||||
Record {
|
||||
id,
|
||||
|
@ -174,13 +161,12 @@ impl DayDetail {
|
|||
} => Some((id.clone(), record.clone())),
|
||||
_ => None,
|
||||
});
|
||||
*/
|
||||
|
||||
let weight_view = match weight_record {
|
||||
Some((id, data)) => Weight::new(Some(data.clone())),
|
||||
None => Weight::new(None),
|
||||
};
|
||||
let weight_view = Weight::new(view_model.weight());
|
||||
s.append(&weight_view.widget());
|
||||
|
||||
/*
|
||||
records.into_iter().for_each(|record| {
|
||||
let record_view = match record {
|
||||
Record {
|
||||
|
@ -224,21 +210,20 @@ impl DayDetail {
|
|||
s.append(&record_view);
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DayEditPrivate {
|
||||
date: gtk::Label,
|
||||
weight: Rc<WeightEdit>,
|
||||
on_finished: RefCell<Box<dyn Fn()>>,
|
||||
}
|
||||
|
||||
impl Default for DayEditPrivate {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
date: gtk::Label::new(None),
|
||||
weight: Rc::new(WeightEdit::new(None)),
|
||||
on_finished: RefCell::new(Box::new(|| {})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -259,25 +244,20 @@ glib::wrapper! {
|
|||
}
|
||||
|
||||
impl DayEdit {
|
||||
pub fn new<PutRecordFn, UpdateRecordFn, CancelFn>(
|
||||
date: chrono::NaiveDate,
|
||||
records: Vec<Record<ft_core::TraxRecord>>,
|
||||
on_put_record: PutRecordFn,
|
||||
on_update_record: UpdateRecordFn,
|
||||
on_cancel: CancelFn,
|
||||
) -> Self
|
||||
pub fn new<OnFinished>(view_model: DayDetailViewModel, on_finished: OnFinished) -> Self
|
||||
where
|
||||
PutRecordFn: Fn(ft_core::TraxRecord) + 'static,
|
||||
UpdateRecordFn: Fn(Record<ft_core::TraxRecord>) + 'static,
|
||||
CancelFn: Fn() + 'static,
|
||||
OnFinished: Fn() + 'static,
|
||||
{
|
||||
let s: Self = Object::builder().build();
|
||||
s.set_orientation(gtk::Orientation::Vertical);
|
||||
s.set_hexpand(true);
|
||||
|
||||
*s.imp().on_finished.borrow_mut() = Box::new(on_finished);
|
||||
|
||||
s.append(
|
||||
&ActionGroup::builder()
|
||||
.primary_action("Save", {
|
||||
/*
|
||||
let s = s.clone();
|
||||
let records = records.clone();
|
||||
move || {
|
||||
|
@ -309,11 +289,20 @@ impl DayEdit {
|
|||
}
|
||||
};
|
||||
}
|
||||
*/
|
||||
let s = s.clone();
|
||||
move || {
|
||||
s.finish();
|
||||
}
|
||||
})
|
||||
.secondary_action("Cancel", {
|
||||
let s = s.clone();
|
||||
move || s.finish()
|
||||
})
|
||||
.secondary_action("Cancel", on_cancel)
|
||||
.build(),
|
||||
);
|
||||
|
||||
/*
|
||||
let weight_record = records.iter().find_map(|record| match record {
|
||||
Record {
|
||||
id,
|
||||
|
@ -327,7 +316,13 @@ impl DayEdit {
|
|||
None => s.imp().weight.set_value(None),
|
||||
};
|
||||
s.append(&s.imp().weight.widget());
|
||||
*/
|
||||
s.append(&WeightEdit::new(view_model.weight()).widget());
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
fn finish(&self) {
|
||||
(self.imp().on_finished.borrow())()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,14 +29,14 @@ pub struct Weight {
|
|||
}
|
||||
|
||||
impl Weight {
|
||||
pub fn new(weight: Option<ft_core::Weight>) -> Self {
|
||||
pub fn new(weight: Option<si::Kilogram<f64>>) -> Self {
|
||||
let label = gtk::Label::builder()
|
||||
.css_classes(["card", "weight-view"])
|
||||
.can_focus(true)
|
||||
.build();
|
||||
|
||||
match weight {
|
||||
Some(w) => label.set_text(&format!("{:?}", w.weight)),
|
||||
Some(w) => label.set_text(&format!("{:?}", w)),
|
||||
None => label.set_text("No weight recorded"),
|
||||
}
|
||||
|
||||
|
@ -54,11 +54,11 @@ pub struct WeightEdit {
|
|||
}
|
||||
|
||||
impl WeightEdit {
|
||||
pub fn new(weight: Option<ft_core::Weight>) -> Self {
|
||||
pub fn new(weight: Option<si::Kilogram<f64>>) -> Self {
|
||||
Self {
|
||||
entry: TextEntry::new(
|
||||
"0 kg",
|
||||
weight.map(|w| w.weight),
|
||||
weight,
|
||||
|val: &si::Kilogram<f64>| val.to_string(),
|
||||
|v: &str| v.parse::<f64>().map(|w| w * si::KG).map_err(|_| ParseError),
|
||||
),
|
||||
|
|
|
@ -18,18 +18,85 @@ use crate::{
|
|||
app::App,
|
||||
components::{DayDetail, DayEdit, Singleton, SingletonImpl},
|
||||
};
|
||||
use emseries::Record;
|
||||
use dimensioned::si;
|
||||
use emseries::{Record, RecordId};
|
||||
use ft_core::TraxRecord;
|
||||
use glib::Object;
|
||||
use gtk::{prelude::*, subclass::prelude::*};
|
||||
use std::cell::RefCell;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
rc::Rc,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct DayDetailViewModelInner {
|
||||
records: Vec<Record<TraxRecord>>,
|
||||
updated_records: Vec<Record<TraxRecord>>,
|
||||
new_records: Vec<TraxRecord>,
|
||||
deleted_records: Vec<RecordId>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DayDetailViewModel {
|
||||
app: Option<App>,
|
||||
pub date: chrono::NaiveDate,
|
||||
inner: Arc<RwLock<DayDetailViewModelInner>>,
|
||||
}
|
||||
|
||||
impl DayDetailViewModel {
|
||||
pub fn new(date: chrono::NaiveDate, records: Vec<Record<TraxRecord>>, app: App) -> Self {
|
||||
Self {
|
||||
app: Some(app),
|
||||
date,
|
||||
inner: Arc::new(RwLock::new(DayDetailViewModelInner {
|
||||
records,
|
||||
updated_records: vec![],
|
||||
new_records: vec![],
|
||||
deleted_records: vec![],
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn weight(&self) -> Option<si::Kilogram<f64>> {
|
||||
self.inner
|
||||
.read()
|
||||
.unwrap()
|
||||
.records
|
||||
.iter()
|
||||
.find_map(|record| match record {
|
||||
Record {
|
||||
data: ft_core::TraxRecord::Weight(record),
|
||||
..
|
||||
} => Some(record.weight.clone()),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
glib::spawn_future({
|
||||
let s = self.clone();
|
||||
async move {
|
||||
if let Some(app) = s.app {
|
||||
let updated_records = {
|
||||
let mut data = s.inner.write().unwrap();
|
||||
data.updated_records
|
||||
.drain(..)
|
||||
.collect::<Vec<Record<TraxRecord>>>()
|
||||
};
|
||||
for record in updated_records.into_iter() {
|
||||
let _ = app.update_record(record.clone()).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DayDetailViewPrivate {
|
||||
app: RefCell<Option<App>>,
|
||||
container: Singleton,
|
||||
date: RefCell<chrono::NaiveDate>,
|
||||
records: RefCell<Vec<Record<TraxRecord>>>,
|
||||
view_model: RefCell<DayDetailViewModel>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -49,62 +116,36 @@ glib::wrapper! {
|
|||
}
|
||||
|
||||
impl DayDetailView {
|
||||
pub fn new(date: chrono::NaiveDate, records: Vec<Record<TraxRecord>>, app: App) -> Self {
|
||||
pub fn new(view_model: DayDetailViewModel) -> Self {
|
||||
let s: Self = Object::builder().build();
|
||||
|
||||
*s.imp().date.borrow_mut() = date;
|
||||
*s.imp().records.borrow_mut() = records;
|
||||
*s.imp().app.borrow_mut() = Some(app);
|
||||
*s.imp().view_model.borrow_mut() = view_model;
|
||||
|
||||
s.append(&s.imp().container);
|
||||
|
||||
/*
|
||||
s.imp()
|
||||
.container
|
||||
.swap(&DayDetail::new(date, records.clone(), {
|
||||
let s = s.clone();
|
||||
let records = records.clone();
|
||||
move || {
|
||||
s.imp().container.swap(&DayEdit::new(
|
||||
date,
|
||||
records,
|
||||
s.on_put_record(),
|
||||
// s.on_update_record(),
|
||||
|_| {},
|
||||
))
|
||||
}
|
||||
}));
|
||||
*/
|
||||
|
||||
s.view();
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
fn view(&self) {
|
||||
self.imp().container.swap(&DayDetail::new(
|
||||
self.imp().date.borrow().clone(),
|
||||
self.imp().records.borrow().clone(),
|
||||
{
|
||||
self.imp()
|
||||
.container
|
||||
.swap(&DayDetail::new(self.imp().view_model.borrow().clone(), {
|
||||
let s = self.clone();
|
||||
move || s.edit()
|
||||
},
|
||||
));
|
||||
}));
|
||||
}
|
||||
|
||||
fn edit(&self) {
|
||||
self.imp().container.swap(&DayEdit::new(
|
||||
self.imp().date.borrow().clone(),
|
||||
self.imp().records.borrow().clone(),
|
||||
self.on_put_record(),
|
||||
self.on_update_record(),
|
||||
{
|
||||
self.imp()
|
||||
.container
|
||||
.swap(&DayEdit::new(self.imp().view_model.borrow().clone(), {
|
||||
let s = self.clone();
|
||||
move || s.view()
|
||||
},
|
||||
));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
fn on_put_record(&self) -> Box<dyn Fn(ft_core::TraxRecord)> {
|
||||
let s = self.clone();
|
||||
let app = self.imp().app.clone();
|
||||
|
@ -161,4 +202,5 @@ impl DayDetailView {
|
|||
});
|
||||
})
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with Fit
|
|||
use gtk::prelude::*;
|
||||
|
||||
mod day_detail_view;
|
||||
pub use day_detail_view::DayDetailView;
|
||||
pub use day_detail_view::{DayDetailView, DayDetailViewModel};
|
||||
|
||||
mod historical_view;
|
||||
pub use historical_view::HistoricalView;
|
||||
|
|
Loading…
Reference in New Issue