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