A vibe coded tangled fork which supports pijul.
at 3f6e50b7512609295303f4756793a6ff3238c847 151 lines 4.0 kB view raw
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}