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