A community based topic aggregation platform built on atproto
1package common
2
3import (
4 "Coves/internal/api/middleware"
5 "Coves/internal/core/posts"
6 "Coves/internal/core/votes"
7 "context"
8 "log"
9 "net/http"
10)
11
12// FeedPostProvider is implemented by any feed post wrapper that contains a PostView.
13// This allows the helper to work with different feed post types (discover, timeline, communityFeed).
14type FeedPostProvider interface {
15 GetPost() *posts.PostView
16}
17
18// PopulateViewerVoteState enriches feed posts with the authenticated user's vote state.
19// This is a no-op if voteService is nil or the request is unauthenticated.
20//
21// Parameters:
22// - ctx: Request context for PDS calls
23// - r: HTTP request (used to extract OAuth session)
24// - voteService: Vote service for cache lookup (may be nil)
25// - feedPosts: Posts to enrich with viewer state (must implement FeedPostProvider)
26//
27// The function logs but does not fail on errors - viewer state is optional enrichment.
28func PopulateViewerVoteState[T FeedPostProvider](
29 ctx context.Context,
30 r *http.Request,
31 voteService votes.Service,
32 feedPosts []T,
33) {
34 if voteService == nil {
35 return
36 }
37
38 session := middleware.GetOAuthSession(r)
39 if session == nil {
40 return
41 }
42
43 userDID := middleware.GetUserDID(r)
44
45 // Ensure vote cache is populated from PDS
46 if err := voteService.EnsureCachePopulated(ctx, session); err != nil {
47 log.Printf("Warning: failed to populate vote cache: %v", err)
48 return
49 }
50
51 // Collect post URIs to batch lookup
52 postURIs := make([]string, 0, len(feedPosts))
53 for _, feedPost := range feedPosts {
54 if post := feedPost.GetPost(); post != nil {
55 postURIs = append(postURIs, post.URI)
56 }
57 }
58
59 // Get viewer votes for all posts
60 viewerVotes := voteService.GetViewerVotesForSubjects(userDID, postURIs)
61
62 // Populate viewer state on each post
63 for _, feedPost := range feedPosts {
64 if post := feedPost.GetPost(); post != nil {
65 if vote, exists := viewerVotes[post.URI]; exists {
66 post.Viewer = &posts.ViewerState{
67 Vote: &vote.Direction,
68 VoteURI: &vote.URI,
69 }
70 }
71 }
72 }
73}