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
72func GetStarCount(e Execer, repoAt syntax.ATURI) (int, error) {
73 stars := 0
74 err := e.QueryRow(
75 `select count(starred_by_did) from stars where repo_at = ?`, repoAt).Scan(&stars)
76 if err != nil {
77 return 0, err
78 }
79 return stars, nil
80}
81
82func GetStarStatus(e Execer, userDid string, repoAt syntax.ATURI) bool {
83 if _, err := GetStar(e, userDid, repoAt); err != nil {
84 return false
85 } else {
86 return true
87 }
88}
89
90func GetAllStars(e Execer, limit int) ([]Star, error) {
91 var stars []Star
92
93 rows, err := e.Query(`
94 select
95 s.starred_by_did,
96 s.repo_at,
97 s.rkey,
98 s.created,
99 r.did,
100 r.name,
101 r.knot,
102 r.rkey,
103 r.created,
104 r.at_uri
105 from stars s
106 join repos r on s.repo_at = r.at_uri
107 `)
108
109 if err != nil {
110 return nil, err
111 }
112 defer rows.Close()
113
114 for rows.Next() {
115 var star Star
116 var repo Repo
117 var starCreatedAt, repoCreatedAt string
118
119 if err := rows.Scan(
120 &star.StarredByDid,
121 &star.RepoAt,
122 &star.Rkey,
123 &starCreatedAt,
124 &repo.Did,
125 &repo.Name,
126 &repo.Knot,
127 &repo.Rkey,
128 &repoCreatedAt,
129 &repo.AtUri,
130 ); err != nil {
131 return nil, err
132 }
133
134 star.Created, err = time.Parse(time.RFC3339, starCreatedAt)
135 if err != nil {
136 star.Created = time.Now()
137 }
138 repo.Created, err = time.Parse(time.RFC3339, repoCreatedAt)
139 if err != nil {
140 repo.Created = time.Now()
141 }
142 star.Repo = &repo
143
144 stars = append(stars, star)
145 }
146
147 if err := rows.Err(); err != nil {
148 return nil, err
149 }
150
151 return stars, nil
152}