Monorepo for Tangled tangled.org
6

Configure Feed

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

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