tylermmorton / torque

`torque` is a web framework for building hypermedia driven applications in Go
https://lbft.dev/
36 stars 3 forks source link

Demo: chat app #4

Closed gedw99 closed 3 months ago

gedw99 commented 10 months ago

Saw that SSE is getting turned on.

Is there a chat app example or in fact any examples yet ?

tylermmorton commented 10 months ago

I haven't had a chance to write any formal examples, although I do have a working chat implementation in a private repo.

The main change for SSE is a new interface type added to the route module system:

package torque

type EventSource interface {
  Subscribe(wr http.ResponseWriter, req *http.Request) error
}

Implementing this enables torque to handle incoming text/event-stream requests. The API really isn't much different from using plain net/http ... maybe we could figure out how to try and improve this.

There is an example of what an implementation might look like when sending json objects under eventstream.go

I also created an htmx sse extension adapter function under pkg/htmx/sse.go

It allows you to define a map of functions taking a string channel as a parameter. Whenever you'd like to send data to the client, just pass it through the channel. Since its htmx, it expects HTML fragments for swapping into the document. In the following example I'm using eventbus as the realtime message broker:

func (rm *RouteModule) Subscribe(wr http.ResponseWriter, req *http.Request) error {
    return htmx.SSE(wr, req, htmx.EventSourceMap{
        "message-created": func(sse chan string) {
            // subscribe to real time updates for messages
            var sub = make(chan *model.Message)
            defer close(sub)
            rm.ChatService.OnCreateMessage.Subscribe(sessionFromCtx(req.Context()).ID, sub)

            for {
                select {
                case <-req.Context().Done():
                    // the text/event-stream connection was closed
                    // unsubscribe from real time updates for messages
                    rm.ChatService.OnCreateMessage.Unsubscribe(sessionFromCtx(req.Context()).ID, sub)
                    return

                case msg, ok := <-sub:
                    if !ok {
                        return
                    }

                    // render a template fragment and send it to the client
                    sse <- fmt.Sprintf(`<div id='message'>%s</div>`, msg.Body)
                }
            }
        },
    })
}

Hopefully this helps a bit. I'm currently working on testmail as a requirement for another project. It uses torque and will have a realtime component associated with it. I'll be sure to update this thread when I have a more concrete example of SSE usage.