From d8e52f695b6de4228599983f9e54b9dbdfd93afd Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sun, 17 May 2026 10:22:23 +0200 Subject: [PATCH] fix(17): own messages right-aligned; restore .divide-y for SSE compatibility - Add IsOwn bool to DiscussionMessageView; set via user.ID comparison in DiscussionMessagesFromRows and DiscussionMessageFromRow - Thread currentUserID through loadDiscussionTabData and all call sites - discussion.templ: branch message-own vs message-other on message.IsOwn - Restore .divide-y wrapper inside #discussion-messages (discussion-sse.js depends on it to locate the message list before appending SSE events) Co-Authored-By: Claude Sonnet 4.6 (1M context) --- backend/internal/web/handlers_discussion.go | 10 +++--- backend/templates/discussion.templ | 36 ++++++++++++++------- backend/templates/discussion_forms.go | 7 ++-- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/backend/internal/web/handlers_discussion.go b/backend/internal/web/handlers_discussion.go index 7212a59..50c4bde 100644 --- a/backend/internal/web/handlers_discussion.go +++ b/backend/internal/web/handlers_discussion.go @@ -22,14 +22,14 @@ type DiscussionDeps struct { Realtime DiscussionRealtime } -func loadDiscussionTabData(w http.ResponseWriter, r *http.Request, q *sqlc.Queries, tablo sqlc.Tablo) (templates.DiscussionTabData, bool) { +func loadDiscussionTabData(w http.ResponseWriter, r *http.Request, q *sqlc.Queries, tablo sqlc.Tablo, currentUserID uuid.UUID) (templates.DiscussionTabData, bool) { rows, err := q.ListDiscussionMessagesByTablo(r.Context(), tablo.ID) if err != nil { slog.Default().Error("discussion: ListDiscussionMessagesByTablo failed", "tablo_id", tablo.ID, "err", err) http.Error(w, "internal server error", http.StatusInternalServerError) return templates.DiscussionTabData{}, false } - data := templates.DiscussionTabData{Messages: templates.DiscussionMessagesFromRows(rows)} + data := templates.DiscussionTabData{Messages: templates.DiscussionMessagesFromRows(rows, currentUserID)} return data, true } @@ -53,7 +53,7 @@ func TabloDiscussionTabHandler(deps DiscussionDeps) http.HandlerFunc { if !ok { return } - data, ok := loadDiscussionTabData(w, r, deps.Queries, tablo) + data, ok := loadDiscussionTabData(w, r, deps.Queries, tablo, user.ID) if !ok { return } @@ -118,9 +118,9 @@ func DiscussionMessageCreateHandler(deps DiscussionDeps) http.HandlerFunc { http.Error(w, "internal server error", http.StatusInternalServerError) return } - data := templates.DiscussionTabData{Messages: []templates.DiscussionMessageView{templates.DiscussionMessageFromRow(row)}} + message := templates.DiscussionMessageFromRow(row, row.AuthorUserID == user.ID) + data := templates.DiscussionTabData{Messages: []templates.DiscussionMessageView{message}} markDiscussionRead(r, deps.Queries, tablo, user.ID, data) - message := templates.DiscussionMessageFromRow(row) if deps.Realtime != nil { html, err := renderDiscussionMessageHTML(r, message) if err != nil { diff --git a/backend/templates/discussion.templ b/backend/templates/discussion.templ index a9f9cc0..69bfb8e 100644 --- a/backend/templates/discussion.templ +++ b/backend/templates/discussion.templ @@ -19,12 +19,14 @@ templ DiscussionTabFragment(tablo sqlc.Tablo, data DiscussionTabData, form Discu if len(data.Messages) == 0 { @DiscussionEmptyState() } else { - for i, message := range data.Messages { - if DiscussionShowDaySeparator(data.Messages, i) { - @DiscussionDaySeparator(message.CreatedAt) +
+ for i, message := range data.Messages { + if DiscussionShowDaySeparator(data.Messages, i) { + @DiscussionDaySeparator(message.CreatedAt) + } + @DiscussionMessageRow(message) } - @DiscussionMessageRow(message) - } +
} @DiscussionComposer(tablo, form, errs, csrfToken) @@ -45,13 +47,23 @@ templ DiscussionDaySeparator(createdAt time.Time) { } templ DiscussionMessageRow(message DiscussionMessageView) { -
-
- { message.AuthorEmail } - -
-
{ message.Body }
-
+ if message.IsOwn { +
+
+ { message.AuthorEmail } + +
+
{ message.Body }
+
+ } else { +
+
+ { message.AuthorEmail } + +
+
{ message.Body }
+
+ } } templ DiscussionComposer(tablo sqlc.Tablo, form DiscussionForm, errs DiscussionErrors, csrfToken string) { diff --git a/backend/templates/discussion_forms.go b/backend/templates/discussion_forms.go index 758c405..c90cb9b 100644 --- a/backend/templates/discussion_forms.go +++ b/backend/templates/discussion_forms.go @@ -25,6 +25,7 @@ type DiscussionMessageView struct { AuthorEmail string Body string CreatedAt time.Time + IsOwn bool } type DiscussionTabData struct { @@ -52,7 +53,7 @@ func DiscussionStreamURL(tabloID uuid.UUID) string { return "/tablos/" + tabloID.String() + "/discussion/stream" } -func DiscussionMessagesFromRows(rows []sqlc.ListDiscussionMessagesByTabloRow) []DiscussionMessageView { +func DiscussionMessagesFromRows(rows []sqlc.ListDiscussionMessagesByTabloRow, currentUserID uuid.UUID) []DiscussionMessageView { messages := make([]DiscussionMessageView, 0, len(rows)) for _, row := range rows { messages = append(messages, DiscussionMessageView{ @@ -60,17 +61,19 @@ func DiscussionMessagesFromRows(rows []sqlc.ListDiscussionMessagesByTabloRow) [] AuthorEmail: row.AuthorEmail, Body: row.Body, CreatedAt: row.CreatedAt.Time, + IsOwn: row.AuthorUserID == currentUserID, }) } return messages } -func DiscussionMessageFromRow(row sqlc.GetDiscussionMessageWithAuthorRow) DiscussionMessageView { +func DiscussionMessageFromRow(row sqlc.GetDiscussionMessageWithAuthorRow, isOwn bool) DiscussionMessageView { return DiscussionMessageView{ ID: row.ID, AuthorEmail: row.AuthorEmail, Body: row.Body, CreatedAt: row.CreatedAt.Time, + IsOwn: isOwn, } }