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