From 681c094b0cafc5e2d7d17ef4e4399ab73ee807b0 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sun, 17 May 2026 12:47:21 +0200 Subject: [PATCH] fix(17): skip own-user SSE messages in JS to eliminate left-then-right flash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The server-side flush didn't eliminate the race because browser HTMX and SSE event processing are inherently async. Real fix: embed data-current-user-id on #discussion-tab (from DiscussionTabData. CurrentUserID). The SSE handler now skips any event whose authorUserId matches the current user — those messages are always delivered via HTMX (IsOwn=true, right-aligned). SSE only appends messages from other users. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- backend/internal/web/handlers_discussion.go | 5 ++++- backend/static/discussion-sse.js | 6 ++++++ backend/templates/discussion.templ | 2 +- backend/templates/discussion_forms.go | 3 ++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/backend/internal/web/handlers_discussion.go b/backend/internal/web/handlers_discussion.go index 07ca9a9..062a3ec 100644 --- a/backend/internal/web/handlers_discussion.go +++ b/backend/internal/web/handlers_discussion.go @@ -29,7 +29,10 @@ func loadDiscussionTabData(w http.ResponseWriter, r *http.Request, q *sqlc.Queri http.Error(w, "internal server error", http.StatusInternalServerError) return templates.DiscussionTabData{}, false } - data := templates.DiscussionTabData{Messages: templates.DiscussionMessagesFromRows(rows, currentUserID)} + data := templates.DiscussionTabData{ + Messages: templates.DiscussionMessagesFromRows(rows, currentUserID), + CurrentUserID: currentUserID, + } return data, true } diff --git a/backend/static/discussion-sse.js b/backend/static/discussion-sse.js index 6c8059b..7834be2 100644 --- a/backend/static/discussion-sse.js +++ b/backend/static/discussion-sse.js @@ -42,6 +42,12 @@ if (!event || !event.messageId || !event.messageHtml || messageExists(event.messageId)) { return; } + // Skip messages authored by the current user — HTMX already appended them + // with IsOwn=true. Let SSE handle only messages from other users. + var currentUserId = container.dataset.currentUserId; + if (currentUserId && event.authorUserId === currentUserId) { + return; + } var list = ensureMessageList(container); if (!list) return; diff --git a/backend/templates/discussion.templ b/backend/templates/discussion.templ index de981c5..d830f19 100644 --- a/backend/templates/discussion.templ +++ b/backend/templates/discussion.templ @@ -8,7 +8,7 @@ import ( ) templ DiscussionTabFragment(tablo sqlc.Tablo, data DiscussionTabData, form DiscussionForm, errs DiscussionErrors, csrfToken string) { -
+

Discussion

diff --git a/backend/templates/discussion_forms.go b/backend/templates/discussion_forms.go index 7a0ba0a..2ae02d0 100644 --- a/backend/templates/discussion_forms.go +++ b/backend/templates/discussion_forms.go @@ -29,7 +29,8 @@ type DiscussionMessageView struct { } type DiscussionTabData struct { - Messages []DiscussionMessageView + Messages []DiscussionMessageView + CurrentUserID uuid.UUID } type TabloCardView struct {