time_rs/lib/
app.rs

1use crate::lib::db::Db;
2use crate::lib::throbber::Throbber;
3use chrono::{DateTime, Duration, Utc};
4use ratatui::widgets::TableState;
5
6#[derive(Debug)]
7pub enum CurrentScreen {
8    Main,
9    Edit,
10    Add,
11    Exit,
12}
13
14#[derive(Debug)]
15pub enum CurrentlyEditing {
16    Name,
17    Description,
18}
19
20#[derive(Debug)]
21pub struct App {
22    pub timers: Vec<Timer>,
23    pub name_input: String,
24    pub description_input: String,
25    pub currently_editing: Option<CurrentlyEditing>,
26    pub current_screen: CurrentScreen,
27    pub state: TableState,
28    pub selectable_rows: Vec<bool>,
29    pub db: Db,
30    pub throbber: Throbber,
31    pub exit_button_selected: bool, // true for Yes, false for No
32}
33
34impl App {
35    pub fn edit_timer(&mut self) {
36        if let Some(selected) = self.state.selected() {
37            self.timers[selected - 1].name = self.name_input.clone();
38            self.timers[selected - 1].description = self.description_input.clone();
39            self.db
40                .edit_timer(
41                    &self.timers[selected - 1],
42                    &self.name_input,
43                    &self.description_input,
44                )
45                .expect("Unable to edit timer");
46            self.currently_editing = None;
47        }
48    }
49}
50
51#[derive(Debug)]
52pub struct Timer {
53    pub start_time: DateTime<Utc>,
54    pub name: String,
55    pub(crate) duration: Duration,
56    pub description: String,
57    pub id: usize,
58    pub running: bool,
59}
60
61impl App {
62    /// Create a new App instance using the platform-appropriate database path
63    pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
64        let db = Db::new_with_default_path()?;
65
66        Ok(App {
67            state: TableState::default().with_selected(1),
68            timers: Vec::new(),
69            current_screen: CurrentScreen::Main,
70            name_input: String::new(),
71            description_input: String::new(),
72            currently_editing: None,
73            selectable_rows: Vec::new(),
74            db,
75            throbber: Throbber::new(),
76            exit_button_selected: false,
77        })
78    }
79
80    pub fn next_row(&mut self) {
81        if self.selectable_rows.is_empty() {
82            return;
83        }
84
85        let current = self.state.selected().unwrap_or(0);
86        let mut next = current;
87
88        loop {
89            next = (next + 1) % self.selectable_rows.len();
90            if self.selectable_rows[next] || next == current {
91                self.state.select(Some(next));
92                break;
93            }
94        }
95        self.state.select(Some(next));
96    }
97
98    pub fn previous_row(&mut self) {
99        if self.selectable_rows.is_empty() {
100            return;
101        }
102
103        let current = self.state.selected().unwrap_or(0);
104        let mut prev = current;
105        loop {
106            prev = (prev + self.selectable_rows.len() - 1) % self.selectable_rows.len();
107            if self.selectable_rows[prev] || prev == current {
108                self.state.select(Some(prev));
109                break;
110            }
111        }
112
113        self.state.select(Some(prev));
114    }
115
116    pub fn add_timer(&mut self) {
117        let mut timer = Timer::new(self.name_input.clone(), self.description_input.clone());
118        match self.timers.last_mut() {
119            Some(t) => t.stop(),
120            None => (),
121        }
122        self.db
123            .add_timer_to_db(&mut timer)
124            .expect("TODO: panic message");
125        self.timers.push(timer);
126        self.name_input = String::new();
127        self.description_input = String::new();
128    }
129
130    pub fn delete_selected_timer(&mut self) -> Result<(), rusqlite::Error> {
131        let selected = self.state.selected();
132        if let Some(selected) = selected {
133            self.db.delete_timer(self.timers[selected - 1].id)?;
134        }
135        self.timers.remove(selected.unwrap_or(0) - 1);
136        Ok(())
137    }
138
139    pub fn toggle_editing(&mut self) {
140        if let Some(edit_mode) = &self.currently_editing {
141            match edit_mode {
142                CurrentlyEditing::Name => {
143                    self.currently_editing = Some(CurrentlyEditing::Description)
144                }
145                CurrentlyEditing::Description => {
146                    self.currently_editing = Some(CurrentlyEditing::Name)
147                }
148            }
149        } else {
150            self.currently_editing = Some(CurrentlyEditing::Name);
151        }
152    }
153
154    pub fn toggle_timer(&mut self) {
155        if let Some(selected) = self.state.selected() {
156            if self.timers[selected - 1].running {
157                self.timers[selected - 1].stop();
158            } else {
159                self.timers[selected - 1].start();
160            }
161        }
162    }
163
164    pub fn toggle_exit_button(&mut self) {
165        self.exit_button_selected = !self.exit_button_selected;
166    }
167}
168
169impl Timer {
170    pub fn new(name: String, description: String) -> Timer {
171        Timer {
172            start_time: Utc::now(),
173            duration: Duration::zero(),
174            name,
175            description,
176            id: 0,
177            running: true,
178        }
179    }
180
181    pub fn stop(&mut self) {
182        self.running = false;
183    }
184
185    pub fn start(&mut self) {
186        self.running = true;
187    }
188    pub fn tick(&mut self) {
189        self.duration += Duration::seconds(1);
190    }
191
192    pub fn formatted_duration(&self) -> String {
193        format!(
194            "{:02}:{:02}:{:02}",
195            self.duration.num_hours(),
196            self.duration.num_minutes() % 60,
197            self.duration.num_seconds() % 60
198        )
199    }
200
201    pub fn formatted_date(&self) -> String {
202        self.start_time.format("%d-%m-%Y").to_string()
203    }
204}