forked from tangled.org/core
Monorepo for Tangled — https://tangled.org

appview/models: move db.Follow into models

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li 451ebd4a fde77b9f

verified
Changed files
+98 -85
appview
+26 -57
appview/db/follow.go
··· 5 "log" 6 "strings" 7 "time" 8 ) 9 10 - type Follow struct { 11 - UserDid string 12 - SubjectDid string 13 - FollowedAt time.Time 14 - Rkey string 15 - } 16 - 17 - func AddFollow(e Execer, follow *Follow) error { 18 query := `insert or ignore into follows (user_did, subject_did, rkey) values (?, ?, ?)` 19 _, err := e.Exec(query, follow.UserDid, follow.SubjectDid, follow.Rkey) 20 return err 21 } 22 23 // Get a follow record 24 - func GetFollow(e Execer, userDid, subjectDid string) (*Follow, error) { 25 query := `select user_did, subject_did, followed_at, rkey from follows where user_did = ? and subject_did = ?` 26 row := e.QueryRow(query, userDid, subjectDid) 27 28 - var follow Follow 29 var followedAt string 30 err := row.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey) 31 if err != nil { ··· 55 return err 56 } 57 58 - type FollowStats struct { 59 - Followers int64 60 - Following int64 61 - } 62 - 63 - func GetFollowerFollowingCount(e Execer, did string) (FollowStats, error) { 64 var followers, following int64 65 err := e.QueryRow( 66 `SELECT ··· 68 COUNT(CASE WHEN user_did = ? THEN 1 END) AS following 69 FROM follows;`, did, did).Scan(&followers, &following) 70 if err != nil { 71 - return FollowStats{}, err 72 } 73 - return FollowStats{ 74 Followers: followers, 75 Following: following, 76 }, nil 77 } 78 79 - func GetFollowerFollowingCounts(e Execer, dids []string) (map[string]FollowStats, error) { 80 if len(dids) == 0 { 81 return nil, nil 82 } ··· 112 ) g on f.did = g.did`, 113 placeholderStr, placeholderStr) 114 115 - result := make(map[string]FollowStats) 116 117 rows, err := e.Query(query, args...) 118 if err != nil { ··· 126 if err := rows.Scan(&did, &followers, &following); err != nil { 127 return nil, err 128 } 129 - result[did] = FollowStats{ 130 Followers: followers, 131 Following: following, 132 } ··· 134 135 for _, did := range dids { 136 if _, exists := result[did]; !exists { 137 - result[did] = FollowStats{ 138 Followers: 0, 139 Following: 0, 140 } ··· 144 return result, nil 145 } 146 147 - func GetFollows(e Execer, limit int, filters ...filter) ([]Follow, error) { 148 - var follows []Follow 149 150 var conditions []string 151 var args []any ··· 177 return nil, err 178 } 179 for rows.Next() { 180 - var follow Follow 181 var followedAt string 182 err := rows.Scan( 183 &follow.UserDid, ··· 200 return follows, nil 201 } 202 203 - func GetFollowers(e Execer, did string) ([]Follow, error) { 204 return GetFollows(e, 0, FilterEq("subject_did", did)) 205 } 206 207 - func GetFollowing(e Execer, did string) ([]Follow, error) { 208 return GetFollows(e, 0, FilterEq("user_did", did)) 209 } 210 211 - type FollowStatus int 212 - 213 - const ( 214 - IsNotFollowing FollowStatus = iota 215 - IsFollowing 216 - IsSelf 217 - ) 218 - 219 - func (s FollowStatus) String() string { 220 - switch s { 221 - case IsNotFollowing: 222 - return "IsNotFollowing" 223 - case IsFollowing: 224 - return "IsFollowing" 225 - case IsSelf: 226 - return "IsSelf" 227 - default: 228 - return "IsNotFollowing" 229 - } 230 - } 231 - 232 - func getFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]FollowStatus, error) { 233 if len(subjectDids) == 0 || userDid == "" { 234 - return make(map[string]FollowStatus), nil 235 } 236 237 - result := make(map[string]FollowStatus) 238 239 for _, subjectDid := range subjectDids { 240 if userDid == subjectDid { 241 - result[subjectDid] = IsSelf 242 } else { 243 - result[subjectDid] = IsNotFollowing 244 } 245 } 246 ··· 281 if err := rows.Scan(&subjectDid); err != nil { 282 return nil, err 283 } 284 - result[subjectDid] = IsFollowing 285 } 286 287 return result, nil 288 } 289 290 - func GetFollowStatus(e Execer, userDid, subjectDid string) FollowStatus { 291 statuses, err := getFollowStatuses(e, userDid, []string{subjectDid}) 292 if err != nil { 293 - return IsNotFollowing 294 } 295 return statuses[subjectDid] 296 } 297 298 - func GetFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]FollowStatus, error) { 299 return getFollowStatuses(e, userDid, subjectDids) 300 }
··· 5 "log" 6 "strings" 7 "time" 8 + 9 + "tangled.org/core/appview/models" 10 ) 11 12 + func AddFollow(e Execer, follow *models.Follow) error { 13 query := `insert or ignore into follows (user_did, subject_did, rkey) values (?, ?, ?)` 14 _, err := e.Exec(query, follow.UserDid, follow.SubjectDid, follow.Rkey) 15 return err 16 } 17 18 // Get a follow record 19 + func GetFollow(e Execer, userDid, subjectDid string) (*models.Follow, error) { 20 query := `select user_did, subject_did, followed_at, rkey from follows where user_did = ? and subject_did = ?` 21 row := e.QueryRow(query, userDid, subjectDid) 22 23 + var follow models.Follow 24 var followedAt string 25 err := row.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey) 26 if err != nil { ··· 50 return err 51 } 52 53 + func GetFollowerFollowingCount(e Execer, did string) (models.FollowStats, error) { 54 var followers, following int64 55 err := e.QueryRow( 56 `SELECT ··· 58 COUNT(CASE WHEN user_did = ? THEN 1 END) AS following 59 FROM follows;`, did, did).Scan(&followers, &following) 60 if err != nil { 61 + return models.FollowStats{}, err 62 } 63 + return models.FollowStats{ 64 Followers: followers, 65 Following: following, 66 }, nil 67 } 68 69 + func GetFollowerFollowingCounts(e Execer, dids []string) (map[string]models.FollowStats, error) { 70 if len(dids) == 0 { 71 return nil, nil 72 } ··· 102 ) g on f.did = g.did`, 103 placeholderStr, placeholderStr) 104 105 + result := make(map[string]models.FollowStats) 106 107 rows, err := e.Query(query, args...) 108 if err != nil { ··· 116 if err := rows.Scan(&did, &followers, &following); err != nil { 117 return nil, err 118 } 119 + result[did] = models.FollowStats{ 120 Followers: followers, 121 Following: following, 122 } ··· 124 125 for _, did := range dids { 126 if _, exists := result[did]; !exists { 127 + result[did] = models.FollowStats{ 128 Followers: 0, 129 Following: 0, 130 } ··· 134 return result, nil 135 } 136 137 + func GetFollows(e Execer, limit int, filters ...filter) ([]models.Follow, error) { 138 + var follows []models.Follow 139 140 var conditions []string 141 var args []any ··· 167 return nil, err 168 } 169 for rows.Next() { 170 + var follow models.Follow 171 var followedAt string 172 err := rows.Scan( 173 &follow.UserDid, ··· 190 return follows, nil 191 } 192 193 + func GetFollowers(e Execer, did string) ([]models.Follow, error) { 194 return GetFollows(e, 0, FilterEq("subject_did", did)) 195 } 196 197 + func GetFollowing(e Execer, did string) ([]models.Follow, error) { 198 return GetFollows(e, 0, FilterEq("user_did", did)) 199 } 200 201 + func getFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]models.FollowStatus, error) { 202 if len(subjectDids) == 0 || userDid == "" { 203 + return make(map[string]models.FollowStatus), nil 204 } 205 206 + result := make(map[string]models.FollowStatus) 207 208 for _, subjectDid := range subjectDids { 209 if userDid == subjectDid { 210 + result[subjectDid] = models.IsSelf 211 } else { 212 + result[subjectDid] = models.IsNotFollowing 213 } 214 } 215 ··· 250 if err := rows.Scan(&subjectDid); err != nil { 251 return nil, err 252 } 253 + result[subjectDid] = models.IsFollowing 254 } 255 256 return result, nil 257 } 258 259 + func GetFollowStatus(e Execer, userDid, subjectDid string) models.FollowStatus { 260 statuses, err := getFollowStatuses(e, userDid, []string{subjectDid}) 261 if err != nil { 262 + return models.IsNotFollowing 263 } 264 return statuses[subjectDid] 265 } 266 267 + func GetFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]models.FollowStatus, error) { 268 return getFollowStatuses(e, userDid, subjectDids) 269 }
+6 -5
appview/db/timeline.go
··· 5 "time" 6 7 "github.com/bluesky-social/indigo/atproto/syntax" 8 ) 9 10 type TimelineEvent struct { 11 *Repo 12 - *Follow 13 *Star 14 15 EventAt time.Time ··· 19 20 // optional: populate only if event is Follow 21 *Profile 22 - *FollowStats 23 - *FollowStatus 24 25 // optional: populate only if event is Repo 26 IsStarred bool ··· 211 return nil, err 212 } 213 214 - var followStatuses map[string]FollowStatus 215 if loggedInUserDid != "" { 216 followStatuses, err = GetFollowStatuses(e, loggedInUserDid, subjects) 217 if err != nil { ··· 224 profile, _ := profiles[f.SubjectDid] 225 followStatMap, _ := followStatMap[f.SubjectDid] 226 227 - followStatus := IsNotFollowing 228 if followStatuses != nil { 229 followStatus = followStatuses[f.SubjectDid] 230 }
··· 5 "time" 6 7 "github.com/bluesky-social/indigo/atproto/syntax" 8 + "tangled.org/core/appview/models" 9 ) 10 11 type TimelineEvent struct { 12 *Repo 13 + *models.Follow 14 *Star 15 16 EventAt time.Time ··· 20 21 // optional: populate only if event is Follow 22 *Profile 23 + *models.FollowStats 24 + *models.FollowStatus 25 26 // optional: populate only if event is Repo 27 IsStarred bool ··· 212 return nil, err 213 } 214 215 + var followStatuses map[string]models.FollowStatus 216 if loggedInUserDid != "" { 217 followStatuses, err = GetFollowStatuses(e, loggedInUserDid, subjects) 218 if err != nil { ··· 225 profile, _ := profiles[f.SubjectDid] 226 followStatMap, _ := followStatMap[f.SubjectDid] 227 228 + followStatus := models.IsNotFollowing 229 if followStatuses != nil { 230 followStatus = followStatuses[f.SubjectDid] 231 }
+1 -1
appview/ingester.go
··· 149 return err 150 } 151 152 - err = db.AddFollow(i.Db, &db.Follow{ 153 UserDid: did, 154 SubjectDid: record.Subject, 155 Rkey: e.Commit.RKey,
··· 149 return err 150 } 151 152 + err = db.AddFollow(i.Db, &models.Follow{ 153 UserDid: did, 154 SubjectDid: record.Subject, 155 Rkey: e.Commit.RKey,
+38
appview/models/follow.go
···
··· 1 + package models 2 + 3 + import ( 4 + "time" 5 + ) 6 + 7 + type Follow struct { 8 + UserDid string 9 + SubjectDid string 10 + FollowedAt time.Time 11 + Rkey string 12 + } 13 + 14 + type FollowStats struct { 15 + Followers int64 16 + Following int64 17 + } 18 + 19 + type FollowStatus int 20 + 21 + const ( 22 + IsNotFollowing FollowStatus = iota 23 + IsFollowing 24 + IsSelf 25 + ) 26 + 27 + func (s FollowStatus) String() string { 28 + switch s { 29 + case IsNotFollowing: 30 + return "IsNotFollowing" 31 + case IsFollowing: 32 + return "IsFollowing" 33 + case IsSelf: 34 + return "IsSelf" 35 + default: 36 + return "IsNotFollowing" 37 + } 38 + }
+3 -2
appview/notify/merged_notifier.go
··· 4 "context" 5 6 "tangled.org/core/appview/db" 7 ) 8 9 type mergedNotifier struct { ··· 39 } 40 } 41 42 - func (m *mergedNotifier) NewFollow(ctx context.Context, follow *db.Follow) { 43 for _, notifier := range m.notifiers { 44 notifier.NewFollow(ctx, follow) 45 } 46 } 47 - func (m *mergedNotifier) DeleteFollow(ctx context.Context, follow *db.Follow) { 48 for _, notifier := range m.notifiers { 49 notifier.DeleteFollow(ctx, follow) 50 }
··· 4 "context" 5 6 "tangled.org/core/appview/db" 7 + "tangled.org/core/appview/models" 8 ) 9 10 type mergedNotifier struct { ··· 40 } 41 } 42 43 + func (m *mergedNotifier) NewFollow(ctx context.Context, follow *models.Follow) { 44 for _, notifier := range m.notifiers { 45 notifier.NewFollow(ctx, follow) 46 } 47 } 48 + func (m *mergedNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) { 49 for _, notifier := range m.notifiers { 50 notifier.DeleteFollow(ctx, follow) 51 }
+5 -4
appview/notify/notifier.go
··· 4 "context" 5 6 "tangled.org/core/appview/db" 7 ) 8 9 type Notifier interface { ··· 14 15 NewIssue(ctx context.Context, issue *db.Issue) 16 17 - NewFollow(ctx context.Context, follow *db.Follow) 18 - DeleteFollow(ctx context.Context, follow *db.Follow) 19 20 NewPull(ctx context.Context, pull *db.Pull) 21 NewPullComment(ctx context.Context, comment *db.PullComment) ··· 39 40 func (m *BaseNotifier) NewIssue(ctx context.Context, issue *db.Issue) {} 41 42 - func (m *BaseNotifier) NewFollow(ctx context.Context, follow *db.Follow) {} 43 - func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *db.Follow) {} 44 45 func (m *BaseNotifier) NewPull(ctx context.Context, pull *db.Pull) {} 46 func (m *BaseNotifier) NewPullComment(ctx context.Context, comment *db.PullComment) {}
··· 4 "context" 5 6 "tangled.org/core/appview/db" 7 + "tangled.org/core/appview/models" 8 ) 9 10 type Notifier interface { ··· 15 16 NewIssue(ctx context.Context, issue *db.Issue) 17 18 + NewFollow(ctx context.Context, follow *models.Follow) 19 + DeleteFollow(ctx context.Context, follow *models.Follow) 20 21 NewPull(ctx context.Context, pull *db.Pull) 22 NewPullComment(ctx context.Context, comment *db.PullComment) ··· 40 41 func (m *BaseNotifier) NewIssue(ctx context.Context, issue *db.Issue) {} 42 43 + func (m *BaseNotifier) NewFollow(ctx context.Context, follow *models.Follow) {} 44 + func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {} 45 46 func (m *BaseNotifier) NewPull(ctx context.Context, pull *db.Pull) {} 47 func (m *BaseNotifier) NewPullComment(ctx context.Context, comment *db.PullComment) {}
+3 -3
appview/pages/pages.go
··· 411 type ProfileCard struct { 412 UserDid string 413 UserHandle string 414 - FollowStatus db.FollowStatus 415 Punchcard *db.Punchcard 416 Profile *db.Profile 417 Stats ProfileStats ··· 489 490 type FollowCard struct { 491 UserDid string 492 - FollowStatus db.FollowStatus 493 FollowersCount int64 494 FollowingCount int64 495 Profile *db.Profile ··· 521 522 type FollowFragmentParams struct { 523 UserDid string 524 - FollowStatus db.FollowStatus 525 } 526 527 func (p *Pages) FollowFragment(w io.Writer, params FollowFragmentParams) error {
··· 411 type ProfileCard struct { 412 UserDid string 413 UserHandle string 414 + FollowStatus models.FollowStatus 415 Punchcard *db.Punchcard 416 Profile *db.Profile 417 Stats ProfileStats ··· 489 490 type FollowCard struct { 491 UserDid string 492 + FollowStatus models.FollowStatus 493 FollowersCount int64 494 FollowingCount int64 495 Profile *db.Profile ··· 521 522 type FollowFragmentParams struct { 523 UserDid string 524 + FollowStatus models.FollowStatus 525 } 526 527 func (p *Pages) FollowFragment(w io.Writer, params FollowFragmentParams) error {
+3 -2
appview/posthog/notifier.go
··· 6 7 "github.com/posthog/posthog-go" 8 "tangled.org/core/appview/db" 9 "tangled.org/core/appview/notify" 10 ) 11 ··· 98 } 99 } 100 101 - func (n *posthogNotifier) NewFollow(ctx context.Context, follow *db.Follow) { 102 err := n.client.Enqueue(posthog.Capture{ 103 DistinctId: follow.UserDid, 104 Event: "follow", ··· 109 } 110 } 111 112 - func (n *posthogNotifier) DeleteFollow(ctx context.Context, follow *db.Follow) { 113 err := n.client.Enqueue(posthog.Capture{ 114 DistinctId: follow.UserDid, 115 Event: "unfollow",
··· 6 7 "github.com/posthog/posthog-go" 8 "tangled.org/core/appview/db" 9 + "tangled.org/core/appview/models" 10 "tangled.org/core/appview/notify" 11 ) 12 ··· 99 } 100 } 101 102 + func (n *posthogNotifier) NewFollow(ctx context.Context, follow *models.Follow) { 103 err := n.client.Enqueue(posthog.Capture{ 104 DistinctId: follow.UserDid, 105 Event: "follow", ··· 110 } 111 } 112 113 + func (n *posthogNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) { 114 err := n.client.Enqueue(posthog.Capture{ 115 DistinctId: follow.UserDid, 116 Event: "unfollow",
+4 -3
appview/state/follow.go
··· 9 lexutil "github.com/bluesky-social/indigo/lex/util" 10 "tangled.org/core/api/tangled" 11 "tangled.org/core/appview/db" 12 "tangled.org/core/appview/pages" 13 "tangled.org/core/tid" 14 ) ··· 59 60 log.Println("created atproto record: ", resp.Uri) 61 62 - follow := &db.Follow{ 63 UserDid: currentUser.Did, 64 SubjectDid: subjectIdent.DID.String(), 65 Rkey: rkey, ··· 75 76 s.pages.FollowFragment(w, pages.FollowFragmentParams{ 77 UserDid: subjectIdent.DID.String(), 78 - FollowStatus: db.IsFollowing, 79 }) 80 81 return ··· 106 107 s.pages.FollowFragment(w, pages.FollowFragmentParams{ 108 UserDid: subjectIdent.DID.String(), 109 - FollowStatus: db.IsNotFollowing, 110 }) 111 112 s.notifier.DeleteFollow(r.Context(), follow)
··· 9 lexutil "github.com/bluesky-social/indigo/lex/util" 10 "tangled.org/core/api/tangled" 11 "tangled.org/core/appview/db" 12 + "tangled.org/core/appview/models" 13 "tangled.org/core/appview/pages" 14 "tangled.org/core/tid" 15 ) ··· 60 61 log.Println("created atproto record: ", resp.Uri) 62 63 + follow := &models.Follow{ 64 UserDid: currentUser.Did, 65 SubjectDid: subjectIdent.DID.String(), 66 Rkey: rkey, ··· 76 77 s.pages.FollowFragment(w, pages.FollowFragmentParams{ 78 UserDid: subjectIdent.DID.String(), 79 + FollowStatus: models.IsFollowing, 80 }) 81 82 return ··· 107 108 s.pages.FollowFragment(w, pages.FollowFragmentParams{ 109 UserDid: subjectIdent.DID.String(), 110 + FollowStatus: models.IsNotFollowing, 111 }) 112 113 s.notifier.DeleteFollow(r.Context(), follow)
+9 -8
appview/state/profile.go
··· 17 "github.com/gorilla/feeds" 18 "tangled.org/core/api/tangled" 19 "tangled.org/core/appview/db" 20 "tangled.org/core/appview/pages" 21 ) 22 ··· 76 } 77 78 loggedInUser := s.oauth.GetUser(r) 79 - followStatus := db.IsNotFollowing 80 if loggedInUser != nil { 81 followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, did) 82 } ··· 271 272 func (s *State) followPage( 273 r *http.Request, 274 - fetchFollows func(db.Execer, string) ([]db.Follow, error), 275 - extractDid func(db.Follow) string, 276 ) (*FollowsPageParams, error) { 277 l := s.logger.With("handler", "reposPage") 278 ··· 329 followCards := make([]pages.FollowCard, len(follows)) 330 for i, did := range followDids { 331 followStats := followStatsMap[did] 332 - followStatus := db.IsNotFollowing 333 if _, exists := loggedInUserFollowing[did]; exists { 334 - followStatus = db.IsFollowing 335 } else if loggedInUser != nil && loggedInUser.Did == did { 336 - followStatus = db.IsSelf 337 } 338 339 var profile *db.Profile ··· 358 } 359 360 func (s *State) followersPage(w http.ResponseWriter, r *http.Request) { 361 - followPage, err := s.followPage(r, db.GetFollowers, func(f db.Follow) string { return f.UserDid }) 362 if err != nil { 363 s.pages.Notice(w, "all-followers", "Failed to load followers") 364 return ··· 372 } 373 374 func (s *State) followingPage(w http.ResponseWriter, r *http.Request) { 375 - followPage, err := s.followPage(r, db.GetFollowing, func(f db.Follow) string { return f.SubjectDid }) 376 if err != nil { 377 s.pages.Notice(w, "all-following", "Failed to load following") 378 return
··· 17 "github.com/gorilla/feeds" 18 "tangled.org/core/api/tangled" 19 "tangled.org/core/appview/db" 20 + "tangled.org/core/appview/models" 21 "tangled.org/core/appview/pages" 22 ) 23 ··· 77 } 78 79 loggedInUser := s.oauth.GetUser(r) 80 + followStatus := models.IsNotFollowing 81 if loggedInUser != nil { 82 followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, did) 83 } ··· 272 273 func (s *State) followPage( 274 r *http.Request, 275 + fetchFollows func(db.Execer, string) ([]models.Follow, error), 276 + extractDid func(models.Follow) string, 277 ) (*FollowsPageParams, error) { 278 l := s.logger.With("handler", "reposPage") 279 ··· 330 followCards := make([]pages.FollowCard, len(follows)) 331 for i, did := range followDids { 332 followStats := followStatsMap[did] 333 + followStatus := models.IsNotFollowing 334 if _, exists := loggedInUserFollowing[did]; exists { 335 + followStatus = models.IsFollowing 336 } else if loggedInUser != nil && loggedInUser.Did == did { 337 + followStatus = models.IsSelf 338 } 339 340 var profile *db.Profile ··· 359 } 360 361 func (s *State) followersPage(w http.ResponseWriter, r *http.Request) { 362 + followPage, err := s.followPage(r, db.GetFollowers, func(f models.Follow) string { return f.UserDid }) 363 if err != nil { 364 s.pages.Notice(w, "all-followers", "Failed to load followers") 365 return ··· 373 } 374 375 func (s *State) followingPage(w http.ResponseWriter, r *http.Request) { 376 + followPage, err := s.followPage(r, db.GetFollowing, func(f models.Follow) string { return f.SubjectDid }) 377 if err != nil { 378 s.pages.Notice(w, "all-following", "Failed to load following") 379 return