▍ humdrum codex / soft

TUI: 60s sports-only live poll while a game is in progress

27f29920d734d374d60affc5208b835973b4b6f1
humdrum <me@humdrum.me> · 2026-07-08 16:20

parent a108f506

TUI: 60s sports-only live poll while a game is in progress

Adds poll_sports_live worker that force-refreshes only the sports
source every 60s while a game is live, re-rendering just the Scores
panel instead of re-fetching every source. Dormant when nothing is
live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

1 files changed

tui/dashboard.py +23 −0
@@ -1078,6 +1078,7 @@         table.cursor_type = "none"
 
         self.refresh_data()
         self.set_interval(REFRESH_SECS, self.refresh_data)
+        self.set_interval(60, self.poll_sports_live)
 
     def _q(self, selector: str, expect_type):
         """query_one against the main screen — app.query_one resolves against
@@ -1194,6 +1195,28 @@             self._sports_id = None
             self._sports_live = False
         self._connected = connected
         self._update_subtitle()
+
+    @work(exclusive=True, group="sports")
+    async def poll_sports_live(self) -> None:
+        # While any game is in progress, force-refresh ONLY the sports source so
+        # live scores tick without re-fetching every other source. Dormant when
+        # nothing is live.
+        if not (self._sports_live and self._sports_id):
+            return
+        try:
+            async with httpx.AsyncClient(timeout=15) as c:
+                r = await c.post(f"{BASE_URL}/api/sources/{self._sports_id}/refresh")
+                r.raise_for_status()
+                state = r.json().get("source") or {}
+        except Exception:
+            return
+        snap = state.get("snapshot") or {}
+        p = snap.get("payload") if snap.get("ok") else None
+        self._q("#sports", DashPanel).set_content(
+            render_sports(p) if p else Text("No games.", style="dim italic")
+        )
+        games = (p or {}).get("games", [])
+        self._sports_live = any(g.get("state") == "in" for g in games)
 
     @work(exclusive=True, thread=True, group="sys")
     def refresh_system(self) -> None: