Skip to content

Commit 7d7ab1e

Browse files
jolheiserlafrikssapksilverwindlunny
authored andcommitted
Issue/PR Context Popups (#9822)
* Add data-index attribute to issue anchors Signed-off-by: jolheiser <john.olheiser@gmail.com> * Init JS Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add required data to anchor Signed-off-by: jolheiser <john.olheiser@gmail.com> * Finish popup Signed-off-by: jolheiser <john.olheiser@gmail.com> * Revert changes to html.go Signed-off-by: jolheiser <john.olheiser@gmail.com> * Better octicon contexts Signed-off-by: jolheiser <john.olheiser@gmail.com> * Split out popup function for re-use Signed-off-by: jolheiser <john.olheiser@gmail.com> * Style changes, test fixes, and cross-reference support Signed-off-by: jolheiser <john.olheiser@gmail.com> * Prefer em to px Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move label margin to base CSS Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move JS to separate file. Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move JS to features and fix module Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove query-string and hash Co-Authored-By: silverwind <me@silverwind.io> Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: Antoine GIRARD <sapk@users.noreply.github.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: zeripath <art27@cantab.net>
1 parent c5014a7 commit 7d7ab1e

File tree

9 files changed

+113
-16
lines changed

9 files changed

+113
-16
lines changed

Diff for: modules/markup/html.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -636,11 +636,11 @@ func fullIssuePatternProcessor(ctx *postProcessCtx, node *html.Node) {
636636
if matchOrg == ctx.metas["user"] && matchRepo == ctx.metas["repo"] {
637637
// TODO if m[4]:m[5] is not nil, then link is to a comment,
638638
// and we should indicate that in the text somehow
639-
replaceContent(node, m[0], m[1], createLink(link, id, "issue"))
639+
replaceContent(node, m[0], m[1], createLink(link, id, "ref-issue"))
640640

641641
} else {
642642
orgRepoID := matchOrg + "/" + matchRepo + id
643-
replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "issue"))
643+
replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "ref-issue"))
644644
}
645645
}
646646

@@ -676,7 +676,7 @@ func issueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
676676
reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
677677
if exttrack && !ref.IsPull {
678678
ctx.metas["index"] = ref.Issue
679-
link = createLink(com.Expand(ctx.metas["format"], ctx.metas), reftext, "issue")
679+
link = createLink(com.Expand(ctx.metas["format"], ctx.metas), reftext, "ref-issue")
680680
} else {
681681
// Path determines the type of link that will be rendered. It's unknown at this point whether
682682
// the linked item is actually a PR or an issue. Luckily it's of no real consequence because
@@ -686,9 +686,9 @@ func issueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
686686
path = "pulls"
687687
}
688688
if ref.Owner == "" {
689-
link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], path, ref.Issue), reftext, "issue")
689+
link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], path, ref.Issue), reftext, "ref-issue")
690690
} else {
691-
link = createLink(util.URLJoin(setting.AppURL, ref.Owner, ref.Name, path, ref.Issue), reftext, "issue")
691+
link = createLink(util.URLJoin(setting.AppURL, ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue")
692692
}
693693
}
694694

Diff for: modules/markup/html_internal_test.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,13 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
106106

107107
links := make([]interface{}, len(indices))
108108
for i, index := range indices {
109-
links[i] = numericIssueLink(util.URLJoin(setting.AppSubURL, path), "issue", index, marker)
109+
links[i] = numericIssueLink(util.URLJoin(setting.AppSubURL, path), "ref-issue", index, marker)
110110
}
111111
expectedNil := fmt.Sprintf(expectedFmt, links...)
112112
testRenderIssueIndexPattern(t, s, expectedNil, &postProcessCtx{metas: localMetas})
113113

114114
for i, index := range indices {
115-
links[i] = numericIssueLink(prefix, "issue", index, marker)
115+
links[i] = numericIssueLink(prefix, "ref-issue", index, marker)
116116
}
117117
expectedNum := fmt.Sprintf(expectedFmt, links...)
118118
testRenderIssueIndexPattern(t, s, expectedNum, &postProcessCtx{metas: numericMetas})
@@ -178,7 +178,7 @@ func TestRender_IssueIndexPattern4(t *testing.T) {
178178
test := func(s, expectedFmt string, names ...string) {
179179
links := make([]interface{}, len(names))
180180
for i, name := range names {
181-
links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", "issue", name)
181+
links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue", name)
182182
}
183183
expected := fmt.Sprintf(expectedFmt, links...)
184184
testRenderIssueIndexPattern(t, s, expected, &postProcessCtx{metas: alphanumericMetas})
@@ -217,7 +217,7 @@ func TestRender_AutoLink(t *testing.T) {
217217

218218
// render valid issue URLs
219219
test(util.URLJoin(setting.AppSubURL, "issues", "3333"),
220-
numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), "issue", 3333, "#"))
220+
numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), "ref-issue", 3333, "#"))
221221

