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