Skip to content

Commit 796c4ec

Browse files
Gustedsilverwindlunny
authored
Prettify number of issues (#17760)
* Prettify number of issues - Use the PrettyNumber function to add commas in large amount of issues. * Use client-side formatting * prettify on both server and client * remove unused i18n entries * handle more cases, support other int types in PrettyNumber * specify locale to avoid issues with node default locale * remove superfluos argument * introduce template helper, octicon tweaks, js refactor * Update modules/templates/helper.go * Apply some suggestions. * Add comment * Update templates/user/dashboard/issues.tmpl Co-authored-by: silverwind <[email protected]> Co-authored-by: silverwind <[email protected]> Co-authored-by: Lunny Xiao <[email protected]>
1 parent 0097fbc commit 796c4ec

File tree

14 files changed

+118
-45
lines changed

14 files changed

+118
-45
lines changed

modules/base/tool.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"code.gitea.io/gitea/modules/git"
2525
"code.gitea.io/gitea/modules/log"
2626
"code.gitea.io/gitea/modules/setting"
27+
"code.gitea.io/gitea/modules/util"
2728

2829
"github.com/dustin/go-humanize"
2930
)
@@ -143,9 +144,9 @@ func FileSize(s int64) string {
143144
}
144145

145146
// PrettyNumber produces a string form of the given number in base 10 with
146-
// commas after every three orders of magnitud
147-
func PrettyNumber(v int64) string {
148-
return humanize.Comma(v)
147+
// commas after every three orders of magnitude
148+
func PrettyNumber(i interface{}) string {
149+
return humanize.Comma(util.NumberIntoInt64(i))
149150
}
150151

151152
// Subtract deals with subtraction of all types of number.

modules/base/tool_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ func TestFileSize(t *testing.T) {
117117

118118
func TestPrettyNumber(t *testing.T) {
119119
assert.Equal(t, "23,342,432", PrettyNumber(23342432))
120+
assert.Equal(t, "23,342,432", PrettyNumber(int32(23342432)))
120121
assert.Equal(t, "0", PrettyNumber(0))
121122
assert.Equal(t, "-100,000", PrettyNumber(-100000))
122123
}

modules/templates/helper.go

+21-12
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,19 @@ func NewFuncMap() []template.FuncMap {
9898
"CustomEmojis": func() map[string]string {
9999
return setting.UI.CustomEmojisMap
100100
},
101-
"Safe": Safe,
102-
"SafeJS": SafeJS,
103-
"JSEscape": JSEscape,
104-
"Str2html": Str2html,
105-
"TimeSince": timeutil.TimeSince,
106-
"TimeSinceUnix": timeutil.TimeSinceUnix,
107-
"RawTimeSince": timeutil.RawTimeSince,
108-
"FileSize": base.FileSize,
109-
"PrettyNumber": base.PrettyNumber,
110-
"Subtract": base.Subtract,
111-
"EntryIcon": base.EntryIcon,
112-
"MigrationIcon": MigrationIcon,
101+
"Safe": Safe,
102+
"SafeJS": SafeJS,
103+
"JSEscape": JSEscape,
104+
"Str2html": Str2html,
105+
"TimeSince": timeutil.TimeSince,
106+
"TimeSinceUnix": timeutil.TimeSinceUnix,
107+
"RawTimeSince": timeutil.RawTimeSince,
108+
"FileSize": base.FileSize,
109+
"PrettyNumber": base.PrettyNumber,
110+
"JsPrettyNumber": JsPrettyNumber,
111+
"Subtract": base.Subtract,
112+
"EntryIcon": base.EntryIcon,
113+
"MigrationIcon": MigrationIcon,
113114
"Add": func(a ...int) int {
114115
sum := 0
115116
for _, val := range a {
@@ -1005,3 +1006,11 @@ func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteNa
10051006

10061007
return a
10071008
}
1009+
1010+
// JsPrettyNumber renders a number using english decimal separators, e.g. 1,200 and subsequent
1011+
// JS will replace the number with locale-specific separators, based on the user's selected language
1012+
func JsPrettyNumber(i interface{}) template.HTML {
1013+
num := util.NumberIntoInt64(i)
1014+
1015+
return template.HTML(`<span class="js-pretty-number" data-value="` + strconv.FormatInt(num, 10) + `">` + base.PrettyNumber(num) + `</span>`)
1016+
}

modules/util/util.go

+18
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,21 @@ func Dedent(s string) string {
224224
}
225225
return strings.TrimSpace(s)
226226
}
227+
228+
// NumberIntoInt64 transform a given int into int64.
229+
func NumberIntoInt64(number interface{}) int64 {
230+
var value int64
231+
switch v := number.(type) {
232+
case int:
233+
value = int64(v)
234+
case int8:
235+
value = int64(v)
236+
case int16:
237+
value = int64(v)
238+
case int32:
239+
value = int64(v)
240+
case int64:
241+
value = v
242+
}
243+
return value
244+
}

options/locale/locale_en-US.ini

-4
Original file line numberDiff line numberDiff line change
@@ -1259,8 +1259,6 @@ issues.change_ref_at = `changed reference from <b><strike>%s</strike></b> to <b>
12591259
issues.remove_ref_at = `removed reference <b>%s</b> %s`
12601260
issues.add_ref_at = `added reference <b>%s</b> %s`
12611261
issues.delete_branch_at = `deleted branch <b>%s</b> %s`
1262-
issues.open_tab = %d Open
1263-
issues.close_tab = %d Closed
12641262
issues.filter_label = Label
12651263
issues.filter_label_exclude = `Use <code>alt</code> + <code>click/enter</code> to exclude labels`
12661264
issues.filter_label_no_select = All labels
@@ -1613,8 +1611,6 @@ pulls.auto_merge_newly_scheduled_comment = `scheduled this pull request to auto
16131611
pulls.auto_merge_canceled_schedule_comment = `canceled auto merging this pull request when all checks succeed %[1]s`
16141612

16151613
milestones.new = New Milestone
1616-
milestones.open_tab = %d Open
1617-
milestones.close_tab = %d Closed
16181614
milestones.closed = Closed %s
16191615
milestones.update_ago = Updated %s ago
16201616
milestones.no_due_date = No due date

templates/repo/issue/milestones.tmpl

+7-5
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
<div class="ui compact tiny menu">
1919
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/milestones?state=open&q={{$.Keyword}}">
2020
{{svg "octicon-milestone" 16 "mr-3"}}
21-
{{.i18n.Tr "repo.milestones.open_tab" .OpenCount}}
21+
{{JsPrettyNumber .OpenCount}}&nbsp;{{.i18n.Tr "repo.issues.open_title"}}
2222
</a>
2323
<a class="item{{if .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/milestones?state=closed&q={{$.Keyword}}">
24-
{{svg "octicon-milestone" 16 "mr-3"}}
25-
{{.i18n.Tr "repo.milestones.close_tab" .ClosedCount}}
24+
{{svg "octicon-check" 16 "mr-3"}}
25+
{{JsPrettyNumber .ClosedCount}}&nbsp;{{.i18n.Tr "repo.issues.closed_title"}}
2626
</a>
2727
</div>
2828
</div>
@@ -83,8 +83,10 @@
8383
{{end}}
8484
{{end}}
8585
<span class="issue-stats">
86-
{{svg "octicon-issue-opened"}} {{$.i18n.Tr "repo.issues.open_tab" .NumOpenIssues}}
87-
{{svg "octicon-issue-closed"}} {{$.i18n.Tr "repo.issues.close_tab" .NumClosedIssues}}
86+
{{svg "octicon-issue-opened" 16 "mr-3"}}
87+
{{JsPrettyNumber .NumOpenIssues}}&nbsp;{{$.i18n.Tr "repo.issues.open_title"}}
88+
{{svg "octicon-check" 16 "mr-3"}}
89+
{{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.i18n.Tr "repo.issues.closed_title"}}
8890
{{if .TotalTrackedTime}}{{svg "octicon-clock"}} {{.TotalTrackedTime|Sec2Time}}{{end}}
8991
{{if .UpdatedUnix}}{{svg "octicon-clock"}} {{$.i18n.Tr "repo.milestones.update_ago" (.TimeSinceUpdate|Sec2Time)}}{{end}}
9092
</span>

templates/repo/issue/openclose.tmpl

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
<div class="ui compact tiny menu">
22
<a class="{{if not .IsShowClosed}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state=open&labels={{.SelectLabels}}&milestone={{.MilestoneID}}&assignee={{.AssigneeID}}">
3-
{{svg "octicon-issue-opened" 16 "mr-3"}}
4-
{{.i18n.Tr "repo.issues.open_tab" .IssueStats.OpenCount}}
3+
{{if .PageIsPullList}}
4+
{{svg "octicon-git-pull-request" 16 "mr-3"}}
5+
{{else}}
6+
{{svg "octicon-issue-opened" 16 "mr-3"}}
7+
{{end}}
8+
{{JsPrettyNumber .IssueStats.OpenCount}}&nbsp;{{.i18n.Tr "repo.issues.open_title"}}
59
</a>
610
<a class="{{if .IsShowClosed}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{.ViewType}}&sort={{$.SortType}}&state=closed&labels={{.SelectLabels}}&milestone={{.MilestoneID}}&assignee={{.AssigneeID}}">
7-
{{svg "octicon-issue-closed" 16 "mr-3"}}
8-
{{.i18n.Tr "repo.issues.close_tab" .IssueStats.ClosedCount}}
11+
{{svg "octicon-check" 16 "mr-3"}}
12+
{{JsPrettyNumber .IssueStats.ClosedCount}}&nbsp;{{.i18n.Tr "repo.issues.closed_title"}}
913
</a>
1014
</div>

templates/repo/projects/list.tmpl

+8-6
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
{{template "base/alert" .}}
1515
<div class="ui compact tiny menu">
1616
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/projects?state=open">
17-
{{svg "octicon-project" 16 "mr-2"}}
18-
{{.i18n.Tr "repo.issues.open_tab" .OpenCount}}
17+
{{svg "octicon-project" 16 "mr-3"}}
18+
{{JsPrettyNumber .OpenCount}}&nbsp;{{.i18n.Tr "repo.issues.open_title"}}
1919
</a>
2020
<a class="item{{if .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/projects?state=closed">
21-
{{svg "octicon-check" 16 "mr-2"}}
22-
{{.i18n.Tr "repo.milestones.close_tab" .ClosedCount}}
21+
{{svg "octicon-check" 16 "mr-3"}}
22+
{{JsPrettyNumber .ClosedCount}}&nbsp;{{.i18n.Tr "repo.issues.closed_title"}}
2323
</a>
2424
</div>
2525

@@ -47,8 +47,10 @@
4747
{{svg "octicon-clock"}} {{$.i18n.Tr "repo.milestones.closed" $closedDate|Str2html}}
4848
{{end}}
4949
<span class="issue-stats">
50-
{{svg "octicon-issue-opened"}} {{$.i18n.Tr "repo.issues.open_tab" .NumOpenIssues}}
51-
{{svg "octicon-issue-closed"}} {{$.i18n.Tr "repo.issues.close_tab" .NumClosedIssues}}
50+
{{svg "octicon-issue-opened" 16 "mr-3"}}
51+
{{JsPrettyNumber .NumOpenIssues}}&nbsp;{{$.i18n.Tr "repo.issues.open_title"}}
52+
{{svg "octicon-check" 16 "mr-3"}}
53+
{{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.i18n.Tr "repo.issues.closed_title"}}
5254
</span>
5355
</div>
5456
{{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}}

templates/user/dashboard/issues.tmpl

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@
6161
<div class="ui compact tiny menu">
6262
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}">
6363
{{svg "octicon-issue-opened" 16 "mr-3"}}
64-
{{.i18n.Tr "repo.issues.open_tab" .IssueStats.OpenCount}}
64+
{{JsPrettyNumber .ShownIssueStats.OpenCount}}&nbsp;{{.i18n.Tr "repo.issues.open_title"}}
6565
</a>
6666
<a class="item{{if .IsShowClosed}} active{{end}}" href="{{.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=closed&q={{$.Keyword}}">
6767
{{svg "octicon-issue-closed" 16 "mr-3"}}
68-
{{.i18n.Tr "repo.issues.close_tab" .IssueStats.ClosedCount}}
68+
{{JsPrettyNumber .ShownIssueStats.ClosedCount}}&nbsp;{{.i18n.Tr "repo.issues.closed_title"}}
6969
</a>
7070
</div>
7171
</div>

templates/user/dashboard/milestones.tmpl

+11-7
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@
3838
<div class="column">
3939
<div class="ui compact tiny menu">
4040
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}">
41-
{{svg "octicon-issue-opened" 16 "mr-3"}}
42-
{{.i18n.Tr "repo.milestones.open_tab" .MilestoneStats.OpenCount}}
41+
{{svg "octicon-milestone" 16 "mr-3"}}
42+
{{JsPrettyNumber .MilestoneStats.OpenCount}}&nbsp;{{.i18n.Tr "repo.issues.open_title"}}
4343
</a>
4444
<a class="item{{if .IsShowClosed}} active{{end}}" href="{{.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=closed&q={{$.Keyword}}">
45-
{{svg "octicon-issue-closed" 16 "mr-3"}}
46-
{{.i18n.Tr "repo.milestones.close_tab" .MilestoneStats.ClosedCount}}
45+
{{svg "octicon-check" 16 "mr-3"}}
46+
{{JsPrettyNumber .MilestoneStats.ClosedCount}}&nbsp;{{.i18n.Tr "repo.issues.closed_title"}}
4747
</a>
4848
</div>
4949
</div>
@@ -103,9 +103,13 @@
103103
{{end}}
104104
{{end}}
105105
<span class="issue-stats">
106-
{{svg "octicon-issue-opened"}} {{$.i18n.Tr "repo.milestones.open_tab" .NumOpenIssues}}
107-
{{svg "octicon-issue-closed"}} {{$.i18n.Tr "repo.milestones.close_tab" .NumClosedIssues}}
108-
{{if .TotalTrackedTime}}{{svg "octicon-clock"}} {{.TotalTrackedTime|Sec2Time}}{{end}}
106+
{{svg "octicon-issue-opened" 16 "mr-3"}}
107+
{{JsPrettyNumber .NumOpenIssues}}&nbsp;{{$.i18n.Tr "repo.issues.open_title"}}
108+
{{svg "octicon-check" 16 "mr-3"}}
109+
{{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.i18n.Tr "repo.issues.closed_title"}}
110+
{{if .TotalTrackedTime}}
111+
{{svg "octicon-clock"}} {{.TotalTrackedTime|Sec2Time}}
112+
{{end}}
109113
</span>
110114
</div>
111115
{{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}}

web_src/js/features/formatting.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {prettyNumber} from '../utils.js';
2+
3+
const {lang} = document.documentElement;
4+
5+
export function initFormattingReplacements() {
6+
// replace english formatted numbers with locale-specific separators
7+
for (const el of document.getElementsByClassName('js-pretty-number')) {
8+
const num = Number(el.getAttribute('data-value'));
9+
const formatted = prettyNumber(num, lang);
10+
if (formatted && formatted !== el.textContent) {
11+
el.textContent = formatted;
12+
}
13+
}
14+
}

web_src/js/index.js

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ import {initRepoBranchButton} from './features/repo-branch.js';
8484
import {initCommonOrganization} from './features/common-organization.js';
8585
import {initRepoWikiForm} from './features/repo-wiki.js';
8686
import {initRepoCommentForm, initRepository} from './features/repo-legacy.js';
87+
import {initFormattingReplacements} from './features/formatting.js';
88+
89+
// Run time-critical code as soon as possible. This is safe to do because this
90+
// script appears at the end of <body> and rendered HTML is accessible at that point.
91+
initFormattingReplacements();
8792

8893
// Silence fomantic's error logging when tabs are used without a target content element
8994
$.fn.tab.settings.silent = true;

web_src/js/utils.js

+7
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,10 @@ export function strSubMatch(full, sub) {
9090
}
9191
return res;
9292
}
93+
94+
// pretty-print a number using locale-specific separators, e.g. 1200 -> 1,200
95+
export function prettyNumber(num, locale = 'en-US') {
96+
if (typeof num !== 'number') return '';
97+
const {format} = new Intl.NumberFormat(locale);
98+
return format(num);
99+
}

