Skip to main content

things3_cloud/ui/components/
progress_badge.rs

1use crate::common::ICONS;
2use crate::store::{Task, ThingsStore};
3use iocraft::prelude::*;
4use std::sync::Arc;
5
6#[derive(Default, Props)]
7pub struct ProgressBadgeProps<'a> {
8    pub project: Option<&'a Task>,
9    pub show_count: bool,
10    pub title: Option<String>,
11    pub color: Option<Color>,
12    pub weight: Weight,
13}
14
15#[component]
16pub fn ProgressBadge<'a>(
17    hooks: Hooks,
18    props: &ProgressBadgeProps<'a>,
19) -> impl Into<AnyElement<'a>> {
20    let Some(project) = props.project else {
21        return element!(Fragment).into_any();
22    };
23
24    let store = hooks.use_context::<Arc<ThingsStore>>().clone();
25    let progress = project_progress(project, store.as_ref());
26    let color = props.color.unwrap_or(Color::DarkGrey);
27    let weight = props.weight;
28
29    let title = if let Some(title) = &props.title {
30        element!(Text(content: title.clone(), color, weight)).into_any()
31    } else {
32        element!(Fragment).into_any()
33    };
34
35    let count = if props.show_count {
36        let content = format!("({}/{})", progress.done, progress.done + progress.total);
37        element!(Text(content, color, weight)).into_any()
38    } else {
39        element!(Fragment).into_any()
40    };
41
42    element! {
43        View(flex_direction: FlexDirection::Row, gap: 1) {
44            Text(content: progress.marker, color, weight)
45            #(title)
46            #(count)
47        }
48    }
49    .into_any()
50}
51
52struct Progress {
53    marker: &'static str,
54    total: i32,
55    done: i32,
56}
57
58fn project_progress(project: &Task, store: &ThingsStore) -> Progress {
59    if project.in_someday() {
60        return Progress {
61            marker: ICONS.project_someday,
62            total: 0,
63            done: 0,
64        };
65    }
66
67    let progress = store.project_progress(&project.uuid);
68    let total = progress.total;
69    let done = progress.done;
70
71    if total == 0 || done == 0 {
72        return Progress {
73            marker: ICONS.progress_empty,
74            total,
75            done,
76        };
77    }
78
79    if done == total {
80        return Progress {
81            marker: ICONS.progress_full,
82            total,
83            done,
84        };
85    }
86
87    let ratio = done as f32 / total as f32;
88    let marker = if ratio < (1.0 / 3.0) {
89        ICONS.progress_quarter
90    } else if ratio < (2.0 / 3.0) {
91        ICONS.progress_half
92    } else {
93        ICONS.progress_three_quarter
94    };
95
96    Progress {
97        marker,
98        total,
99        done,
100    }
101}