A community based topic aggregation platform built on atproto
1package timeline
2
3import (
4 "context"
5 "fmt"
6)
7
8type timelineService struct {
9 repo Repository
10}
11
12// NewTimelineService creates a new timeline service
13func NewTimelineService(repo Repository) Service {
14 return &timelineService{
15 repo: repo,
16 }
17}
18
19// GetTimeline retrieves posts from all communities the user subscribes to
20func (s *timelineService) GetTimeline(ctx context.Context, req GetTimelineRequest) (*TimelineResponse, error) {
21 // 1. Validate request
22 if err := s.validateRequest(&req); err != nil {
23 return nil, err
24 }
25
26 // 2. UserDID must be set (from auth middleware)
27 if req.UserDID == "" {
28 return nil, ErrUnauthorized
29 }
30
31 // 3. Fetch timeline from repository (hydrated posts from subscribed communities)
32 feedPosts, cursor, err := s.repo.GetTimeline(ctx, req)
33 if err != nil {
34 return nil, fmt.Errorf("failed to get timeline: %w", err)
35 }
36
37 // 4. Return timeline response
38 return &TimelineResponse{
39 Feed: feedPosts,
40 Cursor: cursor,
41 }, nil
42}
43
44// validateRequest validates the timeline request parameters
45func (s *timelineService) validateRequest(req *GetTimelineRequest) error {
46 // Validate and set defaults for sort
47 if req.Sort == "" {
48 req.Sort = "hot"
49 }
50 validSorts := map[string]bool{"hot": true, "top": true, "new": true}
51 if !validSorts[req.Sort] {
52 return NewValidationError("sort", "sort must be one of: hot, top, new")
53 }
54
55 // Validate and set defaults for limit
56 if req.Limit <= 0 {
57 req.Limit = 15
58 }
59 if req.Limit > 50 {
60 return NewValidationError("limit", "limit must not exceed 50")
61 }
62
63 // Validate and set defaults for timeframe (only used with top sort)
64 if req.Sort == "top" && req.Timeframe == "" {
65 req.Timeframe = "day"
66 }
67 validTimeframes := map[string]bool{
68 "hour": true, "day": true, "week": true,
69 "month": true, "year": true, "all": true,
70 }
71 if req.Timeframe != "" && !validTimeframes[req.Timeframe] {
72 return NewValidationError("timeframe", "timeframe must be one of: hour, day, week, month, year, all")
73 }
74
75 return nil
76}