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