Reload data when the user saves on the DayEdit panel
This required some big overhauls. The view model no longer takes records. It only takes the date that it is responsible for, and it will ask the database for records pertaining to that date. This means that once the view model has saved all of its records, it can simply reload those records from the database. This has the effect that as soon as the user moves from DayEdit back to DayDetail, all of the interesting information has been repopulated.
This commit is contained in:
parent
79d705c1d0
commit
07f487b139
|
@ -57,6 +57,20 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn get_record(&self, id: RecordId) -> Result<Option<Record<TraxRecord>>, AppError> {
|
||||
let db = self.database.clone();
|
||||
self.runtime
|
||||
.spawn_blocking(move || {
|
||||
if let Some(ref db) = *db.read().unwrap() {
|
||||
Ok(db.get(&id))
|
||||
} else {
|
||||
Err(AppError::NoDatabase)
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn records(
|
||||
&self,
|
||||
start: NaiveDate,
|
||||
|
|
|
@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with Fit
|
|||
|
||||
use crate::{
|
||||
app::App,
|
||||
types::DayInterval,
|
||||
view_models::DayDetailViewModel,
|
||||
views::{DayDetailView, HistoricalView, PlaceholderView, View, WelcomeView},
|
||||
};
|
||||
|
@ -133,25 +134,34 @@ impl AppWindow {
|
|||
self.swap_main(view);
|
||||
}
|
||||
|
||||
fn show_historical_view(&self, records: Vec<Record<TraxRecord>>) {
|
||||
let view = View::Historical(HistoricalView::new(self.app.clone(), records, {
|
||||
fn show_historical_view(&self, start_date: chrono::NaiveDate, end_date: chrono::NaiveDate) {
|
||||
let on_select_day = {
|
||||
let s = self.clone();
|
||||
Rc::new(move |date, records| {
|
||||
move |date, records| {
|
||||
let s = s.clone();
|
||||
glib::spawn_future_local(async move {
|
||||
let view_model = DayDetailViewModel::new(date, s.app.clone()).await;
|
||||
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(DayDetailViewModel::new(
|
||||
date,
|
||||
records,
|
||||
s.app.clone(),
|
||||
)));
|
||||
layout.append(&DayDetailView::new(view_model));
|
||||
let page = &adw::NavigationPage::builder()
|
||||
.title(date.format("%Y-%m-%d").to_string())
|
||||
.child(&layout)
|
||||
.build();
|
||||
s.navigation.push(page);
|
||||
})
|
||||
}));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let view = View::Historical(HistoricalView::new(
|
||||
self.app.clone(),
|
||||
DayInterval {
|
||||
start: start_date,
|
||||
end: end_date,
|
||||
},
|
||||
Rc::new(on_select_day),
|
||||
));
|
||||
self.swap_main(view);
|
||||
}
|
||||
|
||||
|
@ -162,7 +172,7 @@ impl AppWindow {
|
|||
let end = Local::now().date_naive();
|
||||
let start = end - Duration::days(7);
|
||||
match s.app.records(start, end).await {
|
||||
Ok(records) => s.show_historical_view(records),
|
||||
Ok(records) => s.show_historical_view(start, end),
|
||||
Err(_) => s.show_welcome_view(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,31 +142,6 @@ impl DayDetail {
|
|||
.build(),
|
||||
);
|
||||
|
||||
/*
|
||||
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::<WeightView>() {
|
||||
println!("focused child is the weight view");
|
||||
widget.blur();
|
||||
}
|
||||
}
|
||||
});
|
||||
s.add_controller(click_controller);
|
||||
*/
|
||||
|
||||
/*
|
||||
let weight_record = records.iter().find_map(|record| match record {
|
||||
Record {
|
||||
id,
|
||||
data: ft_core::TraxRecord::Weight(record),
|
||||
} => Some((id.clone(), record.clone())),
|
||||
_ => None,
|
||||
});
|
||||
*/
|
||||
|
||||
let top_row = gtk::Box::builder()
|
||||
.orientation(gtk::Orientation::Horizontal)
|
||||
.build();
|
||||
|
@ -283,7 +258,17 @@ impl DayEdit {
|
|||
}
|
||||
|
||||
fn finish(&self) {
|
||||
(self.imp().on_finished.borrow())()
|
||||
glib::spawn_future_local({
|
||||
let s = self.clone();
|
||||
async move {
|
||||
let view_model = s.imp().view_model.borrow();
|
||||
let view_model = view_model
|
||||
.as_ref()
|
||||
.expect("DayEdit has not been initialized with the view model");
|
||||
let _ = view_model.save().await;
|
||||
(s.imp().on_finished.borrow())()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn add_row(&self, workout: Record<TraxRecord>) {
|
||||
|
@ -335,10 +320,7 @@ fn control_buttons(s: &DayEdit, view_model: &DayDetailViewModel) -> ActionGroup
|
|||
.primary_action("Save", {
|
||||
let s = s.clone();
|
||||
let view_model = view_model.clone();
|
||||
move || {
|
||||
view_model.save();
|
||||
s.finish();
|
||||
}
|
||||
move || s.finish()
|
||||
})
|
||||
.secondary_action("Cancel", {
|
||||
let s = s.clone();
|
||||
|
|
|
@ -99,12 +99,10 @@ pub struct DayDetailViewModel {
|
|||
weight: Arc<RwLock<Option<RecordState<ft_core::Weight>>>>,
|
||||
steps: Arc<RwLock<Option<RecordState<ft_core::Steps>>>>,
|
||||
records: Arc<RwLock<HashMap<RecordId, RecordState<TraxRecord>>>>,
|
||||
|
||||
original_records: Vec<Record<TraxRecord>>,
|
||||
}
|
||||
|
||||
impl DayDetailViewModel {
|
||||
pub fn new(date: chrono::NaiveDate, records: Vec<Record<TraxRecord>>, app: App) -> Self {
|
||||
pub async fn new(date: chrono::NaiveDate, app: App) -> Self {
|
||||
let s = Self {
|
||||
app,
|
||||
date,
|
||||
|
@ -112,10 +110,8 @@ impl DayDetailViewModel {
|
|||
weight: Arc::new(RwLock::new(None)),
|
||||
steps: Arc::new(RwLock::new(None)),
|
||||
records: Arc::new(RwLock::new(HashMap::new())),
|
||||
|
||||
original_records: records,
|
||||
};
|
||||
s.populate_records();
|
||||
s.populate_records().await;
|
||||
s
|
||||
}
|
||||
|
||||
|
@ -224,7 +220,7 @@ impl DayDetailViewModel {
|
|||
.collect::<Vec<Record<TraxRecord>>>()
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
pub fn save(&self) -> glib::JoinHandle<()> {
|
||||
glib::spawn_future({
|
||||
let s = self.clone();
|
||||
async move {
|
||||
|
@ -287,16 +283,18 @@ impl DayDetailViewModel {
|
|||
RecordState::Deleted(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
s.populate_records().await;
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
pub fn revert(&self) {
|
||||
self.populate_records();
|
||||
}
|
||||
|
||||
fn populate_records(&self) {
|
||||
let records = self.original_records.clone();
|
||||
async fn populate_records(&self) {
|
||||
let records = self.app.records(self.date, self.date).await.unwrap();
|
||||
|
||||
let (weight_records, records): (Vec<Record<TraxRecord>>, Vec<Record<TraxRecord>>) =
|
||||
records.into_iter().partition(|r| r.data.is_weight());
|
||||
|
|
|
@ -60,12 +60,12 @@ impl ObjectSubclass for HistoricalViewPrivate {
|
|||
factory.connect_bind({
|
||||
let app = s.app.clone();
|
||||
move |_, list_item| {
|
||||
let records = list_item
|
||||
let date = list_item
|
||||
.downcast_ref::<gtk::ListItem>()
|
||||
.expect("should be a ListItem")
|
||||
.item()
|
||||
.and_downcast::<DayRecords>()
|
||||
.expect("should be a DaySummary");
|
||||
.and_downcast::<Date>()
|
||||
.expect("should be a Date");
|
||||
|
||||
let summary = list_item
|
||||
.downcast_ref::<gtk::ListItem>()
|
||||
|
@ -75,11 +75,14 @@ impl ObjectSubclass for HistoricalViewPrivate {
|
|||
.expect("should be a DaySummary");
|
||||
|
||||
if let Some(app) = app.borrow().clone() {
|
||||
summary.set_data(DayDetailViewModel::new(
|
||||
records.date(),
|
||||
records.records(),
|
||||
app.clone(),
|
||||
));
|
||||
let _ = glib::spawn_future_local(async move {
|
||||
println!(
|
||||
"setting up a DayDetailViewModel for {}",
|
||||
date.date().format("%Y-%m-%d")
|
||||
);
|
||||
let view_model = DayDetailViewModel::new(date.date(), app.clone()).await;
|
||||
summary.set_data(view_model);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -97,11 +100,7 @@ glib::wrapper! {
|
|||
}
|
||||
|
||||
impl HistoricalView {
|
||||
pub fn new<SelectFn>(
|
||||
app: App,
|
||||
records: Vec<Record<TraxRecord>>,
|
||||
on_select_day: Rc<SelectFn>,
|
||||
) -> Self
|
||||
pub fn new<SelectFn>(app: App, interval: DayInterval, on_select_day: Rc<SelectFn>) -> Self
|
||||
where
|
||||
SelectFn: Fn(chrono::NaiveDate, Vec<Record<TraxRecord>>) + 'static,
|
||||
{
|
||||
|
@ -111,11 +110,8 @@ impl HistoricalView {
|
|||
|
||||
*s.imp().app.borrow_mut() = Some(app);
|
||||
|
||||
let grouped_records =
|
||||
GroupedRecords::new((*s.imp().time_window.borrow()).clone()).with_data(records);
|
||||
|
||||
let mut model = gio::ListStore::new::<DayRecords>();
|
||||
model.extend(grouped_records.items());
|
||||
let mut model = gio::ListStore::new::<Date>();
|
||||
model.extend(interval.days().map(|d| Date::new(d)));
|
||||
s.imp()
|
||||
.list_view
|
||||
.set_model(Some(>k::NoSelection::new(Some(model))));
|
||||
|
@ -125,8 +121,8 @@ impl HistoricalView {
|
|||
move |s, idx| {
|
||||
// This gets triggered whenever the user clicks on an item on the list.
|
||||
let item = s.model().unwrap().item(idx).unwrap();
|
||||
let records = item.downcast_ref::<DayRecords>().unwrap();
|
||||
on_select_day(records.date(), records.records());
|
||||
let date = item.downcast_ref::<Date>().unwrap();
|
||||
on_select_day(date.date(), vec![]);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -135,101 +131,37 @@ impl HistoricalView {
|
|||
s
|
||||
}
|
||||
|
||||
pub fn set_records(&self, records: Vec<Record<TraxRecord>>) {
|
||||
println!("set_records: {:?}", records);
|
||||
let grouped_records =
|
||||
GroupedRecords::new((self.imp().time_window.borrow()).clone()).with_data(records);
|
||||
let mut model = gio::ListStore::new::<DayRecords>();
|
||||
model.extend(grouped_records.items());
|
||||
self.imp()
|
||||
.list_view
|
||||
.set_model(Some(>k::NoSelection::new(Some(model))));
|
||||
}
|
||||
|
||||
pub fn time_window(&self) -> DayInterval {
|
||||
self.imp().time_window.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DayRecordsPrivate {
|
||||
pub struct DatePrivate {
|
||||
date: RefCell<chrono::NaiveDate>,
|
||||
records: RefCell<Vec<Record<TraxRecord>>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for DayRecordsPrivate {
|
||||
const NAME: &'static str = "DayRecords";
|
||||
type Type = DayRecords;
|
||||
impl ObjectSubclass for DatePrivate {
|
||||
const NAME: &'static str = "Date";
|
||||
type Type = Date;
|
||||
}
|
||||
|
||||
impl ObjectImpl for DayRecordsPrivate {}
|
||||
impl ObjectImpl for DatePrivate {}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct DayRecords(ObjectSubclass<DayRecordsPrivate>);
|
||||
pub struct Date(ObjectSubclass<DatePrivate>);
|
||||
}
|
||||
|
||||
impl DayRecords {
|
||||
pub fn new(date: chrono::NaiveDate, records: Vec<Record<TraxRecord>>) -> Self {
|
||||
impl Date {
|
||||
pub fn new(date: chrono::NaiveDate) -> Self {
|
||||
let s: Self = Object::builder().build();
|
||||
|
||||
*s.imp().date.borrow_mut() = date;
|
||||
*s.imp().records.borrow_mut() = records;
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
pub fn date(&self) -> chrono::NaiveDate {
|
||||
*self.imp().date.borrow()
|
||||
}
|
||||
|
||||
pub fn records(&self) -> Vec<Record<TraxRecord>> {
|
||||
self.imp().records.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn add_record(&self, record: Record<TraxRecord>) {
|
||||
self.imp().records.borrow_mut().push(record);
|
||||
}
|
||||
}
|
||||
|
||||
// This isn't feeling quite right. DayRecords is a glib object, but I'm not sure that I want to
|
||||
// really be passing that around. It seems not generic enough. I feel like this whole grouped
|
||||
// records thing can be made more generic.
|
||||
struct GroupedRecords {
|
||||
interval: DayInterval,
|
||||
data: HashMap<NaiveDate, DayRecords>,
|
||||
}
|
||||
|
||||
impl GroupedRecords {
|
||||
fn new(interval: DayInterval) -> Self {
|
||||
let mut s = Self {
|
||||
interval: interval.clone(),
|
||||
data: HashMap::new(),
|
||||
};
|
||||
interval.days().for_each(|date| {
|
||||
let _ = s.data.insert(date, DayRecords::new(date, vec![]));
|
||||
});
|
||||
s
|
||||
}
|
||||
|
||||
fn with_data(mut self, records: Vec<Record<TraxRecord>>) -> Self {
|
||||
records.into_iter().for_each(|record| {
|
||||
self.data
|
||||
.entry(record.date())
|
||||
.and_modify(|entry: &mut DayRecords| (*entry).add_record(record.clone()))
|
||||
.or_insert(DayRecords::new(record.date(), vec![record]));
|
||||
});
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn items(&self) -> impl Iterator<Item = DayRecords> + '_ {
|
||||
self.interval.days().map(|date| {
|
||||
self.data
|
||||
.get(&date)
|
||||
.cloned()
|
||||
.unwrap_or(DayRecords::new(date, vec![]))
|
||||
})
|
||||
self.imp().date.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue