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