web_src/js/utils.test.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
basename, extname, isObject, uniq, stripTags, joinPaths, parseIssueHref, strSubMatch,
2+
basename, extname, isObject, uniq, stripTags, joinPaths, parseIssueHref, strSubMatch, prettyNumber,
33
} from './utils.js';
44

55
test('basename', () => {
@@ -85,7 +85,6 @@ test('parseIssueHref', () => {
8585
expect(parseIssueHref('')).toEqual({owner: undefined, repo: undefined, type: undefined, index: undefined});
8686
});
8787

88-
8988
test('strSubMatch', () => {
9089
expect(strSubMatch('abc', '')).toEqual(['abc']);
9190
expect(strSubMatch('abc', 'a')).toEqual(['', 'a', 'bc']);
@@ -98,3 +97,14 @@ test('strSubMatch', () => {
9897
expect(strSubMatch('aabbcc', 'abc')).toEqual(['', 'a', 'a', 'b', 'b', 'c', 'c']);
9998
expect(strSubMatch('the/directory', 'hedir')).toEqual(['t', 'he', '/', 'dir', 'ectory']);
10099
});
100+
101+
test('prettyNumber', () => {
102+
expect(prettyNumber()).toEqual('');
103+
expect(prettyNumber(null)).toEqual('');
104+
expect(prettyNumber(undefined)).toEqual('');
105+
expect(prettyNumber('1200')).toEqual('');
106+
expect(prettyNumber(12345678, 'en-US')).toEqual('12,345,678');
107+
expect(prettyNumber(12345678, 'de-DE')).toEqual('12.345.678');
108+
expect(prettyNumber(12345678, 'be-BE')).toEqual('12 345 678');
109+
expect(prettyNumber(12345678, 'hi-IN')).toEqual('1,23,45,678');
110+
});

0 commit comments

Comments
 (0)