1package db
2
3import (
4 "log"
5 "time"
6)
7
8type Follow struct {
9 UserDid string
10 SubjectDid string
11 FollowedAt time.Time
12 Rkey string
13}
14
15func AddFollow(e Execer, userDid, subjectDid, rkey string) error {
16 query := `insert or ignore into follows (user_did, subject_did, rkey) values (?, ?, ?)`
17 _, err := e.Exec(query, userDid, subjectDid, rkey)
18 return err
19}
20
21// Get a follow record
22func GetFollow(e Execer, userDid, subjectDid string) (*Follow, error) {
23 query := `select user_did, subject_did, followed_at, rkey from follows where user_did = ? and subject_did = ?`
24 row := e.QueryRow(query, userDid, subjectDid)
25
26 var follow Follow
27 var followedAt string
28 err := row.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey)
29 if err != nil {
30 return nil, err
31 }
32
33 followedAtTime, err := time.Parse(time.RFC3339, followedAt)
34 if err != nil {
35 log.Println("unable to determine followed at time")
36 follow.FollowedAt = time.Now()
37 } else {
38 follow.FollowedAt = followedAtTime
39 }
40
41 return &follow, nil
42}
43
44// Remove a follow
45func DeleteFollow(e Execer, userDid, subjectDid string) error {
46 _, err := e.Exec(`delete from follows where user_did = ? and subject_did = ?`, userDid, subjectDid)
47 return err
48}
49
50// Remove a follow
51func DeleteFollowByRkey(e Execer, userDid, rkey string) error {
52 _, err := e.Exec(`delete from follows where user_did = ? and rkey = ?`, userDid, rkey)
53 return err
54}
55
56func GetFollowerFollowing(e Execer, did string) (int, int, error) {
57 followers, following := 0, 0
58 err := e.QueryRow(
59 `SELECT
60 COUNT(CASE WHEN subject_did = ? THEN 1 END) AS followers,
61 COUNT(CASE WHEN user_did = ? THEN 1 END) AS following
62 FROM follows;`, did, did).Scan(&followers, &following)
63 if err != nil {
64 return 0, 0, err
65 }
66 return followers, following, nil
67}
68
69type FollowStatus int
70
71const (
72 IsNotFollowing FollowStatus = iota
73 IsFollowing
74 IsSelf
75)
76
77func (s FollowStatus) String() string {
78 switch s {
79 case IsNotFollowing:
80 return "IsNotFollowing"
81 case IsFollowing:
82 return "IsFollowing"
83 case IsSelf:
84 return "IsSelf"
85 default:
86 return "IsNotFollowing"
87 }
88}
89
90func GetFollowStatus(e Execer, userDid, subjectDid string) FollowStatus {
91 if userDid == subjectDid {
92 return IsSelf
93 } else if _, err := GetFollow(e, userDid, subjectDid); err != nil {
94 return IsNotFollowing
95 } else {
96 return IsFollowing
97 }
98}
99
100func GetAllFollows(e Execer, limit int) ([]Follow, error) {
101 var follows []Follow
102
103 rows, err := e.Query(`
104 select user_did, subject_did, followed_at, rkey
105 from follows
106 order by followed_at desc
107 limit ?`, limit,
108 )
109 if err != nil {
110 return nil, err
111 }
112 defer rows.Close()
113
114 for rows.Next() {
115 var follow Follow
116 var followedAt string
117 if err := rows.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey); err != nil {
118 return nil, err
119 }
120
121 followedAtTime, err := time.Parse(time.RFC3339, followedAt)
122 if err != nil {
123 log.Println("unable to determine followed at time")
124 follow.FollowedAt = time.Now()
125 } else {
126 follow.FollowedAt = followedAtTime
127 }
128
129 follows = append(follows, follow)
130 }
131
132 if err := rows.Err(); err != nil {
133 return nil, err
134 }
135
136 return follows, nil
137}