loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

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

Merge pull request 'Port archived labels visual filter' (#2887) from 0ko/forgejo:lable-archive-bw into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2887
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>

+138 -36
+6 -6
models/issues/label.go
··· 116 116 func (l *Label) SetArchived(isArchived bool) { 117 117 if !isArchived { 118 118 l.ArchivedUnix = timeutil.TimeStamp(0) 119 - } else if isArchived && l.ArchivedUnix.IsZero() { 119 + } else if isArchived && !l.IsArchived() { 120 120 // Only change the date when it is newly archived. 121 121 l.ArchivedUnix = timeutil.TimeStampNow() 122 122 } 123 + } 124 + 125 + // IsArchived returns true if label is an archived 126 + func (l *Label) IsArchived() bool { 127 + return !l.ArchivedUnix.IsZero() 123 128 } 124 129 125 130 // CalOpenOrgIssues calculates the open issues of a label for a specific repo ··· 164 169 // BelongsToOrg returns true if label is an organization label 165 170 func (l *Label) BelongsToOrg() bool { 166 171 return l.OrgID > 0 167 - } 168 - 169 - // IsArchived returns true if label is an archived 170 - func (l *Label) IsArchived() bool { 171 - return l.ArchivedUnix > 0 172 172 } 173 173 174 174 // BelongsToRepo returns true if label is a repository label
+39 -11
modules/templates/util_render.go
··· 20 20 "code.gitea.io/gitea/modules/markup" 21 21 "code.gitea.io/gitea/modules/markup/markdown" 22 22 "code.gitea.io/gitea/modules/setting" 23 + "code.gitea.io/gitea/modules/translation" 23 24 "code.gitea.io/gitea/modules/util" 24 25 ) 25 26 ··· 104 105 return template.HTML(htmlWithCodeTags) 105 106 } 106 107 108 + const ( 109 + activeLabelOpacity = uint8(255) 110 + archivedLabelOpacity = uint8(127) 111 + ) 112 + 113 + func GetLabelOpacityByte(isArchived bool) uint8 { 114 + if isArchived { 115 + return archivedLabelOpacity 116 + } 117 + return activeLabelOpacity 118 + } 119 + 107 120 // RenderIssueTitle renders issue/pull title with defined post processors 108 121 func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) template.HTML { 109 122 renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{ ··· 118 131 } 119 132 120 133 // RenderLabel renders a label 121 - func RenderLabel(ctx context.Context, label *issues_model.Label) template.HTML { 122 - labelScope := label.ExclusiveScope() 123 - 124 - textColor := "#111" 134 + // locale is needed due to an import cycle with our context providing the `Tr` function 135 + func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML { 136 + var ( 137 + archivedCSSClass string 138 + textColor = "#111" 139 + labelScope = label.ExclusiveScope() 140 + ) 125 141 r, g, b := util.HexToRBGColor(label.Color) 142 + 126 143 // Determine if label text should be light or dark to be readable on background color 144 + // this doesn't account for saturation or transparency 127 145 if util.UseLightTextOnBackground(r, g, b) { 128 146 textColor = "#eee" 129 147 } 130 148 131 149 description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description)) 132 150 151 + if label.IsArchived() { 152 + archivedCSSClass = "archived-label" 153 + description = locale.TrString("repo.issues.archived_label_description", description) 154 + } 155 + 133 156 if labelScope == "" { 134 157 // Regular label 135 - s := fmt.Sprintf("<div class='ui label' style='color: %s !important; background-color: %s !important' data-tooltip-content title='%s'>%s</div>", 136 - textColor, label.Color, description, RenderEmoji(ctx, label.Name)) 158 + 159 + labelColor := label.Color + hex.EncodeToString([]byte{GetLabelOpacityByte(label.IsArchived())}) 160 + s := fmt.Sprintf("<div class='ui label %s' style='color: %s !important; background-color: %s !important;' data-tooltip-content title='%s'>%s</div>", 161 + archivedCSSClass, textColor, labelColor, description, RenderEmoji(ctx, label.Name)) 137 162 return template.HTML(s) 138 163 } 139 164 ··· 152 177 darkenFactor := math.Max(luminance-darken, 0.0) / math.Max(luminance, 1.0/255.0) 153 178 lightenFactor := math.Min(luminance+lighten, 1.0) / math.Max(luminance, 1.0/255.0) 154 179 180 + opacity := GetLabelOpacityByte(label.IsArchived()) 155 181 scopeBytes := []byte{ 156 182 uint8(math.Min(math.Round(r*darkenFactor), 255)), 157 183 uint8(math.Min(math.Round(g*darkenFactor), 255)), 158 184 uint8(math.Min(math.Round(b*darkenFactor), 255)), 185 + opacity, 159 186 } 160 187 itemBytes := []byte{ 161 188 uint8(math.Min(math.Round(r*lightenFactor), 255)), 162 189 uint8(math.Min(math.Round(g*lightenFactor), 255)), 163 190 uint8(math.Min(math.Round(b*lightenFactor), 255)), 191 + opacity, 164 192 } 165 193 166 - itemColor := "#" + hex.EncodeToString(itemBytes) 167 194 scopeColor := "#" + hex.EncodeToString(scopeBytes) 195 + itemColor := "#" + hex.EncodeToString(itemBytes) 168 196 169 - s := fmt.Sprintf("<span class='ui label scope-parent' data-tooltip-content title='%s'>"+ 197 + s := fmt.Sprintf("<span class='ui label %s scope-parent' data-tooltip-content title='%s'>"+ 170 198 "<div class='ui label scope-left' style='color: %s !important; background-color: %s !important'>%s</div>"+ 171 199 "<div class='ui label scope-right' style='color: %s !important; background-color: %s !important'>%s</div>"+ 172 200 "</span>", 173 - description, 201 + archivedCSSClass, description, 174 202 textColor, scopeColor, scopeText, 175 203 textColor, itemColor, itemText) 176 204 return template.HTML(s) ··· 211 239 return output 212 240 } 213 241 214 - func RenderLabels(ctx context.Context, labels []*issues_model.Label, repoLink string) template.HTML { 242 + func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string) template.HTML { 215 243 htmlCode := `<span class="labels-list">` 216 244 for _, label := range labels { 217 245 // Protect against nil value in labels - shouldn't happen but would cause a panic if so ··· 219 247 continue 220 248 } 221 249 htmlCode += fmt.Sprintf("<a href='%s/issues?labels=%d'>%s</a> ", 222 - repoLink, label.ID, RenderLabel(ctx, label)) 250 + repoLink, label.ID, RenderLabel(ctx, locale, label)) 223 251 } 224 252 htmlCode += "</span>" 225 253 return template.HTML(htmlCode)
+1
options/locale/locale_en-US.ini
··· 1628 1628 issues.label_deletion = Delete label 1629 1629 issues.label_deletion_desc = Deleting a label removes it from all issues. Continue? 1630 1630 issues.label_deletion_success = The label has been deleted. 1631 + issues.archived_label_description = (Archived) %s 1631 1632 issues.label.filter_sort.alphabetically = Alphabetically 1632 1633 issues.label.filter_sort.reverse_alphabetically = Reverse alphabetically 1633 1634 issues.label.filter_sort.by_size = Smallest size
+5 -7
routers/web/repo/issue_label.go
··· 13 13 "code.gitea.io/gitea/modules/label" 14 14 "code.gitea.io/gitea/modules/log" 15 15 repo_module "code.gitea.io/gitea/modules/repository" 16 - "code.gitea.io/gitea/modules/timeutil" 17 16 "code.gitea.io/gitea/modules/web" 18 17 "code.gitea.io/gitea/services/context" 19 18 "code.gitea.io/gitea/services/forms" ··· 112 111 } 113 112 114 113 l := &issues_model.Label{ 115 - RepoID: ctx.Repo.Repository.ID, 116 - Name: form.Title, 117 - Exclusive: form.Exclusive, 118 - Description: form.Description, 119 - Color: form.Color, 120 - ArchivedUnix: timeutil.TimeStamp(0), 114 + RepoID: ctx.Repo.Repository.ID, 115 + Name: form.Title, 116 + Exclusive: form.Exclusive, 117 + Description: form.Description, 118 + Color: form.Color, 121 119 } 122 120 if err := issues_model.NewLabel(ctx, l); err != nil { 123 121 ctx.ServerError("NewLabel", err)
+1 -1
templates/repo/issue/card.tmpl
··· 61 61 {{if or .Labels .Assignees}} 62 62 <div class="extra content labels-list tw-p-0 tw-pt-1"> 63 63 {{range .Labels}} 64 - <a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{RenderLabel ctx .}}</a> 64 + <a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{RenderLabel ctx ctx.Locale .}}</a> 65 65 {{end}} 66 66 <div class="right floated"> 67 67 {{range .Assignees}}
+1 -1
templates/repo/issue/filter_actions.tmpl
··· 30 30 {{end}} 31 31 {{$previousExclusiveScope = $exclusiveScope}} 32 32 <div class="item issue-action tw-flex tw-justify-between" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels"> 33 - {{if SliceUtils.Contains $.SelLabelIDs .ID}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel $.Context .}} 33 + {{if SliceUtils.Contains $.SelLabelIDs .ID}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel $.Context ctx.Locale .}} 34 34 {{template "repo/issue/labels/label_archived" .}} 35 35 </div> 36 36 {{end}}
+1 -1
templates/repo/issue/filter_list.tmpl
··· 42 42 {{svg "octicon-check"}} 43 43 {{end}} 44 44 {{end}} 45 - {{RenderLabel $.Context .}} 45 + {{RenderLabel $.Context ctx.Locale .}} 46 46 <p class="tw-ml-auto">{{template "repo/issue/labels/label_archived" .}}</p> 47 47 </a> 48 48 {{end}}
+1 -1
templates/repo/issue/labels/label.tmpl
··· 3 3 id="label_{{.label.ID}}" 4 4 href="{{.root.RepoLink}}/{{if or .root.IsPull .root.Issue.IsPull}}pulls{{else}}issues{{end}}?labels={{.label.ID}}"{{/* FIXME: use .root.Issue.Link or create .root.Link */}} 5 5 > 6 - {{- RenderLabel $.Context .label -}} 6 + {{- RenderLabel $.Context ctx.Locale .label -}} 7 7 </a>
+2 -2
templates/repo/issue/labels/label_list.tmpl
··· 32 32 {{range .Labels}} 33 33 <li class="item"> 34 34 <div class="label-title"> 35 - {{RenderLabel $.Context .}} 35 + {{RenderLabel $.Context ctx.Locale .}} 36 36 {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}} 37 37 </div> 38 38 <div class="label-issues"> ··· 72 72 {{range .OrgLabels}} 73 73 <li class="item org-label"> 74 74 <div class="label-title"> 75 - {{RenderLabel $.Context .}} 75 + {{RenderLabel $.Context ctx.Locale .}} 76 76 {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}} 77 77 </div> 78 78 <div class="label-issues">
+2 -2
templates/repo/issue/labels/labels_selector_field.tmpl
··· 21 21 <div class="divider"></div> 22 22 {{end}} 23 23 {{$previousExclusiveScope = $exclusiveScope}} 24 - <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" {{if .IsArchived}}data-is-archived{{end}} data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}tw-invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span>&nbsp;&nbsp;{{RenderLabel $.Context .}} 24 + <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" {{if .IsArchived}}data-is-archived{{end}} data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}tw-invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span>&nbsp;&nbsp;{{RenderLabel $.Context ctx.Locale .}} 25 25 {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}} 26 26 <p class="archived-label-hint">{{template "repo/issue/labels/label_archived" .}}</p> 27 27 </a> ··· 34 34 <div class="divider"></div> 35 35 {{end}} 36 36 {{$previousExclusiveScope = $exclusiveScope}} 37 - <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" {{if .IsArchived}}data-is-archived{{end}} data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}tw-invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span>&nbsp;&nbsp;{{RenderLabel $.Context .}} 37 + <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" {{if .IsArchived}}data-is-archived{{end}} data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}tw-invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span>&nbsp;&nbsp;{{RenderLabel $.Context ctx.Locale .}} 38 38 {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}} 39 39 <p class="archived-label-hint">{{template "repo/issue/labels/label_archived" .}}</p> 40 40 </a>
+3 -3
templates/repo/issue/view_content/comments.tmpl
··· 177 177 <span class="text grey muted-links"> 178 178 {{template "shared/user/authorlink" .Poster}} 179 179 {{if and .AddedLabels (not .RemovedLabels)}} 180 - {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context .AddedLabels $.RepoLink) $createdStr}} 180 + {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) $createdStr}} 181 181 {{else if and (not .AddedLabels) .RemovedLabels}} 182 - {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context .RemovedLabels $.RepoLink) $createdStr}} 182 + {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}} 183 183 {{else}} 184 - {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context .AddedLabels $.RepoLink) (RenderLabels $.Context .RemovedLabels $.RepoLink) $createdStr}} 184 + {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}} 185 185 {{end}} 186 186 </span> 187 187 </div>
+1 -1
templates/shared/issuelist.tmpl
··· 21 21 {{end}} 22 22 <span class="labels-list tw-ml-1"> 23 23 {{range .Labels}} 24 - <a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{RenderLabel $.Context .}}</a> 24 + <a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{RenderLabel $.Context ctx.Locale .}}</a> 25 25 {{end}} 26 26 </span> 27 27 </div>
+71
tests/integration/archived_labels_display_test.go
··· 1 + // Copyright 2024 The Forgejo Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package integration 5 + 6 + import ( 7 + "net/http" 8 + "net/url" 9 + "strings" 10 + "testing" 11 + 12 + "github.com/PuerkitoBio/goquery" 13 + "github.com/stretchr/testify/assert" 14 + ) 15 + 16 + func TestArchivedLabelVisualProperties(t *testing.T) { 17 + onGiteaRun(t, func(t *testing.T, u *url.URL) { 18 + session := loginUser(t, "user2") 19 + 20 + // Create labels 21 + session.MakeRequest(t, NewRequestWithValues(t, "POST", "user2/repo1/labels/new", map[string]string{ 22 + "_csrf": GetCSRF(t, session, "user2/repo1/labels"), 23 + "title": "active_label", 24 + "description": "", 25 + "color": "#aa00aa", 26 + }), http.StatusSeeOther) 27 + session.MakeRequest(t, NewRequestWithValues(t, "POST", "user2/repo1/labels/new", map[string]string{ 28 + "_csrf": GetCSRF(t, session, "user2/repo1/labels"), 29 + "title": "archived_label", 30 + "description": "", 31 + "color": "#00aa00", 32 + }), http.StatusSeeOther) 33 + 34 + // Get ID of label to archive it 35 + var id string 36 + doc := NewHTMLParser(t, session.MakeRequest(t, NewRequest(t, "GET", "user2/repo1/labels"), http.StatusOK).Body) 37 + doc.Find(".issue-label-list .item").Each(func(i int, s *goquery.Selection) { 38 + label := s.Find(".label-title .label") 39 + if label.Text() == "archived_label" { 40 + href, _ := s.Find(".label-issues a.open-issues").Attr("href") 41 + hrefParts := strings.Split(href, "=") 42 + id = hrefParts[len(hrefParts)-1] 43 + } 44 + }) 45 + 46 + // Make label archived 47 + session.MakeRequest(t, NewRequestWithValues(t, "POST", "user2/repo1/labels/edit", map[string]string{ 48 + "_csrf": GetCSRF(t, session, "user2/repo1/labels"), 49 + "id": id, 50 + "title": "archived_label", 51 + "is_archived": "on", 52 + "description": "", 53 + "color": "#00aa00", 54 + }), http.StatusSeeOther) 55 + 56 + // Test label properties 57 + doc = NewHTMLParser(t, session.MakeRequest(t, NewRequest(t, "GET", "user2/repo1/labels"), http.StatusOK).Body) 58 + doc.Find(".issue-label-list .item").Each(func(i int, s *goquery.Selection) { 59 + label := s.Find(".label-title .label") 60 + style, _ := label.Attr("style") 61 + 62 + if label.Text() == "active_label" { 63 + assert.False(t, label.HasClass("archived-label")) 64 + assert.Contains(t, style, "background-color: #aa00aaff") 65 + } else if label.Text() == "archived_label" { 66 + assert.True(t, label.HasClass("archived-label")) 67 + assert.Contains(t, style, "background-color: #00aa007f") 68 + } 69 + }) 70 + }) 71 + }
+4
web_src/css/repo.css
··· 2435 2435 margin-left: 0; 2436 2436 } 2437 2437 2438 + .archived-label { 2439 + filter: grayscale(0.25) saturate(0.75); 2440 + } 2441 + 2438 2442 .repo-button-row { 2439 2443 margin: 10px 0; 2440 2444 display: flex;