Commit c057842053e5b621b8944a045b087dd563abe19e
Implement inline comment replies; render inline comment threads
cel committed on 11/23/2017, 11:00:55 PMParent: 63bcff079120449a73ea2cceec00484d3378041c
Files changed
index.js | changed |
lib/forms.js | changed |
lib/repos/index.js | changed |
lib/repos/pulls.js | changed |
locale/en.json | changed |
locale/eo.json | changed |
schemas.md | changed |
static/styles.css | changed |
index.js | ||
---|---|---|
@@ -334,8 +334,11 @@ | ||
334 | 334 … | new ParamError('missing repo id'), 400)) |
335 | 335 … | if (!data.commitId) |
336 | 336 … | return cb(null, self.serveError(req, |
337 | 337 … | new ParamError('missing commit id'), 400)) |
338 … | + if (!data.updateId) | |
339 … | + return cb(null, self.serveError(req, | |
340 … | + new ParamError('missing update id'), 400)) | |
338 | 341 … | if (!data.filePath) |
339 | 342 … | return cb(null, self.serveError(req, |
340 | 343 … | new ParamError('missing file path'), 400)) |
341 | 344 … | if (!data.line) |
@@ -344,29 +347,51 @@ | ||
344 | 347 … | var lineNumber = Number(data.line) |
345 | 348 … | if (isNaN(lineNumber)) |
346 | 349 … | return cb(null, self.serveError(req, |
347 | 350 … | new ParamError('bad line number'), 400)) |
348 | - var repoBranches = data.repoBranch | |
349 | - ? data.repoBranch.split(',') : '' | |
350 | 351 … | var msg = { |
351 | 352 … | type: 'line-comment', |
352 | 353 … | text: data.text, |
353 | 354 … | repo: data.repo, |
354 | - repoBranch: repoBranches, | |
355 … | + updateId: data.updateId, | |
355 | 356 … | commitId: data.commitId, |
356 | 357 … | filePath: data.filePath, |
357 | 358 … | line: lineNumber, |
358 | 359 … | } |
359 | 360 … | msg.issue = data.issue |
360 | 361 … | var mentions = Mentions(data.text) |
361 | 362 … | if (mentions.length) |
362 | 363 … | msg.mentions = mentions |
363 | - return cb(null, self.serveBuffer(200, JSON.stringify(msg, 0, 2))) | |
364 | 364 … | return self.ssb.publish(msg, function (err) { |
365 | 365 … | if (err) return cb(null, self.serveError(req, err)) |
366 | 366 … | cb(null, self.serveRedirect(req, req.url)) |
367 | 367 … | }) |
368 | 368 … | |
369 … | + case 'line-comment-reply': | |
370 … | + if (!data.root) | |
371 … | + return cb(null, self.serveError(req, | |
372 … | + new ParamError('missing thread root'), 400)) | |
373 … | + if (!data.branch) | |
374 … | + return cb(null, self.serveError(req, | |
375 … | + new ParamError('missing thread branch'), 400)) | |
376 … | + if (!data.text) | |
377 … | + return cb(null, self.serveError(req, | |
378 … | + new ParamError('missing post text'), 400)) | |
379 … | + var msg = { | |
380 … | + type: 'post', | |
381 … | + root: data.root, | |
382 … | + branch: data.branch, | |
383 … | + text: data.text, | |
384 … | + } | |
385 … | + var mentions = Mentions(data.text) | |
386 … | + if (mentions.length) | |
387 … | + msg.mentions = mentions | |
388 … | + return self.ssb.publish(msg, function (err) { | |
389 … | + if (err) return cb(null, self.serveError(req, err)) | |
390 … | + | |
391 … | + cb(null, self.serveRedirect(req, req.url)) | |
392 … | + }) | |
393 … | + | |
369 | 394 … | case 'new-issue': |
370 | 395 … | var msg = Issues.schemas.new(dir, data.text) |
371 | 396 … | var mentions = Mentions(data.text) |
372 | 397 … | if (mentions.length) |
lib/forms.js | ||
---|---|---|
@@ -2,26 +2,27 @@ | ||
2 | 2 … | var u = require('./util') |
3 | 3 … | var forms = exports |
4 | 4 … | |
5 | 5 … | forms.post = function (req, repo, placeholder, rows) { |
6 | - return '<input type="radio" class="tab-radio" id="tab1" name="tab" checked="checked"/>' + | |
6 … | + return '<div class="post-form">' + | |
7 … | + '<input type="radio" class="tab-radio" id="tab1" name="tab" checked="checked"/>' + | |
7 | 8 … | '<input type="radio" class="tab-radio" id="tab2" name="tab"/>' + |
8 | 9 … | '<div id="tab-links" class="tab-links" style="display:none">' + |
9 | 10 … | '<label for="tab1" id="write-tab-link" class="tab1-link">' + |
10 | 11 … | req._t('post.Write') + '</label>' + |
11 | 12 … | '<label for="tab2" id="preview-tab-link" class="tab2-link">' + |
12 | 13 … | req._t('post.Preview') + '</label>' + |
13 | 14 … | '</div>' + |
14 | 15 … | (repo ? |
15 | - '<input type="hidden" id="repo-id" value="' + repo.id + '"/>' | |
16 … | + '<input type="hidden" id="repo-id" class="repo-id" value="' + repo.id + '"/>' | |
16 | 17 … | : '') + |
17 | 18 … | '<div id="write-tab" class="tab1">' + |
18 | - '<textarea id="post-text" name="text" class="wide-input"' + | |
19 … | + '<textarea id="post-text" class="post-text" name="text" class="wide-input"' + | |
19 | 20 … | ' rows="' + (rows||4) + '" cols="77"' + |
20 | 21 … | (placeholder ? ' placeholder="' + placeholder + '"' : '') + |
21 | 22 … | '></textarea>' + |
22 | 23 … | '</div>' + |
23 | - '<div class="preview-text tab2" id="preview-tab"></div>' + | |
24 … | + '<div class="preview-text tab2" id="preview-tab"></div></div>' + | |
24 | 25 … | '<script>' + issueCommentScript + '</script>' |
25 | 26 … | } |
26 | 27 … | |
27 | 28 … | forms.name = function (req, enabled, id, name, action, inputId, title, header) { |
@@ -53,19 +54,21 @@ | ||
53 | 54 … | ]) |
54 | 55 … | } |
55 | 56 … | |
56 | 57 … | var issueCommentScript = '(' + function () { |
57 | - var $ = document.getElementById.bind(document) | |
58 | - $('tab-links').style.display = 'block' | |
59 | - $('preview-tab-link').onclick = function (e) { | |
58 … | + var container = [].slice.call(document.querySelectorAll('.post-form')).pop() || document.body | |
59 … | + var $ = container.querySelector.bind(container) | |
60 … | + $('.tab-links').style.display = 'block' | |
61 … | + $('.tab2-link').onclick = function (e) { | |
60 | 62 … | with (new XMLHttpRequest()) { |
61 | 63 … | open('POST', '', true) |
62 | 64 … | onload = function() { |
63 | - $('preview-tab').innerHTML = responseText | |
65 … | + $('.preview-text').innerHTML = responseText | |
64 | 66 … | } |
67 … | + var repoInput = $('.repo-id') | |
65 | 68 … | send('action=markdown' + |
66 | - '&repo=' + encodeURIComponent($('repo-id').value) + | |
67 | - '&text=' + encodeURIComponent($('post-text').value)) | |
69 … | + (repoInput ? '&repo=' + encodeURIComponent(repoInput.value) : '') + | |
70 … | + '&text=' + encodeURIComponent($('.post-text').value)) | |
68 | 71 … | } |
69 | 72 … | } |
70 | 73 … | }.toString() + ')()' |
71 | 74 … | |
@@ -102,12 +105,12 @@ | ||
102 | 105 … | '<script>' + issueCommentButtonScript + '</script>' + |
103 | 106 … | '</form></section>' |
104 | 107 … | } |
105 | 108 … | |
106 | -forms.lineComment = function (req, repo, repoBranch, commitId, filePath, line) { | |
109 … | +forms.lineComment = function (req, repo, updateId, commitId, filePath, line) { | |
107 | 110 … | return '<section><form action="" method="post">' + |
108 | 111 … | '<input type="hidden" name="action" value="line-comment">' + |
109 | - '<input type="hidden" name="repoBranch" value="' + repoBranch.join(',') + '">' + | |
112 … | + '<input type="hidden" name="updateId" value="' + updateId + '">' + | |
110 | 113 … | '<input type="hidden" name="repo" value="' + repo.id + '">' + |
111 | 114 … | '<input type="hidden" name="commitId" value="' + commitId + '">' + |
112 | 115 … | '<input type="hidden" name="filePath" value="' + filePath + '">' + |
113 | 116 … | '<input type="hidden" name="line" value="' + line + '">' + |
@@ -115,4 +118,15 @@ | ||
115 | 118 … | '<input type="submit" class="btn open" value="' + |
116 | 119 … | req._t('issue.LineComment') + '" />' + |
117 | 120 … | '</form></section>' |
118 | 121 … | } |
122 … | + | |
123 … | +forms.lineCommentReply = function (req, root, branch) { | |
124 … | + return '<section><form action="" method="post">' + | |
125 … | + '<input type="hidden" name="action" value="line-comment-reply">' + | |
126 … | + '<input type="hidden" name="root" value="' + root + '">' + | |
127 … | + '<input type="hidden" name="branch" value="' + branch + '">' + | |
128 … | + forms.post(req) + | |
129 … | + '<input type="submit" class="btn open" value="' + | |
130 … | + req._t('Reply') + '" />' + | |
131 … | + '</form></section>' | |
132 … | +} |
lib/repos/index.js | |||
---|---|---|---|
@@ -14,8 +14,9 @@ | |||
14 | 14 … | var ssbRef = require('ssb-ref') | |
15 | 15 … | var zlib = require('zlib') | |
16 | 16 … | var toPull = require('stream-to-pull-stream') | |
17 | 17 … | var h = require('pull-hyperscript') | |
18 … | +var getObjectMsgId = require('../../lib/obj-msg-id') | ||
18 | 19 … | ||
19 | 20 … | function extend(obj, props) { | |
20 | 21 … | for (var k in props) | |
21 | 22 … | obj[k] = props[k] | |
@@ -45,8 +46,115 @@ | |||
45 | 46 … | } | |
46 | 47 … | ||
47 | 48 … | /* Repo */ | |
48 | 49 … | ||
50 … | +R.getLineCommentThreads = function (req, repo, updateId, commitId, filename, cb) { | ||
51 … | + var self = this | ||
52 … | + var sbot = self.web.ssb | ||
53 … | + var lineCommentThreads = {} | ||
54 … | + pull( | ||
55 … | + sbot.backlinks ? sbot.backlinks.read({ | ||
56 … | + query: [ | ||
57 … | + {$filter: { | ||
58 … | + dest: updateId, | ||
59 … | + value: { | ||
60 … | + content: { | ||
61 … | + type: 'line-comment', | ||
62 … | + repo: repo.id, | ||
63 … | + updateId: updateId, | ||
64 … | + commitId: commitId, | ||
65 … | + filePath: filename | ||
66 … | + } | ||
67 … | + } | ||
68 … | + }} | ||
69 … | + ] | ||
70 … | + }) : pull( | ||
71 … | + sbot.links({ | ||
72 … | + dest: updateId, | ||
73 … | + rel: 'updateId', | ||
74 … | + values: true | ||
75 … | + }), | ||
76 … | + pull.filter(function (msg) { | ||
77 … | + var c = msg && msg.value && msg.value.content | ||
78 … | + return c && c.type === 'line-comment' | ||
79 … | + && c.updateId === updateId | ||
80 … | + && c.commitId === commitId | ||
81 … | + && c.filePath === filename | ||
82 … | + }) | ||
83 … | + ), | ||
84 … | + paramap(function (msg, cb) { | ||
85 … | + pull( | ||
86 … | + self.renderThread(req, repo, msg), | ||
87 … | + pull.collect(function (err, parts) { | ||
88 … | + if (err) return cb(err) | ||
89 … | + cb(null, { | ||
90 … | + line: msg.value.content.line, | ||
91 … | + html: parts.join(''), | ||
92 … | + }) | ||
93 … | + }) | ||
94 … | + ) | ||
95 … | + }, 4), | ||
96 … | + pull.drain(function (thread) { | ||
97 … | + lineCommentThreads[thread.line] = thread.html | ||
98 … | + }, function (err) { | ||
99 … | + if (err) return cb(err) | ||
100 … | + cb(null, lineCommentThreads) | ||
101 … | + }) | ||
102 … | + ) | ||
103 … | +} | ||
104 … | + | ||
105 … | +R.renderThread = function (req, repo, msg) { | ||
106 … | + var newestMsg = msg | ||
107 … | + var root = msg.key | ||
108 … | + var self = this | ||
109 … | + return h('div', [ | ||
110 … | + pull( | ||
111 … | + cat([ | ||
112 … | + pull.once(msg), | ||
113 … | + self.web.ssb.links({ | ||
114 … | + dest: root, | ||
115 … | + values: true | ||
116 … | + }), | ||
117 … | + ]), | ||
118 … | + pull.unique('key'), | ||
119 … | + u.decryptMessages(self.web.ssb), | ||
120 … | + u.readableMessages(), | ||
121 … | + self.web.addAuthorName(), | ||
122 … | + u.sortMsgs(), | ||
123 … | + pull.filter(function (msg) { | ||
124 … | + var c = msg && msg.value && msg.value.content | ||
125 … | + return c && (c.type === 'post' | ||
126 … | + || msg.key === root) | ||
127 … | + }), | ||
128 … | + pull.through(function (msg) { | ||
129 … | + // TODO: correctly calculate the thread branches | ||
130 … | + if (msg.value | ||
131 … | + && msg.value.timestamp > newestMsg.value.timestamp) | ||
132 … | + newestMsg = msg | ||
133 … | + }), | ||
134 … | + pull.map(function (msg) { | ||
135 … | + return self.renderLineComment(req, repo, msg) | ||
136 … | + }) | ||
137 … | + ), | ||
138 … | + self.web.isPublic ? '' : | ||
139 … | + pull.once(forms.lineCommentReply(req, root, newestMsg.key)) | ||
140 … | + ]) | ||
141 … | +} | ||
142 … | + | ||
143 … | +R.renderLineComment = function (req, repo, msg) { | ||
144 … | + var c = msg && msg.value && msg.value.content | ||
145 … | + return h('section', {class: 'collapse'}, [ | ||
146 … | + h('div', [ | ||
147 … | + u.link([msg.value.author], msg.authorName), | ||
148 … | + ' ', | ||
149 … | + h('tt', {class: 'right-bar item-id'}, msg.key), | ||
150 … | + ' · ', | ||
151 … | + h('a', {href: u.encodeLink(msg.key) + '#' + encodeURIComponent(msg.key)}, new Date(msg.value.timestamp).toLocaleString(req._locale)), | ||
152 … | + ]), | ||
153 … | + markdown(c.text, repo) | ||
154 … | + ]) | ||
155 … | +} | ||
156 … | + | ||
49 | 157 … | R.serveRepoPage = function (req, repo, path) { | |
50 | 158 … | var self = this | |
51 | 159 … | var defaultBranch = 'master' | |
52 | 160 … | var query = req._u.query | |
@@ -593,46 +701,50 @@ | |||
593 | 701 … | /* Repo commit */ | |
594 | 702 … | ||
595 | 703 … | R.serveRepoCommit = function (req, repo, rev) { | |
596 | 704 … | var self = this | |
597 | - var repoBranch = [] | ||
598 | 705 … | return u.readNext(function (cb) { | |
599 | 706 … | repo.getCommitParsed(rev, function (err, commit) { | |
600 | 707 … | if (err) return cb(null, | |
601 | 708 … | self.serveRepoTemplate(req, repo, null, rev, `%{author}/%{repo}@${rev}`, | |
602 | 709 … | pull.once(self.web.renderError(err)))) | |
603 | - var commitPath = [repo.id, 'commit', commit.id] | ||
604 | - var treePath = [repo.id, 'tree', commit.id] | ||
605 | - var title = u.escape(commit.title) + ' · ' + | ||
606 | - '%{author}/%{repo}@' + commit.id.substr(0, 8) | ||
607 | - cb(null, self.serveRepoTemplate(req, repo, null, rev, title, cat([ | ||
608 | - pull.once( | ||
609 | - '<h3>' + u.link(commitPath, | ||
610 | - req._t('CommitRev', {rev: rev})) + '</h3>' + | ||
611 | - '<section class="collapse">' + | ||
612 | - '<div class="right-bar">' + | ||
613 | - u.link(treePath, req._t('BrowseFiles')) + | ||
614 | - '</div>' + | ||
615 | - '<h4>' + u.linkify(u.escape(commit.title)) + '</h4>' + | ||
616 | - (commit.body ? u.linkify(u.pre(commit.body)) : '') + | ||
617 | - (commit.separateAuthor ? req._t('AuthoredOn', { | ||
618 | - name: u.escape(commit.author.name), | ||
619 | - date: commit.author.date.toLocaleString(req._locale) | ||
620 | - }) + '<br/>' : '') + | ||
621 | - req._t('CommittedOn', { | ||
622 | - name: u.escape(commit.committer.name), | ||
623 | - date: commit.committer.date.toLocaleString(req._locale) | ||
624 | - }) + '<br/>' + | ||
625 | - commit.parents.map(function (id) { | ||
626 | - return req._t('Parent') + ': ' + | ||
627 | - u.link([repo.id, 'commit', id], id) | ||
628 | - }).join('<br>') + | ||
629 | - '</section>' + | ||
630 | - '<section><h3>' + req._t('FilesChanged') + '</h3>'), | ||
631 | - // TODO: show diff from all parents (merge commits) | ||
632 | - self.renderDiffStat(req, [repo, repo], [commit.parents[0], commit.id], commit.id, repoBranch), | ||
633 | - pull.once('</section>') | ||
634 | - ]))) | ||
710 … | + getObjectMsgId(repo, commit.id, function (err, objMsgId) { | ||
711 … | + if (err) return cb(null, | ||
712 … | + self.serveRepoTemplate(req, repo, null, rev, `%{author}/%{repo}@${rev}`, | ||
713 … | + pull.once(self.web.renderError(err)))) | ||
714 … | + var commitPath = [repo.id, 'commit', commit.id] | ||
715 … | + var treePath = [repo.id, 'tree', commit.id] | ||
716 … | + var title = u.escape(commit.title) + ' · ' + | ||
717 … | + '%{author}/%{repo}@' + commit.id.substr(0, 8) | ||
718 … | + cb(null, self.serveRepoTemplate(req, repo, null, rev, title, cat([ | ||
719 … | + pull.once( | ||
720 … | + '<h3>' + u.link(commitPath, | ||
721 … | + req._t('CommitRev', {rev: rev})) + '</h3>' + | ||
722 … | + '<section class="collapse">' + | ||
723 … | + '<div class="right-bar">' + | ||
724 … | + u.link(treePath, req._t('BrowseFiles')) + | ||
725 … | + '</div>' + | ||
726 … | + '<h4>' + u.linkify(u.escape(commit.title)) + '</h4>' + | ||
727 … | + (commit.body ? u.linkify(u.pre(commit.body)) : '') + | ||
728 … | + (commit.separateAuthor ? req._t('AuthoredOn', { | ||
729 … | + name: u.escape(commit.author.name), | ||
730 … | + date: commit.author.date.toLocaleString(req._locale) | ||
731 … | + }) + '<br/>' : '') + | ||
732 … | + req._t('CommittedOn', { | ||
733 … | + name: u.escape(commit.committer.name), | ||
734 … | + date: commit.committer.date.toLocaleString(req._locale) | ||
735 … | + }) + '<br/>' + | ||
736 … | + commit.parents.map(function (id) { | ||
737 … | + return req._t('Parent') + ': ' + | ||
738 … | + u.link([repo.id, 'commit', id], id) | ||
739 … | + }).join('<br>') + | ||
740 … | + '</section>' + | ||
741 … | + '<section><h3>' + req._t('FilesChanged') + '</h3>'), | ||
742 … | + // TODO: show diff from all parents (merge commits) | ||
743 … | + self.renderDiffStat(req, [repo, repo], [commit.parents[0], commit.id], commit.id, objMsgId), | ||
744 … | + pull.once('</section>') | ||
745 … | + ]))) | ||
746 … | + }) | ||
635 | 747 … | }) | |
636 | 748 … | }) | |
637 | 749 … | } | |
638 | 750 … | ||
@@ -673,9 +785,10 @@ | |||
673 | 785 … | ||
674 | 786 … | ||
675 | 787 … | /* Diff stat */ | |
676 | 788 … | ||
677 | -R.renderDiffStat = function (req, repos, treeIds, commit, repoBranch) { | ||
789 … | +R.renderDiffStat = function (req, repos, treeIds, commit, updateId) { | ||
790 … | + var self = this | ||
678 | 791 … | if (treeIds.length == 0) treeIds = [null] | |
679 | 792 … | var id = treeIds[0] | |
680 | 793 … | var lastI = treeIds.length - 1 | |
681 | 794 … | var oldTree = treeIds[0] | |
@@ -734,25 +847,26 @@ | |||
734 | 847 … | var done = multicb({ pluck: 1, spread: true }) | |
735 | 848 … | var mode0 = item.mode && item.mode[0] | |
736 | 849 … | var modeI = item.mode && item.mode[lastI] | |
737 | 850 … | var isSubmodule = (modeI == 0160000) | |
851 … | + var repo = repos[1] | ||
738 | 852 … | getRepoObjectString(repos[0], item.id[0], mode0, done()) | |
739 | 853 … | getRepoObjectString(repos[1], item.id[lastI], modeI, done()) | |
740 | - done(function (err, strOld, strNew) { | ||
854 … | + self.getLineCommentThreads(req, repo, updateId, commit, item.filename, done()) | ||
855 … | + done(function (err, strOld, strNew, lineCommentThreads) { | ||
741 | 856 … | if (err) return cb(err) | |
742 | - var repo = repos[1] | ||
743 | - cb(null, htmlLineDiff(req, repo, repoBranch, commit, item.filename, item.filename, | ||
857 … | + cb(null, htmlLineDiff(req, repo, updateId, commit, item.filename, item.filename, | ||
744 | 858 … | strOld, strNew, | |
745 | - u.encodeLink(item.blobPath), !isSubmodule)) | ||
859 … | + u.encodeLink(item.blobPath), !isSubmodule, lineCommentThreads)) | ||
746 | 860 … | }) | |
747 | 861 … | }, 4) | |
748 | 862 … | ) | |
749 | 863 … | ]) | |
750 | 864 … | } | |
751 | 865 … | ||
752 | -function htmlLineDiff(req, repo, repoBranch, commit, filename, anchor, oldStr, newStr, blobHref, | ||
753 | - showViewLink) { | ||
754 | - return '<pre><table class="code">' + | ||
866 … | +function htmlLineDiff(req, repo, updateId, commit, filename, anchor, oldStr, newStr, blobHref, | ||
867 … | + showViewLink, lineCommentThreads) { | ||
868 … | + return '<div class="code-wrap"><table class="code">' + | ||
755 | 869 … | '<tr><th colspan=3 id="' + u.escape(anchor) + '">' + filename + | |
756 | 870 … | (showViewLink === false ? '' : | |
757 | 871 … | '<span class="right-bar">' + | |
758 | 872 … | '<a href="' + blobHref + '">' + req._t('View') + '</a> ' + | |
@@ -761,13 +875,13 @@ | |||
761 | 875 … | (oldStr.length + newStr.length > 200000 | |
762 | 876 … | ? '<tr><td class="diff-info" colspan=3>' + req._t('diff.TooLarge') + '<br>' + | |
763 | 877 … | req._t('diff.OldFileSize', {bytes: oldStr.length}) + '<br>' + | |
764 | 878 … | req._t('diff.NewFileSize', {bytes: newStr.length}) + '</td></tr>' | |
765 | - : tableDiff(req, repo, repoBranch, commit, oldStr, newStr, filename)) + | ||
766 | - '</table></pre>' | ||
879 … | + : tableDiff(req, repo, updateId, commit, oldStr, newStr, filename, lineCommentThreads)) + | ||
880 … | + '</table></div>' | ||
767 | 881 … | } | |
768 | 882 … | ||
769 | -function tableDiff(req, repo, repoBranch, commit, oldStr, newStr, filename) { | ||
883 … | +function tableDiff(req, repo, updateId, commit, oldStr, newStr, filename, lineCommentThreads) { | ||
770 | 884 … | var query = req._u.query | |
771 | 885 … | var diff = JsDiff.structuredPatch('', '', oldStr, newStr) | |
772 | 886 … | var groups = diff.hunks.map(function (hunk) { | |
773 | 887 … | var oldLine = hunk.oldStart | |
@@ -782,25 +896,31 @@ | |||
782 | 896 … | var html = u.highlight(line, u.getExtension(filename)) | |
783 | 897 … | var trClass = s == '+' ? 'diff-new' : s == '-' ? 'diff-old' : '' | |
784 | 898 … | var lineNums = [s == '+' ? '' : oldLine++, s == '-' ? '' : newLine++] | |
785 | 899 … | var id = [filename].concat(lineNums).join('-') | |
900 … | + var newLineNum = lineNums[lineNums.length-1] | ||
786 | 901 … | return '<tr id="' + u.escape(id) + '" class="' + trClass + '">' + | |
787 | 902 … | lineNums.map(function (num, i) { | |
788 | 903 … | var idEnc = encodeURIComponent(id) | |
789 | 904 … | return '<td class="code-linenum">' + | |
790 | 905 … | (num ? '<a href="#' + idEnc + '">' + | |
791 | 906 … | num + '</a>' + | |
792 | - (i === lineNums.length-1 && s !== '-' ? | ||
907 … | + (updateId && i === lineNums.length-1 && s !== '-' ? | ||
793 | 908 … | // TODO: use a more descriptive icon for the comment action | |
794 | 909 … | ' <a href="?comment=' + idEnc + '#' + idEnc + '">…</a>' | |
795 | 910 … | : '') | |
796 | 911 … | : '') + '</td>' | |
797 | 912 … | }).join('') + | |
798 | 913 … | '<td class="code-text">' + html + '</td></tr>' + | |
799 | 914 … | (commit && query.comment === id ? | |
800 | 915 … | '<tr><td colspan=4>' + | |
801 | - forms.lineComment(req, repo, repoBranch, commit, filename, lineNums[lineNums.length-1]) + | ||
916 … | + forms.lineComment(req, repo, updateId, commit, filename, newLineNum) + | ||
802 | 917 … | '</td></tr>' | |
918 … | + : '') + | ||
919 … | + (lineCommentThreads[newLineNum] ? | ||
920 … | + '<tr><td colspan=4>' + | ||
921 … | + lineCommentThreads[newLineNum] + | ||
922 … | + '</td></tr>' | ||
803 | 923 … | : '') | |
804 | 924 … | })) | |
805 | 925 … | }) | |
806 | 926 … | return [].concat.apply([], groups).join('') |
lib/repos/pulls.js | ||
---|---|---|
@@ -7,8 +7,9 @@ | ||
7 | 7 … | var u = require('../../lib/util') |
8 | 8 … | var markdown = require('../../lib/markdown') |
9 | 9 … | var forms = require('../../lib/forms') |
10 | 10 … | var ssbRef = require('ssb-ref') |
11 … | +var getObjectMsgId = require('../../lib/obj-msg-id') | |
11 | 12 … | |
12 | 13 … | module.exports = function (repoRoutes, web) { |
13 | 14 … | return new RepoPullReqRoutes(repoRoutes, web) |
14 | 15 … | } |
@@ -112,16 +113,18 @@ | ||
112 | 113 … | if (!revs.head) return cb(null, pull.once('<section>' + |
113 | 114 … | req._t('pullRequest.NoChanges') + '</section>')) |
114 | 115 … | GitRepo.getMergeBase(baseRepo, revs.base, headRepo, revs.head, |
115 | 116 … | function (err, mergeBase) { |
116 | - var repoBranch = [] | |
117 | 117 … | if (err) return cb(err) |
118 | - cb(null, cat([ | |
119 | - pull.once('<section>'), | |
120 | - self.repo.renderDiffStat(req, | |
121 | - [baseRepo, headRepo], [mergeBase, revs.head], revs.head, repoBranch), | |
122 | - pull.once('</section>') | |
123 | - ])) | |
118 … | + getObjectMsgId(headRepo, revs.head, function (err, objMsgId) { | |
119 … | + if (err) return cb(err) | |
120 … | + cb(null, cat([ | |
121 … | + pull.once('<section>'), | |
122 … | + self.repo.renderDiffStat(req, | |
123 … | + [baseRepo, headRepo], [mergeBase, revs.head], revs.head, objMsgId), | |
124 … | + pull.once('</section>') | |
125 … | + ])) | |
126 … | + }) | |
124 | 127 … | } |
125 | 128 … | ) |
126 | 129 … | }) |
127 | 130 … | } |
locale/en.json | ||
---|---|---|
@@ -136,8 +136,9 @@ | ||
136 | 136 … | "CommentAndClose": "Comment and Close", |
137 | 137 … | "Reopen": "Reopen %{type}", |
138 | 138 … | "CommentAndReopen": "Comment and Reopen" |
139 | 139 … | }, |
140 … | + "Reply": "Reply", | |
140 | 141 … | "pullRequest": { |
141 | 142 … | "WantToMerge": "%{name} wants to merge commits into %{base} from %{head}", |
142 | 143 … | "Discussion": "Discussion", |
143 | 144 … | "New": "New Pull Request", |
locale/eo.json | ||
---|---|---|
@@ -136,8 +136,9 @@ | ||
136 | 136 … | "CommentAndClose": "Komenti kaj Fermi", |
137 | 137 … | "Reopen": "Remalfermi %{type}", |
138 | 138 … | "CommandAndReopen": "Komenti kaj Remalfermi" |
139 | 139 … | }, |
140 … | + "Reply": "Reply", | |
140 | 141 … | "pullRequest": { |
141 | 142 … | "WantToMerge": "%{name} volas kunfandi enmetoj en %{base} de %{head}", |
142 | 143 … | "Discussion": "Priparolado", |
143 | 144 … | "New": "Nova Tiro-peto", |
schemas.md | ||
---|---|---|
@@ -4,14 +4,14 @@ | ||
4 | 4 … | { |
5 | 5 … | "type": "line-comment", |
6 | 6 … | "text": string, |
7 | 7 … | "repo": MsgId, |
8 | - "repoBranch": MgsIds, | |
8 … | + "updateId": MgsIds, | |
9 | 9 … | "line": number, |
10 | 10 … | } |
11 | 11 … | ``` |
12 | 12 … | `repo`: id of `git-repo` |
13 | -`repoBranch`: id(s) of `git-update` messages that pushed the git commit, git trees, and git blobs needed to render the diff. | |
13 … | +`updateId`: id of a `git-update` message that pushed the git commit, git trees, and git blobs needed to render the diff. | |
14 | 14 … | `commitId`: commit that the comment is on |
15 | 15 … | `filePath`: path to the file that the comment is on |
16 | 16 … | `line`: line number of the file that the comment is on |
17 | 17 … |
static/styles.css | ||
---|---|---|
@@ -17,8 +17,21 @@ | ||
17 | 17 … | margin-left: auto; |
18 | 18 … | margin-right: auto; |
19 | 19 … | } |
20 | 20 … | |
21 … | +.code-wrap { | |
22 … | + padding: .5em 1.25ex; | |
23 … | + border: 1px solid #ddd; | |
24 … | + background: #f5f5f5; | |
25 … | +} | |
26 … | + | |
27 … | +.diff-hunk-header, | |
28 … | +.code-linenum, | |
29 … | +.code-text { | |
30 … | + font: .8em Consolas, "Liberation Mono", Menlo, Courier, monospace; | |
31 … | + white-space: pre-wrap; | |
32 … | +} | |
33 … | + | |
21 | 34 … | pre { |
22 | 35 … | font: .8em Consolas, "Liberation Mono", Menlo, Courier, monospace; |
23 | 36 … | padding: .5em 1.25ex; |
24 | 37 … | border: 1px solid #ddd; |
@@ -31,8 +44,9 @@ | ||
31 | 44 … | border-radius: .5ex; |
32 | 45 … | background-color: #fff; |
33 | 46 … | } |
34 | 47 … | |
48 … | +.code-wrap > code, | |
35 | 49 … | pre > code { |
36 | 50 … | padding: 0; |
37 | 51 … | background-color: #f5f5f5; |
38 | 52 … | } |
Built with git-ssb-web