use crate::db::{Database, SectionRevs};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{broadcast, Mutex};
#[derive(Clone, Debug, serde::Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum SseEvent {
Header,
Description,
Ticket,
Links,
Todos,
Scraps,
Worktrees,
Repos,
}
#[derive(Clone, Debug, PartialEq)]
struct ChangeState {
current_task_id: Option<i64>,
revs: SectionRevs,
}
#[derive(Clone)]
pub struct AppState {
pub db: Arc<Mutex<Database>>,
pub sse_tx: broadcast::Sender<SseEvent>,
last_state: Arc<Mutex<Option<ChangeState>>>,
}
impl AppState {
pub fn from_database(db: Database) -> Self {
let (sse_tx, _) = broadcast::channel(100);
Self {
db: Arc::new(Mutex::new(db)),
sse_tx,
last_state: Arc::new(Mutex::new(None)),
}
}
pub fn new() -> anyhow::Result<Self> {
let db = Database::new()?;
let (sse_tx, _) = broadcast::channel(100);
Ok(Self {
db: Arc::new(Mutex::new(db)),
sse_tx,
last_state: Arc::new(Mutex::new(None)),
})
}
pub fn broadcast(&self, event: SseEvent) {
let _ = self.sse_tx.send(event);
}
async fn get_change_state(&self) -> anyhow::Result<ChangeState> {
let db = self.db.lock().await;
let current_task_id = db.get_current_task_id()?;
let revs = db.get_all_revs()?;
Ok(ChangeState {
current_task_id,
revs,
})
}
fn broadcast_all(&self) {
self.broadcast(SseEvent::Header);
self.broadcast(SseEvent::Description);
self.broadcast(SseEvent::Ticket);
self.broadcast(SseEvent::Links);
self.broadcast(SseEvent::Todos);
self.broadcast(SseEvent::Scraps);
self.broadcast(SseEvent::Repos);
self.broadcast(SseEvent::Worktrees);
}
pub async fn start_change_detection(&self) {
let mut interval = tokio::time::interval(Duration::from_secs(1));
{
let mut last = self.last_state.lock().await;
if last.is_none() {
if let Ok(initial_state) = self.get_change_state().await {
*last = Some(initial_state);
}
}
}
loop {
interval.tick().await;
let current = match self.get_change_state().await {
Ok(state) => state,
Err(e) => {
eprintln!("Error getting change state: {}", e);
continue;
}
};
let mut last = self.last_state.lock().await;
if let Some(ref prev) = *last {
if current.current_task_id != prev.current_task_id {
self.broadcast_all();
} else {
if current.revs.task != prev.revs.task {
self.broadcast(SseEvent::Header);
self.broadcast(SseEvent::Description);
self.broadcast(SseEvent::Ticket);
}
if current.revs.links != prev.revs.links {
self.broadcast(SseEvent::Links);
}
if current.revs.todos != prev.revs.todos
|| current.revs.worktrees != prev.revs.worktrees
{
self.broadcast(SseEvent::Todos);
}
if current.revs.repos != prev.revs.repos {
self.broadcast(SseEvent::Repos);
}
if current.revs.scraps != prev.revs.scraps {
self.broadcast(SseEvent::Scraps);
}
}
}
*last = Some(current);
}
}
}