fix: components not using parent page patchShan; fix: chat example nil pointer error when sync outside action

This commit is contained in:
Joao Goncalves
2025-11-15 02:34:11 -01:00
parent 042975fdd7
commit 80879216b2
4 changed files with 20 additions and 17 deletions

View File

@@ -189,7 +189,7 @@ func (c *Context) injectSignals(sigs map[string]any) {
} }
} }
func (c *Context) getSSE() chan patch { func (c *Context) getPatchChan() chan patch {
// components use parent page sse stream // components use parent page sse stream
var patchChan chan patch var patchChan chan patch
if c.isComponent() { if c.isComponent() {
@@ -220,12 +220,13 @@ func (c *Context) prepareSignalsForPatch() map[string]any {
// Sync pushes the current view state and signal changes to the browser immediately // Sync pushes the current view state and signal changes to the browser immediately
// over the live SSE event stream. // over the live SSE event stream.
func (c *Context) Sync() { func (c *Context) Sync() {
patchChan := c.getPatchChan()
elemsPatch := bytes.NewBuffer(make([]byte, 0)) elemsPatch := bytes.NewBuffer(make([]byte, 0))
if err := c.view().Render(elemsPatch); err != nil { if err := c.view().Render(elemsPatch); err != nil {
c.app.logErr(c, "sync view failed: %v", err) c.app.logErr(c, "sync view failed: %v", err)
return return
} }
c.patchChan <- patch{patchTypeElements, elemsPatch.String()} patchChan <- patch{patchTypeElements, elemsPatch.String()}
c.mutex.RLock() c.mutex.RLock()
defer c.mutex.RUnlock() defer c.mutex.RUnlock()
@@ -233,7 +234,7 @@ func (c *Context) Sync() {
if len(updatedSigs) != 0 { if len(updatedSigs) != 0 {
outgoingSigs, _ := json.Marshal(updatedSigs) outgoingSigs, _ := json.Marshal(updatedSigs)
c.patchChan <- patch{patchTypeSignals, string(outgoingSigs)} patchChan <- patch{patchTypeSignals, string(outgoingSigs)}
} }
} }
@@ -255,11 +256,7 @@ func (c *Context) Sync() {
// Then, the merge will only occur if the ID of the top level element in the patch // Then, the merge will only occur if the ID of the top level element in the patch
// matches 'my-element'. // matches 'my-element'.
func (c *Context) SyncElements(elem h.H) { func (c *Context) SyncElements(elem h.H) {
sse := c.getSSE() patchChan := c.getPatchChan()
if sse == nil {
c.app.logWarn(c, "elements out of sync: no sse stream")
return
}
if c.view == nil { if c.view == nil {
c.app.logErr(c, "sync element failed: viewfn is nil") c.app.logErr(c, "sync element failed: viewfn is nil")
return return
@@ -270,29 +267,29 @@ func (c *Context) SyncElements(elem h.H) {
} }
b := bytes.NewBuffer(make([]byte, 0)) b := bytes.NewBuffer(make([]byte, 0))
_ = elem.Render(b) _ = elem.Render(b)
c.patchChan <- patch{patchTypeElements, b.String()} patchChan <- patch{patchTypeElements, b.String()}
} }
// SyncSignals pushes the current signal changes to the browser immediately // SyncSignals pushes the current signal changes to the browser immediately
// over the live SSE event stream. // over the live SSE event stream.
func (c *Context) SyncSignals() { func (c *Context) SyncSignals() {
patchChan := c.getPatchChan()
c.mutex.RLock() c.mutex.RLock()
updatedSigs := c.prepareSignalsForPatch() updatedSigs := c.prepareSignalsForPatch()
defer c.mutex.RUnlock() defer c.mutex.RUnlock()
if len(updatedSigs) != 0 { if len(updatedSigs) != 0 {
outgoingSignals, _ := json.Marshal(updatedSigs) outgoingSignals, _ := json.Marshal(updatedSigs)
c.patchChan <- patch{patchTypeSignals, string(outgoingSignals)} patchChan <- patch{patchTypeSignals, string(outgoingSignals)}
} }
} }
func (c *Context) ExecScript(s string) { func (c *Context) ExecScript(s string) {
sse := c.getSSE() if s == "" {
if sse == nil {
c.app.logWarn(c, "script out of sync: no sse stream")
return return
} }
c.patchChan <- patch{patchTypeScript, s} patchChan := c.getPatchChan()
patchChan <- patch{patchTypeScript, s}
} }
func newContext(id string, route string, app *V) *Context { func newContext(id string, route string, app *V) *Context {

View File

@@ -111,11 +111,11 @@ func main() {
newRoom.Join(&UserAndSync[Chat, UserInfo]{user: &currentUser, sync: c}) newRoom.Join(&UserAndSync[Chat, UserInfo]{user: &currentUser, sync: c})
currentRoom = newRoom currentRoom = newRoom
roomNameString = newRoom.Name roomNameString = newRoom.Name
c.Sync()
} }
switchRoomAction := c.Action(func() { switchRoomAction := c.Action(func() {
switchRoom() switchRoom()
c.Sync()
}) })
switchRoom() switchRoom()

View File

@@ -13,6 +13,11 @@ import (
func main() { func main() {
v := via.New() v := via.New()
v.Config(via.Options{
LogLvl: via.LogLevelDebug,
DevMode: true,
})
v.AppendToHead( v.AppendToHead(
h.Link(h.Rel("stylesheet"), h.Href("https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css")), h.Link(h.Rel("stylesheet"), h.Href("https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css")),
h.Script(h.Src("https://unpkg.com/echarts@6.0.0/dist/echarts.min.js")), h.Script(h.Src("https://unpkg.com/echarts@6.0.0/dist/echarts.min.js")),
@@ -35,7 +40,7 @@ func chartCompFn(c *via.Context) {
data := make([]float64, 1000) data := make([]float64, 1000)
labels := make([]string, 1000) labels := make([]string, 1000)
isLive := false isLive := true
refreshRate := c.Signal(1) refreshRate := c.Signal(1)
tkr := time.NewTicker(1000 * time.Millisecond) tkr := time.NewTicker(1000 * time.Millisecond)
@@ -71,7 +76,7 @@ func chartCompFn(c *via.Context) {
return h.Div( return h.Div(
h.Div(h.ID("chart"), h.Style("width:100%;height:400px;"), h.Div(h.ID("chart"), h.Style("width:100%;height:400px;"),
h.Script(h.Raw(` h.Script(h.Raw(`
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
var myChart = echarts.init(document.getElementById('chart'), prefersDark.matches ? 'dark' : 'light'); var myChart = echarts.init(document.getElementById('chart'), prefersDark.matches ? 'dark' : 'light');
var option = { var option = {
backgroundColor: prefersDark.matches ? 'transparent' : '#ffffff', backgroundColor: prefersDark.matches ? 'transparent' : '#ffffff',

1
via.go
View File

@@ -153,6 +153,7 @@ func (v *V) Page(route string, initContextFn func(c *Context)) {
bottomBodyElements = append(bottomBodyElements, v.documentFootIncludes...) bottomBodyElements = append(bottomBodyElements, v.documentFootIncludes...)
if v.cfg.DevMode { if v.cfg.DevMode {
bottomBodyElements = append(bottomBodyElements, h.Script(h.Type("module"), h.Src("https://cdn.jsdelivr.net/gh/dataSPA/dataSPA-inspector@latest/dataspa-inspector.bundled.js"))) bottomBodyElements = append(bottomBodyElements, h.Script(h.Type("module"), h.Src("https://cdn.jsdelivr.net/gh/dataSPA/dataSPA-inspector@latest/dataspa-inspector.bundled.js")))
bottomBodyElements = append(bottomBodyElements, h.Raw("<dataspa-inspector/>"))
} }
view := h.HTML5(h.HTML5Props{ view := h.HTML5(h.HTML5Props{
Title: v.cfg.DocumentTitle, Title: v.cfg.DocumentTitle,