this repo has no description
1package db 2 3import ( 4 "sort" 5 "time" 6 7 "github.com/bluesky-social/indigo/atproto/syntax" 8) 9 10type TimelineEvent struct { 11 *Repo 12 *Follow 13 *Star 14 15 EventAt time.Time 16 17 // optional: populate only if Repo is a fork 18 Source *Repo 19 20 // optional: populate only if event is Follow 21 *Profile 22 *FollowStats 23 *FollowStatus 24 25 // optional: populate only if event is Repo 26 IsStarred bool 27 StarCount int64 28} 29 30// TODO: this gathers heterogenous events from different sources and aggregates 31// them in code; if we did this entirely in sql, we could order and limit and paginate easily 32func MakeTimeline(e Execer, limit int, loggedInUserDid string) ([]TimelineEvent, error) { 33 var events []TimelineEvent 34 35 repos, err := getTimelineRepos(e, limit, loggedInUserDid) 36 if err != nil { 37 return nil, err 38 } 39 40 stars, err := getTimelineStars(e, limit) 41 if err != nil { 42 return nil, err 43 } 44 45 follows, err := getTimelineFollows(e, limit, loggedInUserDid) 46 if err != nil { 47 return nil, err 48 } 49 50 events = append(events, repos...) 51 events = append(events, stars...) 52 events = append(events, follows...) 53 54 sort.Slice(events, func(i, j int) bool { 55 return events[i].EventAt.After(events[j].EventAt) 56 }) 57 58 // Limit the slice to 100 events 59 if len(events) > limit { 60 events = events[:limit] 61 } 62 63 return events, nil 64} 65 66func getTimelineRepos(e Execer, limit int, loggedInUserDid string) ([]TimelineEvent, error) { 67 repos, err := GetRepos(e, limit) 68 if err != nil { 69 return nil, err 70 } 71 72 // fetch all source repos 73 var args []string 74 for _, r := range repos { 75 if r.Source != "" { 76 args = append(args, r.Source) 77 } 78 } 79 80 var origRepos []Repo 81 if args != nil { 82 origRepos, err = GetRepos(e, 0, FilterIn("at_uri", args)) 83 } 84 if err != nil { 85 return nil, err 86 } 87 88 uriToRepo := make(map[string]Repo) 89 for _, r := range origRepos { 90 uriToRepo[r.RepoAt().String()] = r 91 } 92 93 var starStatuses map[string]bool 94 if loggedInUserDid != "" { 95 var repoAts []syntax.ATURI 96 for _, r := range repos { 97 repoAts = append(repoAts, r.RepoAt()) 98 } 99 var err error 100 starStatuses, err = GetStarStatuses(e, loggedInUserDid, repoAts) 101 if err != nil { 102 return nil, err 103 } 104 } 105 106 var events []TimelineEvent 107 for _, r := range repos { 108 var source *Repo 109 if r.Source != "" { 110 if origRepo, ok := uriToRepo[r.Source]; ok { 111 source = &origRepo 112 } 113 } 114 115 var isStarred bool 116 if starStatuses != nil { 117 isStarred = starStatuses[r.RepoAt().String()] 118 } 119 120 var starCount int64 121 if r.RepoStats != nil { 122 starCount = int64(r.RepoStats.StarCount) 123 } 124 125 events = append(events, TimelineEvent{ 126 Repo: &r, 127 EventAt: r.Created, 128 Source: source, 129 IsStarred: isStarred, 130 StarCount: starCount, 131 }) 132 } 133 134 return events, nil 135} 136 137func getTimelineStars(e Execer, limit int) ([]TimelineEvent, error) { 138 stars, err := GetStars(e, limit) 139 if err != nil { 140 return nil, err 141 } 142 143 // filter star records without a repo 144 n := 0 145 for _, s := range stars { 146 if s.Repo != nil { 147 stars[n] = s 148 n++ 149 } 150 } 151 stars = stars[:n] 152 153 var events []TimelineEvent 154 for _, s := range stars { 155 events = append(events, TimelineEvent{ 156 Star: &s, 157 EventAt: s.Created, 158 }) 159 } 160 161 return events, nil 162} 163 164func getTimelineFollows(e Execer, limit int, loggedInUserDid string) ([]TimelineEvent, error) { 165 follows, err := GetFollows(e, limit) 166 if err != nil { 167 return nil, err 168 } 169 170 var subjects []string 171 for _, f := range follows { 172 subjects = append(subjects, f.SubjectDid) 173 } 174 175 if subjects == nil { 176 return nil, nil 177 } 178 179 profiles, err := GetProfiles(e, FilterIn("did", subjects)) 180 if err != nil { 181 return nil, err 182 } 183 184 followStatMap, err := GetFollowerFollowingCounts(e, subjects) 185 if err != nil { 186 return nil, err 187 } 188 189 var followStatuses map[string]FollowStatus 190 if loggedInUserDid != "" { 191 followStatuses, err = GetFollowStatuses(e, loggedInUserDid, subjects) 192 if err != nil { 193 return nil, err 194 } 195 } 196 197 var events []TimelineEvent 198 for _, f := range follows { 199 profile, _ := profiles[f.SubjectDid] 200 followStatMap, _ := followStatMap[f.SubjectDid] 201 202 followStatus := IsNotFollowing 203 if followStatuses != nil { 204 followStatus = followStatuses[f.SubjectDid] 205 } 206 207 events = append(events, TimelineEvent{ 208 Follow: &f, 209 Profile: profile, 210 FollowStats: &followStatMap, 211 FollowStatus: &followStatus, 212 EventAt: f.FollowedAt, 213 }) 214 } 215 216 return events, nil 217}