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
50func GetFollowerFollowing(e Execer, did string) (int, int, error) {
51 followers, following := 0, 0
52 err := e.QueryRow(
53 `SELECT
54 COUNT(CASE WHEN subject_did = ? THEN 1 END) AS followers,
55 COUNT(CASE WHEN user_did = ? THEN 1 END) AS following
56 FROM follows;`, did, did).Scan(&followers, &following)
57 if err != nil {
58 return 0, 0, err
59 }
60 return followers, following, nil
61}
62
63type FollowStatus int
64
65const (
66 IsNotFollowing FollowStatus = iota
67 IsFollowing
68 IsSelf
69)
70
71func (s FollowStatus) String() string {
72 switch s {
73 case IsNotFollowing:
74 return "IsNotFollowing"
75 case IsFollowing:
76 return "IsFollowing"
77 case IsSelf:
78 return "IsSelf"
79 default:
80 return "IsNotFollowing"
81 }
82}
83
84func GetFollowStatus(e Execer, userDid, subjectDid string) FollowStatus {
85 if userDid == subjectDid {
86 return IsSelf
87 } else if _, err := GetFollow(e, userDid, subjectDid); err != nil {
88 return IsNotFollowing
89 } else {
90 return IsFollowing
91 }
92}
93
94func GetAllFollows(e Execer, limit int) ([]Follow, error) {
95 var follows []Follow
96
97 rows, err := e.Query(`
98 select user_did, subject_did, followed_at, rkey
99 from follows
100 order by followed_at desc
101 limit ?`, limit,
102 )
103 if err != nil {
104 return nil, err
105 }
106 defer rows.Close()
107
108 for rows.Next() {
109 var follow Follow
110 var followedAt string
111 if err := rows.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey); err != nil {
112 return nil, err
113 }
114
115 followedAtTime, err := time.Parse(time.RFC3339, followedAt)
116 if err != nil {
117 log.Println("unable to determine followed at time")
118 follow.FollowedAt = time.Now()
119 } else {
120 follow.FollowedAt = followedAtTime
121 }
122
123 follows = append(follows, follow)
124 }
125
126 if err := rows.Err(); err != nil {
127 return nil, err
128 }
129
130 return follows, nil
131}