Skip to main content

things3_cloud/ui/views/
upcoming.rs

1use std::sync::Arc;
2
3use iocraft::prelude::*;
4
5use crate::{
6    common::{ICONS, fmt_date},
7    store::{Task, ThingsStore},
8    ui::components::{
9        empty_text::EmptyText,
10        tasks::{TaskList, TaskOptions},
11    },
12};
13
14const LIST_INDENT: u32 = 4;
15
16#[derive(Default, Props)]
17pub struct UpcomingViewProps<'a> {
18    pub items: Option<&'a Vec<Task>>,
19    pub detailed: bool,
20}
21
22#[component]
23pub fn UpcomingView<'a>(hooks: Hooks, props: &UpcomingViewProps<'a>) -> impl Into<AnyElement<'a>> {
24    let store = hooks.use_context::<Arc<ThingsStore>>().clone();
25    let Some(items) = props.items else {
26        return element! { Text(content: "") }.into_any();
27    };
28
29    if items.is_empty() {
30        return element! { EmptyText(content: "No upcoming tasks.") }.into_any();
31    }
32
33    let id_prefix_len =
34        store.unique_prefix_length(&items.iter().map(|t| t.uuid.clone()).collect::<Vec<_>>());
35
36    let mut groups: Vec<(String, Vec<&Task>)> = Vec::new();
37    for task in items {
38        let day = fmt_date(task.start_date);
39        if let Some((current_day, day_tasks)) = groups.last_mut()
40            && *current_day == day
41        {
42            day_tasks.push(task);
43        } else {
44            groups.push((day, vec![task]));
45        }
46    }
47
48    let options = TaskOptions {
49        detailed: props.detailed,
50        show_project: false,
51        show_area: false,
52        show_today_markers: true,
53        show_staged_today_marker: false,
54    };
55
56    let mut sections: Vec<AnyElement<'a>> = Vec::new();
57    for (day, day_items) in groups.into_iter() {
58        sections.push(
59            element! {
60                View(flex_direction: FlexDirection::Column) {
61                    Text(content: format!("  {}", day), wrap: TextWrap::NoWrap, weight: Weight::Bold)
62                    View(flex_direction: FlexDirection::Column, padding_left: LIST_INDENT) {
63                        TaskList(items: day_items, id_prefix_len, options)
64                    }
65                }
66            }
67            .into_any(),
68        );
69    }
70
71    element! {
72        View(flex_direction: FlexDirection::Column, gap: 1) {
73            Text(
74                content: format!("{} Upcoming  ({} tasks)", ICONS.upcoming, items.len()),
75                wrap: TextWrap::NoWrap,
76                color: Color::Cyan,
77                weight: Weight::Bold,
78            )
79            View(flex_direction: FlexDirection::Column, gap: 1) {
80                #(sections)
81            }
82        }
83    }
84    .into_any()
85}