1package db
2
3import (
4 "log"
5 "time"
6
7 "github.com/bluesky-social/indigo/atproto/syntax"
8)
9
10type Star struct {
11 StarredByDid string
12 RepoAt syntax.ATURI
13 Created time.Time
14 Rkey string
15
16 // optionally, populate this when querying for reverse mappings
17 Repo *Repo
18}
19
20func (star *Star) ResolveRepo(e Execer) error {
21 if star.Repo != nil {
22 return nil
23 }
24
25 repo, err := GetRepoByAtUri(e, star.RepoAt.String())
26 if err != nil {
27 return err
28 }
29
30 star.Repo = repo
31 return nil
32}
33
34func AddStar(e Execer, starredByDid string, repoAt syntax.ATURI, rkey string) error {
35 query := `insert or ignore into stars (starred_by_did, repo_at, rkey) values (?, ?, ?)`
36 _, err := e.Exec(query, starredByDid, repoAt, rkey)
37 return err
38}
39
40// Get a star record
41func GetStar(e Execer, starredByDid string, repoAt syntax.ATURI) (*Star, error) {
42 query := `
43 select starred_by_did, repo_at, created, rkey
44 from stars
45 where starred_by_did = ? and repo_at = ?`
46 row := e.QueryRow(query, starredByDid, repoAt)
47
48 var star Star
49 var created string
50 err := row.Scan(&star.StarredByDid, &star.RepoAt, &created, &star.Rkey)
51 if err != nil {
52 return nil, err
53 }
54
55 createdAtTime, err := time.Parse(time.RFC3339, created)
56 if err != nil {
57 log.Println("unable to determine followed at time")
58 star.Created = time.Now()
59 } else {
60 star.Created = createdAtTime
61 }
62
63 return &star, nil
64}
65
66// Remove a star
67func DeleteStar(e Execer, starredByDid string, repoAt syntax.ATURI) error {
68 _, err := e.Exec(`delete from stars where starred_by_did = ? and repo_at = ?`, starredByDid, repoAt)
69 return err
70}
71
72// Remove a star
73func DeleteStarByRkey(e Execer, starredByDid string, rkey string) error {
74 _, err := e.Exec(`delete from stars where starred_by_did = ? and rkey = ?`, starredByDid, rkey)
75 return err
76}
77
78func GetStarCount(e Execer, repoAt syntax.ATURI) (int, error) {
79 stars := 0
80 err := e.QueryRow(
81 `select count(starred_by_did) from stars where repo_at = ?`, repoAt).Scan(&stars)
82 if err != nil {
83 return 0, err
84 }
85 return stars, nil
86}
87
88func GetStarStatus(e Execer, userDid string, repoAt syntax.ATURI) bool {
89 if _, err := GetStar(e, userDid, repoAt); err != nil {
90 return false
91 } else {
92 return true
93 }
94}
95
96func GetAllStars(e Execer, limit int) ([]Star, error) {
97 var stars []Star
98
99 rows, err := e.Query(`
100 select
101 s.starred_by_did,
102 s.repo_at,
103 s.rkey,
104 s.created,
105 r.did,
106 r.name,
107 r.knot,
108 r.rkey,
109 r.created,
110 r.at_uri
111 from stars s
112 join repos r on s.repo_at = r.at_uri
113 `)
114
115 if err != nil {
116 return nil, err
117 }
118 defer rows.Close()
119
120 for rows.Next() {
121 var star Star
122 var repo Repo
123 var starCreatedAt, repoCreatedAt string
124
125 if err := rows.Scan(
126 &star.StarredByDid,
127 &star.RepoAt,
128 &star.Rkey,
129 &starCreatedAt,
130 &repo.Did,
131 &repo.Name,
132 &repo.Knot,
133 &repo.Rkey,
134 &repoCreatedAt,
135 &repo.AtUri,
136 ); err != nil {
137 return nil, err
138 }
139
140 star.Created, err = time.Parse(time.RFC3339, starCreatedAt)
141 if err != nil {
142 star.Created = time.Now()
143 }
144 repo.Created, err = time.Parse(time.RFC3339, repoCreatedAt)
145 if err != nil {
146 repo.Created = time.Now()
147 }
148 star.Repo = &repo
149
150 stars = append(stars, star)
151 }
152
153 if err := rows.Err(); err != nil {
154 return nil, err
155 }
156
157 return stars, nil
158}