1use crate::db::{Database, SectionRevs};
4use crate::utils::Result;
5use std::sync::Arc;
6use std::time::Duration;
7use tokio::sync::{broadcast, Mutex};
8
9#[derive(Clone, Debug, serde::Serialize)]
11#[serde(tag = "type", rename_all = "snake_case")]
12pub enum SseEvent {
13 Header,
15 Description,
17 Ticket,
19 Links,
21 Todos,
23 Scraps,
25 Worktrees,
27 Repos,
29}
30
31#[derive(Clone, Debug, PartialEq)]
33struct ChangeState {
34 current_task_id: Option<i64>,
35 revs: SectionRevs,
36}
37
38#[derive(Clone)]
40pub struct AppState {
41 pub db: Arc<Mutex<Database>>,
43 pub sse_tx: broadcast::Sender<SseEvent>,
45 last_state: Arc<Mutex<Option<ChangeState>>>,
47}
48
49impl AppState {
50 pub fn from_database(db: Database) -> Self {
52 let (sse_tx, _) = broadcast::channel(100);
53
54 Self {
55 db: Arc::new(Mutex::new(db)),
56 sse_tx,
57 last_state: Arc::new(Mutex::new(None)),
58 }
59 }
60
61 pub fn new() -> Result<Self> {
63 let db = Database::new()?;
64 let (sse_tx, _) = broadcast::channel(100);
65
66 Ok(Self {
67 db: Arc::new(Mutex::new(db)),
68 sse_tx,
69 last_state: Arc::new(Mutex::new(None)),
70 })
71 }
72
73 pub fn broadcast(&self, event: SseEvent) {
75 let _ = self.sse_tx.send(event);
77 }
78
79 async fn get_change_state(&self) -> Result<ChangeState> {
81 let db = self.db.lock().await;
82 let current_task_id = db.get_current_task_id()?;
83 let revs = db.get_all_revs()?;
84
85 Ok(ChangeState {
86 current_task_id,
87 revs,
88 })
89 }
90
91 fn broadcast_all(&self) {
93 self.broadcast(SseEvent::Header);
94 self.broadcast(SseEvent::Description);
95 self.broadcast(SseEvent::Ticket);
96 self.broadcast(SseEvent::Links);
97 self.broadcast(SseEvent::Todos);
98 self.broadcast(SseEvent::Scraps);
99 self.broadcast(SseEvent::Repos);
100 self.broadcast(SseEvent::Worktrees);
101 }
102
103 pub async fn start_change_detection(&self) {
105 let mut interval = tokio::time::interval(Duration::from_secs(1));
106
107 {
109 let mut last = self.last_state.lock().await;
110 if last.is_none() {
111 if let Ok(initial_state) = self.get_change_state().await {
112 *last = Some(initial_state);
113 }
114 }
115 }
116
117 loop {
118 interval.tick().await;
119
120 let current = match self.get_change_state().await {
122 Ok(state) => state,
123 Err(e) => {
124 eprintln!("Error getting change state: {}", e);
125 continue;
126 }
127 };
128
129 let mut last = self.last_state.lock().await;
131
132 if let Some(ref prev) = *last {
133 if current.current_task_id != prev.current_task_id {
135 self.broadcast_all();
137 } else {
138 if current.revs.task != prev.revs.task {
140 self.broadcast(SseEvent::Header);
142 self.broadcast(SseEvent::Description);
143 self.broadcast(SseEvent::Ticket);
144 }
145
146 if current.revs.links != prev.revs.links {
147 self.broadcast(SseEvent::Links);
148 }
149
150 if current.revs.todos != prev.revs.todos
152 || current.revs.worktrees != prev.revs.worktrees
153 {
154 self.broadcast(SseEvent::Todos);
155 }
156
157 if current.revs.repos != prev.revs.repos {
158 self.broadcast(SseEvent::Repos);
159 }
160
161 if current.revs.scraps != prev.revs.scraps {
162 self.broadcast(SseEvent::Scraps);
163 }
164 }
165 }
166
167 *last = Some(current);
169 }
170 }
171}