forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
at master 5.6 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, limitToUsersIsFollowing bool) ([]models.TimelineEvent, error) { 13 var events []models.TimelineEvent 14 15 var userIsFollowing []string 16 if limitToUsersIsFollowing { 17 following, err := GetFollowing(e, loggedInUserDid) 18 if err != nil { 19 return nil, err 20 } 21 22 userIsFollowing = make([]string, 0, len(following)) 23 for _, follow := range following { 24 userIsFollowing = append(userIsFollowing, follow.SubjectDid) 25 } 26 } 27 28 repos, err := getTimelineRepos(e, limit, loggedInUserDid, userIsFollowing) 29 if err != nil { 30 return nil, err 31 } 32 33 stars, err := getTimelineStars(e, limit, loggedInUserDid, userIsFollowing) 34 if err != nil { 35 return nil, err 36 } 37 38 follows, err := getTimelineFollows(e, limit, loggedInUserDid, userIsFollowing) 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 fetchStarStatuses(e Execer, loggedInUserDid string, repos []models.Repo) (map[string]bool, error) { 60 if loggedInUserDid == "" { 61 return nil, nil 62 } 63 64 var repoAts []syntax.ATURI 65 for _, r := range repos { 66 repoAts = append(repoAts, r.RepoAt()) 67 } 68 69 return GetStarStatuses(e, loggedInUserDid, repoAts) 70} 71 72func getRepoStarInfo(repo *models.Repo, starStatuses map[string]bool) (bool, int64) { 73 var isStarred bool 74 if starStatuses != nil { 75 isStarred = starStatuses[repo.RepoAt().String()] 76 } 77 78 var starCount int64 79 if repo.RepoStats != nil { 80 starCount = int64(repo.RepoStats.StarCount) 81 } 82 83 return isStarred, starCount 84} 85 86func getTimelineRepos(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 87 filters := make([]filter, 0) 88 if userIsFollowing != nil { 89 filters = append(filters, FilterIn("did", userIsFollowing)) 90 } 91 92 repos, err := GetRepos(e, limit, filters...) 93 if err != nil { 94 return nil, err 95 } 96 97 // fetch all source repos 98 var args []string 99 for _, r := range repos { 100 if r.Source != "" { 101 args = append(args, r.Source) 102 } 103 } 104 105 var origRepos []models.Repo 106 if args != nil { 107 origRepos, err = GetRepos(e, 0, FilterIn("at_uri", args)) 108 } 109 if err != nil { 110 return nil, err 111 } 112 113 uriToRepo := make(map[string]models.Repo) 114 for _, r := range origRepos { 115 uriToRepo[r.RepoAt().String()] = r 116 } 117 118 starStatuses, err := fetchStarStatuses(e, loggedInUserDid, repos) 119 if err != nil { 120 return nil, err 121 } 122 123 var events []models.TimelineEvent 124 for _, r := range repos { 125 var source *models.Repo 126 if r.Source != "" { 127 if origRepo, ok := uriToRepo[r.Source]; ok { 128 source = &origRepo 129 } 130 } 131 132 isStarred, starCount := getRepoStarInfo(&r, starStatuses) 133 134 events = append(events, models.TimelineEvent{ 135 Repo: &r, 136 EventAt: r.Created, 137 Source: source, 138 IsStarred: isStarred, 139 StarCount: starCount, 140 }) 141 } 142 143 return events, nil 144} 145 146func getTimelineStars(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 147 filters := make([]filter, 0) 148 if userIsFollowing != nil { 149 filters = append(filters, FilterIn("did", userIsFollowing)) 150 } 151 152 stars, err := GetRepoStars(e, limit, filters...) 153 if err != nil { 154 return nil, err 155 } 156 157 var repos []models.Repo 158 for _, s := range stars { 159 repos = append(repos, *s.Repo) 160 } 161 162 starStatuses, err := fetchStarStatuses(e, loggedInUserDid, repos) 163 if err != nil { 164 return nil, err 165 } 166 167 var events []models.TimelineEvent 168 for _, s := range stars { 169 isStarred, starCount := getRepoStarInfo(s.Repo, starStatuses) 170 171 events = append(events, models.TimelineEvent{ 172 RepoStar: &s, 173 EventAt: s.Created, 174 IsStarred: isStarred, 175 StarCount: starCount, 176 }) 177 } 178 179 return events, nil 180} 181 182func getTimelineFollows(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 183 filters := make([]filter, 0) 184 if userIsFollowing != nil { 185 filters = append(filters, FilterIn("user_did", userIsFollowing)) 186 } 187 188 follows, err := GetFollows(e, limit, filters...) 189 if err != nil { 190 return nil, err 191 } 192 193 var subjects []string 194 for _, f := range follows { 195 subjects = append(subjects, f.SubjectDid) 196 } 197 198 if subjects == nil { 199 return nil, nil 200 } 201 202 profiles, err := GetProfiles(e, FilterIn("did", subjects)) 203 if err != nil { 204 return nil, err 205 } 206 207 followStatMap, err := GetFollowerFollowingCounts(e, subjects) 208 if err != nil { 209 return nil, err 210 } 211 212 var followStatuses map[string]models.FollowStatus 213 if loggedInUserDid != "" { 214 followStatuses, err = GetFollowStatuses(e, loggedInUserDid, subjects) 215 if err != nil { 216 return nil, err 217 } 218 } 219 220 var events []models.TimelineEvent 221 for _, f := range follows { 222 profile, _ := profiles[f.SubjectDid] 223 followStatMap, _ := followStatMap[f.SubjectDid] 224 225 followStatus := models.IsNotFollowing 226 if followStatuses != nil { 227 followStatus = followStatuses[f.SubjectDid] 228 } 229 230 events = append(events, models.TimelineEvent{ 231 Follow: &f, 232 Profile: profile, 233 FollowStats: &followStatMap, 234 FollowStatus: &followStatus, 235 EventAt: f.FollowedAt, 236 }) 237 } 238 239 return events, nil 240}