saturn_cli/ui/
state.rs

1use crate::{
2    config::{Config, DBType},
3    db::{google::GoogleClient, memory::MemoryDB, remote::RemoteDBClient, DB},
4    list_ui, map_record, process_ui_command,
5    record::{Record, RecurringRecord},
6    time::now,
7};
8use anyhow::Result;
9use ratatui::widgets::*;
10use std::{sync::Arc, time::Duration};
11use tokio::sync::Mutex;
12
13#[derive(Debug, Clone, Default)]
14pub struct State<'a> {
15    pub records: Vec<Record>,
16    pub recurring_records: Vec<RecurringRecord>,
17    pub list_type: super::types::ListType,
18    pub notification: Option<(String, chrono::NaiveDateTime)>,
19    pub errors: Vec<String>,
20    pub line_buf: String,
21    pub commands: Vec<super::types::CommandType>,
22    pub show: Option<Record>,
23    pub show_recurring: Option<RecurringRecord>,
24    pub calendar: Option<(Arc<Table<'a>>, chrono::NaiveDateTime)>,
25    pub events: Option<(Arc<Table<'a>>, chrono::NaiveDateTime)>,
26    pub redraw: bool,
27    pub block_ui: bool,
28}
29
30#[derive(Debug, Clone, Default)]
31pub struct ProtectedState<'a>(Arc<Mutex<State<'a>>>);
32
33impl<'a> std::ops::Deref for ProtectedState<'a> {
34    type Target = Arc<Mutex<State<'a>>>;
35    fn deref(&self) -> &Self::Target {
36        &self.0
37    }
38}
39
40impl<'a> ProtectedState<'a> {
41    pub fn google_db(&self, config: Config) -> Result<RemoteDBClient<GoogleClient>> {
42        let client = GoogleClient::new(config.clone())?;
43
44        Ok(RemoteDBClient::new(config.calendar_id(), client.clone()))
45    }
46
47    pub fn memory_db(&self) -> Result<MemoryDB> {
48        Ok(MemoryDB::new())
49    }
50
51    pub async fn list_google_recurring(&self, config: Config) -> Result<Vec<RecurringRecord>> {
52        let mut db = self.google_db(config)?;
53        db.load().await?;
54        let res = db.list_recurrence().await?;
55        db.dump().await?;
56        Ok(res)
57    }
58
59    pub async fn list_file_recurring(&self) -> Result<Vec<RecurringRecord>> {
60        let mut db = self.memory_db()?;
61        db.load().await?;
62        let res = db.list_recurrence().await?;
63        db.dump().await?;
64        Ok(res)
65    }
66
67    pub async fn list_google(
68        &self,
69        config: Config,
70        list_type: super::types::ListType,
71    ) -> Result<Vec<Record>> {
72        let mut db = self.google_db(config)?;
73        list_ui!(db, list_type)
74    }
75
76    pub async fn list_file(&self, list_type: super::types::ListType) -> Result<Vec<Record>> {
77        let mut db = self.memory_db()?;
78        list_ui!(db, list_type)
79    }
80
81    pub async fn command_google(&self, config: Config) -> Result<()> {
82        let client = GoogleClient::new(config.clone())?;
83
84        let mut db = RemoteDBClient::new(config.calendar_id(), client.clone());
85        process_ui_command!(self, db, config);
86        Ok(())
87    }
88
89    pub async fn command_file(&self, config: Config) -> Result<()> {
90        let mut db = MemoryDB::new();
91        process_ui_command!(self, db, config);
92        Ok(())
93    }
94
95    pub async fn get_google(&self, config: Config, id: u64) -> Result<Record> {
96        let client = GoogleClient::new(config.clone())?;
97
98        let mut db = RemoteDBClient::new(config.calendar_id(), client.clone());
99        map_record!(db, id)
100    }
101
102    pub async fn get_file(&self, id: u64) -> Result<Record> {
103        let mut db = MemoryDB::new();
104        map_record!(db, id)
105    }
106
107    pub async fn get_recurring_google(&self, config: Config, id: u64) -> Result<RecurringRecord> {
108        let client = GoogleClient::new(config.clone())?;
109
110        let mut db = RemoteDBClient::new(config.calendar_id(), client.clone());
111        map_record!(db, id, true)
112    }
113
114    pub async fn get_recurring_file(&self, id: u64) -> Result<RecurringRecord> {
115        let mut db = MemoryDB::new();
116        map_record!(db, id, true)
117    }
118
119    pub async fn update_state(&self) {
120        let config = Config::load(None).unwrap_or_default();
121
122        let typ = config.db_type();
123
124        let res = match typ {
125            DBType::UnixFile => self.command_file(config.clone()).await,
126            DBType::Google => self.command_google(config.clone()).await,
127        };
128
129        if let Err(e) = res {
130            let mut lock = self.lock().await;
131            lock.block_ui = false;
132            if lock.commands.len() > 1 {
133                lock.commands = lock.commands[1..lock.commands.len()].to_vec();
134            } else {
135                lock.commands = Vec::new();
136            }
137            lock.notification = None;
138            drop(lock);
139            self.add_error(e).await;
140            return;
141        }
142
143        let list_type = self.lock().await.list_type.clone();
144
145        if matches!(list_type, super::types::ListType::Recurring) {
146            let res = match typ {
147                DBType::UnixFile => self.list_file_recurring().await,
148                DBType::Google => self.list_google_recurring(config).await,
149            };
150
151            let mut list = match res {
152                Ok(list) => list,
153                Err(e) => {
154                    self.add_error(e).await;
155                    return;
156                }
157            };
158
159            let mut inner = self.lock().await;
160            inner.recurring_records.clear();
161            inner.recurring_records.append(&mut list);
162            inner.redraw = true;
163        } else if !matches!(list_type, super::types::ListType::Search) {
164            let res = match typ {
165                DBType::UnixFile => self.list_file(list_type).await,
166                DBType::Google => self.list_google(config, list_type).await,
167            };
168
169            let mut list = match res {
170                Ok(list) => list,
171                Err(e) => {
172                    self.add_error(e).await;
173                    return;
174                }
175            };
176
177            list.sort_by(crate::record::sort_records);
178            let mut inner = self.lock().await;
179            inner.records.clear();
180            inner.records.append(&mut list);
181            inner.redraw = true;
182        }
183    }
184
185    pub async fn refresh(&self) -> Result<()> {
186        loop {
187            self.update_state().await;
188            tokio::time::sleep(Duration::new(60, 0)).await;
189        }
190    }
191
192    pub async fn add_notification(&self, notification: &str) {
193        self.lock().await.notification = Some((notification.to_string(), now().naive_local()))
194    }
195
196    pub async fn add_error(&self, error: anyhow::Error) {
197        self.lock().await.errors.push(error.to_string())
198    }
199}