1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use std::convert::TryInto;

use crate::{
    core::{i18n, ni18n_f, Database, Settings},
    plugins::{PluginName, PluginSummaryRow},
};
use adw::prelude::*;
use gtk::{glib, subclass::prelude::*};

mod imp {
    use crate::{
        core::Database, plugins::PluginSummaryRow, prelude::*, widgets::CircularProgressBar,
    };
    use adw::subclass::prelude::*;
    use gtk::{gio, glib, prelude::*, CompositeTemplate};

    #[derive(Debug, CompositeTemplate, Default)]
    #[template(resource = "/dev/Cogitri/Health/ui/plugins/steps/summary.ui")]
    pub struct PluginStepsSummaryRow {
        #[template_child]
        pub circular_progress_bar: TemplateChild<CircularProgressBar>,
        #[template_child]
        pub activity_subtext: TemplateChild<gtk::Label>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for PluginStepsSummaryRow {
        const NAME: &'static str = "HealthPluginStepsSummaryRow";
        type ParentType = PluginSummaryRow;
        type Type = super::PluginStepsSummaryRow;

        fn class_init(klass: &mut Self::Class) {
            Self::bind_template(klass);
        }

        fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
            obj.init_template();
        }
    }

    impl ObjectImpl for PluginStepsSummaryRow {
        fn constructed(&self) {
            self.parent_constructed();

            let obj = self.obj();

            Database::instance().connect_activities_updated(glib::clone!(@weak obj => move |_| {
                gtk_macros::spawn!(async move {
                    obj.update().await;
                });
            }));
        }
    }
    impl WidgetImpl for PluginStepsSummaryRow {}
    impl ListBoxRowImpl for PluginStepsSummaryRow {}
    impl PreferencesRowImpl for PluginStepsSummaryRow {}
    impl ActionRowImpl for PluginStepsSummaryRow {}
    impl PluginSummaryRowImpl for PluginStepsSummaryRow {
        fn update(&self, obj: &PluginSummaryRow) -> PinnedResultFuture<()> {
            Box::pin(gio::GioFuture::new(
                obj,
                glib::clone!(@weak obj => move |_, _, send| {
                    gtk_macros::spawn!(async move {
                        obj.downcast_ref::<super::PluginStepsSummaryRow>()
                            .unwrap()
                            .update()
                            .await;
                        send.resolve(Ok(()));
                    });
                }),
            ))
        }
    }
}

glib::wrapper! {
    /// An implementation of [PluginSummaryRow] giving the user a quick glance over their step count.
    pub struct PluginStepsSummaryRow(ObjectSubclass<imp::PluginStepsSummaryRow>)
    @extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow, PluginSummaryRow,
    @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
}

impl PluginStepsSummaryRow {
    pub fn new(name: PluginName) -> Self {
        glib::Object::builder()
            .property("plugin-name", &name)
            .property("activatable", true)
            .build()
    }

    pub async fn update(&self) {
        let imp = self.imp();
        let db = Database::instance();
        let user_id = i64::from(Settings::instance().active_user_id());
        let user = &db.user(user_id).await.unwrap();
        let step_count = db.todays_steps().await.unwrap_or(0);
        let step_goal = user.user_stepgoal().unwrap_or(0) as u32;

        self.set_subtitle(&ni18n_f(
            "{} step taken today",
            "{} steps taken today",
            step_count.try_into().unwrap(),
            &[&step_count.to_string()],
        ));
        imp.circular_progress_bar.set_step_goal(step_goal);
        imp.circular_progress_bar
            .set_step_count(step_count.try_into().unwrap());
        let steps_percentage = (step_count as f32 / step_goal.max(1) as f32 * 100.0) as u32;
        if steps_percentage < 100 {
            imp.activity_subtext.set_text(&ni18n_f(
                "Reached {} percent of daily step goal",
                "Reached {} percent of daily step goal",
                steps_percentage,
                &[&steps_percentage.to_string()],
            ));
        } else {
            imp.activity_subtext
                .set_text(&i18n("Well done! You have reached your daily step goal!"));
        }
    }
}

#[cfg(test)]
mod test {
    use super::{PluginName, PluginStepsSummaryRow};
    use crate::utils::init_gtk;

    #[gtk::test]
    fn new() {
        init_gtk();
        PluginStepsSummaryRow::new(PluginName::Steps);
    }
}