Files
via/routine.go
Ryan Hamamura 27b8540b71 feat: add SPA navigation with view transitions
Swap page content over the existing SSE connection without full page
loads. A persistent Context resets its page-specific state (signals,
actions, intervals, subscriptions) on navigate while preserving the
SSE stream, CSRF token, and session.

- c.Navigate(path) for programmatic SPA navigation from actions
- Injected JS intercepts same-origin <a> clicks (opt out with
  data-via-no-boost) and handles popstate for back/forward
- v.Layout() wraps pages in a shared shell for DRY nav/chrome
- View Transition API integration via WithViewTransitions() on
  PatchElements and h.DataViewTransition() helper
- POST /_navigate endpoint with CSRF validation and rate limiting
- pageStopChan cancels page-level OnInterval goroutines on navigate
- Includes SPA example with layout, counter, and live clock pages
2026-02-12 13:52:47 -10:00

35 lines
562 B
Go

package via
import (
"sync/atomic"
"time"
)
func newOnInterval(ctxDisposedChan, pageStopChan chan struct{}, duration time.Duration, handler func()) func() {
localInterrupt := make(chan struct{})
var stopped atomic.Bool
go func() {
tkr := time.NewTicker(duration)
defer tkr.Stop()
for {
select {
case <-ctxDisposedChan:
return
case <-pageStopChan:
return
case <-localInterrupt:
return
case <-tkr.C:
handler()
}
}
}()
return func() {
if stopped.CompareAndSwap(false, true) {
close(localInterrupt)
}
}
}