Monorepo for Tangled
tangled.org
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}