Monorepo for Tangled tangled.org

appview: replace PullComment to Comment #865

open opened by boltless.me targeting master from sl/wnrvrwyvrlzo
Labels

None yet.

assignee

None yet.

Participants 2
AT URI
at://did:plc:xasnlahkri4ewmbuzly2rlc5/sh.tangled.repo.pull/3m7iohv2yb422
+458 -199
Diff #4
+198
appview/db/comments.go
···
··· 1 + package db 2 + 3 + import ( 4 + "database/sql" 5 + "fmt" 6 + "maps" 7 + "slices" 8 + "sort" 9 + "strings" 10 + "time" 11 + 12 + "github.com/bluesky-social/indigo/atproto/syntax" 13 + "tangled.org/core/appview/models" 14 + "tangled.org/core/orm" 15 + ) 16 + 17 + func PutComment(tx *sql.Tx, c *models.Comment) error { 18 + result, err := tx.Exec( 19 + `insert into comments ( 20 + did, 21 + rkey, 22 + subject_at, 23 + reply_to, 24 + body, 25 + pull_submission_id, 26 + created 27 + ) 28 + values (?, ?, ?, ?, ?, ?, ?) 29 + on conflict(did, rkey) do update set 30 + subject_at = excluded.subject_at, 31 + reply_to = excluded.reply_to, 32 + body = excluded.body, 33 + edited = case 34 + when 35 + comments.subject_at != excluded.subject_at 36 + or comments.body != excluded.body 37 + or comments.reply_to != excluded.reply_to 38 + then ? 39 + else comments.edited 40 + end`, 41 + c.Did, 42 + c.Rkey, 43 + c.Subject, 44 + c.ReplyTo, 45 + c.Body, 46 + c.PullSubmissionId, 47 + c.Created.Format(time.RFC3339), 48 + time.Now().Format(time.RFC3339), 49 + ) 50 + if err != nil { 51 + return err 52 + } 53 + 54 + c.Id, err = result.LastInsertId() 55 + if err != nil { 56 + return err 57 + } 58 + 59 + if err := putReferences(tx, c.AtUri(), c.References); err != nil { 60 + return fmt.Errorf("put reference_links: %w", err) 61 + } 62 + 63 + return nil 64 + } 65 + 66 + func DeleteComments(e Execer, filters ...orm.Filter) error { 67 + var conditions []string 68 + var args []any 69 + for _, filter := range filters { 70 + conditions = append(conditions, filter.Condition()) 71 + args = append(args, filter.Arg()...) 72 + } 73 + 74 + whereClause := "" 75 + if conditions != nil { 76 + whereClause = " where " + strings.Join(conditions, " and ") 77 + } 78 + 79 + query := fmt.Sprintf(`update comments set body = "", deleted = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now') %s`, whereClause) 80 + 81 + _, err := e.Exec(query, args...) 82 + return err 83 + } 84 + 85 + func GetComments(e Execer, filters ...orm.Filter) ([]models.Comment, error) { 86 + commentMap := make(map[string]*models.Comment) 87 + 88 + var conditions []string 89 + var args []any 90 + for _, filter := range filters { 91 + conditions = append(conditions, filter.Condition()) 92 + args = append(args, filter.Arg()...) 93 + } 94 + 95 + whereClause := "" 96 + if conditions != nil { 97 + whereClause = " where " + strings.Join(conditions, " and ") 98 + } 99 + 100 + query := fmt.Sprintf(` 101 + select 102 + id, 103 + did, 104 + rkey, 105 + subject_at, 106 + reply_to, 107 + body, 108 + pull_submission_id, 109 + created, 110 + edited, 111 + deleted 112 + from 113 + comments 114 + %s 115 + `, whereClause) 116 + 117 + rows, err := e.Query(query, args...) 118 + if err != nil { 119 + return nil, err 120 + } 121 + 122 + for rows.Next() { 123 + var comment models.Comment 124 + var created string 125 + var rkey, edited, deleted, replyTo sql.Null[string] 126 + err := rows.Scan( 127 + &comment.Id, 128 + &comment.Did, 129 + &rkey, 130 + &comment.Subject, 131 + &replyTo, 132 + &comment.Body, 133 + &comment.PullSubmissionId, 134 + &created, 135 + &edited, 136 + &deleted, 137 + ) 138 + if err != nil { 139 + return nil, err 140 + } 141 + 142 + // this is a remnant from old times, newer comments always have rkey 143 + if rkey.Valid { 144 + comment.Rkey = rkey.V 145 + } 146 + 147 + if t, err := time.Parse(time.RFC3339, created); err == nil { 148 + comment.Created = t 149 + } 150 + 151 + if edited.Valid { 152 + if t, err := time.Parse(time.RFC3339, edited.V); err == nil { 153 + comment.Edited = &t 154 + } 155 + } 156 + 157 + if deleted.Valid { 158 + if t, err := time.Parse(time.RFC3339, deleted.V); err == nil { 159 + comment.Deleted = &t 160 + } 161 + } 162 + 163 + if replyTo.Valid { 164 + rt := syntax.ATURI(replyTo.V) 165 + comment.ReplyTo = &rt 166 + } 167 + 168 + atUri := comment.AtUri().String() 169 + commentMap[atUri] = &comment 170 + } 171 + 172 + if err := rows.Err(); err != nil { 173 + return nil, err 174 + } 175 + 176 + // collect references from each comments 177 + commentAts := slices.Collect(maps.Keys(commentMap)) 178 + allReferencs, err := GetReferencesAll(e, orm.FilterIn("from_at", commentAts)) 179 + if err != nil { 180 + return nil, fmt.Errorf("failed to query reference_links: %w", err) 181 + } 182 + for commentAt, references := range allReferencs { 183 + if comment, ok := commentMap[commentAt.String()]; ok { 184 + comment.References = references 185 + } 186 + } 187 + 188 + var comments []models.Comment 189 + for _, c := range commentMap { 190 + comments = append(comments, *c) 191 + } 192 + 193 + sort.Slice(comments, func(i, j int) bool { 194 + return comments[i].Created.After(comments[j].Created) 195 + }) 196 + 197 + return comments, nil 198 + }
+81
appview/db/db.go
··· 1173 return err 1174 }) 1175 1176 return &DB{ 1177 db, 1178 logger,
··· 1173 return err 1174 }) 1175 1176 + orm.RunMigration(conn, logger, "add-comments-table", func(tx *sql.Tx) error { 1177 + _, err := tx.Exec(` 1178 + drop table if exists comments; 1179 + 1180 + create table comments ( 1181 + -- identifiers 1182 + id integer primary key autoincrement, 1183 + did text not null, 1184 + collection text not null default 'sh.tangled.comment', 1185 + rkey text not null, 1186 + at_uri text generated always as ('at://' || did || '/' || collection || '/' || rkey) stored, 1187 + 1188 + -- at identifiers 1189 + subject_at text not null, 1190 + reply_to text, -- at_uri of parent comment 1191 + 1192 + pull_submission_id integer, -- dirty fix until we atprotate the pull-rounds 1193 + 1194 + -- content 1195 + body text not null, 1196 + created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 1197 + edited text, 1198 + deleted text, 1199 + 1200 + -- constraints 1201 + unique(did, rkey) 1202 + ); 1203 + 1204 + insert into comments ( 1205 + did, 1206 + collection, 1207 + rkey, 1208 + subject_at, 1209 + reply_to, 1210 + body, 1211 + created, 1212 + edited, 1213 + deleted 1214 + ) 1215 + select 1216 + did, 1217 + 'sh.tangled.repo.issue.comment', 1218 + rkey, 1219 + issue_at, 1220 + reply_to, 1221 + body, 1222 + created, 1223 + edited, 1224 + deleted 1225 + from issue_comments 1226 + where rkey is not null; 1227 + 1228 + insert into comments ( 1229 + did, 1230 + collection, 1231 + rkey, 1232 + subject_at, 1233 + pull_submission_id, 1234 + body, 1235 + created 1236 + ) 1237 + select 1238 + c.owner_did, 1239 + 'sh.tangled.repo.pull.comment', 1240 + substr( 1241 + substr(c.comment_at, 6 + instr(substr(c.comment_at, 6), '/')), -- nsid/rkey 1242 + instr( 1243 + substr(c.comment_at, 6 + instr(substr(c.comment_at, 6), '/')), -- nsid/rkey 1244 + '/' 1245 + ) + 1 1246 + ), -- rkey 1247 + p.at_uri, 1248 + c.submission_id, 1249 + c.body, 1250 + c.created 1251 + from pull_comments c 1252 + join pulls p on c.repo_at = p.repo_at and c.pull_id = p.pull_id; 1253 + `) 1254 + return err 1255 + }) 1256 + 1257 return &DB{ 1258 db, 1259 logger,
+6 -121
appview/db/pulls.go
··· 391 return nil, err 392 } 393 394 - // Get comments for all submissions using GetPullComments 395 submissionIds := slices.Collect(maps.Keys(submissionMap)) 396 - comments, err := GetPullComments(e, orm.FilterIn("submission_id", submissionIds)) 397 if err != nil { 398 return nil, fmt.Errorf("failed to get pull comments: %w", err) 399 } 400 for _, comment := range comments { 401 - if submission, ok := submissionMap[comment.SubmissionId]; ok { 402 - submission.Comments = append(submission.Comments, comment) 403 } 404 } 405 ··· 419 return m, nil 420 } 421 422 - func GetPullComments(e Execer, filters ...orm.Filter) ([]models.PullComment, error) { 423 - var conditions []string 424 - var args []any 425 - for _, filter := range filters { 426 - conditions = append(conditions, filter.Condition()) 427 - args = append(args, filter.Arg()...) 428 - } 429 - 430 - whereClause := "" 431 - if conditions != nil { 432 - whereClause = " where " + strings.Join(conditions, " and ") 433 - } 434 - 435 - query := fmt.Sprintf(` 436 - select 437 - id, 438 - pull_id, 439 - submission_id, 440 - repo_at, 441 - owner_did, 442 - comment_at, 443 - body, 444 - created 445 - from 446 - pull_comments 447 - %s 448 - order by 449 - created asc 450 - `, whereClause) 451 - 452 - rows, err := e.Query(query, args...) 453 - if err != nil { 454 - return nil, err 455 - } 456 - defer rows.Close() 457 - 458 - commentMap := make(map[string]*models.PullComment) 459 - for rows.Next() { 460 - var comment models.PullComment 461 - var createdAt string 462 - err := rows.Scan( 463 - &comment.ID, 464 - &comment.PullId, 465 - &comment.SubmissionId, 466 - &comment.RepoAt, 467 - &comment.OwnerDid, 468 - &comment.CommentAt, 469 - &comment.Body, 470 - &createdAt, 471 - ) 472 - if err != nil { 473 - return nil, err 474 - } 475 - 476 - if t, err := time.Parse(time.RFC3339, createdAt); err == nil { 477 - comment.Created = t 478 - } 479 - 480 - atUri := comment.AtUri().String() 481 - commentMap[atUri] = &comment 482 - } 483 - 484 - if err := rows.Err(); err != nil { 485 - return nil, err 486 - } 487 - 488 - // collect references for each comments 489 - commentAts := slices.Collect(maps.Keys(commentMap)) 490 - allReferencs, err := GetReferencesAll(e, orm.FilterIn("from_at", commentAts)) 491 - if err != nil { 492 - return nil, fmt.Errorf("failed to query reference_links: %w", err) 493 - } 494 - for commentAt, references := range allReferencs { 495 - if comment, ok := commentMap[commentAt.String()]; ok { 496 - comment.References = references 497 - } 498 - } 499 - 500 - var comments []models.PullComment 501 - for _, c := range commentMap { 502 - comments = append(comments, *c) 503 - } 504 - 505 - sort.Slice(comments, func(i, j int) bool { 506 - return comments[i].Created.Before(comments[j].Created) 507 - }) 508 - 509 - return comments, nil 510 - } 511 - 512 // timeframe here is directly passed into the sql query filter, and any 513 // timeframe in the past should be negative; e.g.: "-3 months" 514 func GetPullsByOwnerDid(e Execer, did, timeframe string) ([]models.Pull, error) { ··· 585 return pulls, nil 586 } 587 588 - func NewPullComment(tx *sql.Tx, comment *models.PullComment) (int64, error) { 589 - query := `insert into pull_comments (owner_did, repo_at, submission_id, comment_at, pull_id, body) values (?, ?, ?, ?, ?, ?)` 590 - res, err := tx.Exec( 591 - query, 592 - comment.OwnerDid, 593 - comment.RepoAt, 594 - comment.SubmissionId, 595 - comment.CommentAt, 596 - comment.PullId, 597 - comment.Body, 598 - ) 599 - if err != nil { 600 - return 0, err 601 - } 602 - 603 - i, err := res.LastInsertId() 604 - if err != nil { 605 - return 0, err 606 - } 607 - 608 - if err := putReferences(tx, comment.AtUri(), comment.References); err != nil { 609 - return 0, fmt.Errorf("put reference_links: %w", err) 610 - } 611 - 612 - return i, nil 613 - } 614 - 615 func SetPullState(e Execer, repoAt syntax.ATURI, pullId int, pullState models.PullState) error { 616 _, err := e.Exec( 617 `update pulls set state = ? where repo_at = ? and pull_id = ? and (state <> ? or state <> ?)`,
··· 391 return nil, err 392 } 393 394 + // Get comments for all submissions using GetComments 395 submissionIds := slices.Collect(maps.Keys(submissionMap)) 396 + comments, err := GetComments(e, orm.FilterIn("pull_submission_id", submissionIds)) 397 if err != nil { 398 return nil, fmt.Errorf("failed to get pull comments: %w", err) 399 } 400 for _, comment := range comments { 401 + if comment.PullSubmissionId != nil { 402 + if submission, ok := submissionMap[*comment.PullSubmissionId]; ok { 403 + submission.Comments = append(submission.Comments, comment) 404 + } 405 } 406 } 407 ··· 421 return m, nil 422 } 423 424 // timeframe here is directly passed into the sql query filter, and any 425 // timeframe in the past should be negative; e.g.: "-3 months" 426 func GetPullsByOwnerDid(e Execer, did, timeframe string) ([]models.Pull, error) { ··· 497 return pulls, nil 498 } 499 500 func SetPullState(e Execer, repoAt syntax.ATURI, pullId int, pullState models.PullState) error { 501 _, err := e.Exec( 502 `update pulls set state = ? where repo_at = ? and pull_id = ? and (state <> ? or state <> ?)`,
+7 -8
appview/db/reference.go
··· 124 values %s 125 ) 126 select 127 - p.owner_did, p.rkey, 128 - c.comment_at 129 from input inp 130 join repos r 131 on r.did = inp.owner_did ··· 133 join pulls p 134 on p.repo_at = r.at_uri 135 and p.pull_id = inp.pull_id 136 - left join pull_comments c 137 on inp.comment_id is not null 138 - and c.repo_at = r.at_uri and c.pull_id = p.pull_id 139 and c.id = inp.comment_id 140 `, 141 strings.Join(vals, ","), ··· 293 return nil, fmt.Errorf("get pull backlinks: %w", err) 294 } 295 backlinks = append(backlinks, ls...) 296 - ls, err = getPullCommentBacklinks(e, backlinksMap[tangled.RepoPullCommentNSID]) 297 if err != nil { 298 return nil, fmt.Errorf("get pull_comment backlinks: %w", err) 299 } ··· 428 if len(aturis) == 0 { 429 return nil, nil 430 } 431 - filter := orm.FilterIn("c.comment_at", aturis) 432 rows, err := e.Query( 433 fmt.Sprintf( 434 `select r.did, r.name, p.pull_id, c.id, p.title, p.state 435 from repos r 436 join pulls p 437 on r.at_uri = p.repo_at 438 - join pull_comments c 439 - on r.at_uri = c.repo_at and p.pull_id = c.pull_id 440 where %s`, 441 filter.Condition(), 442 ),
··· 124 values %s 125 ) 126 select 127 + p.owner_did, p.rkey, c.at_uri 128 from input inp 129 join repos r 130 on r.did = inp.owner_did ··· 132 join pulls p 133 on p.repo_at = r.at_uri 134 and p.pull_id = inp.pull_id 135 + left join comments c 136 on inp.comment_id is not null 137 + and c.subject_at = ('at://' || p.owner_did || '/' || 'sh.tangled.repo.pull' || '/' || p.rkey) 138 and c.id = inp.comment_id 139 `, 140 strings.Join(vals, ","), ··· 292 return nil, fmt.Errorf("get pull backlinks: %w", err) 293 } 294 backlinks = append(backlinks, ls...) 295 + ls, err = getPullCommentBacklinks(e, backlinksMap[tangled.CommentNSID]) 296 if err != nil { 297 return nil, fmt.Errorf("get pull_comment backlinks: %w", err) 298 } ··· 427 if len(aturis) == 0 { 428 return nil, nil 429 } 430 + filter := orm.FilterIn("c.at_uri", aturis) 431 rows, err := e.Query( 432 fmt.Sprintf( 433 `select r.did, r.name, p.pull_id, c.id, p.title, p.state 434 from repos r 435 join pulls p 436 on r.at_uri = p.repo_at 437 + join comments c 438 + on ('at://' || p.owner_did || '/' || 'sh.tangled.repo.pull' || '/' || p.rkey) = c.subject_at 439 where %s`, 440 filter.Condition(), 441 ),
+117
appview/models/comment.go
···
··· 1 + package models 2 + 3 + import ( 4 + "fmt" 5 + "strings" 6 + "time" 7 + 8 + "github.com/bluesky-social/indigo/atproto/syntax" 9 + "tangled.org/core/api/tangled" 10 + ) 11 + 12 + type Comment struct { 13 + Id int64 14 + Did syntax.DID 15 + Rkey string 16 + Subject syntax.ATURI 17 + ReplyTo *syntax.ATURI 18 + Body string 19 + Created time.Time 20 + Edited *time.Time 21 + Deleted *time.Time 22 + Mentions []syntax.DID 23 + References []syntax.ATURI 24 + PullSubmissionId *int 25 + } 26 + 27 + func (c *Comment) AtUri() syntax.ATURI { 28 + return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", c.Did, tangled.CommentNSID, c.Rkey)) 29 + } 30 + 31 + func (c *Comment) AsRecord() tangled.Comment { 32 + mentions := make([]string, len(c.Mentions)) 33 + for i, did := range c.Mentions { 34 + mentions[i] = string(did) 35 + } 36 + references := make([]string, len(c.References)) 37 + for i, uri := range c.References { 38 + references[i] = string(uri) 39 + } 40 + var replyTo *string 41 + if c.ReplyTo != nil { 42 + replyToStr := c.ReplyTo.String() 43 + replyTo = &replyToStr 44 + } 45 + return tangled.Comment{ 46 + Subject: c.Subject.String(), 47 + Body: c.Body, 48 + CreatedAt: c.Created.Format(time.RFC3339), 49 + ReplyTo: replyTo, 50 + Mentions: mentions, 51 + References: references, 52 + } 53 + } 54 + 55 + func (c *Comment) IsTopLevel() bool { 56 + return c.ReplyTo == nil 57 + } 58 + 59 + func (c *Comment) IsReply() bool { 60 + return c.ReplyTo != nil 61 + } 62 + 63 + func (c *Comment) Validate() error { 64 + // TODO: sanitize the body and then trim space 65 + if sb := strings.TrimSpace(c.Body); sb == "" { 66 + return fmt.Errorf("body is empty after HTML sanitization") 67 + } 68 + 69 + // if it's for PR, PullSubmissionId should not be nil 70 + if c.Subject.Collection().String() == tangled.RepoPullNSID { 71 + if c.PullSubmissionId == nil { 72 + return fmt.Errorf("PullSubmissionId should not be nil") 73 + } 74 + } 75 + return nil 76 + } 77 + 78 + func CommentFromRecord(did, rkey string, record tangled.Comment) (*Comment, error) { 79 + created, err := time.Parse(time.RFC3339, record.CreatedAt) 80 + if err != nil { 81 + created = time.Now() 82 + } 83 + 84 + ownerDid := did 85 + 86 + if _, err = syntax.ParseATURI(record.Subject); err != nil { 87 + return nil, err 88 + } 89 + 90 + i := record 91 + mentions := make([]syntax.DID, len(record.Mentions)) 92 + for i, did := range record.Mentions { 93 + mentions[i] = syntax.DID(did) 94 + } 95 + references := make([]syntax.ATURI, len(record.References)) 96 + for i, uri := range i.References { 97 + references[i] = syntax.ATURI(uri) 98 + } 99 + var replyTo *syntax.ATURI 100 + if record.ReplyTo != nil { 101 + replyToAtUri := syntax.ATURI(*record.ReplyTo) 102 + replyTo = &replyToAtUri 103 + } 104 + 105 + comment := Comment{ 106 + Did: syntax.DID(ownerDid), 107 + Rkey: rkey, 108 + Body: record.Body, 109 + Subject: syntax.ATURI(record.Subject), 110 + ReplyTo: replyTo, 111 + Created: created, 112 + Mentions: mentions, 113 + References: references, 114 + } 115 + 116 + return &comment, nil 117 + }
+2 -28
appview/models/pull.go
··· 138 RoundNumber int 139 Patch string 140 Combined string 141 - Comments []PullComment 142 SourceRev string // include the rev that was used to create this submission: only for branch/fork PRs 143 144 // meta 145 Created time.Time 146 } 147 148 - type PullComment struct { 149 - // ids 150 - ID int 151 - PullId int 152 - SubmissionId int 153 - 154 - // at ids 155 - RepoAt string 156 - OwnerDid string 157 - CommentAt string 158 - 159 - // content 160 - Body string 161 - 162 - // meta 163 - Mentions []syntax.DID 164 - References []syntax.ATURI 165 - 166 - // meta 167 - Created time.Time 168 - } 169 - 170 - func (p *PullComment) AtUri() syntax.ATURI { 171 - return syntax.ATURI(p.CommentAt) 172 - } 173 - 174 func (p *Pull) TotalComments() int { 175 total := 0 176 for _, s := range p.Submissions { ··· 279 addParticipant(s.PullAt.Authority().String()) 280 281 for _, c := range s.Comments { 282 - addParticipant(c.OwnerDid) 283 } 284 285 return participants
··· 138 RoundNumber int 139 Patch string 140 Combined string 141 + Comments []Comment 142 SourceRev string // include the rev that was used to create this submission: only for branch/fork PRs 143 144 // meta 145 Created time.Time 146 } 147 148 func (p *Pull) TotalComments() int { 149 total := 0 150 for _, s := range p.Submissions { ··· 253 addParticipant(s.PullAt.Authority().String()) 254 255 for _, c := range s.Comments { 256 + addParticipant(c.Did.String()) 257 } 258 259 return participants
+11 -6
appview/notify/db/db.go
··· 260 ) 261 } 262 263 - func (n *databaseNotifier) NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) { 264 - pull, err := db.GetPull(n.db, 265 - syntax.ATURI(comment.RepoAt), 266 - comment.PullId, 267 ) 268 if err != nil { 269 log.Printf("NewPullComment: failed to get pulls: %v", err) 270 return 271 } 272 273 - repo, err := db.GetRepo(n.db, orm.FilterEq("at_uri", comment.RepoAt)) 274 if err != nil { 275 log.Printf("NewPullComment: failed to get repos: %v", err) 276 return ··· 288 recipients.Remove(m) 289 } 290 291 - actorDid := syntax.DID(comment.OwnerDid) 292 eventType := models.NotificationTypePullCommented 293 entityType := "pull" 294 entityId := pull.AtUri().String()
··· 260 ) 261 } 262 263 + func (n *databaseNotifier) NewPullComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 264 + pulls, err := db.GetPulls(n.db, 265 + orm.FilterEq("owner_did", comment.Subject.Authority()), 266 + orm.FilterEq("rkey", comment.Subject.RecordKey()), 267 ) 268 if err != nil { 269 log.Printf("NewPullComment: failed to get pulls: %v", err) 270 return 271 } 272 + if len(pulls) == 0 { 273 + log.Printf("NewPullComment: no pull found for %s", comment.Subject) 274 + return 275 + } 276 + pull := pulls[0] 277 278 + repo, err := db.GetRepo(n.db, orm.FilterEq("at_uri", pull.RepoAt)) 279 if err != nil { 280 log.Printf("NewPullComment: failed to get repos: %v", err) 281 return ··· 293 recipients.Remove(m) 294 } 295 296 + actorDid := comment.Did 297 eventType := models.NotificationTypePullCommented 298 entityType := "pull" 299 entityId := pull.AtUri().String()
+1 -1
appview/notify/merged_notifier.go
··· 81 m.fanout("NewPull", ctx, pull) 82 } 83 84 - func (m *mergedNotifier) NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) { 85 m.fanout("NewPullComment", ctx, comment, mentions) 86 } 87
··· 81 m.fanout("NewPull", ctx, pull) 82 } 83 84 + func (m *mergedNotifier) NewPullComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 85 m.fanout("NewPullComment", ctx, comment, mentions) 86 } 87
+2 -2
appview/notify/notifier.go
··· 22 DeleteFollow(ctx context.Context, follow *models.Follow) 23 24 NewPull(ctx context.Context, pull *models.Pull) 25 - NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) 26 NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) 27 28 UpdateProfile(ctx context.Context, profile *models.Profile) ··· 52 func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {} 53 54 func (m *BaseNotifier) NewPull(ctx context.Context, pull *models.Pull) {} 55 - func (m *BaseNotifier) NewPullComment(ctx context.Context, models *models.PullComment, mentions []syntax.DID) { 56 } 57 func (m *BaseNotifier) NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) {} 58
··· 22 DeleteFollow(ctx context.Context, follow *models.Follow) 23 24 NewPull(ctx context.Context, pull *models.Pull) 25 + NewPullComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) 26 NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) 27 28 UpdateProfile(ctx context.Context, profile *models.Profile) ··· 52 func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {} 53 54 func (m *BaseNotifier) NewPull(ctx context.Context, pull *models.Pull) {} 55 + func (m *BaseNotifier) NewPullComment(ctx context.Context, models *models.Comment, mentions []syntax.DID) { 56 } 57 func (m *BaseNotifier) NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) {} 58
+3 -4
appview/notify/posthog/notifier.go
··· 86 } 87 } 88 89 - func (n *posthogNotifier) NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) { 90 err := n.client.Enqueue(posthog.Capture{ 91 - DistinctId: comment.OwnerDid, 92 Event: "new_pull_comment", 93 Properties: posthog.Properties{ 94 - "repo_at": comment.RepoAt, 95 - "pull_id": comment.PullId, 96 "mentions": mentions, 97 }, 98 })
··· 86 } 87 } 88 89 + func (n *posthogNotifier) NewPullComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 90 err := n.client.Enqueue(posthog.Capture{ 91 + DistinctId: comment.Did.String(), 92 Event: "new_pull_comment", 93 Properties: posthog.Properties{ 94 + "pull_at": comment.Subject, 95 "mentions": mentions, 96 }, 97 })
+4 -4
appview/pages/templates/repo/pulls/pull.html
··· 505 {{ end }} 506 507 {{ define "submissionComment" }} 508 - <div id="comment-{{.ID}}" class="flex gap-2 -ml-4 py-4 w-full mx-auto"> 509 <!-- left column: profile picture --> 510 <div class="flex-shrink-0"> 511 <img 512 - src="{{ tinyAvatar .OwnerDid }}" 513 alt="" 514 class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 515 /> ··· 518 <div class="flex-1 min-w-0"> 519 <!-- Row 1: Author and timestamp --> 520 <div class="text-sm text-gray-500 dark:text-gray-400 flex items-center gap-1"> 521 - <span>{{ resolve .OwnerDid }}</span> 522 <span class="before:content-['路']"></span> 523 - <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" href="#comment-{{.ID}}"> 524 {{ template "repo/fragments/time" .Created }} 525 </a> 526 </div>
··· 505 {{ end }} 506 507 {{ define "submissionComment" }} 508 + <div id="comment-{{.Id}}" class="flex gap-2 -ml-4 py-4 w-full mx-auto"> 509 <!-- left column: profile picture --> 510 <div class="flex-shrink-0"> 511 <img 512 + src="{{ tinyAvatar .Did.String }}" 513 alt="" 514 class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 515 /> ··· 518 <div class="flex-1 min-w-0"> 519 <!-- Row 1: Author and timestamp --> 520 <div class="text-sm text-gray-500 dark:text-gray-400 flex items-center gap-1"> 521 + <span>{{ resolve .Did.String }}</span> 522 <span class="before:content-['路']"></span> 523 + <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" href="#comment-{{.Id}}"> 524 {{ template "repo/fragments/time" .Created }} 525 </a> 526 </div>
+1 -1
appview/pulls/opengraph.go
··· 277 } 278 279 // Get comment count from database 280 - comments, err := db.GetPullComments(s.db, orm.FilterEq("pull_id", pull.ID)) 281 if err != nil { 282 log.Printf("failed to get pull comments: %v", err) 283 }
··· 277 } 278 279 // Get comment count from database 280 + comments, err := db.GetComments(s.db, orm.FilterEq("subject_at", pull.AtUri())) 281 if err != nil { 282 log.Printf("failed to get pull comments: %v", err) 283 }
+25 -24
appview/pulls/pulls.go
··· 722 } 723 defer tx.Rollback() 724 725 - createdAt := time.Now().Format(time.RFC3339) 726 727 client, err := s.oauth.AuthorizedClient(r) 728 if err != nil { ··· 730 s.pages.Notice(w, "pull-comment", "Failed to create comment.") 731 return 732 } 733 - atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 734 - Collection: tangled.RepoPullCommentNSID, 735 - Repo: user.Active.Did, 736 - Rkey: tid.TID(), 737 Record: &lexutil.LexiconTypeDecoder{ 738 - Val: &tangled.RepoPullComment{ 739 - Pull: pull.AtUri().String(), 740 - Body: body, 741 - CreatedAt: createdAt, 742 - }, 743 }, 744 }) 745 if err != nil { ··· 748 return 749 } 750 751 - comment := &models.PullComment{ 752 - OwnerDid: user.Active.Did, 753 - RepoAt: f.RepoAt().String(), 754 - PullId: pull.PullId, 755 - Body: body, 756 - CommentAt: atResp.Uri, 757 - SubmissionId: pull.Submissions[roundNumber].ID, 758 - Mentions: mentions, 759 - References: references, 760 - } 761 - 762 // Create the pull comment in the database with the commentAt field 763 - commentId, err := db.NewPullComment(tx, comment) 764 if err != nil { 765 log.Println("failed to create pull comment", err) 766 s.pages.Notice(w, "pull-comment", "Failed to create comment.") ··· 774 return 775 } 776 777 - s.notifier.NewPullComment(r.Context(), comment, mentions) 778 779 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f) 780 - s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", ownerSlashRepo, pull.PullId, commentId)) 781 return 782 } 783 }
··· 722 } 723 defer tx.Rollback() 724 725 + comment := models.Comment{ 726 + Did: syntax.DID(user.Active.Did), 727 + Rkey: tid.TID(), 728 + Subject: pull.AtUri(), 729 + ReplyTo: nil, 730 + Body: body, 731 + Created: time.Now(), 732 + Mentions: mentions, 733 + References: references, 734 + PullSubmissionId: &pull.Submissions[roundNumber].ID, 735 + } 736 + if err = comment.Validate(); err != nil { 737 + log.Println("failed to validate comment", err) 738 + s.pages.Notice(w, "pull-comment", "Failed to create comment.") 739 + return 740 + } 741 + record := comment.AsRecord() 742 743 client, err := s.oauth.AuthorizedClient(r) 744 if err != nil { ··· 746 s.pages.Notice(w, "pull-comment", "Failed to create comment.") 747 return 748 } 749 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 750 + Collection: tangled.CommentNSID, 751 + Repo: comment.Did.String(), 752 + Rkey: comment.Rkey, 753 Record: &lexutil.LexiconTypeDecoder{ 754 + Val: &record, 755 }, 756 }) 757 if err != nil { ··· 760 return 761 } 762 763 // Create the pull comment in the database with the commentAt field 764 + err = db.PutComment(tx, &comment) 765 if err != nil { 766 log.Println("failed to create pull comment", err) 767 s.pages.Notice(w, "pull-comment", "Failed to create comment.") ··· 775 return 776 } 777 778 + s.notifier.NewPullComment(r.Context(), &comment, mentions) 779 780 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f) 781 + s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", ownerSlashRepo, pull.PullId, comment.Id)) 782 return 783 } 784 }

