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) <noreply@anthropic.com>
This commit is contained in:
Arthur Belleville 2026-05-17 10:22:23 +02:00
parent 56194cfdb5
commit d8e52f695b
No known key found for this signature in database
3 changed files with 34 additions and 19 deletions

View file

@ -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 {

View file

@ -19,12 +19,14 @@ templ DiscussionTabFragment(tablo sqlc.Tablo, data DiscussionTabData, form Discu
if len(data.Messages) == 0 {
@DiscussionEmptyState()
} else {
<div class="divide-y divide-slate-100">
for i, message := range data.Messages {
if DiscussionShowDaySeparator(data.Messages, i) {
@DiscussionDaySeparator(message.CreatedAt)
}
@DiscussionMessageRow(message)
}
</div>
}
</div>
@DiscussionComposer(tablo, form, errs, csrfToken)
@ -45,6 +47,15 @@ templ DiscussionDaySeparator(createdAt time.Time) {
}
templ DiscussionMessageRow(message DiscussionMessageView) {
if message.IsOwn {
<article id={ "discussion-message-" + message.ID.String() } data-message-id={ message.ID.String() } class="message-row message-own">
<div class="message-meta">
<span class="message-author">{ message.AuthorEmail }</span>
<time class="message-timestamp" datetime={ message.CreatedAt.Format(time.RFC3339) }>{ DiscussionTimestampLabel(message.CreatedAt) }</time>
</div>
<div class="message-bubble">{ message.Body }</div>
</article>
} else {
<article id={ "discussion-message-" + message.ID.String() } data-message-id={ message.ID.String() } class="message-row message-other">
<div class="message-meta">
<span class="message-author">{ message.AuthorEmail }</span>
@ -53,6 +64,7 @@ templ DiscussionMessageRow(message DiscussionMessageView) {
<div class="message-bubble">{ message.Body }</div>
</article>
}
}
templ DiscussionComposer(tablo sqlc.Tablo, form DiscussionForm, errs DiscussionErrors, csrfToken string) {
<form

View file

@ -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,
}
}