A community based topic aggregation platform built on atproto
1package community
2
3import (
4 "encoding/json"
5 "net/http"
6 "strconv"
7
8 "Coves/internal/core/communities"
9)
10
11// ListHandler handles listing communities
12type ListHandler struct {
13 service communities.Service
14}
15
16// NewListHandler creates a new list handler
17func NewListHandler(service communities.Service) *ListHandler {
18 return &ListHandler{
19 service: service,
20 }
21}
22
23// HandleList lists communities with filters
24// GET /xrpc/social.coves.community.list?limit={n}&cursor={str}&sort={popular|active|new|alphabetical}&visibility={public|unlisted|private}
25func (h *ListHandler) HandleList(w http.ResponseWriter, r *http.Request) {
26 if r.Method != http.MethodGet {
27 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
28 return
29 }
30
31 // Parse query parameters
32 query := r.URL.Query()
33
34 // Parse limit (1-100, default 50)
35 limit := 50
36 if limitStr := query.Get("limit"); limitStr != "" {
37 if l, err := strconv.Atoi(limitStr); err == nil {
38 if l < 1 {
39 limit = 1
40 } else if l > 100 {
41 limit = 100
42 } else {
43 limit = l
44 }
45 }
46 }
47
48 // Parse cursor (offset-based for now)
49 offset := 0
50 if cursorStr := query.Get("cursor"); cursorStr != "" {
51 if o, err := strconv.Atoi(cursorStr); err == nil && o >= 0 {
52 offset = o
53 }
54 }
55
56 // Parse sort enum (default: popular)
57 sort := query.Get("sort")
58 if sort == "" {
59 sort = "popular"
60 }
61
62 // Validate sort value
63 validSorts := map[string]bool{
64 "popular": true,
65 "active": true,
66 "new": true,
67 "alphabetical": true,
68 }
69 if !validSorts[sort] {
70 http.Error(w, "Invalid sort value. Must be: popular, active, new, or alphabetical", http.StatusBadRequest)
71 return
72 }
73
74 // Validate visibility value if provided
75 visibility := query.Get("visibility")
76 if visibility != "" {
77 validVisibilities := map[string]bool{
78 "public": true,
79 "unlisted": true,
80 "private": true,
81 }
82 if !validVisibilities[visibility] {
83 http.Error(w, "Invalid visibility value. Must be: public, unlisted, or private", http.StatusBadRequest)
84 return
85 }
86 }
87
88 req := communities.ListCommunitiesRequest{
89 Limit: limit,
90 Offset: offset,
91 Sort: sort,
92 Visibility: visibility,
93 Category: query.Get("category"),
94 Language: query.Get("language"),
95 }
96
97 // Get communities from AppView DB
98 results, err := h.service.ListCommunities(r.Context(), req)
99 if err != nil {
100 handleServiceError(w, err)
101 return
102 }
103
104 // Build response
105 var cursor string
106 if len(results) == limit {
107 // More results available - return next cursor
108 cursor = strconv.Itoa(offset + len(results))
109 }
110 // If len(results) < limit, we've reached the end - cursor remains empty string
111
112 response := map[string]interface{}{
113 "communities": results,
114 "cursor": cursor,
115 }
116
117 w.Header().Set("Content-Type", "application/json")
118 w.WriteHeader(http.StatusOK)
119 if err := json.NewEncoder(w).Encode(response); err != nil {
120 // Log encoding errors but don't return error response (headers already sent)
121 // This follows Go's standard practice for HTTP handlers
122 _ = err
123 }
124}