forked from
tangled.org/core
Monorepo for Tangled
1package db
2
3import (
4 "log"
5 "time"
6
7 "github.com/bluesky-social/indigo/atproto/syntax"
8 "tangled.org/core/appview/models"
9)
10
11func AddReaction(e Execer, reactedByDid string, threadAt syntax.ATURI, kind models.ReactionKind, rkey string) error {
12 query := `insert or ignore into reactions (reacted_by_did, thread_at, kind, rkey) values (?, ?, ?, ?)`
13 _, err := e.Exec(query, reactedByDid, threadAt, kind, rkey)
14 return err
15}
16
17// Get a reaction record
18func GetReaction(e Execer, reactedByDid string, threadAt syntax.ATURI, kind models.ReactionKind) (*models.Reaction, error) {
19 query := `
20 select reacted_by_did, thread_at, created, rkey
21 from reactions
22 where reacted_by_did = ? and thread_at = ? and kind = ?`
23 row := e.QueryRow(query, reactedByDid, threadAt, kind)
24
25 var reaction models.Reaction
26 var created string
27 err := row.Scan(&reaction.ReactedByDid, &reaction.ThreadAt, &created, &reaction.Rkey)
28 if err != nil {
29 return nil, err
30 }
31
32 createdAtTime, err := time.Parse(time.RFC3339, created)
33 if err != nil {
34 log.Println("unable to determine followed at time")
35 reaction.Created = time.Now()
36 } else {
37 reaction.Created = createdAtTime
38 }
39
40 return &reaction, nil
41}
42
43// Remove a reaction
44func DeleteReaction(e Execer, reactedByDid string, threadAt syntax.ATURI, kind models.ReactionKind) error {
45 _, err := e.Exec(`delete from reactions where reacted_by_did = ? and thread_at = ? and kind = ?`, reactedByDid, threadAt, kind)
46 return err
47}
48
49// Remove a reaction
50func DeleteReactionByRkey(e Execer, reactedByDid string, rkey string) error {
51 _, err := e.Exec(`delete from reactions where reacted_by_did = ? and rkey = ?`, reactedByDid, rkey)
52 return err
53}
54
55func GetReactionCount(e Execer, threadAt syntax.ATURI) (int, error) {
56 count := 0
57 err := e.QueryRow(`select count(reacted_by_did) from reactions where thread_at = ?`, threadAt).Scan(&count)
58 if err != nil {
59 return 0, err
60 }
61 return count, nil
62}
63
64func GetReactionCountByKind(e Execer, threadAt syntax.ATURI, kind models.ReactionKind) (int, error) {
65 count := 0
66 err := e.QueryRow(
67 `select count(reacted_by_did) from reactions where thread_at = ? and kind = ?`, threadAt, kind).Scan(&count)
68 if err != nil {
69 return 0, err
70 }
71 return count, nil
72}
73
74func GetReactionMap(e Execer, userLimit int, threadAt syntax.ATURI) (map[models.ReactionKind]models.ReactionDisplayData, error) {
75 query := `
76 select kind, reacted_by_did,
77 row_number() over (partition by kind order by created asc) as rn,
78 count(*) over (partition by kind) as total
79 from reactions
80 where thread_at = ?
81 order by kind, created asc`
82
83 rows, err := e.Query(query, threadAt)
84 if err != nil {
85 return nil, err
86 }
87 defer rows.Close()
88
89 reactionMap := map[models.ReactionKind]models.ReactionDisplayData{}
90 for _, kind := range models.OrderedReactionKinds {
91 reactionMap[kind] = models.ReactionDisplayData{Count: 0, Users: []string{}}
92 }
93
94 for rows.Next() {
95 var kind models.ReactionKind
96 var did string
97 var rn, total int
98 if err := rows.Scan(&kind, &did, &rn, &total); err != nil {
99 return nil, err
100 }
101
102 data := reactionMap[kind]
103 data.Count = total
104 if userLimit > 0 && rn <= userLimit {
105 data.Users = append(data.Users, did)
106 }
107 reactionMap[kind] = data
108 }
109
110 return reactionMap, rows.Err()
111}
112
113func GetReactionStatus(e Execer, userDid string, threadAt syntax.ATURI, kind models.ReactionKind) bool {
114 if _, err := GetReaction(e, userDid, threadAt, kind); err != nil {
115 return false
116 } else {
117 return true
118 }
119}
120
121func GetReactionStatusMap(e Execer, userDid string, threadAt syntax.ATURI) map[models.ReactionKind]bool {
122 statusMap := map[models.ReactionKind]bool{}
123 for _, kind := range models.OrderedReactionKinds {
124 count := GetReactionStatus(e, userDid, threadAt, kind)
125 statusMap[kind] = count
126 }
127 return statusMap
128}