Monorepo for Tangled tangled.org
2

Configure Feed

Select the types of activity you want to include in your feed.

appview: fix comment bugs

- use optimistic `feed.comment` at-uri for reactions
- do not notify on ingester. It creates notification on record rewrite

Signed-off-by: Seongmin Lee <git@boltless.me>

author
Seongmin Lee
date (May 22, 2026, 7:59 PM +0900) commit e636df2c parent cc005e5b change-id vryssrpw
+30 -21
+2 -7
appview/ingester.go
··· 1621 1621 return fmt.Errorf("failed to validate comment: %w", err) 1622 1622 } 1623 1623 1624 - var mentions []syntax.DID 1625 1624 var references []syntax.ATURI 1626 1625 if comment.Body.Original != nil { 1627 - mentions, references = i.MentionsResolver.Resolve(ctx, *comment.Body.Original) 1626 + _, references = i.MentionsResolver.Resolve(ctx, *comment.Body.Original) 1628 1627 } 1629 1628 1630 1629 tx, err := i.Db.Begin() ··· 1633 1632 } 1634 1633 defer tx.Rollback() 1635 1634 1636 - updated, err := db.PutComment(tx, comment, references) 1635 + _, err = db.PutComment(tx, comment, references) 1637 1636 if err != nil { 1638 1637 return fmt.Errorf("failed to create comment: %w", err) 1639 1638 } 1640 1639 1641 1640 if err := tx.Commit(); err != nil { 1642 1641 return err 1643 - } 1644 - 1645 - if e.Commit.Operation == jmodels.CommitOperationCreate && updated { 1646 - i.Notifier.NewComment(ctx, comment, mentions) 1647 1642 } 1648 1643 1649 1644 case jmodels.CommitOperationDelete:
+1 -1
appview/issues/issues.go
··· 101 101 102 102 entities := []syntax.ATURI{issue.AtUri()} 103 103 for _, c := range issue.Comments { 104 - entities = append(entities, c.AtUri()) 104 + entities = append(entities, c.FeedCommentAtUri()) 105 105 } 106 106 reactions, err := db.ListReactionDisplayDataMap(rp.db, entities, 20) 107 107 if err != nil {
+5
appview/models/comment.go
··· 36 36 return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", c.Did, c.Collection, c.Rkey)) 37 37 } 38 38 39 + // force-return the feed.comment NSID 40 + func (c Comment) FeedCommentAtUri() syntax.ATURI { 41 + return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", c.Did, tangled.FeedCommentNSID, c.Rkey)) 42 + } 43 + 39 44 func (c Comment) StrongRef() comatproto.RepoStrongRef { 40 45 return comatproto.RepoStrongRef{ 41 46 Uri: c.AtUri().String(),
+1 -1
appview/pages/templates/fragments/comment/commentBody.html
··· 5 5 {{ template "repo/fragments/reactions" 6 6 (dict "Reactions" .Reactions 7 7 "UserReacted" .UserReacted 8 - "ThreadAt" .Comment.AtUri) }} 8 + "ThreadAt" .Comment.FeedCommentAtUri) }} 9 9 {{ else }} 10 10 <div class="prose dark:prose-invert italic text-gray-500 dark:text-gray-400">[deleted by author]</div> 11 11 {{ end }}
+4 -4
appview/pages/templates/fragments/comment/commentList.html
··· 14 14 {{ template "topLevelComment" 15 15 (dict 16 16 "LoggedInUser" $root.LoggedInUser 17 - "Reactions" (index (asReactionMapMap $root.Reactions) $item.Self.AtUri) 18 - "UserReacted" (index (asReactionStatusMapMap $root.UserReacted) $item.Self.AtUri) 17 + "Reactions" (index (asReactionMapMap $root.Reactions) $item.Self.FeedCommentAtUri) 18 + "UserReacted" (index (asReactionStatusMapMap $root.UserReacted) $item.Self.FeedCommentAtUri) 19 19 "VouchRelationship" (index $root.VouchRelationships $item.Self.Did) 20 20 "Comment" $item.Self) }} 21 21 ··· 25 25 {{ template "replyComment" 26 26 (dict 27 27 "LoggedInUser" $root.LoggedInUser 28 - "Reactions" (index (asReactionMapMap $root.Reactions) $reply.AtUri) 29 - "UserReacted" (index (asReactionStatusMapMap $root.UserReacted) $reply.AtUri) 28 + "Reactions" (index (asReactionMapMap $root.Reactions) $reply.FeedCommentAtUri) 29 + "UserReacted" (index (asReactionStatusMapMap $root.UserReacted) $reply.FeedCommentAtUri) 30 30 "VouchRelationship" (index $root.VouchRelationships $reply.Did) 31 31 "Comment" $reply) }} 32 32 </div>
+2 -2
appview/pages/templates/repo/pulls/pull.html
··· 598 598 {{/* template "submissionComment" . */}} 599 599 {{ template "comment" 600 600 (dict "LoggedInUser" $root.LoggedInUser 601 - "Reactions" (index (asReactionMapMap $root.Reactions) .AtUri) 602 - "UserReacted" (index (asReactionStatusMapMap $root.UserReacted) .AtUri) 601 + "Reactions" (index (asReactionMapMap $root.Reactions) .FeedCommentAtUri) 602 + "UserReacted" (index (asReactionStatusMapMap $root.UserReacted) .FeedCommentAtUri) 603 603 "VouchRelationship" (index $root.VouchRelationships .Did) 604 604 "Comment" .) }} 605 605 {{ end }}
+1 -1
appview/pulls/single.go
··· 163 163 entities := []syntax.ATURI{pull.AtUri()} 164 164 for _, s := range pull.Submissions { 165 165 for _, c := range s.Comments { 166 - entities = append(entities, c.AtUri()) 166 + entities = append(entities, c.FeedCommentAtUri()) 167 167 } 168 168 } 169 169 reactions, err := db.ListReactionDisplayDataMap(s.db, entities, 20)
+7 -5
appview/state/comment.go
··· 34 34 return 35 35 } 36 36 37 - reactions, err := db.GetReactionMap(s.db, 20, comment.AtUri()) 37 + reactions, err := db.GetReactionMap(s.db, 20, comment.FeedCommentAtUri()) 38 38 if err != nil { 39 39 l.Error("failed to get reactions", "err", err) 40 40 } 41 41 var userReactions map[models.ReactionKind]bool 42 42 if user != nil { 43 - userReactions, err = db.GetReactionStatusMap(s.db, syntax.DID(user.Did), comment.AtUri()) 43 + userReactions, err = db.GetReactionStatusMap(s.db, syntax.DID(user.Did), comment.FeedCommentAtUri()) 44 44 if err != nil { 45 45 l.Error("failed to get user reactions", "err", err) 46 46 } ··· 102 102 103 103 // TODO(boltless): normalize markdown body 104 104 normalizedBody := body 105 - _, references := s.mentionsResolver.Resolve(ctx, body) 105 + mentions, references := s.mentionsResolver.Resolve(ctx, body) 106 106 107 107 markdownBody := tangled.MarkupMarkdown{ 108 108 Text: normalizedBody, ··· 293 293 return 294 294 } 295 295 296 + s.notifier.NewComment(ctx, &comment, mentions) 297 + 296 298 // TODO: return comment or reply-comment fragment 297 299 // onattach, htmx-callback to focus on comment. 298 300 s.pages.HxRefresh(w) ··· 390 392 return 391 393 } 392 394 393 - reactions, err := db.GetReactionMap(s.db, 20, comment.AtUri()) 395 + reactions, err := db.GetReactionMap(s.db, 20, comment.FeedCommentAtUri()) 394 396 if err != nil { 395 397 l.Error("failed to get reactions", "err", err) 396 398 } 397 - userReactions, err := db.GetReactionStatusMap(s.db, syntax.DID(user.Did), comment.AtUri()) 399 + userReactions, err := db.GetReactionStatusMap(s.db, syntax.DID(user.Did), comment.FeedCommentAtUri()) 398 400 if err != nil { 399 401 l.Error("failed to get user reactions", "err", err) 400 402 }
+7
appview/state/reaction.go
··· 1 1 package state 2 2 3 3 import ( 4 + "fmt" 4 5 "net/http" 5 6 "time" 6 7 ··· 29 30 if err != nil { 30 31 l.Warn("invalid form", "subject", subject, "err", err) 31 32 return 33 + } 34 + 35 + // override collection NSID to new one 36 + switch subjectUri.Collection() { 37 + case tangled.RepoIssueCommentNSID, tangled.RepoPullCommentNSID: 38 + subjectUri = syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", subjectUri.Authority(), tangled.FeedCommentNSID, subjectUri.RecordKey())) 32 39 } 33 40 34 41 reactionKind, ok := models.ParseReactionKind(r.URL.Query().Get("kind"))