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