From 96a58ef0eac0b8b2594255cd3a061a3c511f96e4 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sun, 17 May 2026 12:26:13 +0200 Subject: [PATCH] fix(17): flush HTTP response before SSE publish to fix own-message alignment race The server was publishing to the SSE broker before writing the HTMX response, causing a race: if the SSE event (IsOwn=false, left-aligned) arrived at the browser before HTMX appended the response (IsOwn=true, right-aligned), the SSE path won and messageExists() then blocked the correct HTMX append. Fix: write and flush the HTMX response first, then publish to SSE. This ensures the sender's own message lands in the DOM right-aligned before the SSE event fires. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- backend/internal/web/handlers_discussion.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/backend/internal/web/handlers_discussion.go b/backend/internal/web/handlers_discussion.go index e843b5e..07ca9a9 100644 --- a/backend/internal/web/handlers_discussion.go +++ b/backend/internal/web/handlers_discussion.go @@ -125,6 +125,18 @@ func DiscussionMessageCreateHandler(deps DiscussionDeps) http.HandlerFunc { message := templates.DiscussionMessageFromRow(row, user.ID) data := templates.DiscussionTabData{Messages: []templates.DiscussionMessageView{message}} markDiscussionRead(r, deps.Queries, tablo, user.ID, data) + + // Send HTTP response before publishing to SSE so the HTMX append (IsOwn=true) + // lands in the DOM before the SSE event fires. This prevents the race where SSE + // wins, appends IsOwn=false, and messageExists() then blocks the HTMX append. + if r.Header.Get("HX-Request") == "true" { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + _ = templates.DiscussionMessageRow(message).Render(r.Context(), w) + if f, ok := w.(http.Flusher); ok { + f.Flush() + } + } + if deps.Realtime != nil { // SSE recipients are never the author — always render as IsOwn: false. sseMessage := message @@ -143,12 +155,9 @@ func DiscussionMessageCreateHandler(deps DiscussionDeps) http.HandlerFunc { } } - if r.Header.Get("HX-Request") == "true" { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - _ = templates.DiscussionMessageRow(message).Render(r.Context(), w) - return + if r.Header.Get("HX-Request") != "true" { + http.Redirect(w, r, templates.DiscussionURL(tablo.ID), http.StatusSeeOther) } - http.Redirect(w, r, templates.DiscussionURL(tablo.ID), http.StatusSeeOther) } }