···
6
-
mathrand "math/rand/v2"
···
// optionally, populate this when querying for reverse mappings
// like comment counts, parent repo etc.
28
-
Metadata *IssueMetadata
32
+
Comments []IssueComment
31
-
type IssueMetadata struct {
34
-
// labels, assignee etc.
36
+
func (i *Issue) AtUri() syntax.ATURI {
37
+
return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueNSID, i.Rkey))
37
-
type Comment struct {
40
+
func (i *Issue) AsRecord() tangled.RepoIssue {
41
+
return tangled.RepoIssue{
42
+
Repo: i.RepoAt.String(),
45
+
CreatedAt: i.Created.Format(time.RFC3339),
49
-
func (i *Issue) AtUri() syntax.ATURI {
50
-
return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.OwnerDid, tangled.RepoIssueNSID, i.Rkey))
49
+
type CommentListItem struct {
51
+
Replies []*IssueComment
54
+
func (i *Issue) CommentList() []CommentListItem {
55
+
// Create a map to quickly find comments by their aturi
56
+
toplevel := make(map[string]*CommentListItem)
57
+
var replies []*IssueComment
59
+
// collect top level comments into the map
60
+
for _, comment := range i.Comments {
61
+
if comment.IsTopLevel() {
62
+
toplevel[comment.AtUri().String()] = &CommentListItem{
66
+
replies = append(replies, &comment)
70
+
for _, r := range replies {
71
+
parentAt := *r.ReplyTo
72
+
if parent, exists := toplevel[parentAt]; exists {
73
+
parent.Replies = append(parent.Replies, r)
77
+
var listing []CommentListItem
78
+
for _, v := range toplevel {
79
+
listing = append(listing, *v)
83
+
sortFunc := func(a, b *IssueComment) bool {
84
+
return a.Created.Before(b.Created)
86
+
sort.Slice(listing, func(i, j int) bool {
87
+
return sortFunc(listing[i].Self, listing[j].Self)
89
+
for _, r := range listing {
90
+
sort.Slice(r.Replies, func(i, j int) bool {
91
+
return sortFunc(r.Replies[i], r.Replies[j])
func IssueFromRecord(did, rkey string, record tangled.RepoIssue) Issue {
···
65
-
RepoAt: syntax.ATURI(record.Repo),
69
-
Title: record.Title,
71
-
Open: true, // new issues are open by default
110
+
RepoAt: syntax.ATURI(record.Repo),
114
+
Title: record.Title,
116
+
Open: true, // new issues are open by default
75
-
func ResolveIssueFromAtUri(e Execer, issueUri syntax.ATURI) (syntax.ATURI, int, error) {
76
-
ownerDid := issueUri.Authority().String()
77
-
issueRkey := issueUri.RecordKey().String()
120
+
type IssueComment struct {
132
+
func (i *IssueComment) AtUri() syntax.ATURI {
133
+
return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueCommentNSID, i.Rkey))
82
-
query := `select repo_at, issue_id from issues where owner_did = ? and rkey = ?`
83
-
err := e.QueryRow(query, ownerDid, issueRkey).Scan(&repoAt, &issueId)
136
+
func (i *IssueComment) AsRecord() tangled.RepoIssueComment {
137
+
return tangled.RepoIssueComment{
140
+
CreatedAt: i.Created.Format(time.RFC3339),
141
+
ReplyTo: i.ReplyTo,
88
-
return syntax.ATURI(repoAt), issueId, nil
145
+
func (i *IssueComment) IsTopLevel() bool {
146
+
return i.ReplyTo == nil
91
-
func IssueCommentFromRecord(e Execer, did, rkey string, record tangled.RepoIssueComment) (Comment, error) {
149
+
func IssueCommentFromRecord(e Execer, did, rkey string, record tangled.RepoIssueComment) (*IssueComment, error) {
created, err := time.Parse(time.RFC3339, record.CreatedAt)
98
-
if record.Owner != nil {
99
-
ownerDid = *record.Owner
102
-
issueUri, err := syntax.ParseATURI(record.Issue)
104
-
return Comment{}, err
107
-
repoAt, issueId, err := ResolveIssueFromAtUri(e, issueUri)
109
-
return Comment{}, err
157
+
if _, err = syntax.ParseATURI(record.Issue); err != nil {
112
-
comment := Comment{
113
-
OwnerDid: ownerDid,
118
-
CommentId: mathrand.IntN(1000000),
161
+
comment := IssueComment{
165
+
IssueAt: record.Issue,
166
+
ReplyTo: record.ReplyTo,
122
-
return comment, nil
170
+
return &comment, nil
func NewIssue(tx *sql.Tx, issue *Issue) error {