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