1use crate::db::{Database, SectionRevs};
4use std::sync::Arc;
5use std::time::Duration;
6use tokio::sync::{broadcast, Mutex};
7
8#[derive(Clone, Debug, serde::Serialize)]
10#[serde(tag = "type", rename_all = "snake_case")]
11pub enum SseEvent {
12 Header,
14 Description,
16 Ticket,
18 Links,
20 Todos,
22 Scraps,
24 Worktrees,
26 Repos,
28}
29
30#[derive(Clone, Debug, PartialEq)]
32struct ChangeState {
33 current_task_id: Option<i64>,
34 revs: SectionRevs,
35}
36
37#[derive(Clone)]
39pub struct AppState {
40 pub db: Arc<Mutex<Database>>,
42 pub sse_tx: broadcast::Sender<SseEvent>,
44 last_state: Arc<Mutex<Option<ChangeState>>>,
46}
47
48impl AppState {
49 pub fn from_database(db: Database) -> Self {
51 let (sse_tx, _) = broadcast::channel(100);
52
53 Self {
54 db: Arc::new(Mutex::new(db)),
55 sse_tx,
56 last_state: Arc::new(Mutex::new(None)),
57 }
58 }
59
60 pub fn new() -> anyhow::Result<Self> {
62 let db = Database::new()?;
63 let (sse_tx, _) = broadcast::channel(100);
64
65 Ok(Self {
66 db: Arc::new(Mutex::new(db)),
67 sse_tx,
68 last_state: Arc::new(Mutex::new(None)),
69 })
70 }
71
72 pub fn broadcast(&self, event: SseEvent) {
74 let _ = self.sse_tx.send(event);
76 }
77
78 async fn get_change_state(&self) -> anyhow::Result<ChangeState> {
80 let db = self.db.lock().await;
81 let current_task_id = db.get_current_task_id()?;
82 let revs = db.get_all_revs()?;
83
84 Ok(ChangeState {
85 current_task_id,
86 revs,
87 })
88 }
89
90 fn broadcast_all(&self) {
92 self.broadcast(SseEvent::Header);
93 self.broadcast(SseEvent::Description);
94 self.broadcast(SseEvent::Ticket);
95 self.broadcast(SseEvent::Links);
96 self.broadcast(SseEvent::Todos);
97 self.broadcast(SseEvent::Scraps);
98 self.broadcast(SseEvent::Repos);
99 self.broadcast(SseEvent::Worktrees);
100 }
101
102 pub async fn start_change_detection(&self) {
104 let mut interval = tokio::time::interval(Duration::from_secs(1));
105
106 {
108 let mut last = self.last_state.lock().await;
109 if last.is_none() {
110 if let Ok(initial_state) = self.get_change_state().await {
111 *last = Some(initial_state);
112 }
113 }
114 }
115
116 loop {
117 interval.tick().await;
118
119 let current = match self.get_change_state().await {
121 Ok(state) => state,
122 Err(e) => {
123 eprintln!("Error getting change state: {}", e);
124 continue;
125 }
126 };
127
128 let mut last = self.last_state.lock().await;
130
131 if let Some(ref prev) = *last {
132 if current.current_task_id != prev.current_task_id {
134 self.broadcast_all();
136 } else {
137 if current.revs.task != prev.revs.task {
139 self.broadcast(SseEvent::Header);
141 self.broadcast(SseEvent::Description);
142 self.broadcast(SseEvent::Ticket);
143 }
144
145 if current.revs.links != prev.revs.links {
146 self.broadcast(SseEvent::Links);
147 }
148
149 if current.revs.todos != prev.revs.todos
151 || current.revs.worktrees != prev.revs.worktrees
152 {
153 self.broadcast(SseEvent::Todos);
154 }
155
156 if current.revs.repos != prev.revs.repos {
157 self.broadcast(SseEvent::Repos);
158 }
159
160 if current.revs.scraps != prev.revs.scraps {
161 self.broadcast(SseEvent::Scraps);
162 }
163 }
164 }
165
166 *last = Some(current);
168 }
169 }
170}