git ssb

0+

ev / microbay



forked from Dominic / patchbay

Tree: 4de55198f28a26699cb48dbe58ca2d058e0ebe88

Files: 4de55198f28a26699cb48dbe58ca2d058e0ebe88 / modules / git.js

8559 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 var issues = msg.value.content.issues
36 if (!Array.isArray(issues)) return
37 return issues.filter(function (issue) {
38 return issue.link === id
39 })
40 }),
41 pull.flatten(),
42 pull.map(function (issue) {
43 return issue.merged ? 'merged' : issue.open ? 'open' : 'closed'
44 }),
45 pull.take(1),
46 pull.collect(function (err, updates) {
47 cb(err, updates && updates[0] || 'open')
48 })
49 )
50}
51
52//todo:
53function messageTimestampLink(msg) {
54 return h('a.timestamp', {
55 timestamp: msg.value.timestamp,
56 title: new Date(msg.value.timestamp),
57 href: '#'+msg.key
58 }, human(msg.value.timestamp))
59}
60
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
86exports.message_content = function (msg, sbot) {
87 var c = msg.value.content
88
89 if(c.type === 'git-repo') {
90 var branchesT, tagsT, openIssuesT, closedIssuesT, openPRsT, closedPRsT
91 var forksT
92 var div = h('div',
93 h('p', 'git repo ', repoName(msg.key)),
94 c.upstream ? h('p', 'fork of ', repoName(c.upstream, true)) : '',
95 h('p', h('code', 'ssb://' + msg.key)),
96 h('div.git-table-wrapper', {style: {'max-height': '12em'}},
97 h('table',
98 branchesT = tableRows(h('tr',
99 h('th', 'branch'),
100 h('th', 'commit'),
101 h('th', 'last update'))),
102 tagsT = tableRows(h('tr',
103 h('th', 'tag'),
104 h('th', 'commit'),
105 h('th', 'last update'))))),
106 h('div.git-table-wrapper', {style: {'max-height': '16em'}},
107 h('table',
108 openIssuesT = tableRows(h('tr',
109 h('th', 'open issues'))),
110 closedIssuesT = tableRows(h('tr',
111 h('th', 'closed issues'))))),
112 h('div.git-table-wrapper', {style: {'max-height': '16em'}},
113 h('table',
114 openPRsT = tableRows(h('tr',
115 h('th', 'open pull requests'))),
116 closedPRsT = tableRows(h('tr',
117 h('th', 'closed pull requests'))))),
118 h('div.git-table-wrapper',
119 h('table',
120 forksT = tableRows(h('tr',
121 h('th', 'forks'))))))
122
123 // compute refs
124 var refs = {}
125 pull(
126 sbot_links({
127 reverse: true,
128 source: msg.value.author,
129 dest: msg.key,
130 rel: 'repo',
131 values: true
132 }),
133 pull.drain(function (link) {
134 var refUpdates = link.value.content.refs || {}
135 Object.keys(refUpdates).reverse().filter(function (ref) {
136 if (refs[ref]) return
137 refs[ref] = true
138 var rev = refUpdates[ref]
139 if (!rev) return
140 var parts = /^refs\/(heads|tags)\/(.*)$/.exec(ref) || []
141 var t
142 if (parts[1] === 'heads') t = branchesT
143 else if (parts[1] === 'tags') t = tagsT
144 if (t) t.append(h('tr',
145 h('td', parts[2]),
146 h('td', h('code', rev)),
147 h('td', messageTimestampLink(link))))
148 })
149 }, function (err) {
150 if (err) console.error(err)
151 })
152 )
153
154 // list issues and pull requests
155 pull(
156 sbot_links({
157 reverse: true,
158 dest: msg.key,
159 rel: 'project',
160 values: true
161 }),
162 paramap(function (link, cb) {
163 getIssueState(link.key, function (err, state) {
164 if(err) return cb(err)
165 link.state = state
166 cb(null, link)
167 })
168 }),
169 pull.drain(function (link) {
170 var c = link.value.content
171 var title = c.title || (c.text ? c.text.length > 70
172 ? c.text.substr(0, 70) + '…'
173 : c.text : link.key)
174 var author = link.value.author
175 var t = c.type === 'pull-request'
176 ? link.state === 'open' ? openPRsT : closedPRsT
177 : link.state === 'open' ? openIssuesT : closedIssuesT
178 t.append(h('tr',
179 h('td',
180 h('a', {href: '#'+link.key}, title), h('br'),
181 h('small',
182 'opened ', messageTimestampLink(link),
183 ' by ', h('a', {href: '#'+author}, avatar_name(author))))))
184 }, function (err) {
185 if (err) console.error(err)
186 })
187 )
188
189 // list forks
190 pull(
191 sbot_links({
192 reverse: true,
193 dest: msg.key,
194 rel: 'upstream'
195 }),
196 pull.drain(function (link) {
197 forksT.append(h('tr', h('td',
198 repoName(link.key, true),
199 ' by ', h('a', {href: '#'+link.source}, avatar_name(link.source)))))
200 }, function (err) {
201 if (err) console.error(err)
202 })
203 )
204
205 return div
206 }
207
208 if(c.type === 'git-update') {
209 return h('p',
210 'pushed to ',
211 repoLink(c.repo),
212 c.refs ? h('ul', Object.keys(c.refs).map(function (ref) {
213 var rev = c.refs[ref]
214 return h('li',
215 shortRefName(ref) + ': ',
216 rev ? h('code', rev) : h('em', 'deleted'))
217 })) : null,
218 Array.isArray(c.issues) ? c.issues.map(function (issue) {
219 if (issue.merged === true)
220 return ['Merged ', message_link(issue.link), ' in ',
221 h('code', issue.object), ' ', h('q', issue.label)]
222 if (issue.open === false)
223 return ['Closed ', message_link(issue.link), ' in ',
224 h('code', issue.object), ' ', h('q', issue.label)]
225 }) : null
226 )
227 }
228
229 if(c.type === 'issue-edit') {
230 return h('div',
231 0, false, null, undefined, '', 'ok',
232 c.title ? h('p', 'renamed issue ', message_link(c.issue),
233 ' to ', h('ins', c.title)) : null,
234 c.open === false ? h('p', 'closed issue ', message_link(c.issue)) : null,
235 c.open === true ? h('p', 'reopened issue ', message_link(c.issue)) : '',
236 c.issues ? c.issues : null
237 )
238 }
239
240 if (c.type === 'issue') {
241 return h('div',
242 h('p', 'opened issue on ', repoLink(c.project)),
243 c.title ? h('h4', c.title) : '',
244 markdown(c)
245 )
246 }
247
248 if (c.type === 'pull-request') {
249 return h('div',
250 h('p', 'opened pull-request ',
251 'to ', repoLink(c.repo), ':', c.branch, ' ',
252 'from ', repoLink(c.head_repo), ':', c.head_branch),
253 c.title ? h('h4', c.title) : '',
254 markdown(c)
255 )
256 }
257}
258
259exports.message_meta = function (msg, sbot) {
260 var type = msg.value.content.type
261 if (type == 'issue' || type == 'pull-request') {
262 var el = h('em', '...')
263 getIssueState(msg.key, function (err, state) {
264 if (err) return console.error(err)
265 el.textContent = state
266 })
267 return el
268 }
269}
270
271exports.message_action = function (msg, sbot) {
272 var c = msg.value.content
273 if(c.type === 'issue' || c.type === 'pull-request') {
274 var isOpen
275 var a = h('a', {href: '#', onclick: function () {
276 message_confirm({
277 type: 'issue-edit',
278 issues: [{
279 link: msg.key,
280 open: !isOpen
281 }]
282 })
283 }})
284 getIssueState(msg.key, function (err, state) {
285 if (err) return console.error(err)
286 isOpen = state === 'open'
287 a.textContent = isOpen ? 'Close' : 'Reopen'
288 })
289 return a
290 }
291}
292
293

Built with git-ssb-web