This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / appview / models / issue.go
5.7 kB 260 lines
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 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 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 []IssueComment 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 repoAtStr := i.RepoAt.String() 48 return tangled.RepoIssue{ 49 Repo: &repoAtStr, 50 Title: i.Title, 51 Body: &i.Body, 52 Mentions: mentions, 53 References: references, 54 CreatedAt: i.Created.Format(time.RFC3339), 55 } 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 *IssueComment 67 Replies []*IssueComment 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[string]*CommentListItem) 93 var replies []*IssueComment 94 95 // collect top level comments into the map 96 for _, comment := range i.Comments { 97 if comment.IsTopLevel() { 98 toplevel[comment.AtUri().String()] = &CommentListItem{ 99 Self: &comment, 100 } 101 } else { 102 replies = append(replies, &comment) 103 } 104 } 105 106 for _, r := range replies { 107 parentAt := *r.ReplyTo 108 if parent, exists := toplevel[parentAt]; exists { 109 parent.Replies = append(parent.Replies, r) 110 } 111 } 112 113 var listing []CommentListItem 114 for _, v := range toplevel { 115 listing = append(listing, *v) 116 } 117 118 // sort everything 119 sortFunc := func(a, b *IssueComment) bool { 120 return a.Created.Before(b.Created) 121 } 122 sort.Slice(listing, func(i, j int) bool { 123 return sortFunc(listing[i].Self, listing[j].Self) 124 }) 125 for _, r := range listing { 126 sort.Slice(r.Replies, func(i, j int) bool { 127 return sortFunc(r.Replies[i], r.Replies[j]) 128 }) 129 } 130 131 return listing 132} 133 134func (i *Issue) Participants() []string { 135 participantSet := make(map[string]struct{}) 136 participants := []string{} 137 138 addParticipant := func(did string) { 139 if _, exists := participantSet[did]; !exists { 140 participantSet[did] = struct{}{} 141 participants = append(participants, did) 142 } 143 } 144 145 addParticipant(i.Did) 146 147 for _, c := range i.Comments { 148 addParticipant(c.Did) 149 } 150 151 return participants 152} 153 154func IssueFromRecord(did, rkey string, record tangled.RepoIssue) Issue { 155 created, err := time.Parse(time.RFC3339, record.CreatedAt) 156 if err != nil { 157 created = time.Now() 158 } 159 160 body := "" 161 if record.Body != nil { 162 body = *record.Body 163 } 164 165 var repoAt syntax.ATURI 166 if record.Repo != nil { 167 repoAt = syntax.ATURI(*record.Repo) 168 } 169 170 return Issue{ 171 RepoAt: repoAt, 172 Did: did, 173 Rkey: rkey, 174 Created: created, 175 Title: record.Title, 176 Body: body, 177 Open: true, // new issues are open by default 178 } 179} 180 181type IssueComment struct { 182 Id int64 183 Did string 184 Rkey string 185 IssueAt string 186 ReplyTo *string 187 Body string 188 Created time.Time 189 Edited *time.Time 190 Deleted *time.Time 191 Mentions []syntax.DID 192 References []syntax.ATURI 193} 194 195func (i *IssueComment) AtUri() syntax.ATURI { 196 return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueCommentNSID, i.Rkey)) 197} 198 199func (i *IssueComment) AsRecord() tangled.RepoIssueComment { 200 mentions := make([]string, len(i.Mentions)) 201 for i, did := range i.Mentions { 202 mentions[i] = string(did) 203 } 204 references := make([]string, len(i.References)) 205 for i, uri := range i.References { 206 references[i] = string(uri) 207 } 208 return tangled.RepoIssueComment{ 209 Body: i.Body, 210 Issue: i.IssueAt, 211 CreatedAt: i.Created.Format(time.RFC3339), 212 ReplyTo: i.ReplyTo, 213 Mentions: mentions, 214 References: references, 215 } 216} 217 218func (i *IssueComment) IsTopLevel() bool { 219 return i.ReplyTo == nil 220} 221 222func (i *IssueComment) IsReply() bool { 223 return i.ReplyTo != nil 224} 225 226func IssueCommentFromRecord(did, rkey string, record tangled.RepoIssueComment) (*IssueComment, error) { 227 created, err := time.Parse(time.RFC3339, record.CreatedAt) 228 if err != nil { 229 created = time.Now() 230 } 231 232 ownerDid := did 233 234 if _, err = syntax.ParseATURI(record.Issue); err != nil { 235 return nil, err 236 } 237 238 i := record 239 mentions := make([]syntax.DID, len(record.Mentions)) 240 for i, did := range record.Mentions { 241 mentions[i] = syntax.DID(did) 242 } 243 references := make([]syntax.ATURI, len(record.References)) 244 for i, uri := range i.References { 245 references[i] = syntax.ATURI(uri) 246 } 247 248 comment := IssueComment{ 249 Did: ownerDid, 250 Rkey: rkey, 251 Body: record.Body, 252 IssueAt: record.Issue, 253 ReplyTo: record.ReplyTo, 254 Created: created, 255 Mentions: mentions, 256 References: references, 257 } 258 259 return &comment, nil 260}