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