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
2.9 kB 130 lines
1package models 2 3import ( 4 "fmt" 5 "strings" 6 "time" 7 8 "github.com/bluesky-social/indigo/atproto/syntax" 9 lexutil "github.com/bluesky-social/indigo/lex/util" 10 "tangled.org/core/api/tangled" 11 "tangled.org/core/appview/pages/markup/sanitizer" 12) 13 14type Issue struct { 15 Id int64 16 Did string 17 Rkey string 18 RepoDid syntax.DID 19 IssueId int 20 Created time.Time 21 Edited *time.Time 22 Deleted *time.Time 23 Title string 24 Body string 25 Open bool 26 Mentions []syntax.DID 27 References []syntax.ATURI 28 // images embedded in Body; referenced on the record to pin them against GC 29 Blobs []*lexutil.LexBlob 30 31 // optionally, populate this when querying for reverse mappings 32 // like comment counts, parent repo etc. 33 Comments []Comment 34 Labels LabelState 35 Repo *Repo 36} 37 38func (i *Issue) AtUri() syntax.ATURI { 39 return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueNSID, i.Rkey)) 40} 41 42func (i *Issue) AsRecord() tangled.RepoIssue { 43 mentions := make([]string, len(i.Mentions)) 44 for i, did := range i.Mentions { 45 mentions[i] = string(did) 46 } 47 references := make([]string, len(i.References)) 48 for i, uri := range i.References { 49 references[i] = string(uri) 50 } 51 rec := tangled.RepoIssue{ 52 Repo: string(i.RepoDid), 53 Title: i.Title, 54 Body: &i.Body, 55 Mentions: mentions, 56 References: references, 57 CreatedAt: i.Created.Format(time.RFC3339), 58 Blobs: i.Blobs, 59 } 60 return rec 61} 62 63func (i *Issue) State() string { 64 if i.Open { 65 return "open" 66 } 67 return "closed" 68} 69 70var _ Validator = new(Issue) 71 72func (i *Issue) Validate() error { 73 if i.Title == "" { 74 return fmt.Errorf("issue title is empty") 75 } 76 if i.Body == "" { 77 return fmt.Errorf("issue body is empty") 78 } 79 80 if st := strings.TrimSpace(sanitizer.SanitizeDescription(i.Title)); st == "" { 81 return fmt.Errorf("title is empty after HTML sanitization") 82 } 83 84 if st := strings.TrimSpace(sanitizer.SanitizeDefault(i.Body)); st == "" { 85 return fmt.Errorf("body is empty after HTML sanitization") 86 } 87 return nil 88} 89 90func (i *Issue) Participants() []syntax.DID { 91 participantSet := make(map[syntax.DID]struct{}) 92 participants := []syntax.DID{} 93 94 addParticipant := func(did syntax.DID) { 95 if _, exists := participantSet[did]; !exists { 96 participantSet[did] = struct{}{} 97 participants = append(participants, did) 98 } 99 } 100 101 addParticipant(syntax.DID(i.Did)) 102 103 for _, c := range i.Comments { 104 addParticipant(c.Did) 105 } 106 107 return participants 108} 109 110func IssueFromRecord(did, rkey string, record tangled.RepoIssue) Issue { 111 created, err := time.Parse(time.RFC3339, record.CreatedAt) 112 if err != nil { 113 created = time.Now() 114 } 115 116 body := "" 117 if record.Body != nil { 118 body = *record.Body 119 } 120 121 return Issue{ 122 RepoDid: syntax.DID(record.Repo), 123 Did: did, 124 Rkey: rkey, 125 Created: created, 126 Title: record.Title, 127 Body: body, 128 Open: true, // new issues are open by default 129 } 130}