feat: add comments count
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user