git ssb

0+

ev / microbay



forked from Dominic / patchbay

Tree: 77ca943d1889e9ed48ffceb25d8de3f1b5998515

Files: 77ca943d1889e9ed48ffceb25d8de3f1b5998515 / modules / git.js

9007 bytesRaw
1var h = require('hyperscript')
2var pull = require('pull-stream')
3var paramap = require('pull-paramap')
4var human = require('human-time')
5
6var plugs = require('../plugs')
7var message_link = plugs.first(exports.message_link = [])
8var message_confirm = plugs.first(exports.message_confirm = [])
9var sbot_links = plugs.first(exports.sbot_links = [])
10var sbot_links2 = plugs.first(exports.sbot_links2 = [])
11var sbot_get = plugs.first(exports.sbot_get = [])
12var getAvatar = require('ssb-avatar')
13var avatar_name = plugs.first(exports.avatar_name = [])
14var markdown = plugs.first(exports.markdown = [])
15
16var self_id = require('../keys').id
17
18function shortRefName(ref) {
19 return ref.replace(/^refs\/(heads|tags)\//, '')
20}
21
22function repoLink(id) {
23 var el = h('a', {href: '#'+id}, id.substr(0, 10) + '…')
24 getAvatar({links: sbot_links}, self_id, id, function (err, avatar) {
25 if(err) return console.error(err)
26 el.textContent = avatar.name
27 })
28 return el
29}
30
31function getIssueState(id, cb) {
32 pull(
33 sbot_links({dest: id, rel: 'issues', values: true, reverse: true}),
34 pull.map(function (msg) {
35 return msg.value.content.issues
36 }),
37 pull.flatten(),
38 pull.filter(function (issue) {
39 return issue.link === id
40 }),
41 pull.map(function (issue) {
42 return issue.merged ? 'merged' : issue.open ? 'open' : 'closed'
43 }),
44 pull.take(1),
45 pull.collect(function (err, updates) {
46 cb(err, updates && updates[0] || 'open')
47 })
48 )
49}
50
51//todo:
52function messageTimestampLink(msg) {
53 return h('a.timestamp', {
54 timestamp: msg.value.timestamp,
55 title: new Date(msg.value.timestamp),
56 href: '#'+msg.key
57 }, human(msg.value.timestamp))
58}
59
60// a thead+tbody where the thead only is added when the first row is added
61function tableRows(headerRow) {
62 var thead = h('thead'), tbody = h('tbody')
63 var first = true
64 var t = [thead, tbody]
65 t.append = function (row) {
66 if (first) {
67 first = false
68 thead.appendChild(headerRow)
69 }
70 tbody.appendChild(row)
71 }
72 return t
73}
74
75function repoName(id, link) {
76 var el = link
77 ? h('a', {href: '#'+id}, id.substr(0, 8) + '…')
78 : h('ins', id.substr(0, 8) + '…')
79 getAvatar({links: sbot_links}, self_id, id, function (err, avatar) {
80 if(err) return console.error(err)
81 el.textContent = avatar.name
82 })
83 return el
84}
85
86function renderIssueEdit(c) {
87 var id = c.issue || c.link
88 return [
89 c.title ? h('p', 'renamed issue ', message_link(id),
90 ' to ', h('ins', c.title)) : null,
91 c.open === false ? h('p', 'closed issue ', message_link(id)) : null,
92 c.open === true ? h('p', 'reopened issue ', message_link(id)) : null]
93}
94
95exports.message_content = function (msg, sbot) {
96 var c = msg.value.content
97
98 if(c.type === 'git-repo') {
99 var branchesT, tagsT, openIssuesT, closedIssuesT, openPRsT, closedPRsT
100 var forksT
101 var div = h('div',
102 h('p', 'git repo ', repoName(msg.key)),
103 c.upstream ? h('p', 'fork of ', repoName(c.upstream, true)) : '',
104 h('p', h('code', 'ssb://' + msg.key)),
105 h('div.git-table-wrapper', {style: {'max-height': '12em'}},
106 h('table',
107 branchesT = tableRows(h('tr',
108 h('th', 'branch'),
109 h('th', 'commit'),
110 h('th', 'last update'))),
111 tagsT = tableRows(h('tr',
112 h('th', 'tag'),
113 h('th', 'commit'),
114 h('th', 'last update'))))),
115 h('div.git-table-wrapper', {style: {'max-height': '16em'}},
116 h('table',
117 openIssuesT = tableRows(h('tr',
118 h('th', 'open issues'))),
119 closedIssuesT = tableRows(h('tr',
120 h('th', 'closed issues'))))),
121 h('div.git-table-wrapper', {style: {'max-height': '16em'}},
122 h('table',
123 openPRsT = tableRows(h('tr',
124 h('th', 'open pull requests'))),
125 closedPRsT = tableRows(h('tr',
126 h('th', 'closed pull requests'))))),
127 h('div.git-table-wrapper',
128 h('table',
129 forksT = tableRows(h('tr',
130 h('th', 'forks'))))))
131
132 // compute refs
133 var refs = {}
134 pull(
135 sbot_links({
136 reverse: true,
137 source: msg.value.author,
138 dest: msg.key,
139 rel: 'repo',
140 values: true
141 }),
142 pull.drain(function (link) {
143 var refUpdates = link.value.content.refs || {}
144 Object.keys(refUpdates).reverse().filter(function (ref) {
145 if (refs[ref]) return
146 refs[ref] = true
147 var rev = refUpdates[ref]
148 if (!rev) return
149 var parts = /^refs\/(heads|tags)\/(.*)$/.exec(ref) || []
150 var t
151 if (parts[1] === 'heads') t = branchesT
152 else if (parts[1] === 'tags') t = tagsT
153 if (t) t.append(h('tr',
154 h('td', parts[2]),
155 h('td', h('code', rev)),
156 h('td', messageTimestampLink(link))))
157 })
158 }, function (err) {
159 if (err) console.error(err)
160 })
161 )
162
163 // list issues and pull requests
164 pull(
165 sbot_links({
166 reverse: true,
167 dest: msg.key,
168 rel: 'project',
169 values: true
170 }),
171 paramap(function (link, cb) {
172 getIssueState(link.key, function (err, state) {
173 if(err) return cb(err)
174 link.state = state
175 cb(null, link)
176 })
177 }),
178 pull.drain(function (link) {
179 var c = link.value.content
180 var title = c.title || (c.text ? c.text.length > 70
181 ? c.text.substr(0, 70) + '…'
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 // list forks
199 pull(
200 sbot_links({
201 reverse: true,
202 dest: msg.key,
203 rel: 'upstream'
204 }),
205 pull.drain(function (link) {
206 forksT.append(h('tr', h('td',
207 repoName(link.key, true),
208 ' by ', h('a', {href: '#'+link.source}, avatar_name(link.source)))))
209 }, function (err) {
210 if (err) console.error(err)
211 })
212 )
213
214 return div
215 }
216
217 if(c.type === 'git-update') {
218 return h('p',
219 'pushed to ',
220 repoLink(c.repo),
221 c.refs ? h('ul', Object.keys(c.refs).map(function (ref) {
222 var rev = c.refs[ref]
223 return h('li',
224 shortRefName(ref) + ': ',
225 rev ? h('code', rev) : h('em', 'deleted'))
226 })) : null,
227 Array.isArray(c.issues) ? c.issues.map(function (issue) {
228 if (issue.merged === true)
229 return ['Merged ', message_link(issue.link), ' in ',
230 h('code', issue.object), ' ', h('q', issue.label)]
231 if (issue.open === false)
232 return ['Closed ', message_link(issue.link), ' in ',
233 h('code', issue.object), ' ', h('q', issue.label)]
234 }) : null
235 )
236 }
237
238 if(c.type === 'issue-edit') {
239 return h('div',
240 c.issue ? renderIssueEdit(c) : null,
241 c.issues ? c.issues.map(renderIssueEdit) : null)
242 }
243
244 if(c.type === 'issue') {
245 return h('div',
246 h('p', 'opened issue on ', repoLink(c.project)),
247 c.title ? h('h4', c.title) : '',
248 markdown(c)
249 )
250 }
251
252 if(c.type === 'pull-request') {
253 return h('div',
254 h('p', 'opened pull-request ',
255 'to ', repoLink(c.repo), ':', c.branch, ' ',
256 'from ', repoLink(c.head_repo), ':', c.head_branch),
257 c.title ? h('h4', c.title) : '',
258 markdown(c)
259 )
260 }
261}
262
263exports.message_meta = function (msg, sbot) {
264 var type = msg.value.content.type
265 if (type === 'issue' || type === 'pull-request') {
266 var el = h('em', '...')
267 // TODO: update if issue is changed
268 getIssueState(msg.key, function (err, state) {
269 if (err) return console.error(err)
270 el.textContent = state
271 })
272 return el
273 }
274}
275
276exports.message_action = function (msg, sbot) {
277 var c = msg.value.content
278 if(c.type === 'issue' || c.type === 'pull-request') {
279 var isOpen
280 var a = h('a', {href: '#', onclick: function () {
281 message_confirm({
282 type: 'issue-edit',
283 root: msg.key,
284 issues: [{
285 link: msg.key,
286 open: !isOpen
287 }]
288 }, function (err, msg) {
289 if(err) return alert(err)
290 if(!msg) return
291 isOpen = msg.value.content.open
292 update()
293 })
294 }})
295 getIssueState(msg.key, function (err, state) {
296 if (err) return console.error(err)
297 isOpen = state === 'open'
298 update()
299 })
300 function update() {
301 a.textContent = c.type === 'pull-request'
302 ? isOpen ? 'Close Pull Request' : 'Reopen Pull Request'
303 : isOpen ? 'Close Issue' : 'Reopen Issue'
304 }
305 return a
306 }
307}
308
309

Built with git-ssb-web