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
35 lines
562 B
Go
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)
|
|
}
|
|
}
|
|
}
|