things3_cloud/ui/components/
progress_badge.rs1use 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}