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 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 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}