This repository has no description
1package models
2
3import (
4 "fmt"
5 "sort"
6 "time"
7
8 "github.com/bluesky-social/indigo/atproto/syntax"
9 "tangled.org/core/api/tangled"
10)
11
12type Issue struct {
13 Id int64
14 Did string
15 Rkey string
16 RepoDid syntax.DID
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 Mentions []syntax.DID
25 References []syntax.ATURI
26
27 // optionally, populate this when querying for reverse mappings
28 // like comment counts, parent repo etc.
29 Comments []Comment
30 Labels LabelState
31 Repo *Repo
32}
33
34func (i *Issue) AtUri() syntax.ATURI {
35 return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueNSID, i.Rkey))
36}
37
38func (i *Issue) AsRecord() tangled.RepoIssue {
39 mentions := make([]string, len(i.Mentions))
40 for i, did := range i.Mentions {
41 mentions[i] = string(did)
42 }
43 references := make([]string, len(i.References))
44 for i, uri := range i.References {
45 references[i] = string(uri)
46 }
47 rec := tangled.RepoIssue{
48 Repo: string(i.RepoDid),
49 Title: i.Title,
50 Body: &i.Body,
51 Mentions: mentions,
52 References: references,
53 CreatedAt: i.Created.Format(time.RFC3339),
54 }
55 return rec
56}
57
58func (i *Issue) State() string {
59 if i.Open {
60 return "open"
61 }
62 return "closed"
63}
64
65type CommentListItem struct {
66 Self *Comment
67 Replies []*Comment
68}
69
70func (it *CommentListItem) Participants() []syntax.DID {
71 participantSet := make(map[syntax.DID]struct{})
72 participants := []syntax.DID{}
73
74 addParticipant := func(did syntax.DID) {
75 if _, exists := participantSet[did]; !exists {
76 participantSet[did] = struct{}{}
77 participants = append(participants, did)
78 }
79 }
80
81 addParticipant(syntax.DID(it.Self.Did))
82
83 for _, c := range it.Replies {
84 addParticipant(syntax.DID(c.Did))
85 }
86
87 return participants
88}
89
90func (i *Issue) CommentList() []CommentListItem {
91 // Create a map to quickly find comments by their aturi
92 toplevel := make(map[syntax.ATURI]*CommentListItem)
93 var replies []*Comment
94
95 // collect top level comments into the map
96 for _, comment := range i.Comments {
97 if comment.IsTopLevel() {
98 toplevel[comment.AtUri()] = &CommentListItem{
99 Self: &comment,
100 }
101 } else {
102 replies = append(replies, &comment)
103 }
104 }
105
106 for _, r := range replies {
107 if r.ReplyTo == nil {
108 continue
109 }
110 if parent, exists := toplevel[syntax.ATURI(r.ReplyTo.Uri)]; exists {
111 parent.Replies = append(parent.Replies, r)
112 }
113 }
114
115 var listing []CommentListItem
116 for _, v := range toplevel {
117 listing = append(listing, *v)
118 }
119
120 // sort everything
121 sortFunc := func(a, b *Comment) bool {
122 return a.Created.Before(b.Created)
123 }
124 sort.Slice(listing, func(i, j int) bool {
125 return sortFunc(listing[i].Self, listing[j].Self)
126 })
127 for _, r := range listing {
128 sort.Slice(r.Replies, func(i, j int) bool {
129 return sortFunc(r.Replies[i], r.Replies[j])
130 })
131 }
132
133 return listing
134}
135
136func (i *Issue) Participants() []syntax.DID {
137 participantSet := make(map[syntax.DID]struct{})
138 participants := []syntax.DID{}
139
140 addParticipant := func(did syntax.DID) {
141 if _, exists := participantSet[did]; !exists {
142 participantSet[did] = struct{}{}
143 participants = append(participants, did)
144 }
145 }
146
147 addParticipant(syntax.DID(i.Did))
148
149 for _, c := range i.Comments {
150 addParticipant(c.Did)
151 }
152
153 return participants
154}
155
156func IssueFromRecord(did, rkey string, record tangled.RepoIssue) Issue {
157 created, err := time.Parse(time.RFC3339, record.CreatedAt)
158 if err != nil {
159 created = time.Now()
160 }
161
162 body := ""
163 if record.Body != nil {
164 body = *record.Body
165 }
166
167 return Issue{
168 RepoDid: syntax.DID(record.Repo),
169 Did: did,
170 Rkey: rkey,
171 Created: created,
172 Title: record.Title,
173 Body: body,
174 Open: true, // new issues are open by default
175 }
176}