From 499ef4b9f317fd21ddea1cf586f90486a20e8a0e Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Mon, 12 Jan 2026 07:25:44 +0000 Subject: [PATCH] appview: add reactions to comments Signed-off-by: Seongmin Lee --- appview/issues/issues.go | 3 +++ appview/pages/funcmap.go | 17 +++++++++++++++++ appview/pages/pages.go | 3 ++- appview/pulls/pulls.go | 5 +++++ appview/state/reaction.go | 4 +--- appview/strings/strings.go | 19 +++++++++++++++++++ appview/pages/templates/strings/string.html | 6 ++++-- appview/pages/templates/fragments/comment/commentBody.html | 4 ++++ appview/pages/templates/fragments/comment/commentList.html | 12 +++++++++--- appview/pages/templates/repo/fragments/reaction.html | 7 +++---- appview/pages/templates/repo/fragments/reactions.html | 13 ++++++------- appview/pages/templates/repo/issues/issue.html | 4 +++- appview/pages/templates/repo/pulls/pull.html | 6 +++++- 13 file(s) changed, 81 insertion(s)(+), 22 deletion(s)(-) diff --git a/appview/issues/issues.go b/appview/issues/issues.go --- a/appview/issues/issues.go +++ b/appview/issues/issues.go @@ -99,6 +99,9 @@ } entities := []syntax.ATURI{issue.AtUri()} + for _, c := range issue.Comments { + entities = append(entities, c.AtUri()) + } reactions, err := db.ListReactionDisplayDataMap(rp.db, entities, 20) if err != nil { l.Error("failed to get reactions", "err", err) diff --git a/appview/pages/funcmap.go b/appview/pages/funcmap.go --- a/appview/pages/funcmap.go +++ b/appview/pages/funcmap.go @@ -23,6 +23,7 @@ chromahtml "github.com/alecthomas/chroma/v2/formatters/html" "github.com/alecthomas/chroma/v2/lexers" "github.com/alecthomas/chroma/v2/styles" + "github.com/bluesky-social/indigo/atproto/syntax" "github.com/dustin/go-humanize" "github.com/go-enry/go-enry/v2" "github.com/yuin/goldmark" @@ -486,6 +487,22 @@ }, "isGenerated": func(path string) bool { return enry.IsGenerated(path, nil) + }, + // NOTE(boltless): I know... I hate doing this too + "asReactionMapMap": func(dict any) map[syntax.ATURI]map[models.ReactionKind]models.ReactionDisplayData { + if dict == nil { + return make(map[syntax.ATURI]map[models.ReactionKind]models.ReactionDisplayData) + } + m, _ := dict.(map[syntax.ATURI]map[models.ReactionKind]models.ReactionDisplayData) + return m + }, + "asReactionStatusMapMap": func(dict any) map[syntax.ATURI]map[models.ReactionKind]bool { + if dict == nil { + log.Println("returning empty map") + return make(map[syntax.ATURI]map[models.ReactionKind]bool) + } + m, _ := dict.(map[syntax.ATURI]map[models.ReactionKind]bool) + return m }, // constant values used to define a template "const": func() map[string]any { diff --git a/appview/pages/pages.go b/appview/pages/pages.go --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -1152,7 +1152,6 @@ } type ThreadReactionFragmentParams struct { - ThreadAt syntax.ATURI Kind models.ReactionKind Count int Users []string @@ -1599,6 +1598,8 @@ StarCount int Owner identity.Identity CommentList []models.CommentListItem + Reactions map[syntax.ATURI]map[models.ReactionKind]models.ReactionDisplayData + UserReacted map[syntax.ATURI]map[models.ReactionKind]bool } func (p *Pages) SingleString(w io.Writer, params SingleStringParams) error { diff --git a/appview/pulls/pulls.go b/appview/pulls/pulls.go --- a/appview/pulls/pulls.go +++ b/appview/pulls/pulls.go @@ -241,6 +241,11 @@ } entities := []syntax.ATURI{pull.AtUri()} + for _, s := range pull.Submissions { + for _, c := range s.Comments { + entities = append(entities, c.AtUri()) + } + } reactions, err := db.ListReactionDisplayDataMap(s.db, entities, 20) if err != nil { l.Error("failed to get pull reactions", "err", err) diff --git a/appview/state/reaction.go b/appview/state/reaction.go --- a/appview/state/reaction.go +++ b/appview/state/reaction.go @@ -19,7 +19,7 @@ l := s.logger.With("handler", "React") currentUser := s.oauth.GetMultiAccountUser(r) - subject := r.URL.Query().Get("subject") + subject := r.FormValue("subject-uri") if subject == "" { l.Warn("invalid form") return @@ -78,7 +78,6 @@ l.Info("created atproto record", "uri", resp.Uri) s.pages.ThreadReactionFragment(w, pages.ThreadReactionFragmentParams{ - ThreadAt: subjectUri, Kind: reactionKind, Count: reactionMap[reactionKind].Count, Users: reactionMap[reactionKind].Users, @@ -117,7 +116,6 @@ } s.pages.ThreadReactionFragment(w, pages.ThreadReactionFragmentParams{ - ThreadAt: subjectUri, Kind: reactionKind, Count: reactionMap[reactionKind].Count, Users: reactionMap[reactionKind].Users, diff --git a/appview/strings/strings.go b/appview/strings/strings.go --- a/appview/strings/strings.go +++ b/appview/strings/strings.go @@ -161,6 +161,23 @@ l.Error("failed to get comments", "err", err) } + entities := []syntax.ATURI{string.AtUri()} + for _, c := range comments { + entities = append(entities, c.AtUri()) + } + reactions, err := db.ListReactionDisplayDataMap(s.Db, entities, 20) + if err != nil { + l.Error("failed to get reactions", "err", err) + } + + var userReactions map[syntax.ATURI]map[models.ReactionKind]bool + if user != nil { + userReactions, err = db.ListReactionStatusMap(s.Db, entities, syntax.DID(user.Did)) + if err != nil { + l.Error("failed to get user reactions", "err", err) + } + } + s.Pages.SingleString(w, pages.SingleStringParams{ LoggedInUser: user, RenderToggle: renderToggle, @@ -171,6 +188,8 @@ StarCount: starCount, Owner: id, CommentList: models.NewCommentList(comments), + Reactions: reactions, + UserReacted: userReactions, }) } diff --git a/appview/pages/templates/strings/string.html b/appview/pages/templates/strings/string.html --- a/appview/pages/templates/strings/string.html +++ b/appview/pages/templates/strings/string.html @@ -98,8 +98,10 @@ {{ template "fragments/comment/commentList" (dict - "LoggedInUser" .LoggedInUser - "CommentList" .CommentList) + "LoggedInUser" $.LoggedInUser + "CommentList" $.CommentList + "Reactions" $.Reactions + "UserReacted" $.UserReacted) }} {{ template "newComment" . }} diff --git a/appview/pages/templates/fragments/comment/commentBody.html b/appview/pages/templates/fragments/comment/commentBody.html --- a/appview/pages/templates/fragments/comment/commentBody.html +++ b/appview/pages/templates/fragments/comment/commentBody.html @@ -2,6 +2,10 @@
{{ if not .Comment.Deleted }}
{{ .Comment.Body.Text | markdown }}
+ {{ template "repo/fragments/reactions" + (dict "Reactions" .Reactions + "UserReacted" .UserReacted + "ThreadAt" .Comment.AtUri) }} {{ else }}
[deleted by author]
{{ end }} diff --git a/appview/pages/templates/fragments/comment/commentList.html b/appview/pages/templates/fragments/comment/commentList.html --- a/appview/pages/templates/fragments/comment/commentList.html +++ b/appview/pages/templates/fragments/comment/commentList.html @@ -11,10 +11,14 @@ {{ $item := index . 1 }}
- {{ template "topLevelComment" + {{ + template "topLevelComment" (dict "LoggedInUser" $root.LoggedInUser - "Comment" $item.Self) }} + "Reactions" (index (asReactionMapMap $root.Reactions) $item.Self.AtUri) + "UserReacted" (index (asReactionStatusMapMap $root.UserReacted) $item.Self.AtUri) + "Comment" $item.Self) + }}
{{ range $index, $reply := $item.Replies }} @@ -23,6 +27,8 @@ template "replyComment" (dict "LoggedInUser" $root.LoggedInUser + "Reactions" (index (asReactionMapMap $root.Reactions) $reply.AtUri) + "UserReacted" (index (asReactionStatusMapMap $root.UserReacted) $reply.AtUri) "Comment" $reply) }}
@@ -58,7 +64,7 @@ {{ end }} {{ define "replyComment" }} -
+
{{ template "user/fragments/picLink" (list .Comment.Did.String "size-8 mr-1") }}
diff --git a/appview/pages/templates/repo/fragments/reaction.html b/appview/pages/templates/repo/fragments/reaction.html --- a/appview/pages/templates/repo/fragments/reaction.html +++ b/appview/pages/templates/repo/fragments/reaction.html @@ -1,6 +1,5 @@ {{ define "repo/fragments/reaction" }}