Skip to main content

things3_cloud/ui/components/
progress_badge.rs

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