monorepo/fitnesstrax/app/src/views/historical_view.rs

163 lines
5.1 KiB
Rust

/*
Copyright 2023, Savanni D'Gerinel <savanni@luminescent-dreams.com>
This file is part of FitnessTrax.
FitnessTrax is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
FitnessTrax is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along with FitnessTrax. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::{
app::App, components::DaySummary, types::DayInterval, view_models::DayDetailViewModel,
};
use emseries::Record;
use ft_core::TraxRecord;
use glib::Object;
use gtk::{prelude::*, subclass::prelude::*};
use std::{cell::RefCell, rc::Rc};
/// The historical view will show a window into the main database. It will show some version of
/// daily summaries, daily details, and will provide all functions the user may need for editing
/// records.
pub struct HistoricalViewPrivate {
app: Rc<RefCell<Option<App>>>,
time_window: Rc<RefCell<DayInterval>>,
list_view: gtk::ListView,
}
#[glib::object_subclass]
impl ObjectSubclass for HistoricalViewPrivate {
const NAME: &'static str = "HistoricalView";
type Type = HistoricalView;
type ParentType = gtk::Box;
fn new() -> Self {
let factory = gtk::SignalListItemFactory::new();
factory.connect_setup(move |_, list_item| {
list_item
.downcast_ref::<gtk::ListItem>()
.expect("should be a ListItem")
.set_child(Some(&DaySummary::new()));
});
let s = Self {
app: Rc::new(RefCell::new(None)),
time_window: Rc::new(RefCell::new(DayInterval::default())),
list_view: gtk::ListView::builder()
.factory(&factory)
.single_click_activate(true)
.build(),
};
factory.connect_bind({
let app = s.app.clone();
move |_, list_item| {
let date = list_item
.downcast_ref::<gtk::ListItem>()
.expect("should be a ListItem")
.item()
.and_downcast::<Date>()
.expect("should be a Date");
let summary = list_item
.downcast_ref::<gtk::ListItem>()
.expect("should be a ListItem")
.child()
.and_downcast::<DaySummary>()
.expect("should be a DaySummary");
if let Some(app) = app.borrow().clone() {
glib::spawn_future_local(async move {
let view_model = DayDetailViewModel::new(date.date(), app.clone()).await;
summary.set_data(view_model);
});
}
}
});
s
}
}
impl ObjectImpl for HistoricalViewPrivate {}
impl WidgetImpl for HistoricalViewPrivate {}
impl BoxImpl for HistoricalViewPrivate {}
glib::wrapper! {
pub struct HistoricalView(ObjectSubclass<HistoricalViewPrivate>) @extends gtk::Box, gtk::Widget, @implements gtk::Orientable;
}
impl HistoricalView {
pub fn new<SelectFn>(app: App, interval: DayInterval, on_select_day: Rc<SelectFn>) -> Self
where
SelectFn: Fn(chrono::NaiveDate, Vec<Record<TraxRecord>>) + 'static,
{
let s: Self = Object::builder().build();
s.set_orientation(gtk::Orientation::Vertical);
s.set_css_classes(&["historical"]);
*s.imp().app.borrow_mut() = Some(app);
let mut model = gio::ListStore::new::<Date>();
model.extend(interval.days().map(Date::new));
s.imp()
.list_view
.set_model(Some(&gtk::NoSelection::new(Some(model))));
s.imp().list_view.connect_activate({
let on_select_day = on_select_day.clone();
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 date = item.downcast_ref::<Date>().unwrap();
on_select_day(date.date(), vec![]);
}
});
s.append(&s.imp().list_view);
s
}
pub fn time_window(&self) -> DayInterval {
self.imp().time_window.borrow().clone()
}
}
#[derive(Default)]
pub struct DatePrivate {
date: RefCell<chrono::NaiveDate>,
}
#[glib::object_subclass]
impl ObjectSubclass for DatePrivate {
const NAME: &'static str = "Date";
type Type = Date;
}
impl ObjectImpl for DatePrivate {}
glib::wrapper! {
pub struct Date(ObjectSubclass<DatePrivate>);
}
impl Date {
pub fn new(date: chrono::NaiveDate) -> Self {
let s: Self = Object::builder().build();
*s.imp().date.borrow_mut() = date;
s
}
pub fn date(&self) -> chrono::NaiveDate {
*self.imp().date.borrow()
}
}