Files: f7ca54c0c9db01f4eb92a59c6b61eda900f89e9f / modules / git.js
7388 bytesRaw
1 | var h = require('hyperscript') |
2 | var pull = require('pull-stream') |
3 | var paramap = require('pull-paramap') |
4 | var human = require('human-time') |
5 | |
6 | var plugs = require('../plugs') |
7 | var message_link = plugs.first(exports.message_link = []) |
8 | var sbot_links = plugs.first(exports.sbot_links = []) |
9 | var sbot_links2 = plugs.first(exports.sbot_links2 = []) |
10 | var sbot_get = plugs.first(exports.sbot_get = []) |
11 | var getAvatar = require('ssb-avatar') |
12 | var avatar_name = plugs.first(exports.avatar_name = []) |
13 | var markdown = plugs.first(exports.markdown = []) |
14 | |
15 | var self_id = require('../keys').id |
16 | |
17 | function shortRefName(ref) { |
18 | return ref.replace(/^refs\/(heads|tags)\//, '') |
19 | } |
20 | |
21 | function repoLink(id) { |
22 | var el = h('a', {href: '#'+id}, id.substr(0, 10) + '…') |
23 | getAvatar({links: sbot_links}, self_id, id, function (err, avatar) { |
24 | if(err) return console.error(err) |
25 | el.textContent = avatar.name |
26 | }) |
27 | return el |
28 | } |
29 | |
30 | function getIssueState(id, cb) { |
31 | pull( |
32 | sbot_links({dest: id, rel: 'issues', values: true}), |
33 | pull.map(function (msg) { |
34 | var issues = msg.value.content.issues |
35 | if (!Array.isArray(issues)) return |
36 | return issues.filter(function (issue) { |
37 | return issue.link === id |
38 | }).map(function (issue) { |
39 | return { |
40 | ts: msg.value.timestamp, |
41 | open: issue.open, |
42 | merged: issue.merged, |
43 | } |
44 | }) |
45 | }), |
46 | pull.flatten(), |
47 | pull.collect(function (err, updates) { |
48 | if (err) return cb(err) |
49 | var open = true, merged = false |
50 | updates.sort(function (a, b) { |
51 | return b.ts - a.ts |
52 | }).forEach(function (update) { |
53 | if (update.open != null) |
54 | open = update.open |
55 | if (update.merged != null) |
56 | merged = update.merged |
57 | }) |
58 | cb(null, open ? 'open' : merged ? 'merged' : 'closed') |
59 | }) |
60 | ) |
61 | } |
62 | |
63 | //todo: |
64 | function messageTimestampLink(msg) { |
65 | var m = human(msg.value.timestamp) |
66 | return h('a.timestamp', { |
67 | timestamp: msg.value.timestamp, |
68 | title: new Date(msg.value.timestamp), |
69 | href: '#'+msg.key |
70 | }, m.fromNow()) |
71 | } |
72 | |
73 | function tableRows(headerRow) { |
74 | var thead = h('thead'), tbody = h('tbody') |
75 | var first = true |
76 | var t = [thead, tbody] |
77 | t.append = function (row) { |
78 | if (first) { |
79 | first = false |
80 | thead.appendChild(headerRow) |
81 | } |
82 | tbody.appendChild(row) |
83 | } |
84 | return t |
85 | } |
86 | |
87 | function repoName(id, link) { |
88 | var el = link |
89 | ? h('a', {href: '#'+id}, id.substr(0, 8) + '…') |
90 | : h('ins', id.substr(0, 8) + '…') |
91 | getAvatar({links: sbot_links}, self_id, id, function (err, avatar) { |
92 | if(err) return console.error(err) |
93 | el.textContent = avatar.name |
94 | }) |
95 | return el |
96 | } |
97 | |
98 | exports.message_content = function (msg, sbot) { |
99 | var c = msg.value.content |
100 | |
101 | if(c.type === 'git-repo') { |
102 | var nameEl |
103 | var branchesT, tagsT, openIssuesT, closedIssuesT, openPRsT, closedPRsT |
104 | var div = h('div', |
105 | h('p', 'git repo ', repoName(msg.key)), |
106 | c.upstream ? h('p', 'fork of ', repoName(c.upstream, true)) : '', |
107 | h('p', h('code', 'ssb://' + msg.key)), |
108 | h('div.git-table-wrapper', {style: {'max-height': '12em'}}, |
109 | h('table', |
110 | branchesT = tableRows(h('tr', |
111 | h('th', 'branch'), |
112 | h('th', 'commit'), |
113 | h('th', 'last update'))), |
114 | tagsT = tableRows(h('tr', |
115 | h('th', 'tag'), |
116 | h('th', 'commit'), |
117 | h('th', 'last update'))))), |
118 | h('div.git-table-wrapper', {style: {'max-height': '16em'}}, |
119 | h('table', |
120 | openIssuesT = tableRows(h('tr', |
121 | h('th', 'open issues'))), |
122 | closedIssuesT = tableRows(h('tr', |
123 | h('th', 'closed issues'))))), |
124 | h('div.git-table-wrapper', {style: {'max-height': '16em'}}, |
125 | h('table', |
126 | openPRsT = tableRows(h('tr', |
127 | h('th', 'open pull requests'))), |
128 | closedPRsT = tableRows(h('tr', |
129 | h('th', 'closed pull requests')))))) |
130 | |
131 | // compute refs |
132 | var refs = {} |
133 | pull( |
134 | sbot_links({ |
135 | reverse: true, |
136 | source: msg.value.author, |
137 | dest: msg.key, |
138 | rel: 'repo', |
139 | values: true |
140 | }), |
141 | pull.drain(function (link) { |
142 | var refUpdates = link.value.content.refs |
143 | for (var ref in refUpdates) { |
144 | if (refs[ref]) continue |
145 | refs[ref] = true |
146 | var rev = refUpdates[ref] |
147 | if (!rev) continue |
148 | var parts = /^refs\/(heads|tags)\/(.*)$/.exec(ref) || [] |
149 | var t |
150 | if (parts[1] === 'heads') t = branchesT |
151 | else if (parts[1] === 'tags') t = tagsT |
152 | if (t) t.append(h('tr', |
153 | h('td', parts[2]), |
154 | h('td', h('code', rev)), |
155 | h('td', messageTimestampLink(link)))) |
156 | } |
157 | }, function (err) { |
158 | if (err) console.error(err) |
159 | }) |
160 | ) |
161 | |
162 | // list issues and pull requests |
163 | pull( |
164 | sbot_links({ |
165 | reverse: true, |
166 | dest: msg.key, |
167 | rel: 'project', |
168 | values: true |
169 | }), |
170 | paramap(function (link, cb) { |
171 | getIssueState(link.key, function (err, state) { |
172 | if(err) return cb(err) |
173 | link.state = state |
174 | cb(null, link) |
175 | }) |
176 | }), |
177 | pull.drain(function (link) { |
178 | var c = link.value.content |
179 | // TODO: support renamed issues |
180 | var title = c.title || (c.text ? c.text.length > 30 |
181 | ? c.text.substr(0, 30) + '…' |
182 | : c.text : link.key) |
183 | var author = link.value.author |
184 | var t = c.type === 'pull-request' |
185 | ? link.state === 'open' ? openPRsT : closedPRsT |
186 | : link.state === 'open' ? openIssuesT : closedIssuesT |
187 | t.append(h('tr', |
188 | h('td', |
189 | h('a', {href: '#'+link.key}, title), h('br'), |
190 | h('small', |
191 | 'opened ', messageTimestampLink(link), |
192 | ' by ', h('a', {href: '#'+author}, avatar_name(author)))))) |
193 | }, function (err) { |
194 | if (err) console.error(err) |
195 | }) |
196 | ) |
197 | |
198 | return div |
199 | } |
200 | |
201 | if(c.type === 'git-update') { |
202 | return h('p', |
203 | 'pushed to ', |
204 | repoLink(c.repo), |
205 | c.refs ? h('ul', Object.keys(c.refs).map(function (ref) { |
206 | var rev = c.refs[ref] |
207 | return h('li', |
208 | shortRefName(ref) + ': ', |
209 | rev ? h('code', rev) : h('em', 'deleted')) |
210 | })) : null, |
211 | Array.isArray(c.issues) ? c.issues.map(function (issue) { |
212 | if (issue.merged === true) |
213 | return ['Merged ', message_link(issue.link), ' in ', |
214 | h('code', issue.object), ' ', h('q', issue.label)] |
215 | if (issue.open === false) |
216 | return ['Closed ', message_link(issue.link), ' in ', |
217 | h('code', issue.object), ' ', h('q', issue.label)] |
218 | }) : null |
219 | ) |
220 | } |
221 | |
222 | if (c.type === 'issue') { |
223 | return h('div', |
224 | h('p', 'opened issue on ', repoLink(c.project)), |
225 | c.title ? h('h4', c.title) : '', |
226 | markdown(c) |
227 | ) |
228 | } |
229 | |
230 | if (c.type === 'pull-request') { |
231 | return h('div', |
232 | h('p', 'opened pull-request ', |
233 | 'to ', repoLink(c.repo), ':', c.branch, ' ', |
234 | 'from ', repoLink(c.head_repo), ':', c.head_branch), |
235 | c.title ? h('h4', c.title) : '', |
236 | markdown(c) |
237 | ) |
238 | } |
239 | } |
240 | |
241 | exports.message_meta = function (msg, sbot) { |
242 | var type = msg.value.content.type |
243 | if (type == 'issue' || type == 'pull-request') { |
244 | var el = h('em', '...') |
245 | getIssueState(msg.key, function (err, state) { |
246 | if (err) return console.error(err) |
247 | el.textContent = state |
248 | }) |
249 | return el |
250 | } |
251 | } |
252 | |
253 |
Built with git-ssb-web