use color_eyre::eyre; use futures::stream::StreamExt; use reqwest_sse::EventSource; use std::time::Duration; use tokio::sync::mpsc::{self, Receiver}; use types::{ tag::{Category, Tag}, todo::{AddTodoModel, Todo}, }; #[must_use] pub fn get_todos(url: String, token: String) -> Receiver> { let (tx, rx) = mpsc::channel(8); std::thread::spawn(move || { let Ok(rt) = tokio::runtime::Runtime::new() else { eprintln!("Failed to create Tokio runtime"); return; }; rt.block_on(async move { loop { let todos = loop { match fetch_todos(&url, &token).await { Ok(todos) => break todos, Err(err) => { eprintln!("Error fetching todos: {err}"); tokio::time::sleep(Duration::from_secs(5)).await; } } }; if tx.send(todos).await.is_err() { break; } let mut event_source = match reqwest::Client::new() .get(format!("{url}/api/updates/sse")) .header("x-api-key", &token) .send() .await { Ok(response) => match response.events().await { Ok(events) => events, Err(err) => { eprintln!("Failed to establish SSE stream: {err}"); tokio::time::sleep(Duration::from_secs(5)).await; continue; } }, Err(err) => { eprintln!("Failed to connect to SSE endpoint: {err}"); tokio::time::sleep(Duration::from_secs(5)).await; continue; } }; while let Some(event) = event_source.next().await { match event { Ok(event) => { if event.event_type == "todos" { match serde_json::from_str(&event.data) { Ok(todos) => { if tx.send(todos).await.is_err() { return; } } Err(err) => { eprintln!("Invalid todos payload: {err}"); } } } } Err(err) => { eprintln!("Error: {err}"); break; } } } } }); }); rx } async fn fetch_todos(url: &str, token: &str) -> eyre::Result> { let response = reqwest::Client::new() .get(format!("{}{}", url, "/api/todos")) .header("x-api-key", token) .send() .await? .text() .await?; Ok(serde_json::from_str(&response)?) } pub async fn add_todo(url: &str, token: &str, todo: &AddTodoModel) -> eyre::Result<()> { let response = reqwest::Client::new() .post(format!("{}{}", url, "/api/todos")) .header("x-api-key", token) .json(todo) .send() .await?; if !response.status().is_success() { return Err(eyre::eyre!("Failed to add todo: {}", response.status())); } Ok(()) } pub async fn fetch_tags(url: &str, token: &str) -> eyre::Result> { let response = reqwest::Client::new() .get(format!("{}{}", url, "/api/tags")) .header("x-api-key", token) .send() .await?; if !response.status().is_success() { return Err(eyre::eyre!("Failed to fetch tags: {}", response.status())); } Ok(serde_json::from_str(&response.text().await?)?) } pub async fn fetch_categories(url: &str, token: &str) -> eyre::Result> { let response = reqwest::Client::new() .get(format!("{}{}", url, "/api/categories")) .header("x-api-key", token) .send() .await?; if !response.status().is_success() { return Err(eyre::eyre!( "Failed to fetch categories: {}", response.status() )); } Ok(serde_json::from_str(&response.text().await?)?) }