this repo has no description

appview/models: move db.Issue* into models

- move db.{Issue,IssueComment} into models
- move auxilliary funcs like CommentTree into models

Signed-off-by: oppiliappan <me@oppi.li>

Changed files
+249 -238
appview
+15 -200
appview/db/issues.go
··· 10 "time" 11 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 - "tangled.org/core/api/tangled" 14 "tangled.org/core/appview/models" 15 "tangled.org/core/appview/pagination" 16 ) 17 18 - type Issue struct { 19 - Id int64 20 - Did string 21 - Rkey string 22 - RepoAt syntax.ATURI 23 - IssueId int 24 - Created time.Time 25 - Edited *time.Time 26 - Deleted *time.Time 27 - Title string 28 - Body string 29 - Open bool 30 - 31 - // optionally, populate this when querying for reverse mappings 32 - // like comment counts, parent repo etc. 33 - Comments []IssueComment 34 - Labels models.LabelState 35 - Repo *models.Repo 36 - } 37 - 38 - func (i *Issue) AtUri() syntax.ATURI { 39 - return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueNSID, i.Rkey)) 40 - } 41 - 42 - func (i *Issue) AsRecord() tangled.RepoIssue { 43 - return tangled.RepoIssue{ 44 - Repo: i.RepoAt.String(), 45 - Title: i.Title, 46 - Body: &i.Body, 47 - CreatedAt: i.Created.Format(time.RFC3339), 48 - } 49 - } 50 - 51 - func (i *Issue) State() string { 52 - if i.Open { 53 - return "open" 54 - } 55 - return "closed" 56 - } 57 - 58 - type CommentListItem struct { 59 - Self *IssueComment 60 - Replies []*IssueComment 61 - } 62 - 63 - func (i *Issue) CommentList() []CommentListItem { 64 - // Create a map to quickly find comments by their aturi 65 - toplevel := make(map[string]*CommentListItem) 66 - var replies []*IssueComment 67 - 68 - // collect top level comments into the map 69 - for _, comment := range i.Comments { 70 - if comment.IsTopLevel() { 71 - toplevel[comment.AtUri().String()] = &CommentListItem{ 72 - Self: &comment, 73 - } 74 - } else { 75 - replies = append(replies, &comment) 76 - } 77 - } 78 - 79 - for _, r := range replies { 80 - parentAt := *r.ReplyTo 81 - if parent, exists := toplevel[parentAt]; exists { 82 - parent.Replies = append(parent.Replies, r) 83 - } 84 - } 85 - 86 - var listing []CommentListItem 87 - for _, v := range toplevel { 88 - listing = append(listing, *v) 89 - } 90 - 91 - // sort everything 92 - sortFunc := func(a, b *IssueComment) bool { 93 - return a.Created.Before(b.Created) 94 - } 95 - sort.Slice(listing, func(i, j int) bool { 96 - return sortFunc(listing[i].Self, listing[j].Self) 97 - }) 98 - for _, r := range listing { 99 - sort.Slice(r.Replies, func(i, j int) bool { 100 - return sortFunc(r.Replies[i], r.Replies[j]) 101 - }) 102 - } 103 - 104 - return listing 105 - } 106 - 107 - func (i *Issue) Participants() []string { 108 - participantSet := make(map[string]struct{}) 109 - participants := []string{} 110 - 111 - addParticipant := func(did string) { 112 - if _, exists := participantSet[did]; !exists { 113 - participantSet[did] = struct{}{} 114 - participants = append(participants, did) 115 - } 116 - } 117 - 118 - addParticipant(i.Did) 119 - 120 - for _, c := range i.Comments { 121 - addParticipant(c.Did) 122 - } 123 - 124 - return participants 125 - } 126 - 127 - func IssueFromRecord(did, rkey string, record tangled.RepoIssue) Issue { 128 - created, err := time.Parse(time.RFC3339, record.CreatedAt) 129 - if err != nil { 130 - created = time.Now() 131 - } 132 - 133 - body := "" 134 - if record.Body != nil { 135 - body = *record.Body 136 - } 137 - 138 - return Issue{ 139 - RepoAt: syntax.ATURI(record.Repo), 140 - Did: did, 141 - Rkey: rkey, 142 - Created: created, 143 - Title: record.Title, 144 - Body: body, 145 - Open: true, // new issues are open by default 146 - } 147 - } 148 - 149 - type IssueComment struct { 150 - Id int64 151 - Did string 152 - Rkey string 153 - IssueAt string 154 - ReplyTo *string 155 - Body string 156 - Created time.Time 157 - Edited *time.Time 158 - Deleted *time.Time 159 - } 160 - 161 - func (i *IssueComment) AtUri() syntax.ATURI { 162 - return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueCommentNSID, i.Rkey)) 163 - } 164 - 165 - func (i *IssueComment) AsRecord() tangled.RepoIssueComment { 166 - return tangled.RepoIssueComment{ 167 - Body: i.Body, 168 - Issue: i.IssueAt, 169 - CreatedAt: i.Created.Format(time.RFC3339), 170 - ReplyTo: i.ReplyTo, 171 - } 172 - } 173 - 174 - func (i *IssueComment) IsTopLevel() bool { 175 - return i.ReplyTo == nil 176 - } 177 - 178 - func IssueCommentFromRecord(did, rkey string, record tangled.RepoIssueComment) (*IssueComment, error) { 179 - created, err := time.Parse(time.RFC3339, record.CreatedAt) 180 - if err != nil { 181 - created = time.Now() 182 - } 183 - 184 - ownerDid := did 185 - 186 - if _, err = syntax.ParseATURI(record.Issue); err != nil { 187 - return nil, err 188 - } 189 - 190 - comment := IssueComment{ 191 - Did: ownerDid, 192 - Rkey: rkey, 193 - Body: record.Body, 194 - IssueAt: record.Issue, 195 - ReplyTo: record.ReplyTo, 196 - Created: created, 197 - } 198 - 199 - return &comment, nil 200 - } 201 - 202 - func PutIssue(tx *sql.Tx, issue *Issue) error { 203 // ensure sequence exists 204 _, err := tx.Exec(` 205 insert or ignore into repo_issue_seqs (repo_at, next_issue_id) ··· 234 } 235 } 236 237 - func createNewIssue(tx *sql.Tx, issue *Issue) error { 238 // get next issue_id 239 var newIssueId int 240 err := tx.QueryRow(` ··· 257 return row.Scan(&issue.Id, &issue.IssueId) 258 } 259 260 - func updateIssue(tx *sql.Tx, issue *Issue) error { 261 // update existing issue 262 _, err := tx.Exec(` 263 update issues ··· 267 return err 268 } 269 270 - func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]Issue, error) { 271 - issueMap := make(map[string]*Issue) // at-uri -> issue 272 273 var conditions []string 274 var args []any ··· 323 defer rows.Close() 324 325 for rows.Next() { 326 - var issue Issue 327 var createdAt string 328 var editedAt, deletedAt sql.Null[string] 329 var rowNum int64 ··· 416 } 417 } 418 419 - var issues []Issue 420 for _, i := range issueMap { 421 issues = append(issues, *i) 422 } ··· 428 return issues, nil 429 } 430 431 - func GetIssues(e Execer, filters ...filter) ([]Issue, error) { 432 return GetIssuesPaginated(e, pagination.FirstPage(), filters...) 433 } 434 435 - func GetIssue(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, error) { 436 query := `select id, owner_did, rkey, created, title, body, open from issues where repo_at = ? and issue_id = ?` 437 row := e.QueryRow(query, repoAt, issueId) 438 439 - var issue Issue 440 var createdAt string 441 err := row.Scan(&issue.Id, &issue.Did, &issue.Rkey, &createdAt, &issue.Title, &issue.Body, &issue.Open) 442 if err != nil { ··· 452 return &issue, nil 453 } 454 455 - func AddIssueComment(e Execer, c IssueComment) (int64, error) { 456 result, err := e.Exec( 457 `insert into issue_comments ( 458 did, ··· 514 return err 515 } 516 517 - func GetIssueComments(e Execer, filters ...filter) ([]IssueComment, error) { 518 - var comments []IssueComment 519 520 var conditions []string 521 var args []any ··· 551 } 552 553 for rows.Next() { 554 - var comment IssueComment 555 var created string 556 var rkey, edited, deleted, replyTo sql.Null[string] 557 err := rows.Scan( ··· 670 671 var count models.IssueCount 672 if err := row.Scan(&count.Open, &count.Closed); err != nil { 673 - return models.IssueCount{0, 0}, err 674 } 675 676 return count, nil
··· 10 "time" 11 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 "tangled.org/core/appview/models" 14 "tangled.org/core/appview/pagination" 15 ) 16 17 + func PutIssue(tx *sql.Tx, issue *models.Issue) error { 18 // ensure sequence exists 19 _, err := tx.Exec(` 20 insert or ignore into repo_issue_seqs (repo_at, next_issue_id) ··· 49 } 50 } 51 52 + func createNewIssue(tx *sql.Tx, issue *models.Issue) error { 53 // get next issue_id 54 var newIssueId int 55 err := tx.QueryRow(` ··· 72 return row.Scan(&issue.Id, &issue.IssueId) 73 } 74 75 + func updateIssue(tx *sql.Tx, issue *models.Issue) error { 76 // update existing issue 77 _, err := tx.Exec(` 78 update issues ··· 82 return err 83 } 84 85 + func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]models.Issue, error) { 86 + issueMap := make(map[string]*models.Issue) // at-uri -> issue 87 88 var conditions []string 89 var args []any ··· 138 defer rows.Close() 139 140 for rows.Next() { 141 + var issue models.Issue 142 var createdAt string 143 var editedAt, deletedAt sql.Null[string] 144 var rowNum int64 ··· 231 } 232 } 233 234 + var issues []models.Issue 235 for _, i := range issueMap { 236 issues = append(issues, *i) 237 } ··· 243 return issues, nil 244 } 245 246 + func GetIssues(e Execer, filters ...filter) ([]models.Issue, error) { 247 return GetIssuesPaginated(e, pagination.FirstPage(), filters...) 248 } 249 250 + func GetIssue(e Execer, repoAt syntax.ATURI, issueId int) (*models.Issue, error) { 251 query := `select id, owner_did, rkey, created, title, body, open from issues where repo_at = ? and issue_id = ?` 252 row := e.QueryRow(query, repoAt, issueId) 253 254 + var issue models.Issue 255 var createdAt string 256 err := row.Scan(&issue.Id, &issue.Did, &issue.Rkey, &createdAt, &issue.Title, &issue.Body, &issue.Open) 257 if err != nil { ··· 267 return &issue, nil 268 } 269 270 + func AddIssueComment(e Execer, c models.IssueComment) (int64, error) { 271 result, err := e.Exec( 272 `insert into issue_comments ( 273 did, ··· 329 return err 330 } 331 332 + func GetIssueComments(e Execer, filters ...filter) ([]models.IssueComment, error) { 333 + var comments []models.IssueComment 334 335 var conditions []string 336 var args []any ··· 366 } 367 368 for rows.Next() { 369 + var comment models.IssueComment 370 var created string 371 var rkey, edited, deleted, replyTo sql.Null[string] 372 err := rows.Scan( ··· 485 486 var count models.IssueCount 487 if err := row.Scan(&count.Open, &count.Closed); err != nil { 488 + return models.IssueCount{}, err 489 } 490 491 return count, nil
+1 -1
appview/db/profile.go
··· 50 } 51 52 type IssueEvents struct { 53 - Items []*Issue 54 } 55 56 type IssueEventStats struct {
··· 50 } 51 52 type IssueEvents struct { 53 + Items []*models.Issue 54 } 55 56 type IssueEventStats struct {
+2 -2
appview/ingester.go
··· 803 return err 804 } 805 806 - issue := db.IssueFromRecord(did, rkey, record) 807 808 if err := i.Validator.ValidateIssue(&issue); err != nil { 809 return fmt.Errorf("failed to validate issue: %w", err) ··· 869 return fmt.Errorf("invalid record: %w", err) 870 } 871 872 - comment, err := db.IssueCommentFromRecord(did, rkey, record) 873 if err != nil { 874 return fmt.Errorf("failed to parse comment from record: %w", err) 875 }
··· 803 return err 804 } 805 806 + issue := models.IssueFromRecord(did, rkey, record) 807 808 if err := i.Validator.ValidateIssue(&issue); err != nil { 809 return fmt.Errorf("failed to validate issue: %w", err) ··· 869 return fmt.Errorf("invalid record: %w", err) 870 } 871 872 + comment, err := models.IssueCommentFromRecord(did, rkey, record) 873 if err != nil { 874 return fmt.Errorf("failed to parse comment from record: %w", err) 875 }
+13 -13
appview/issues/issues.go
··· 76 return 77 } 78 79 - issue, ok := r.Context().Value("issue").(*db.Issue) 80 if !ok { 81 l.Error("failed to get issue") 82 rp.pages.Error404(w) ··· 130 return 131 } 132 133 - issue, ok := r.Context().Value("issue").(*db.Issue) 134 if !ok { 135 l.Error("failed to get issue") 136 rp.pages.Error404(w) ··· 226 return 227 } 228 229 - issue, ok := r.Context().Value("issue").(*db.Issue) 230 if !ok { 231 l.Error("failed to get issue") 232 rp.pages.Notice(w, noticeId, "Failed to delete issue.") ··· 273 return 274 } 275 276 - issue, ok := r.Context().Value("issue").(*db.Issue) 277 if !ok { 278 l.Error("failed to get issue") 279 rp.pages.Error404(w) ··· 319 return 320 } 321 322 - issue, ok := r.Context().Value("issue").(*db.Issue) 323 if !ok { 324 l.Error("failed to get issue") 325 rp.pages.Error404(w) ··· 363 return 364 } 365 366 - issue, ok := r.Context().Value("issue").(*db.Issue) 367 if !ok { 368 l.Error("failed to get issue") 369 rp.pages.Error404(w) ··· 382 replyTo = &replyToUri 383 } 384 385 - comment := db.IssueComment{ 386 Did: user.Did, 387 Rkey: tid.TID(), 388 IssueAt: issue.AtUri().String(), ··· 446 return 447 } 448 449 - issue, ok := r.Context().Value("issue").(*db.Issue) 450 if !ok { 451 l.Error("failed to get issue") 452 rp.pages.Error404(w) ··· 487 return 488 } 489 490 - issue, ok := r.Context().Value("issue").(*db.Issue) 491 if !ok { 492 l.Error("failed to get issue") 493 rp.pages.Error404(w) ··· 591 return 592 } 593 594 - issue, ok := r.Context().Value("issue").(*db.Issue) 595 if !ok { 596 l.Error("failed to get issue") 597 rp.pages.Error404(w) ··· 632 return 633 } 634 635 - issue, ok := r.Context().Value("issue").(*db.Issue) 636 if !ok { 637 l.Error("failed to get issue") 638 rp.pages.Error404(w) ··· 673 return 674 } 675 676 - issue, ok := r.Context().Value("issue").(*db.Issue) 677 if !ok { 678 l.Error("failed to get issue") 679 rp.pages.Error404(w) ··· 829 RepoInfo: f.RepoInfo(user), 830 }) 831 case http.MethodPost: 832 - issue := &db.Issue{ 833 RepoAt: f.RepoAt(), 834 Rkey: tid.TID(), 835 Title: r.FormValue("title"),
··· 76 return 77 } 78 79 + issue, ok := r.Context().Value("issue").(*models.Issue) 80 if !ok { 81 l.Error("failed to get issue") 82 rp.pages.Error404(w) ··· 130 return 131 } 132 133 + issue, ok := r.Context().Value("issue").(*models.Issue) 134 if !ok { 135 l.Error("failed to get issue") 136 rp.pages.Error404(w) ··· 226 return 227 } 228 229 + issue, ok := r.Context().Value("issue").(*models.Issue) 230 if !ok { 231 l.Error("failed to get issue") 232 rp.pages.Notice(w, noticeId, "Failed to delete issue.") ··· 273 return 274 } 275 276 + issue, ok := r.Context().Value("issue").(*models.Issue) 277 if !ok { 278 l.Error("failed to get issue") 279 rp.pages.Error404(w) ··· 319 return 320 } 321 322 + issue, ok := r.Context().Value("issue").(*models.Issue) 323 if !ok { 324 l.Error("failed to get issue") 325 rp.pages.Error404(w) ··· 363 return 364 } 365 366 + issue, ok := r.Context().Value("issue").(*models.Issue) 367 if !ok { 368 l.Error("failed to get issue") 369 rp.pages.Error404(w) ··· 382 replyTo = &replyToUri 383 } 384 385 + comment := models.IssueComment{ 386 Did: user.Did, 387 Rkey: tid.TID(), 388 IssueAt: issue.AtUri().String(), ··· 446 return 447 } 448 449 + issue, ok := r.Context().Value("issue").(*models.Issue) 450 if !ok { 451 l.Error("failed to get issue") 452 rp.pages.Error404(w) ··· 487 return 488 } 489 490 + issue, ok := r.Context().Value("issue").(*models.Issue) 491 if !ok { 492 l.Error("failed to get issue") 493 rp.pages.Error404(w) ··· 591 return 592 } 593 594 + issue, ok := r.Context().Value("issue").(*models.Issue) 595 if !ok { 596 l.Error("failed to get issue") 597 rp.pages.Error404(w) ··· 632 return 633 } 634 635 + issue, ok := r.Context().Value("issue").(*models.Issue) 636 if !ok { 637 l.Error("failed to get issue") 638 rp.pages.Error404(w) ··· 673 return 674 } 675 676 + issue, ok := r.Context().Value("issue").(*models.Issue) 677 if !ok { 678 l.Error("failed to get issue") 679 rp.pages.Error404(w) ··· 829 RepoInfo: f.RepoInfo(user), 830 }) 831 case http.MethodPost: 832 + issue := &models.Issue{ 833 RepoAt: f.RepoAt(), 834 Rkey: tid.TID(), 835 Title: r.FormValue("title"),
+194
appview/models/issue.go
···
··· 1 + package models 2 + 3 + import ( 4 + "fmt" 5 + "sort" 6 + "time" 7 + 8 + "github.com/bluesky-social/indigo/atproto/syntax" 9 + "tangled.org/core/api/tangled" 10 + ) 11 + 12 + type Issue struct { 13 + Id int64 14 + Did string 15 + Rkey string 16 + RepoAt syntax.ATURI 17 + IssueId int 18 + Created time.Time 19 + Edited *time.Time 20 + Deleted *time.Time 21 + Title string 22 + Body string 23 + Open bool 24 + 25 + // optionally, populate this when querying for reverse mappings 26 + // like comment counts, parent repo etc. 27 + Comments []IssueComment 28 + Labels LabelState 29 + Repo *Repo 30 + } 31 + 32 + func (i *Issue) AtUri() syntax.ATURI { 33 + return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueNSID, i.Rkey)) 34 + } 35 + 36 + func (i *Issue) AsRecord() tangled.RepoIssue { 37 + return tangled.RepoIssue{ 38 + Repo: i.RepoAt.String(), 39 + Title: i.Title, 40 + Body: &i.Body, 41 + CreatedAt: i.Created.Format(time.RFC3339), 42 + } 43 + } 44 + 45 + func (i *Issue) State() string { 46 + if i.Open { 47 + return "open" 48 + } 49 + return "closed" 50 + } 51 + 52 + type CommentListItem struct { 53 + Self *IssueComment 54 + Replies []*IssueComment 55 + } 56 + 57 + func (i *Issue) CommentList() []CommentListItem { 58 + // Create a map to quickly find comments by their aturi 59 + toplevel := make(map[string]*CommentListItem) 60 + var replies []*IssueComment 61 + 62 + // collect top level comments into the map 63 + for _, comment := range i.Comments { 64 + if comment.IsTopLevel() { 65 + toplevel[comment.AtUri().String()] = &CommentListItem{ 66 + Self: &comment, 67 + } 68 + } else { 69 + replies = append(replies, &comment) 70 + } 71 + } 72 + 73 + for _, r := range replies { 74 + parentAt := *r.ReplyTo 75 + if parent, exists := toplevel[parentAt]; exists { 76 + parent.Replies = append(parent.Replies, r) 77 + } 78 + } 79 + 80 + var listing []CommentListItem 81 + for _, v := range toplevel { 82 + listing = append(listing, *v) 83 + } 84 + 85 + // sort everything 86 + sortFunc := func(a, b *IssueComment) bool { 87 + return a.Created.Before(b.Created) 88 + } 89 + sort.Slice(listing, func(i, j int) bool { 90 + return sortFunc(listing[i].Self, listing[j].Self) 91 + }) 92 + for _, r := range listing { 93 + sort.Slice(r.Replies, func(i, j int) bool { 94 + return sortFunc(r.Replies[i], r.Replies[j]) 95 + }) 96 + } 97 + 98 + return listing 99 + } 100 + 101 + func (i *Issue) Participants() []string { 102 + participantSet := make(map[string]struct{}) 103 + participants := []string{} 104 + 105 + addParticipant := func(did string) { 106 + if _, exists := participantSet[did]; !exists { 107 + participantSet[did] = struct{}{} 108 + participants = append(participants, did) 109 + } 110 + } 111 + 112 + addParticipant(i.Did) 113 + 114 + for _, c := range i.Comments { 115 + addParticipant(c.Did) 116 + } 117 + 118 + return participants 119 + } 120 + 121 + func IssueFromRecord(did, rkey string, record tangled.RepoIssue) Issue { 122 + created, err := time.Parse(time.RFC3339, record.CreatedAt) 123 + if err != nil { 124 + created = time.Now() 125 + } 126 + 127 + body := "" 128 + if record.Body != nil { 129 + body = *record.Body 130 + } 131 + 132 + return Issue{ 133 + RepoAt: syntax.ATURI(record.Repo), 134 + Did: did, 135 + Rkey: rkey, 136 + Created: created, 137 + Title: record.Title, 138 + Body: body, 139 + Open: true, // new issues are open by default 140 + } 141 + } 142 + 143 + type IssueComment struct { 144 + Id int64 145 + Did string 146 + Rkey string 147 + IssueAt string 148 + ReplyTo *string 149 + Body string 150 + Created time.Time 151 + Edited *time.Time 152 + Deleted *time.Time 153 + } 154 + 155 + func (i *IssueComment) AtUri() syntax.ATURI { 156 + return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueCommentNSID, i.Rkey)) 157 + } 158 + 159 + func (i *IssueComment) AsRecord() tangled.RepoIssueComment { 160 + return tangled.RepoIssueComment{ 161 + Body: i.Body, 162 + Issue: i.IssueAt, 163 + CreatedAt: i.Created.Format(time.RFC3339), 164 + ReplyTo: i.ReplyTo, 165 + } 166 + } 167 + 168 + func (i *IssueComment) IsTopLevel() bool { 169 + return i.ReplyTo == nil 170 + } 171 + 172 + func IssueCommentFromRecord(did, rkey string, record tangled.RepoIssueComment) (*IssueComment, error) { 173 + created, err := time.Parse(time.RFC3339, record.CreatedAt) 174 + if err != nil { 175 + created = time.Now() 176 + } 177 + 178 + ownerDid := did 179 + 180 + if _, err = syntax.ParseATURI(record.Issue); err != nil { 181 + return nil, err 182 + } 183 + 184 + comment := IssueComment{ 185 + Did: ownerDid, 186 + Rkey: rkey, 187 + Body: record.Body, 188 + IssueAt: record.Issue, 189 + ReplyTo: record.ReplyTo, 190 + Created: created, 191 + } 192 + 193 + return &comment, nil 194 + }
+1 -1
appview/notify/merged_notifier.go
··· 34 } 35 } 36 37 - func (m *mergedNotifier) NewIssue(ctx context.Context, issue *db.Issue) { 38 for _, notifier := range m.notifiers { 39 notifier.NewIssue(ctx, issue) 40 }
··· 34 } 35 } 36 37 + func (m *mergedNotifier) NewIssue(ctx context.Context, issue *models.Issue) { 38 for _, notifier := range m.notifiers { 39 notifier.NewIssue(ctx, issue) 40 }
+2 -2
appview/notify/notifier.go
··· 13 NewStar(ctx context.Context, star *db.Star) 14 DeleteStar(ctx context.Context, star *db.Star) 15 16 - NewIssue(ctx context.Context, issue *db.Issue) 17 18 NewFollow(ctx context.Context, follow *models.Follow) 19 DeleteFollow(ctx context.Context, follow *models.Follow) ··· 38 func (m *BaseNotifier) NewStar(ctx context.Context, star *db.Star) {} 39 func (m *BaseNotifier) DeleteStar(ctx context.Context, star *db.Star) {} 40 41 - func (m *BaseNotifier) NewIssue(ctx context.Context, issue *db.Issue) {} 42 43 func (m *BaseNotifier) NewFollow(ctx context.Context, follow *models.Follow) {} 44 func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {}
··· 13 NewStar(ctx context.Context, star *db.Star) 14 DeleteStar(ctx context.Context, star *db.Star) 15 16 + NewIssue(ctx context.Context, issue *models.Issue) 17 18 NewFollow(ctx context.Context, follow *models.Follow) 19 DeleteFollow(ctx context.Context, follow *models.Follow) ··· 38 func (m *BaseNotifier) NewStar(ctx context.Context, star *db.Star) {} 39 func (m *BaseNotifier) DeleteStar(ctx context.Context, star *db.Star) {} 40 41 + func (m *BaseNotifier) NewIssue(ctx context.Context, issue *models.Issue) {} 42 43 func (m *BaseNotifier) NewFollow(ctx context.Context, follow *models.Follow) {} 44 func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {}
+13 -13
appview/pages/pages.go
··· 889 LoggedInUser *oauth.User 890 RepoInfo repoinfo.RepoInfo 891 Active string 892 - Issues []db.Issue 893 LabelDefs map[string]*models.LabelDefinition 894 Page pagination.Page 895 FilteringByOpen bool ··· 904 LoggedInUser *oauth.User 905 RepoInfo repoinfo.RepoInfo 906 Active string 907 - Issue *db.Issue 908 - CommentList []db.CommentListItem 909 LabelDefs map[string]*models.LabelDefinition 910 911 OrderedReactionKinds []db.ReactionKind ··· 921 type EditIssueParams struct { 922 LoggedInUser *oauth.User 923 RepoInfo repoinfo.RepoInfo 924 - Issue *db.Issue 925 Action string 926 } 927 ··· 944 type RepoNewIssueParams struct { 945 LoggedInUser *oauth.User 946 RepoInfo repoinfo.RepoInfo 947 - Issue *db.Issue // existing issue if any -- passed when editing 948 Active string 949 Action string 950 } ··· 958 type EditIssueCommentParams struct { 959 LoggedInUser *oauth.User 960 RepoInfo repoinfo.RepoInfo 961 - Issue *db.Issue 962 - Comment *db.IssueComment 963 } 964 965 func (p *Pages) EditIssueCommentFragment(w io.Writer, params EditIssueCommentParams) error { ··· 969 type ReplyIssueCommentPlaceholderParams struct { 970 LoggedInUser *oauth.User 971 RepoInfo repoinfo.RepoInfo 972 - Issue *db.Issue 973 - Comment *db.IssueComment 974 } 975 976 func (p *Pages) ReplyIssueCommentPlaceholderFragment(w io.Writer, params ReplyIssueCommentPlaceholderParams) error { ··· 980 type ReplyIssueCommentParams struct { 981 LoggedInUser *oauth.User 982 RepoInfo repoinfo.RepoInfo 983 - Issue *db.Issue 984 - Comment *db.IssueComment 985 } 986 987 func (p *Pages) ReplyIssueCommentFragment(w io.Writer, params ReplyIssueCommentParams) error { ··· 991 type IssueCommentBodyParams struct { 992 LoggedInUser *oauth.User 993 RepoInfo repoinfo.RepoInfo 994 - Issue *db.Issue 995 - Comment *db.IssueComment 996 } 997 998 func (p *Pages) IssueCommentBodyFragment(w io.Writer, params IssueCommentBodyParams) error {
··· 889 LoggedInUser *oauth.User 890 RepoInfo repoinfo.RepoInfo 891 Active string 892 + Issues []models.Issue 893 LabelDefs map[string]*models.LabelDefinition 894 Page pagination.Page 895 FilteringByOpen bool ··· 904 LoggedInUser *oauth.User 905 RepoInfo repoinfo.RepoInfo 906 Active string 907 + Issue *models.Issue 908 + CommentList []models.CommentListItem 909 LabelDefs map[string]*models.LabelDefinition 910 911 OrderedReactionKinds []db.ReactionKind ··· 921 type EditIssueParams struct { 922 LoggedInUser *oauth.User 923 RepoInfo repoinfo.RepoInfo 924 + Issue *models.Issue 925 Action string 926 } 927 ··· 944 type RepoNewIssueParams struct { 945 LoggedInUser *oauth.User 946 RepoInfo repoinfo.RepoInfo 947 + Issue *models.Issue // existing issue if any -- passed when editing 948 Active string 949 Action string 950 } ··· 958 type EditIssueCommentParams struct { 959 LoggedInUser *oauth.User 960 RepoInfo repoinfo.RepoInfo 961 + Issue *models.Issue 962 + Comment *models.IssueComment 963 } 964 965 func (p *Pages) EditIssueCommentFragment(w io.Writer, params EditIssueCommentParams) error { ··· 969 type ReplyIssueCommentPlaceholderParams struct { 970 LoggedInUser *oauth.User 971 RepoInfo repoinfo.RepoInfo 972 + Issue *models.Issue 973 + Comment *models.IssueComment 974 } 975 976 func (p *Pages) ReplyIssueCommentPlaceholderFragment(w io.Writer, params ReplyIssueCommentPlaceholderParams) error { ··· 980 type ReplyIssueCommentParams struct { 981 LoggedInUser *oauth.User 982 RepoInfo repoinfo.RepoInfo 983 + Issue *models.Issue 984 + Comment *models.IssueComment 985 } 986 987 func (p *Pages) ReplyIssueCommentFragment(w io.Writer, params ReplyIssueCommentParams) error { ··· 991 type IssueCommentBodyParams struct { 992 LoggedInUser *oauth.User 993 RepoInfo repoinfo.RepoInfo 994 + Issue *models.Issue 995 + Comment *models.IssueComment 996 } 997 998 func (p *Pages) IssueCommentBodyFragment(w io.Writer, params IssueCommentBodyParams) error {
+1 -1
appview/posthog/notifier.go
··· 57 } 58 } 59 60 - func (n *posthogNotifier) NewIssue(ctx context.Context, issue *db.Issue) { 61 err := n.client.Enqueue(posthog.Capture{ 62 DistinctId: issue.Did, 63 Event: "new_issue",
··· 57 } 58 } 59 60 + func (n *posthogNotifier) NewIssue(ctx context.Context, issue *models.Issue) { 61 err := n.client.Enqueue(posthog.Capture{ 62 DistinctId: issue.Did, 63 Event: "new_issue",
+2 -1
appview/repo/feed.go
··· 9 "time" 10 11 "tangled.org/core/appview/db" 12 "tangled.org/core/appview/pagination" 13 "tangled.org/core/appview/reporesolver" 14 ··· 108 return items, nil 109 } 110 111 - func (rp *Repo) createIssueItem(ctx context.Context, issue db.Issue, f *reporesolver.ResolvedRepo) (*feeds.Item, error) { 112 owner, err := rp.idResolver.ResolveIdent(ctx, issue.Did) 113 if err != nil { 114 return nil, err
··· 9 "time" 10 11 "tangled.org/core/appview/db" 12 + "tangled.org/core/appview/models" 13 "tangled.org/core/appview/pagination" 14 "tangled.org/core/appview/reporesolver" 15 ··· 109 return items, nil 110 } 111 112 + func (rp *Repo) createIssueItem(ctx context.Context, issue models.Issue, f *reporesolver.ResolvedRepo) (*feeds.Item, error) { 113 owner, err := rp.idResolver.ResolveIdent(ctx, issue.Did) 114 if err != nil { 115 return nil, err
+2 -2
appview/state/profile.go
··· 467 return nil 468 } 469 470 - func (s *State) addIssueItems(ctx context.Context, feed *feeds.Feed, issues []*db.Issue, author *feeds.Author) error { 471 for _, issue := range issues { 472 owner, err := s.idResolver.ResolveIdent(ctx, issue.Repo.Did) 473 if err != nil { ··· 499 } 500 } 501 502 - func (s *State) createIssueItem(issue *db.Issue, owner *identity.Identity, author *feeds.Author) *feeds.Item { 503 return &feeds.Item{ 504 Title: fmt.Sprintf("%s created issue '%s' in @%s/%s", author.Name, issue.Title, owner.Handle, issue.Repo.Name), 505 Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/issues/%d", s.config.Core.AppviewHost, owner.Handle, issue.Repo.Name, issue.IssueId), Type: "text/html", Rel: "alternate"},
··· 467 return nil 468 } 469 470 + func (s *State) addIssueItems(ctx context.Context, feed *feeds.Feed, issues []*models.Issue, author *feeds.Author) error { 471 for _, issue := range issues { 472 owner, err := s.idResolver.ResolveIdent(ctx, issue.Repo.Did) 473 if err != nil { ··· 499 } 500 } 501 502 + func (s *State) createIssueItem(issue *models.Issue, owner *identity.Identity, author *feeds.Author) *feeds.Item { 503 return &feeds.Item{ 504 Title: fmt.Sprintf("%s created issue '%s' in @%s/%s", author.Name, issue.Title, owner.Handle, issue.Repo.Name), 505 Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/issues/%d", s.config.Core.AppviewHost, owner.Handle, issue.Repo.Name, issue.IssueId), Type: "text/html", Rel: "alternate"},
+3 -2
appview/validator/issue.go
··· 5 "strings" 6 7 "tangled.org/core/appview/db" 8 ) 9 10 - func (v *Validator) ValidateIssueComment(comment *db.IssueComment) error { 11 // if comments have parents, only ingest ones that are 1 level deep 12 if comment.ReplyTo != nil { 13 parents, err := db.GetIssueComments(v.db, db.FilterEq("at_uri", *comment.ReplyTo)) ··· 32 return nil 33 } 34 35 - func (v *Validator) ValidateIssue(issue *db.Issue) error { 36 if issue.Title == "" { 37 return fmt.Errorf("issue title is empty") 38 }
··· 5 "strings" 6 7 "tangled.org/core/appview/db" 8 + "tangled.org/core/appview/models" 9 ) 10 11 + func (v *Validator) ValidateIssueComment(comment *models.IssueComment) error { 12 // if comments have parents, only ingest ones that are 1 level deep 13 if comment.ReplyTo != nil { 14 parents, err := db.GetIssueComments(v.db, db.FilterEq("at_uri", *comment.ReplyTo)) ··· 33 return nil 34 } 35 36 + func (v *Validator) ValidateIssue(issue *models.Issue) error { 37 if issue.Title == "" { 38 return fmt.Errorf("issue title is empty") 39 }