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}