feat: add comments count

This commit is contained in:
Krispire Admin
2026-06-13 02:24:21 +05:30
parent 6ebaa36158
commit 05567ad438
4 changed files with 40 additions and 10 deletions
+1 -1
View File
@@ -704,7 +704,7 @@ func (h *ProfileHandler) hydrateFeed(r *http.Request, posts []model.Post) error
if err := h.mentions.HydrateCounts(r.Context(), ptrs); err != nil {
return err
}
return h.comments.HydratePinned(r.Context(), viewerID, ptrs)
return h.comments.Hydrate(r.Context(), viewerID, ptrs)
}
// followListResp is the payload for the followers / following list endpoints:
+4
View File
@@ -55,6 +55,10 @@ type Post struct {
// comment layer so the card footer can show it without a per-card fetch.
// Nil when nothing is pinned.
PinnedComment *Comment `json:"pinned_comment,omitempty"`
// CommentsCount is the total number of comments + replies on the post
// (drives the footer's comment count). Hydrated by the comment layer.
CommentsCount int `json:"comments_count"`
}
// Comment is one comment (or reply) on a post. Threading is one level deep:
+33 -7
View File
@@ -178,9 +178,10 @@ func (r *CommentRepo) ListReplies(ctx context.Context, parentID, viewerID string
return out, total, err
}
// HydratePinned attaches each post's pinned comment (if any) to
// Post.PinnedComment in a single query for the page.
func (r *CommentRepo) HydratePinned(ctx context.Context, viewerID string, posts []*model.Post) error {
// Hydrate fills each post's comment summary — its pinned comment (if any) and
// total comment count (top-level + replies) — in two batched queries for the
// page. Drives the card footer's pinned line and comment count.
func (r *CommentRepo) Hydrate(ctx context.Context, viewerID string, posts []*model.Post) error {
ids := make([]string, 0, len(posts))
for _, p := range posts {
if p != nil {
@@ -190,27 +191,52 @@ func (r *CommentRepo) HydratePinned(ctx context.Context, viewerID string, posts
if len(ids) == 0 {
return nil
}
// Pinned comment per post.
q := `SELECT ` + commentSelect + commentFrom + `
WHERE c.post_id = ANY($1) AND c.pinned_at IS NOT NULL`
rows, err := r.pg.Pool.Query(ctx, q, ids, nullUUIDArg(viewerID))
if err != nil {
return fmt.Errorf("hydrate pinned: %w", err)
}
list, err := scanComments(rows)
list, err := scanComments(rows) // closes rows
if err != nil {
return err
}
byPost := make(map[string]*model.Comment, len(list))
pinnedByPost := make(map[string]*model.Comment, len(list))
for i := range list {
byPost[list[i].PostID] = &list[i]
pinnedByPost[list[i].PostID] = &list[i]
}
// Total comment count per post (top-level + replies).
counts := make(map[string]int, len(ids))
crows, err := r.pg.Pool.Query(ctx,
`SELECT post_id, count(*) FROM post_comments WHERE post_id = ANY($1) GROUP BY post_id`,
ids)
if err != nil {
return fmt.Errorf("hydrate comment counts: %w", err)
}
defer crows.Close()
for crows.Next() {
var id string
var n int
if err := crows.Scan(&id, &n); err != nil {
return fmt.Errorf("scan comment count: %w", err)
}
counts[id] = n
}
if err := crows.Err(); err != nil {
return err
}
for _, p := range posts {
if p == nil {
continue
}
if c, ok := byPost[p.ID]; ok {
if c, ok := pinnedByPost[p.ID]; ok {
p.PinnedComment = c
}
p.CommentsCount = counts[p.ID]
}
return nil
}
+2 -2
View File
@@ -98,7 +98,7 @@ func (s *PostService) Get(ctx context.Context, id, viewerID string) (*model.Post
if err := s.mentions.HydrateCounts(ctx, []*model.Post{p}); err != nil {
return nil, err
}
if err := s.comments.HydratePinned(ctx, viewerID, []*model.Post{p}); err != nil {
if err := s.comments.Hydrate(ctx, viewerID, []*model.Post{p}); err != nil {
return nil, err
}
return p, nil
@@ -221,7 +221,7 @@ func (s *PostService) hydratePinnedSlice(ctx context.Context, viewerID string, p
for i := range posts {
ptrs[i] = &posts[i]
}
return s.comments.HydratePinned(ctx, viewerID, ptrs)
return s.comments.Hydrate(ctx, viewerID, ptrs)
}
// hydrateLikesSlice fills LikesCount + ViewerHasLiked on a value slice.