Monorepo for Tangled tangled.org

appview: replace IssueComment to Comment #866

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/3m7iohv2yb622
+98 -386
Diff #5
+6 -186
appview/db/issues.go
··· 100 100 } 101 101 102 102 func GetIssuesPaginated(e Execer, page pagination.Page, filters ...orm.Filter) ([]models.Issue, error) { 103 - issueMap := make(map[string]*models.Issue) // at-uri -> issue 103 + issueMap := make(map[syntax.ATURI]*models.Issue) // at-uri -> issue 104 104 105 105 var conditions []string 106 106 var args []any ··· 196 196 } 197 197 } 198 198 199 - atUri := issue.AtUri().String() 200 - issueMap[atUri] = &issue 199 + issueMap[issue.AtUri()] = &issue 201 200 } 202 201 203 202 // collect reverse repos ··· 229 228 // collect comments 230 229 issueAts := slices.Collect(maps.Keys(issueMap)) 231 230 232 - comments, err := GetIssueComments(e, orm.FilterIn("issue_at", issueAts)) 231 + comments, err := GetComments(e, orm.FilterIn("subject_at", issueAts)) 233 232 if err != nil { 234 233 return nil, fmt.Errorf("failed to query comments: %w", err) 235 234 } 236 235 for i := range comments { 237 - issueAt := comments[i].IssueAt 236 + issueAt := comments[i].Subject 238 237 if issue, ok := issueMap[issueAt]; ok { 239 238 issue.Comments = append(issue.Comments, comments[i]) 240 239 } ··· 246 245 return nil, fmt.Errorf("failed to query labels: %w", err) 247 246 } 248 247 for issueAt, labels := range allLabels { 249 - if issue, ok := issueMap[issueAt.String()]; ok { 248 + if issue, ok := issueMap[issueAt]; ok { 250 249 issue.Labels = labels 251 250 } 252 251 } ··· 257 256 return nil, fmt.Errorf("failed to query reference_links: %w", err) 258 257 } 259 258 for issueAt, references := range allReferencs { 260 - if issue, ok := issueMap[issueAt.String()]; ok { 259 + if issue, ok := issueMap[issueAt]; ok { 261 260 issue.References = references 262 261 } 263 262 } ··· 295 294 return GetIssuesPaginated(e, pagination.Page{}, filters...) 296 295 } 297 296 298 - func AddIssueComment(tx *sql.Tx, c models.IssueComment) (int64, error) { 299 - result, err := tx.Exec( 300 - `insert into issue_comments ( 301 - did, 302 - rkey, 303 - issue_at, 304 - body, 305 - reply_to, 306 - created, 307 - edited 308 - ) 309 - values (?, ?, ?, ?, ?, ?, null) 310 - on conflict(did, rkey) do update set 311 - issue_at = excluded.issue_at, 312 - body = excluded.body, 313 - edited = case 314 - when 315 - issue_comments.issue_at != excluded.issue_at 316 - or issue_comments.body != excluded.body 317 - or issue_comments.reply_to != excluded.reply_to 318 - then ? 319 - else issue_comments.edited 320 - end`, 321 - c.Did, 322 - c.Rkey, 323 - c.IssueAt, 324 - c.Body, 325 - c.ReplyTo, 326 - c.Created.Format(time.RFC3339), 327 - time.Now().Format(time.RFC3339), 328 - ) 329 - if err != nil { 330 - return 0, err 331 - } 332 - 333 - id, err := result.LastInsertId() 334 - if err != nil { 335 - return 0, err 336 - } 337 - 338 - if err := putReferences(tx, c.AtUri(), c.References); err != nil { 339 - return 0, fmt.Errorf("put reference_links: %w", err) 340 - } 341 - 342 - return id, nil 343 - } 344 - 345 - func DeleteIssueComments(e Execer, filters ...orm.Filter) error { 346 - var conditions []string 347 - var args []any 348 - for _, filter := range filters { 349 - conditions = append(conditions, filter.Condition()) 350 - args = append(args, filter.Arg()...) 351 - } 352 - 353 - whereClause := "" 354 - if conditions != nil { 355 - whereClause = " where " + strings.Join(conditions, " and ") 356 - } 357 - 358 - query := fmt.Sprintf(`update issue_comments set body = "", deleted = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now') %s`, whereClause) 359 - 360 - _, err := e.Exec(query, args...) 361 - return err 362 - } 363 - 364 - func GetIssueComments(e Execer, filters ...orm.Filter) ([]models.IssueComment, error) { 365 - commentMap := make(map[string]*models.IssueComment) 366 - 367 - var conditions []string 368 - var args []any 369 - for _, filter := range filters { 370 - conditions = append(conditions, filter.Condition()) 371 - args = append(args, filter.Arg()...) 372 - } 373 - 374 - whereClause := "" 375 - if conditions != nil { 376 - whereClause = " where " + strings.Join(conditions, " and ") 377 - } 378 - 379 - query := fmt.Sprintf(` 380 - select 381 - id, 382 - did, 383 - rkey, 384 - issue_at, 385 - reply_to, 386 - body, 387 - created, 388 - edited, 389 - deleted 390 - from 391 - issue_comments 392 - %s 393 - `, whereClause) 394 - 395 - rows, err := e.Query(query, args...) 396 - if err != nil { 397 - return nil, err 398 - } 399 - defer rows.Close() 400 - 401 - for rows.Next() { 402 - var comment models.IssueComment 403 - var created string 404 - var rkey, edited, deleted, replyTo sql.Null[string] 405 - err := rows.Scan( 406 - &comment.Id, 407 - &comment.Did, 408 - &rkey, 409 - &comment.IssueAt, 410 - &replyTo, 411 - &comment.Body, 412 - &created, 413 - &edited, 414 - &deleted, 415 - ) 416 - if err != nil { 417 - return nil, err 418 - } 419 - 420 - // this is a remnant from old times, newer comments always have rkey 421 - if rkey.Valid { 422 - comment.Rkey = rkey.V 423 - } 424 - 425 - if t, err := time.Parse(time.RFC3339, created); err == nil { 426 - comment.Created = t 427 - } 428 - 429 - if edited.Valid { 430 - if t, err := time.Parse(time.RFC3339, edited.V); err == nil { 431 - comment.Edited = &t 432 - } 433 - } 434 - 435 - if deleted.Valid { 436 - if t, err := time.Parse(time.RFC3339, deleted.V); err == nil { 437 - comment.Deleted = &t 438 - } 439 - } 440 - 441 - if replyTo.Valid { 442 - comment.ReplyTo = &replyTo.V 443 - } 444 - 445 - atUri := comment.AtUri().String() 446 - commentMap[atUri] = &comment 447 - } 448 - 449 - if err = rows.Err(); err != nil { 450 - return nil, err 451 - } 452 - 453 - // collect references for each comments 454 - commentAts := slices.Collect(maps.Keys(commentMap)) 455 - allReferencs, err := GetReferencesAll(e, orm.FilterIn("from_at", commentAts)) 456 - if err != nil { 457 - return nil, fmt.Errorf("failed to query reference_links: %w", err) 458 - } 459 - for commentAt, references := range allReferencs { 460 - if comment, ok := commentMap[commentAt.String()]; ok { 461 - comment.References = references 462 - } 463 - } 464 - 465 - var comments []models.IssueComment 466 - for _, c := range commentMap { 467 - comments = append(comments, *c) 468 - } 469 - 470 - sort.Slice(comments, func(i, j int) bool { 471 - return comments[i].Created.After(comments[j].Created) 472 - }) 473 - 474 - return comments, nil 475 - } 476 - 477 297 func DeleteIssues(tx *sql.Tx, did, rkey string) error { 478 298 _, err := tx.Exec( 479 299 `delete from issues
+13 -24
appview/db/reference.go
··· 11 11 "tangled.org/core/orm" 12 12 ) 13 13 14 - // ValidateReferenceLinks resolves refLinks to Issue/PR/IssueComment/PullComment ATURIs. 14 + // ValidateReferenceLinks resolves refLinks to Issue/PR/Comment ATURIs. 15 15 // It will ignore missing refLinks. 16 16 func ValidateReferenceLinks(e Execer, refLinks []models.ReferenceLink) ([]syntax.ATURI, error) { 17 17 var ( ··· 53 53 values %s 54 54 ) 55 55 select 56 - i.did, i.rkey, 57 - c.did, c.rkey 56 + i.at_uri, c.at_uri 58 57 from input inp 59 58 join repos r 60 59 on r.did = inp.owner_did ··· 62 61 join issues i 63 62 on i.repo_at = r.at_uri 64 63 and i.issue_id = inp.issue_id 65 - left join issue_comments c 64 + left join comments c 66 65 on inp.comment_id is not null 67 - and c.issue_at = i.at_uri 66 + and c.subject_at = i.at_uri 68 67 and c.id = inp.comment_id 69 68 `, 70 69 strings.Join(vals, ","), ··· 79 78 80 79 for rows.Next() { 81 80 // Scan rows 82 - var issueOwner, issueRkey string 83 - var commentOwner, commentRkey sql.NullString 81 + var issueUri string 82 + var commentUri sql.NullString 84 83 var uri syntax.ATURI 85 - if err := rows.Scan(&issueOwner, &issueRkey, &commentOwner, &commentRkey); err != nil { 84 + if err := rows.Scan(&issueUri, &commentUri); err != nil { 86 85 return nil, err 87 86 } 88 - if commentOwner.Valid && commentRkey.Valid { 89 - uri = syntax.ATURI(fmt.Sprintf( 90 - "at://%s/%s/%s", 91 - commentOwner.String, 92 - tangled.RepoIssueCommentNSID, 93 - commentRkey.String, 94 - )) 87 + if commentUri.Valid { 88 + uri = syntax.ATURI(commentUri.String) 95 89 } else { 96 - uri = syntax.ATURI(fmt.Sprintf( 97 - "at://%s/%s/%s", 98 - issueOwner, 99 - tangled.RepoIssueNSID, 100 - issueRkey, 101 - )) 90 + uri = syntax.ATURI(issueUri) 102 91 } 103 92 uris = append(uris, uri) 104 93 } ··· 282 271 return nil, fmt.Errorf("get issue backlinks: %w", err) 283 272 } 284 273 backlinks = append(backlinks, ls...) 285 - ls, err = getIssueCommentBacklinks(e, backlinksMap[tangled.RepoIssueCommentNSID]) 274 + ls, err = getIssueCommentBacklinks(e, backlinksMap[tangled.CommentNSID]) 286 275 if err != nil { 287 276 return nil, fmt.Errorf("get issue_comment backlinks: %w", err) 288 277 } ··· 351 340 rows, err := e.Query( 352 341 fmt.Sprintf( 353 342 `select r.did, r.name, i.issue_id, c.id, i.title, i.open 354 - from issue_comments c 343 + from comments c 355 344 join issues i 356 - on i.at_uri = c.issue_at 345 + on i.at_uri = c.subject_at 357 346 join repos r 358 347 on r.at_uri = i.repo_at 359 348 where %s`,
+15 -6
appview/ingester.go
··· 885 885 } 886 886 887 887 switch e.Commit.Operation { 888 - case jmodels.CommitOperationCreate, jmodels.CommitOperationUpdate: 888 + case jmodels.CommitOperationUpdate: 889 889 raw := json.RawMessage(e.Commit.Record) 890 890 record := tangled.RepoIssueComment{} 891 891 err = json.Unmarshal(raw, &record) ··· 893 893 return fmt.Errorf("invalid record: %w", err) 894 894 } 895 895 896 - comment, err := models.IssueCommentFromRecord(did, rkey, record) 896 + // convert 'sh.tangled.repo.issue.comment' to 'sh.tangled.comment' 897 + comment, err := models.CommentFromRecord(syntax.DID(did), syntax.RecordKey(rkey), tangled.Comment{ 898 + Body: record.Body, 899 + CreatedAt: record.CreatedAt, 900 + Mentions: record.Mentions, 901 + References: record.References, 902 + ReplyTo: record.ReplyTo, 903 + Subject: record.Issue, 904 + }) 897 905 if err != nil { 898 906 return fmt.Errorf("failed to parse comment from record: %w", err) 899 907 } 900 908 901 - if err := i.Validator.ValidateIssueComment(comment); err != nil { 909 + if err := comment.Validate(); err != nil { 902 910 return fmt.Errorf("failed to validate comment: %w", err) 903 911 } 904 912 ··· 908 916 } 909 917 defer tx.Rollback() 910 918 911 - _, err = db.AddIssueComment(tx, *comment) 919 + err = db.PutComment(tx, comment) 912 920 if err != nil { 913 - return fmt.Errorf("failed to create issue comment: %w", err) 921 + return fmt.Errorf("failed to create comment: %w", err) 914 922 } 915 923 916 924 return tx.Commit() 917 925 918 926 case jmodels.CommitOperationDelete: 919 - if err := db.DeleteIssueComments( 927 + if err := db.DeleteComments( 920 928 ddb, 921 929 orm.FilterEq("did", did), 930 + orm.FilterEq("collection", e.Commit.Collection), 922 931 orm.FilterEq("rkey", rkey), 923 932 ); err != nil { 924 933 return fmt.Errorf("failed to delete issue comment record: %w", err)
+38 -36
appview/issues/issues.go
··· 403 403 404 404 body := r.FormValue("body") 405 405 if body == "" { 406 - rp.pages.Notice(w, "issue", "Body is required") 406 + rp.pages.Notice(w, "issue-comment", "Body is required") 407 407 return 408 408 } 409 409 410 - replyToUri := r.FormValue("reply-to") 411 - var replyTo *string 412 - if replyToUri != "" { 413 - replyTo = &replyToUri 410 + var replyTo *syntax.ATURI 411 + replyToRaw := r.FormValue("reply-to") 412 + if replyToRaw != "" { 413 + aturi, err := syntax.ParseATURI(replyToRaw) 414 + if err != nil { 415 + rp.pages.Notice(w, "issue-comment", "reply-to should be valid AT-URI") 416 + return 417 + } 418 + replyTo = &aturi 414 419 } 415 420 416 421 mentions, references := rp.mentionsResolver.Resolve(r.Context(), body) 417 422 418 - comment := models.IssueComment{ 419 - Did: user.Active.Did, 423 + comment := models.Comment{ 424 + Did: syntax.DID(user.Active.Did), 425 + Collection: tangled.CommentNSID, 420 426 Rkey: tid.TID(), 421 - IssueAt: issue.AtUri().String(), 427 + Subject: issue.AtUri(), 422 428 ReplyTo: replyTo, 423 429 Body: body, 424 430 Created: time.Now(), 425 431 Mentions: mentions, 426 432 References: references, 427 433 } 428 - if err = rp.validator.ValidateIssueComment(&comment); err != nil { 434 + if err = comment.Validate(); err != nil { 429 435 l.Error("failed to validate comment", "err", err) 430 436 rp.pages.Notice(w, "issue-comment", "Failed to create comment.") 431 437 return 432 438 } 433 - record := comment.AsRecord() 434 439 435 440 client, err := rp.oauth.AuthorizedClient(r) 436 441 if err != nil { ··· 441 446 442 447 // create a record first 443 448 resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 444 - Collection: tangled.RepoIssueCommentNSID, 445 - Repo: comment.Did, 449 + Collection: comment.Collection.String(), 450 + Repo: comment.Did.String(), 446 451 Rkey: comment.Rkey, 447 452 Record: &lexutil.LexiconTypeDecoder{ 448 - Val: &record, 453 + Val: comment.AsRecord(), 449 454 }, 450 455 }) 451 456 if err != nil { ··· 468 473 } 469 474 defer tx.Rollback() 470 475 471 - commentId, err := db.AddIssueComment(tx, comment) 476 + err = db.PutComment(tx, &comment) 472 477 if err != nil { 473 478 l.Error("failed to create comment", "err", err) 474 479 rp.pages.Notice(w, "issue-comment", "Failed to create comment.") ··· 484 489 // reset atUri to make rollback a no-op 485 490 atUri = "" 486 491 487 - // notify about the new comment 488 - comment.Id = commentId 489 - 490 492 rp.notifier.NewIssueComment(r.Context(), &comment, mentions) 491 493 492 494 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f) 493 - rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", ownerSlashRepo, issue.IssueId, commentId)) 495 + rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", ownerSlashRepo, issue.IssueId, comment.Id)) 494 496 } 495 497 496 498 func (rp *Issues) IssueComment(w http.ResponseWriter, r *http.Request) { ··· 505 507 } 506 508 507 509 commentId := chi.URLParam(r, "commentId") 508 - comments, err := db.GetIssueComments( 510 + comments, err := db.GetComments( 509 511 rp.db, 510 512 orm.FilterEq("id", commentId), 511 513 ) ··· 541 543 } 542 544 543 545 commentId := chi.URLParam(r, "commentId") 544 - comments, err := db.GetIssueComments( 546 + comments, err := db.GetComments( 545 547 rp.db, 546 548 orm.FilterEq("id", commentId), 547 549 ) ··· 557 559 } 558 560 comment := comments[0] 559 561 560 - if comment.Did != user.Active.Did { 562 + if comment.Did.String() != user.Active.Did { 561 563 l.Error("unauthorized comment edit", "expectedDid", comment.Did, "gotDid", user.Active.Did) 562 564 http.Error(w, "you are not the author of this comment", http.StatusUnauthorized) 563 565 return ··· 587 589 newComment.Edited = &now 588 590 newComment.Mentions, newComment.References = rp.mentionsResolver.Resolve(r.Context(), newBody) 589 591 590 - record := newComment.AsRecord() 591 - 592 592 tx, err := rp.db.Begin() 593 593 if err != nil { 594 594 l.Error("failed to start transaction", "err", err) ··· 597 597 } 598 598 defer tx.Rollback() 599 599 600 - _, err = db.AddIssueComment(tx, newComment) 600 + err = db.PutComment(tx, &newComment) 601 601 if err != nil { 602 602 l.Error("failed to perferom update-description query", "err", err) 603 603 rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") ··· 607 607 608 608 // rkey is optional, it was introduced later 609 609 if newComment.Rkey != "" { 610 + // TODO: update correct comment 611 + 610 612 // update the record on pds 611 - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Active.Did, comment.Rkey) 613 + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", newComment.Collection.String(), newComment.Did.String(), newComment.Rkey) 612 614 if err != nil { 613 615 l.Error("failed to get record", "err", err, "did", newComment.Did, "rkey", newComment.Rkey) 614 - rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.") 616 + rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update comment, no record found on PDS.") 615 617 return 616 618 } 617 619 618 620 _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 619 - Collection: tangled.RepoIssueCommentNSID, 620 - Repo: user.Active.Did, 621 + Collection: newComment.Collection.String(), 622 + Repo: newComment.Did.String(), 621 623 Rkey: newComment.Rkey, 622 624 SwapRecord: ex.Cid, 623 625 Record: &lexutil.LexiconTypeDecoder{ 624 - Val: &record, 626 + Val: newComment.AsRecord(), 625 627 }, 626 628 }) 627 629 if err != nil { ··· 651 653 } 652 654 653 655 commentId := chi.URLParam(r, "commentId") 654 - comments, err := db.GetIssueComments( 656 + comments, err := db.GetComments( 655 657 rp.db, 656 658 orm.FilterEq("id", commentId), 657 659 ) ··· 687 689 } 688 690 689 691 commentId := chi.URLParam(r, "commentId") 690 - comments, err := db.GetIssueComments( 692 + comments, err := db.GetComments( 691 693 rp.db, 692 694 orm.FilterEq("id", commentId), 693 695 ) ··· 723 725 } 724 726 725 727 commentId := chi.URLParam(r, "commentId") 726 - comments, err := db.GetIssueComments( 728 + comments, err := db.GetComments( 727 729 rp.db, 728 730 orm.FilterEq("id", commentId), 729 731 ) ··· 739 741 } 740 742 comment := comments[0] 741 743 742 - if comment.Did != user.Active.Did { 744 + if comment.Did.String() != user.Active.Did { 743 745 l.Error("unauthorized action", "expectedDid", comment.Did, "gotDid", user.Active.Did) 744 746 http.Error(w, "you are not the author of this comment", http.StatusUnauthorized) 745 747 return ··· 752 754 753 755 // optimistic deletion 754 756 deleted := time.Now() 755 - err = db.DeleteIssueComments(rp.db, orm.FilterEq("id", comment.Id)) 757 + err = db.DeleteComments(rp.db, orm.FilterEq("id", comment.Id)) 756 758 if err != nil { 757 759 l.Error("failed to delete comment", "err", err) 758 760 rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment") ··· 768 770 return 769 771 } 770 772 _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 771 - Collection: tangled.RepoIssueCommentNSID, 772 - Repo: user.Active.Did, 773 + Collection: comment.Collection.String(), 774 + Repo: comment.Did.String(), 773 775 Rkey: comment.Rkey, 774 776 }) 775 777 if err != nil {
+8 -89
appview/models/issue.go
··· 26 26 27 27 // optionally, populate this when querying for reverse mappings 28 28 // like comment counts, parent repo etc. 29 - Comments []IssueComment 29 + Comments []Comment 30 30 Labels LabelState 31 31 Repo *Repo 32 32 } ··· 62 62 } 63 63 64 64 type CommentListItem struct { 65 - Self *IssueComment 66 - Replies []*IssueComment 65 + Self *Comment 66 + Replies []*Comment 67 67 } 68 68 69 69 func (it *CommentListItem) Participants() []syntax.DID { ··· 88 88 89 89 func (i *Issue) CommentList() []CommentListItem { 90 90 // Create a map to quickly find comments by their aturi 91 - toplevel := make(map[string]*CommentListItem) 92 - var replies []*IssueComment 91 + toplevel := make(map[syntax.ATURI]*CommentListItem) 92 + var replies []*Comment 93 93 94 94 // collect top level comments into the map 95 95 for _, comment := range i.Comments { 96 96 if comment.IsTopLevel() { 97 - toplevel[comment.AtUri().String()] = &CommentListItem{ 97 + toplevel[comment.AtUri()] = &CommentListItem{ 98 98 Self: &comment, 99 99 } 100 100 } else { ··· 115 115 } 116 116 117 117 // sort everything 118 - sortFunc := func(a, b *IssueComment) bool { 118 + sortFunc := func(a, b *Comment) bool { 119 119 return a.Created.Before(b.Created) 120 120 } 121 121 sort.Slice(listing, func(i, j int) bool { ··· 144 144 addParticipant(i.Did) 145 145 146 146 for _, c := range i.Comments { 147 - addParticipant(c.Did) 147 + addParticipant(c.Did.String()) 148 148 } 149 149 150 150 return participants ··· 171 171 Open: true, // new issues are open by default 172 172 } 173 173 } 174 - 175 - type IssueComment struct { 176 - Id int64 177 - Did string 178 - Rkey string 179 - IssueAt string 180 - ReplyTo *string 181 - Body string 182 - Created time.Time 183 - Edited *time.Time 184 - Deleted *time.Time 185 - Mentions []syntax.DID 186 - References []syntax.ATURI 187 - } 188 - 189 - func (i *IssueComment) AtUri() syntax.ATURI { 190 - return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueCommentNSID, i.Rkey)) 191 - } 192 - 193 - func (i *IssueComment) AsRecord() tangled.RepoIssueComment { 194 - mentions := make([]string, len(i.Mentions)) 195 - for i, did := range i.Mentions { 196 - mentions[i] = string(did) 197 - } 198 - references := make([]string, len(i.References)) 199 - for i, uri := range i.References { 200 - references[i] = string(uri) 201 - } 202 - return tangled.RepoIssueComment{ 203 - Body: i.Body, 204 - Issue: i.IssueAt, 205 - CreatedAt: i.Created.Format(time.RFC3339), 206 - ReplyTo: i.ReplyTo, 207 - Mentions: mentions, 208 - References: references, 209 - } 210 - } 211 - 212 - func (i *IssueComment) IsTopLevel() bool { 213 - return i.ReplyTo == nil 214 - } 215 - 216 - func (i *IssueComment) IsReply() bool { 217 - return i.ReplyTo != nil 218 - } 219 - 220 - func IssueCommentFromRecord(did, rkey string, record tangled.RepoIssueComment) (*IssueComment, error) { 221 - created, err := time.Parse(time.RFC3339, record.CreatedAt) 222 - if err != nil { 223 - created = time.Now() 224 - } 225 - 226 - ownerDid := did 227 - 228 - if _, err = syntax.ParseATURI(record.Issue); err != nil { 229 - return nil, err 230 - } 231 - 232 - i := record 233 - mentions := make([]syntax.DID, len(record.Mentions)) 234 - for i, did := range record.Mentions { 235 - mentions[i] = syntax.DID(did) 236 - } 237 - references := make([]syntax.ATURI, len(record.References)) 238 - for i, uri := range i.References { 239 - references[i] = syntax.ATURI(uri) 240 - } 241 - 242 - comment := IssueComment{ 243 - Did: ownerDid, 244 - Rkey: rkey, 245 - Body: record.Body, 246 - IssueAt: record.Issue, 247 - ReplyTo: record.ReplyTo, 248 - Created: created, 249 - Mentions: mentions, 250 - References: references, 251 - } 252 - 253 - return &comment, nil 254 - }
+4 -4
appview/notify/db/db.go
··· 122 122 ) 123 123 } 124 124 125 - func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 126 - issues, err := db.GetIssues(n.db, orm.FilterEq("at_uri", comment.IssueAt)) 125 + func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 126 + issues, err := db.GetIssues(n.db, orm.FilterEq("at_uri", comment.Subject)) 127 127 if err != nil { 128 128 log.Printf("NewIssueComment: failed to get issues: %v", err) 129 129 return 130 130 } 131 131 if len(issues) == 0 { 132 - log.Printf("NewIssueComment: no issue found for %s", comment.IssueAt) 132 + log.Printf("NewIssueComment: no issue found for %s", comment.Subject) 133 133 return 134 134 } 135 135 issue := issues[0] ··· 147 147 148 148 // find the parent thread, and add all DIDs from here to the recipient list 149 149 for _, t := range issue.CommentList() { 150 - if t.Self.AtUri().String() == parentAtUri { 150 + if t.Self.AtUri() == parentAtUri { 151 151 for _, p := range t.Participants() { 152 152 recipients.Insert(p) 153 153 }
+1 -1
appview/notify/merged_notifier.go
··· 57 57 m.fanout("NewIssue", ctx, issue, mentions) 58 58 } 59 59 60 - func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 60 + func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 61 61 m.fanout("NewIssueComment", ctx, comment, mentions) 62 62 } 63 63
+2 -2
appview/notify/notifier.go
··· 14 14 DeleteStar(ctx context.Context, star *models.Star) 15 15 16 16 NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) 17 - NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) 17 + NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) 18 18 NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) 19 19 DeleteIssue(ctx context.Context, issue *models.Issue) 20 20 ··· 43 43 func (m *BaseNotifier) DeleteStar(ctx context.Context, star *models.Star) {} 44 44 45 45 func (m *BaseNotifier) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) {} 46 - func (m *BaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 46 + func (m *BaseNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 47 47 } 48 48 func (m *BaseNotifier) NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) {} 49 49 func (m *BaseNotifier) DeleteIssue(ctx context.Context, issue *models.Issue) {}
+3 -3
appview/notify/posthog/notifier.go
··· 179 179 } 180 180 } 181 181 182 - func (n *posthogNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 182 + func (n *posthogNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 183 183 err := n.client.Enqueue(posthog.Capture{ 184 - DistinctId: comment.Did, 184 + DistinctId: comment.Did.String(), 185 185 Event: "new_issue_comment", 186 186 Properties: posthog.Properties{ 187 - "issue_at": comment.IssueAt, 187 + "issue_at": comment.Subject, 188 188 "mentions": mentions, 189 189 }, 190 190 })
+4 -4
appview/pages/pages.go
··· 1004 1004 LoggedInUser *oauth.MultiAccountUser 1005 1005 RepoInfo repoinfo.RepoInfo 1006 1006 Issue *models.Issue 1007 - Comment *models.IssueComment 1007 + Comment *models.Comment 1008 1008 } 1009 1009 1010 1010 func (p *Pages) EditIssueCommentFragment(w io.Writer, params EditIssueCommentParams) error { ··· 1015 1015 LoggedInUser *oauth.MultiAccountUser 1016 1016 RepoInfo repoinfo.RepoInfo 1017 1017 Issue *models.Issue 1018 - Comment *models.IssueComment 1018 + Comment *models.Comment 1019 1019 } 1020 1020 1021 1021 func (p *Pages) ReplyIssueCommentPlaceholderFragment(w io.Writer, params ReplyIssueCommentPlaceholderParams) error { ··· 1026 1026 LoggedInUser *oauth.MultiAccountUser 1027 1027 RepoInfo repoinfo.RepoInfo 1028 1028 Issue *models.Issue 1029 - Comment *models.IssueComment 1029 + Comment *models.Comment 1030 1030 } 1031 1031 1032 1032 func (p *Pages) ReplyIssueCommentFragment(w io.Writer, params ReplyIssueCommentParams) error { ··· 1037 1037 LoggedInUser *oauth.MultiAccountUser 1038 1038 RepoInfo repoinfo.RepoInfo 1039 1039 Issue *models.Issue 1040 - Comment *models.IssueComment 1040 + Comment *models.Comment 1041 1041 } 1042 1042 1043 1043 func (p *Pages) IssueCommentBodyFragment(w io.Writer, params IssueCommentBodyParams) error {
+2 -2
appview/pages/templates/repo/issues/fragments/commentList.html
··· 42 42 <div class="rounded px-6 py-4 bg-white dark:bg-gray-800 flex gap-2 "> 43 43 <div class="flex-shrink-0"> 44 44 <img 45 - src="{{ tinyAvatar .Comment.Did }}" 45 + src="{{ tinyAvatar .Comment.Did.String }}" 46 46 alt="" 47 47 class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 48 48 /> ··· 58 58 <div class="py-4 pr-4 w-full mx-auto overflow-hidden flex gap-2 "> 59 59 <div class="flex-shrink-0"> 60 60 <img 61 - src="{{ tinyAvatar .Comment.Did }}" 61 + src="{{ tinyAvatar .Comment.Did.String }}" 62 62 alt="" 63 63 class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 64 64 />
+2 -2
appview/pages/templates/repo/issues/fragments/issueCommentHeader.html
··· 1 1 {{ define "repo/issues/fragments/issueCommentHeader" }} 2 2 <div class="flex flex-wrap items-center gap-2 text-sm text-gray-500 dark:text-gray-400 "> 3 - {{ resolve .Comment.Did }} 3 + {{ resolve .Comment.Did.String }} 4 4 {{ template "hats" $ }} 5 5 <span class="before:content-['路']"></span> 6 6 {{ template "timestamp" . }} 7 - {{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did) }} 7 + {{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did.String) }} 8 8 {{ if and $isCommentOwner (not .Comment.Deleted) }} 9 9 {{ template "editIssueComment" . }} 10 10 {{ template "deleteIssueComment" . }}
-27
appview/validator/issue.go
··· 4 4 "fmt" 5 5 "strings" 6 6 7 - "tangled.org/core/appview/db" 8 7 "tangled.org/core/appview/models" 9 - "tangled.org/core/orm" 10 8 ) 11 9 12 - func (v *Validator) ValidateIssueComment(comment *models.IssueComment) error { 13 - // if comments have parents, only ingest ones that are 1 level deep 14 - if comment.ReplyTo != nil { 15 - parents, err := db.GetIssueComments(v.db, orm.FilterEq("at_uri", *comment.ReplyTo)) 16 - if err != nil { 17 - return fmt.Errorf("failed to fetch parent comment: %w", err) 18 - } 19 - if len(parents) != 1 { 20 - return fmt.Errorf("incorrect number of parent comments returned: %d", len(parents)) 21 - } 22 - 23 - // depth check 24 - parent := parents[0] 25 - if parent.ReplyTo != nil { 26 - return fmt.Errorf("incorrect depth, this comment is replying at depth >1") 27 - } 28 - } 29 - 30 - if sb := strings.TrimSpace(v.sanitizer.SanitizeDefault(comment.Body)); sb == "" { 31 - return fmt.Errorf("body is empty after HTML sanitization") 32 - } 33 - 34 - return nil 35 - } 36 - 37 10 func (v *Validator) ValidateIssue(issue *models.Issue) error { 38 11 if issue.Title == "" { 39 12 return fmt.Errorf("issue title is empty")

History

8 rounds 1 comment
sign up or login to add to the discussion
1 commit
expand
appview: replace IssueComment to Comment
2/3 failed, 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 IssueComment to Comment
2/3 failed, 1/3 success
expand
expand 0 comments
1 commit
expand
appview: replace IssueComment to Comment
3/3 success
expand
expand 0 comments
1 commit
expand
appview: replace IssueComment to Comment
3/3 success
expand
expand 0 comments
1 commit
expand
appview: replace IssueComment to Comment
1/3 failed, 2/3 success
expand
expand 0 comments
1 commit
expand
appview: replace IssueComment to Comment
1/3 failed, 2/3 success
expand
expand 0 comments
1 commit
expand
appview: replace IssueComment to Comment
3/3 success
expand
expand 1 comment

imo: we should still continue to ingest sh.tangled.issue.comment (and pull comments), but just try to convert to the regular lexicon. this would only be used for backfill purposes, newer appviews should ideally not create the old NSID anymore.

the way i see it, the new lexicon is just an alias for the old ones, so the ingesters should be backwards compatible.

i will do a deeper dive into the code itself shortly.

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