···
-
mathrand "math/rand/v2"
···
// optionally, populate this when querying for reverse mappings
// like comment counts, parent repo etc.
-
Metadata *IssueMetadata
-
type IssueMetadata struct {
-
// labels, assignee etc.
-
func (i *Issue) AtUri() syntax.ATURI {
-
return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.OwnerDid, tangled.RepoIssueNSID, i.Rkey))
func IssueFromRecord(did, rkey string, record tangled.RepoIssue) Issue {
···
-
RepoAt: syntax.ATURI(record.Repo),
-
Open: true, // new issues are open by default
-
func ResolveIssueFromAtUri(e Execer, issueUri syntax.ATURI) (syntax.ATURI, int, error) {
-
ownerDid := issueUri.Authority().String()
-
issueRkey := issueUri.RecordKey().String()
-
query := `select repo_at, issue_id from issues where owner_did = ? and rkey = ?`
-
err := e.QueryRow(query, ownerDid, issueRkey).Scan(&repoAt, &issueId)
-
return syntax.ATURI(repoAt), issueId, nil
-
func IssueCommentFromRecord(e Execer, did, rkey string, record tangled.RepoIssueComment) (Comment, error) {
created, err := time.Parse(time.RFC3339, record.CreatedAt)
-
if record.Owner != nil {
-
ownerDid = *record.Owner
-
issueUri, err := syntax.ParseATURI(record.Issue)
-
repoAt, issueId, err := ResolveIssueFromAtUri(e, issueUri)
-
CommentId: mathrand.IntN(1000000),
func NewIssue(tx *sql.Tx, issue *Issue) error {
···
···
// optionally, populate this when querying for reverse mappings
// like comment counts, parent repo etc.
+
Comments []IssueComment
+
func (i *Issue) AtUri() syntax.ATURI {
+
return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueNSID, i.Rkey))
+
func (i *Issue) AsRecord() tangled.RepoIssue {
+
return tangled.RepoIssue{
+
Repo: i.RepoAt.String(),
+
CreatedAt: i.Created.Format(time.RFC3339),
+
type CommentListItem struct {
+
Replies []*IssueComment
+
func (i *Issue) CommentList() []CommentListItem {
+
// Create a map to quickly find comments by their aturi
+
toplevel := make(map[string]*CommentListItem)
+
var replies []*IssueComment
+
// collect top level comments into the map
+
for _, comment := range i.Comments {
+
if comment.IsTopLevel() {
+
toplevel[comment.AtUri().String()] = &CommentListItem{
+
replies = append(replies, &comment)
+
for _, r := range replies {
+
if parent, exists := toplevel[parentAt]; exists {
+
parent.Replies = append(parent.Replies, r)
+
var listing []CommentListItem
+
for _, v := range toplevel {
+
listing = append(listing, *v)
+
sortFunc := func(a, b *IssueComment) bool {
+
return a.Created.Before(b.Created)
+
sort.Slice(listing, func(i, j int) bool {
+
return sortFunc(listing[i].Self, listing[j].Self)
+
for _, r := range listing {
+
sort.Slice(r.Replies, func(i, j int) bool {
+
return sortFunc(r.Replies[i], r.Replies[j])
func IssueFromRecord(did, rkey string, record tangled.RepoIssue) Issue {
···
+
RepoAt: syntax.ATURI(record.Repo),
+
Open: true, // new issues are open by default
+
type IssueComment struct {
+
func (i *IssueComment) AtUri() syntax.ATURI {
+
return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueCommentNSID, i.Rkey))
+
func (i *IssueComment) AsRecord() tangled.RepoIssueComment {
+
return tangled.RepoIssueComment{
+
CreatedAt: i.Created.Format(time.RFC3339),
+
func (i *IssueComment) IsTopLevel() bool {
+
return i.ReplyTo == nil
+
func IssueCommentFromRecord(e Execer, did, rkey string, record tangled.RepoIssueComment) (*IssueComment, error) {
created, err := time.Parse(time.RFC3339, record.CreatedAt)
+
if _, err = syntax.ParseATURI(record.Issue); err != nil {
+
comment := IssueComment{
+
ReplyTo: record.ReplyTo,
func NewIssue(tx *sql.Tx, issue *Issue) error {