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