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, }
33
34impl App {
35 pub fn edit_timer(&mut self) {
36 if let Some(selected) = self.state.selected() {
37 if let Some(timer_index) = self.get_timer_index_from_selection(selected) {
38 self.timers[timer_index].name = self.name_input.clone();
39 self.timers[timer_index].description = self.description_input.clone();
40 self.db
41 .edit_timer(
42 &self.timers[timer_index],
43 &self.name_input,
44 &self.description_input,
45 )
46 .expect("Unable to edit timer");
47 self.currently_editing = None;
48 }
49 }
50 }
51}
52
53#[derive(Debug)]
54pub struct Timer {
55 pub start_time: DateTime<Utc>,
56 pub name: String,
57 pub(crate) duration: Duration,
58 pub description: String,
59 pub id: usize,
60 pub running: bool,
61}
62
63impl App {
64 pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
66 let db = Db::new_with_default_path()?;
67
68 Ok(App {
69 state: TableState::default().with_selected(1),
70 timers: Vec::new(),
71 current_screen: CurrentScreen::Main,
72 name_input: String::new(),
73 description_input: String::new(),
74 currently_editing: None,
75 selectable_rows: Vec::new(),
76 db,
77 throbber: Throbber::new(),
78 exit_button_selected: false,
79 })
80 }
81
82 pub fn next_row(&mut self) {
83 if self.selectable_rows.is_empty() {
84 return;
85 }
86
87 let current = self.state.selected().unwrap_or(0);
88 let mut next = current;
89
90 loop {
91 next = (next + 1) % self.selectable_rows.len();
92 if self.selectable_rows[next] || next == current {
93 self.state.select(Some(next));
94 break;
95 }
96 }
97 self.state.select(Some(next));
98 }
99
100 pub fn previous_row(&mut self) {
101 if self.selectable_rows.is_empty() {
102 return;
103 }
104
105 let current = self.state.selected().unwrap_or(0);
106 let mut prev = current;
107 loop {
108 prev = (prev + self.selectable_rows.len() - 1) % self.selectable_rows.len();
109 if self.selectable_rows[prev] || prev == current {
110 self.state.select(Some(prev));
111 break;
112 }
113 }
114
115 self.state.select(Some(prev));
116 }
117
118 pub fn add_timer(&mut self) {
119 let mut timer = Timer::new(self.name_input.clone(), self.description_input.clone());
120 match self.timers.last_mut() {
121 Some(t) => t.stop(),
122 None => (),
123 }
124 self.db
125 .add_timer_to_db(&mut timer)
126 .expect("TODO: panic message");
127 self.timers.push(timer);
128 self.name_input = String::new();
129 self.description_input = String::new();
130 }
131
132 pub fn delete_selected_timer(&mut self) -> Result<(), rusqlite::Error> {
133 if let Some(selected) = self.state.selected() {
134 if let Some(timer_index) = self.get_timer_index_from_selection(selected) {
135 self.db.delete_timer(self.timers[timer_index].id)?;
136 self.timers.remove(timer_index);
137 }
138 }
139 Ok(())
140 }
141
142 pub fn toggle_editing(&mut self) {
143 if let Some(edit_mode) = &self.currently_editing {
144 match edit_mode {
145 CurrentlyEditing::Name => {
146 self.currently_editing = Some(CurrentlyEditing::Description)
147 }
148 CurrentlyEditing::Description => {
149 self.currently_editing = Some(CurrentlyEditing::Name)
150 }
151 }
152 } else {
153 self.currently_editing = Some(CurrentlyEditing::Name);
154 }
155 }
156
157 pub fn toggle_timer(&mut self) {
158 if let Some(timer) = self.timers.last_mut() {
159 if timer.running {
160 timer.running = false;
161 } else {
162 timer.running = true;
163 }
164 }
165 }
166
167 pub fn toggle_exit_button(&mut self) {
168 self.exit_button_selected = !self.exit_button_selected;
169 }
170
171 pub fn get_timer_index_from_selection(&self, selected_index: usize) -> Option<usize> {
173 if selected_index >= self.selectable_rows.len() || !self.selectable_rows[selected_index] {
174 return None;
175 }
176
177 let timer_index = self.selectable_rows[..selected_index]
179 .iter()
180 .filter(|&&is_selectable| is_selectable)
181 .count();
182
183 if timer_index < self.timers.len() {
184 Some(timer_index)
185 } else {
186 None
187 }
188 }
189}
190
191impl Timer {
192 pub fn new(name: String, description: String) -> Timer {
193 Timer {
194 start_time: Utc::now(),
195 duration: Duration::zero(),
196 name,
197 description,
198 id: 0,
199 running: true,
200 }
201 }
202
203 pub fn stop(&mut self) {
204 self.running = false;
205 }
206
207 pub fn start(&mut self) {
208 self.running = true;
209 }
210 pub fn tick(&mut self) {
211 self.duration += Duration::seconds(1);
212 }
213
214 pub fn formatted_duration(&self) -> String {
215 format!(
216 "{:02}:{:02}:{:02}",
217 self.duration.num_hours(),
218 self.duration.num_minutes() % 60,
219 self.duration.num_seconds() % 60
220 )
221 }
222
223 pub fn formatted_date(&self) -> String {
224 self.start_time.format("%d-%m-%Y").to_string()
225 }
226}