feat(milestone): SSE live-updating progress bars #14

Merged
oleks merged 4 commits from milestone-sse into main 2026-05-16 10:21:38 +03:00
Owner

Implements milestone 34 — push every milestone progress bar over SSE so open/closed counts and the completion bar move live, no refresh. Closes #10, #11, #12 (and the #9 scope).

Design

Mirrors the shipped project-board SSE feature (#7 + the #8 detach-ctx fix). New package services/milestone_events with the same shape: typed payloads, non-blocking Publish* helpers, publish-time permission filter, broadcast/connectedUIDs seams for testability.

  • Event scheme: repo-milestones.{repo_id} — one channel per repo (the milestone-list page watches many milestones at once; per-repo channel + dispatch-by-milestone_id in the payload avoids N subscriptions). Payloads: milestone.progress {repo_id, milestone_id, open_issues, closed_issues, completeness, session_tag}, milestone.deleted {repo_id, milestone_id}.
  • Choke point: all count changes funnel through UpdateMilestoneCounters. models/issues cannot import services/* (real import cycle), so — exactly as project_events did — publish calls live at the service layer: services/issue/{milestone,status,issue}.go (assign/reopen/close/create/delete) + milestone CRUD routers. Documented gap: the cron stats-repair path (models/repo.go) runs in models+cron and can't publish; acceptable (rare repair job; project_events has the same limitation).
  • detach-ctx constraint honoured: the publish goroutine uses graceful.GetManager().ShutdownContext() (NOT the request ctx — the exact bug fixed in #8). Session tag resolved synchronously before the goroutine; milestone re-fetched inside the goroutine on the detached ctx.
  • modules/sessiontag: WithSessionTag/SessionTagFromContext extracted out of project_events into a new tiny modules/sessiontag package so milestone_events and project_events both depend on it without cross-importing. project_events tests still green after the extraction.
  • Frontend: new web_src/js/features/repo-milestone-sse.ts patches the bar on all four render sites (repo milestone list, user-dashboard milestone list, single-milestone view, with shared patchMilestoneCard); templates gained data-milestone-id/data-repo-id + CSS hook classes; init wired in repo-legacy.ts + dashboard.ts.

Tests

services/milestone_events 15 unit tests (mirror project_events). go build, go vet, project_events tests (post-sessiontag-extraction), tsc --noEmit, vite build all green. No new REST endpoints (swagger unchanged by this branch).

Implements milestone 34 — push every milestone progress bar over SSE so open/closed counts and the completion bar move live, no refresh. Closes #10, #11, #12 (and the #9 scope). ## Design Mirrors the shipped project-board SSE feature (#7 + the #8 detach-ctx fix). New package `services/milestone_events` with the same shape: typed payloads, non-blocking `Publish*` helpers, publish-time permission filter, broadcast/connectedUIDs seams for testability. - **Event scheme:** `repo-milestones.{repo_id}` — one channel per repo (the milestone-list page watches many milestones at once; per-repo channel + dispatch-by-milestone_id in the payload avoids N subscriptions). Payloads: `milestone.progress {repo_id, milestone_id, open_issues, closed_issues, completeness, session_tag}`, `milestone.deleted {repo_id, milestone_id}`. - **Choke point:** all count changes funnel through `UpdateMilestoneCounters`. `models/issues` cannot import `services/*` (real import cycle), so — exactly as project_events did — publish calls live at the **service layer**: `services/issue/{milestone,status,issue}.go` (assign/reopen/close/create/delete) + milestone CRUD routers. Documented gap: the cron stats-repair path (`models/repo.go`) runs in models+cron and can't publish; acceptable (rare repair job; project_events has the same limitation). - **detach-ctx constraint honoured:** the publish goroutine uses `graceful.GetManager().ShutdownContext()` (NOT the request ctx — the exact bug fixed in #8). Session tag resolved synchronously before the goroutine; milestone re-fetched inside the goroutine on the detached ctx. - **modules/sessiontag:** `WithSessionTag`/`SessionTagFromContext` extracted out of `project_events` into a new tiny `modules/sessiontag` package so `milestone_events` and `project_events` both depend on it without cross-importing. `project_events` tests still green after the extraction. - **Frontend:** new `web_src/js/features/repo-milestone-sse.ts` patches the bar on all four render sites (repo milestone list, user-dashboard milestone list, single-milestone view, with shared `patchMilestoneCard`); templates gained `data-milestone-id`/`data-repo-id` + CSS hook classes; init wired in repo-legacy.ts + dashboard.ts. ## Tests `services/milestone_events` 15 unit tests (mirror project_events). `go build`, `go vet`, project_events tests (post-sessiontag-extraction), `tsc --noEmit`, `vite build` all green. No new REST endpoints (swagger unchanged by this branch).
oleks added 4 commits 2026-05-16 10:21:32 +03:00
oleks added this to the (deleted) project 2026-05-16 10:21:35 +03:00
oleks merged commit 9f588d3dd3 into main 2026-05-16 10:21:38 +03:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: oleks/gitea#14