forked from
tangled.org/core
Monorepo for Tangled — https://tangled.org
1package state
2
3import (
4 "fmt"
5 "log"
6 "net/http"
7 "sort"
8
9 "github.com/bluesky-social/indigo/atproto/syntax"
10 "tangled.org/core/api/tangled"
11 "tangled.org/core/appview/db"
12 "tangled.org/core/appview/models"
13 "tangled.org/core/appview/pages"
14 "tangled.org/core/appview/pagination"
15 "tangled.org/core/consts"
16)
17
18func (s *State) GoodFirstIssues(w http.ResponseWriter, r *http.Request) {
19 user := s.oauth.GetUser(r)
20
21 page := pagination.FromContext(r.Context())
22
23 goodFirstIssueLabel := fmt.Sprintf("at://%s/%s/%s", consts.TangledDid, tangled.LabelDefinitionNSID, "good-first-issue")
24
25 repoLabels, err := db.GetRepoLabels(s.db, db.FilterEq("label_at", goodFirstIssueLabel))
26 if err != nil {
27 log.Println("failed to get repo labels", err)
28 s.pages.Error503(w)
29 return
30 }
31
32 if len(repoLabels) == 0 {
33 s.pages.GoodFirstIssues(w, pages.GoodFirstIssuesParams{
34 LoggedInUser: user,
35 RepoGroups: []*models.RepoGroup{},
36 LabelDefs: make(map[string]*models.LabelDefinition),
37 Page: page,
38 })
39 return
40 }
41
42 repoUris := make([]string, 0, len(repoLabels))
43 for _, rl := range repoLabels {
44 repoUris = append(repoUris, rl.RepoAt.String())
45 }
46
47 allIssues, err := db.GetIssuesPaginated(
48 s.db,
49 pagination.Page{
50 Limit: 500,
51 },
52 db.FilterIn("repo_at", repoUris),
53 db.FilterEq("open", 1),
54 )
55 if err != nil {
56 log.Println("failed to get issues", err)
57 s.pages.Error503(w)
58 return
59 }
60
61 var goodFirstIssues []models.Issue
62 for _, issue := range allIssues {
63 if issue.Labels.ContainsLabel(goodFirstIssueLabel) {
64 goodFirstIssues = append(goodFirstIssues, issue)
65 }
66 }
67
68 repoGroups := make(map[syntax.ATURI]*models.RepoGroup)
69 for _, issue := range goodFirstIssues {
70 if group, exists := repoGroups[issue.Repo.RepoAt()]; exists {
71 group.Issues = append(group.Issues, issue)
72 } else {
73 repoGroups[issue.Repo.RepoAt()] = &models.RepoGroup{
74 Repo: issue.Repo,
75 Issues: []models.Issue{issue},
76 }
77 }
78 }
79
80 var sortedGroups []*models.RepoGroup
81 for _, group := range repoGroups {
82 sortedGroups = append(sortedGroups, group)
83 }
84
85 sort.Slice(sortedGroups, func(i, j int) bool {
86 iIsTangled := sortedGroups[i].Repo.Did == consts.TangledDid
87 jIsTangled := sortedGroups[j].Repo.Did == consts.TangledDid
88
89 // If one is tangled and the other isn't, non-tangled comes first
90 if iIsTangled != jIsTangled {
91 return jIsTangled // true if j is tangled (i should come first)
92 }
93
94 // Both tangled or both not tangled: sort by name
95 return sortedGroups[i].Repo.Name < sortedGroups[j].Repo.Name
96 })
97
98 groupStart := page.Offset
99 groupEnd := page.Offset + page.Limit
100 if groupStart > len(sortedGroups) {
101 groupStart = len(sortedGroups)
102 }
103 if groupEnd > len(sortedGroups) {
104 groupEnd = len(sortedGroups)
105 }
106
107 paginatedGroups := sortedGroups[groupStart:groupEnd]
108
109 var allIssuesFromGroups []models.Issue
110 for _, group := range paginatedGroups {
111 allIssuesFromGroups = append(allIssuesFromGroups, group.Issues...)
112 }
113
114 var allLabelDefs []models.LabelDefinition
115 if len(allIssuesFromGroups) > 0 {
116 labelDefUris := make(map[string]bool)
117 for _, issue := range allIssuesFromGroups {
118 for labelDefUri := range issue.Labels.Inner() {
119 labelDefUris[labelDefUri] = true
120 }
121 }
122
123 uriList := make([]string, 0, len(labelDefUris))
124 for uri := range labelDefUris {
125 uriList = append(uriList, uri)
126 }
127
128 if len(uriList) > 0 {
129 allLabelDefs, err = db.GetLabelDefinitions(s.db, db.FilterIn("at_uri", uriList))
130 if err != nil {
131 log.Println("failed to fetch labels", err)
132 }
133 }
134 }
135
136 labelDefsMap := make(map[string]*models.LabelDefinition)
137 for i := range allLabelDefs {
138 labelDefsMap[allLabelDefs[i].AtUri().String()] = &allLabelDefs[i]
139 }
140
141 s.pages.GoodFirstIssues(w, pages.GoodFirstIssuesParams{
142 LoggedInUser: user,
143 RepoGroups: paginatedGroups,
144 LabelDefs: labelDefsMap,
145 Page: page,
146 GfiLabel: labelDefsMap[goodFirstIssueLabel],
147 })
148}