222222
// render valid commit URLs
223223
tmp := util.URLJoin(AppSubURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae")
@@ -248,11 +248,11 @@ func TestRender_FullIssueURLs(t *testing.T) {
248248
test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6",
249249
"Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6")
250250
test("Look here http://localhost:3000/person/repo/issues/4",
251-
`Look here <a href="http://localhost:3000/person/repo/issues/4" class="issue">person/repo#4</a>`)
251+
`Look here <a href="http://localhost:3000/person/repo/issues/4" class="ref-issue">person/repo#4</a>`)
252252
test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
253-
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="issue">person/repo#4</a>`)
253+
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="ref-issue">person/repo#4</a>`)
254254
test("http://localhost:3000/gogits/gogs/issues/4",
255-
`<a href="http://localhost:3000/gogits/gogs/issues/4" class="issue">#4</a>`)
255+
`<a href="http://localhost:3000/gogits/gogs/issues/4" class="ref-issue">#4</a>`)
256256
}
257257

258258
func TestRegExp_sha1CurrentPattern(t *testing.T) {

Diff for: modules/markup/html_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ func TestRender_CrossReferences(t *testing.T) {
5858

5959
test(
6060
"gogits/gogs#12345",
61-
`<p><a href="`+util.URLJoin(AppURL, "gogits", "gogs", "issues", "12345")+`" rel="nofollow">gogits/gogs#12345</a></p>`)
61+
`<p><a href="`+util.URLJoin(AppURL, "gogits", "gogs", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogits/gogs#12345</a></p>`)
6262
test(
6363
"go-gitea/gitea#12345",
64-
`<p><a href="`+util.URLJoin(AppURL, "go-gitea", "gitea", "issues", "12345")+`" rel="nofollow">go-gitea/gitea#12345</a></p>`)
64+
`<p><a href="`+util.URLJoin(AppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
6565
test(
6666
"/home/gitea/go-gitea/gitea#12345",
6767
`<p>/home/gitea/go-gitea/gitea#12345</p>`)

Diff for: modules/markup/markdown/markdown_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
105105
<p>See commit <a href="http://localhost:3000/gogits/gogs/commit/65f1bf27bc" rel="nofollow"><code>65f1bf27bc</code></a></p>
106106
<p>Ideas and codes</p>
107107
<ul>
108-
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" rel="nofollow">ocornut/imgui#786</a></li>
109-
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/gogits/gogs/issues/786" rel="nofollow">#786</a></li>
108+
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" class="ref-issue" rel="nofollow">ocornut/imgui#786</a></li>
109+
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/gogits/gogs/issues/786" class="ref-issue" rel="nofollow">#786</a></li>
110110
<li>Node graph editors <a href="https://github.com/ocornut/imgui/issues/306" rel="nofollow">https://github.com/ocornut/imgui/issues/306</a></li>
111111
<li><a href="` + baseURLContent + `/memory_editor_example" rel="nofollow">Memory Editor</a></li>
112112
<li><a href="` + baseURLContent + `/plot_var_example" rel="nofollow">Plot var helper</a></li>

Diff for: modules/markup/sanitizer.go

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ func ReplaceSanitizer() {
5353
// Allow <kbd> tags for keyboard shortcut styling
5454
sanitizer.policy.AllowElements("kbd")
5555

56+
// Allow classes for anchors
57+
sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue`)).OnElements("a")
58+
5659
// Custom keyword markup
5760
for _, rule := range setting.ExternalSanitizerRules {
5861
if rule.Regexp != nil {

Diff for: web_src/js/features/contextPopup.js

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
export default function initContextPopups(suburl) {
2+
const refIssues = $('.ref-issue');
3+
if (!refIssues.length) return;
4+
5+
refIssues.each(function () {
6+
const [index, _issues, repo, owner] = $(this).attr('href').replace(/[#?].*$/, '').split('/').reverse();
7+
issuePopup(suburl, owner, repo, index, $(this));
8+
});
9+
}
10+
11+
function issuePopup(suburl, owner, repo, index, $element) {
12+
$.get(`${suburl}/api/v1/repos/${owner}/${repo}/issues/${index}`, (issue) => {
13+
const createdAt = new Date(issue.created_at).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
14+
15+
let body = issue.body.replace(/\n+/g, ' ');
16+
if (body.length > 85) {
17+
body = `${body.substring(0, 85)}...`;
18+
}
19+
20+
let labels = '';
21+
for (let i = 0; i < issue.labels.length; i++) {
22+
const label = issue.labels[i];
23+
const red = parseInt(label.color.substring(0, 2), 16);
24+
const green = parseInt(label.color.substring(2, 4), 16);
25+
const blue = parseInt(label.color.substring(4, 6), 16);
26+
let color = '#ffffff';
27+
if ((red * 0.299 + green * 0.587 + blue * 0.114) > 125) {
28+
color = '#000000';
29+
}
30+
labels += `<div class="ui label" style="color: ${color}; background-color:#${label.color};">${label.name}</div>`;
31+
}
32+
if (labels.length > 0) {
33+
labels = `<p>${labels}</p>`;
34+
}
35+
36+
let octicon;
37+
if (issue.pull_request !== null) {
38+
if (issue.state === 'open') {
39+
octicon = 'green octicon-git-pull-request'; // Open PR
40+
} else if (issue.pull_request.merged === true) {
41+
octicon = 'purple octicon-git-merge'; // Merged PR
42+
} else {
43+
octicon = 'red octicon-git-pull-request'; // Closed PR
44+
}
45+
} else if (issue.state === 'open') {
46+
octicon = 'green octicon-issue-opened'; // Open Issue
47+
} else {
48+
octicon = 'red octicon-issue-closed'; // Closed Issue
49+
}
50+
51+
$element.popup({
52+
variation: 'wide',
53+
delay: {
54+
show: 250
55+
},
56+
html: `
57+
<div>
58+
<p><small>${issue.repository.full_name} on ${createdAt}</small></p>
59+
<p><i class="octicon ${octicon}"></i> <strong>${issue.title}</strong> #${index}</p>
60+
<p>${body}</p>
61+
${labels}
62+
</div>
63+
`
64+
});
65+
});
66+
}

Diff for: web_src/js/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import './publicPath.js';
66
import './gitGraphLoader.js';
77
import './semanticDropdown.js';
8+
import initContextPopups from './features/contextPopup';
89

910
function htmlEncode(text) {
1011
return jQuery('<div />').text(text).html();
@@ -2556,6 +2557,7 @@ $(document).ready(() => {
25562557
initPullRequestReview();
25572558
initRepoStatusChecker();
25582559
initTemplateSearch();
2560+
initContextPopups(suburl);
25592561

25602562
// Repo clone url.
25612563
if ($('#repo-clone-url').length > 0) {

Diff for: web_src/less/_base.less

+16
Original file line numberDiff line numberDiff line change
@@ -1185,3 +1185,19 @@ i.icon.centerlock {
11851185
vertical-align: middle;
11861186
height: 2.1666em !important;
11871187
}
1188+
1189+
.octicon {
1190+
&.green {
1191+
color: #21ba45;
1192+
}
1193+
&.red {
1194+
color: #db2828;
1195+
}
1196+
&.purple {
1197+
color: #a333c8;
1198+
}
1199+
}
1200+
1201+
.ui.popup .ui.label {
1202+
margin-bottom: 0.4em;
1203+
}

Diff for: web_src/less/themes/arc-green.less

+10
Original file line numberDiff line numberDiff line change
@@ -1463,3 +1463,13 @@ a.ui.labels .label:hover {
14631463
}
14641464
}
14651465
}
1466+
1467+
.ui.popup {
1468+
background-color: #383c4a;
1469+
color: #9e9e9e;
1470+
border-color: #9e9e9e;
1471+
1472+
&.top::before {
1473+
background-color: #383c4a;
1474+
}
1475+
}

0 commit comments

Comments
 (0)