History

8 rounds 4 comments
sign up or login to add to the discussion
1 commit
expand
appview: replace PullComment to Comment
2/3 timeout, 1/3 success
expand
merge conflicts detected
expand
  • appview/notify/db/db.go:260
  • appview/notify/merged_notifier.go:81
  • appview/notify/notifier.go:22
  • appview/pulls/opengraph.go:277
expand 0 comments
1 commit
expand
appview: replace PullComment to Comment
2/3 failed, 1/3 success
expand
expand 0 comments
1 commit
expand
appview: replace PullComment to Comment
3/3 success
expand
expand 0 comments
1 commit
expand
appview: replace PullComment to Comment
3/3 success
expand
expand 1 comment

here, the sort order is reversed, it should be .Before.

1 commit
expand
appview: replace PullComment to Comment
1/3 failed, 2/3 success
expand
expand 0 comments
1 commit
expand
appview: replace PullComment to Comment
1/3 failed, 2/3 success
expand
expand 0 comments
1 commit
expand
appview: replace PullComment to Comment
3/3 success
expand
expand 3 comments

here will this delete all comments made on tangled thus far? don't think this is the right approach if so.

I should've been more precise about my expression. Existing comments won't be dropped as they are stored in issue_comments and pull_comments table (I'm just dropping legacy table with same name there), but yeah this PR itself doesn't include the backward compatibility layer yet so old comments won't be rendered.

Honestly this PR hasn't been updated since you two suggested to just ingest old records. I'll push more commits on top of this stack.

As a side note: I'd suggest to not publish the new sh.tangled.comment lexicon yet.

This unified comment record can be used for PR review comments and it might make us to modify the schema again. I think it would be safest to publish this when PR redesign discussion is finished.

1 commit
expand
appview: replace PullComment to Comment
3/3 success
expand
expand 0 comments