This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / appview / db / comments.go
6.0 kB 278 lines
1package db 2 3import ( 4 "database/sql" 5 "encoding/json" 6 "fmt" 7 "log" 8 "sort" 9 "strings" 10 "time" 11 12 "github.com/bluesky-social/indigo/api/atproto" 13 "github.com/bluesky-social/indigo/atproto/syntax" 14 "tangled.org/core/appview/models" 15 "tangled.org/core/orm" 16) 17 18func PutComment(tx *sql.Tx, c *models.Comment, references []syntax.ATURI) error { 19 var bodyBlobs, replyToUri, replyToCid *string 20 if len(c.Body.Blobs) > 0 { 21 encoded, err := json.Marshal(c.Body.Blobs) 22 if err != nil { 23 return fmt.Errorf("encoding blobs to json: %w", err) 24 } 25 encodedStr := string(encoded) 26 bodyBlobs = &encodedStr 27 } 28 if c.ReplyTo != nil { 29 replyToUri = &c.ReplyTo.Uri 30 replyToCid = &c.ReplyTo.Cid 31 } 32 result, err := tx.Exec( 33 // users can change the 'created' date. 34 // skip update entirely if cid is unchanged. 35 `insert into comments ( 36 did, 37 collection, 38 rkey, 39 cid, 40 subject_uri, 41 subject_cid, 42 body_text, 43 body_original, 44 body_blobs, 45 created, 46 reply_to_uri, 47 reply_to_cid, 48 pull_round_idx 49 ) 50 values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 51 on conflict(did, collection, rkey) 52 do update set 53 cid = excluded.cid, 54 subject_uri = excluded.subject_uri, 55 subject_cid = excluded.subject_cid, 56 body_text = excluded.body_text, 57 body_original = excluded.body_original, 58 body_blobs = excluded.body_blobs, 59 created = excluded.created, 60 reply_to_uri = excluded.reply_to_uri, 61 reply_to_cid = excluded.reply_to_cid, 62 pull_round_idx = excluded.pull_round_idx, 63 edited = ? 64 where comments.cid is not excluded.cid`, 65 c.Did, 66 c.Collection, 67 c.Rkey, 68 c.Cid, 69 c.Subject.Uri, 70 c.Subject.Cid, 71 c.Body.Text, 72 c.Body.Original, 73 bodyBlobs, 74 c.Created.Format(time.RFC3339), 75 replyToUri, 76 replyToCid, 77 c.PullRoundIdx, 78 time.Now().Format(time.RFC3339), 79 ) 80 if err != nil { 81 return err 82 } 83 84 c.Id, err = result.LastInsertId() 85 if err != nil { 86 return err 87 } 88 89 affected, err := result.RowsAffected() 90 if err != nil { 91 return err 92 } 93 94 if affected < 1 { 95 log.Println("record is already stored. skipping operation") 96 return nil 97 } 98 99 // update references when comment is updated 100 if err := putReferences(tx, c.AtUri(), references); err != nil { 101 return fmt.Errorf("put reference_links: %w", err) 102 } 103 104 return nil 105} 106 107// PurgeComments actually purges a comment row from db instead of marking it as "deleted" 108func PurgeComments(e Execer, filters ...orm.Filter) error { 109 var conditions []string 110 var args []any 111 for _, filter := range filters { 112 conditions = append(conditions, filter.Condition()) 113 args = append(args, filter.Arg()...) 114 } 115 116 whereClause := "" 117 if conditions != nil { 118 whereClause = " where " + strings.Join(conditions, " and ") 119 } 120 121 _, err := e.Exec(fmt.Sprintf(`delete from comments %s`, whereClause), args...) 122 return err 123} 124 125func DeleteComments(e Execer, filters ...orm.Filter) error { 126 var conditions []string 127 var args []any 128 for _, filter := range filters { 129 conditions = append(conditions, filter.Condition()) 130 args = append(args, filter.Arg()...) 131 } 132 133 whereClause := "" 134 if conditions != nil { 135 whereClause = " where " + strings.Join(conditions, " and ") 136 } 137 138 query := fmt.Sprintf( 139 `update comments 140 set body_text = "", 141 body_original = null, 142 body_blobs = null, 143 deleted = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now') 144 %s`, 145 whereClause, 146 ) 147 148 _, err := e.Exec(query, args...) 149 return err 150} 151 152func GetComment(e Execer, filters ...orm.Filter) (models.Comment, error) { 153 comments, err := GetComments(e, filters...) 154 if err != nil { 155 return models.Comment{}, err 156 } 157 if len(comments) != 1 { 158 return models.Comment{}, fmt.Errorf("expected 1 comment, got %d", len(comments)) 159 } 160 return comments[0], nil 161} 162 163func GetComments(e Execer, filters ...orm.Filter) ([]models.Comment, error) { 164 var comments []models.Comment 165 166 var conditions []string 167 var args []any 168 for _, filter := range filters { 169 conditions = append(conditions, filter.Condition()) 170 args = append(args, filter.Arg()...) 171 } 172 173 whereClause := "" 174 if conditions != nil { 175 whereClause = " where " + strings.Join(conditions, " and ") 176 } 177 178 query := fmt.Sprintf(` 179 select 180 id, 181 did, 182 collection, 183 rkey, 184 cid, 185 subject_uri, 186 subject_cid, 187 body_text, 188 body_original, 189 body_blobs, 190 created, 191 reply_to_uri, 192 reply_to_cid, 193 pull_round_idx, 194 edited, 195 deleted 196 from 197 comments 198 %s 199 `, whereClause) 200 201 rows, err := e.Query(query, args...) 202 if err != nil { 203 return nil, err 204 } 205 defer rows.Close() 206 207 for rows.Next() { 208 var comment models.Comment 209 var created string 210 var cid, bodyBlobs, replyToUri, replyToCid, edited, deleted sql.Null[string] 211 err := rows.Scan( 212 &comment.Id, 213 &comment.Did, 214 &comment.Collection, 215 &comment.Rkey, 216 &cid, 217 &comment.Subject.Uri, 218 &comment.Subject.Cid, 219 &comment.Body.Text, 220 &comment.Body.Original, 221 &bodyBlobs, 222 &created, 223 &replyToUri, 224 &replyToCid, 225 &comment.PullRoundIdx, 226 &edited, 227 &deleted, 228 ) 229 if err != nil { 230 return nil, err 231 } 232 233 if cid.Valid && cid.V != "" { 234 comment.Cid = syntax.CID(cid.V) 235 } 236 237 if bodyBlobs.Valid && bodyBlobs.V != "" { 238 if err := json.Unmarshal([]byte(bodyBlobs.V), &comment.Body.Blobs); err != nil { 239 return nil, fmt.Errorf("decoding blobs: %w", err) 240 } 241 } 242 243 if t, err := time.Parse(time.RFC3339, created); err == nil { 244 comment.Created = t 245 } 246 247 if replyToUri.Valid && replyToCid.Valid { 248 comment.ReplyTo = &atproto.RepoStrongRef{ 249 Uri: replyToUri.V, 250 Cid: replyToCid.V, 251 } 252 } 253 254 if edited.Valid { 255 if t, err := time.Parse(time.RFC3339, edited.V); err == nil { 256 comment.Edited = &t 257 } 258 } 259 260 if deleted.Valid { 261 if t, err := time.Parse(time.RFC3339, deleted.V); err == nil { 262 comment.Deleted = &t 263 } 264 } 265 266 comments = append(comments, comment) 267 } 268 269 if err := rows.Err(); err != nil { 270 return nil, err 271 } 272 273 sort.Slice(comments, func(i, j int) bool { 274 return comments[i].Created.Before(comments[j].Created) 275 }) 276 277 return comments, nil 278}