git ssb

0+

cel / ssb-issues



Tree: 5ea6e56dfd2a564632fb28da8cb907fff8464ec4

Files: 5ea6e56dfd2a564632fb28da8cb907fff8464ec4 / index.js

6449 bytesRaw
1var pull = require('pull-stream')
2var paramap = require('pull-paramap')
3var asyncMemo = require('asyncmemo')
4var issueSchemas = require('./lib/schemas')
5var multicb = require('multicb')
6
7function Cache(fn, ssb) {
8 return asyncMemo(fn)
9 return function (key, cb) { ac.get(key, cb) }
10}
11
12function truncate(str, len) {
13 return str.length > len ? str.substr(0, len) + '...' : str
14}
15
16exports.name = 'issues'
17
18exports.manifest = {
19 get: 'async',
20 list: 'source',
21 new: 'async',
22 edit: 'async',
23 close: 'async',
24 reopen: 'async',
25 getMention: 'sync',
26 isStatusChanged: 'sync'
27}
28
29exports.schemas = issueSchemas
30
31function isStatusChanged(msg, issue) {
32 var mention = getMention(msg, issue)
33 return mention ? mention.open : null
34}
35
36function getMention(msg, issue) {
37 var c = msg.value.content
38 if (msg.key == issue.id || c.issue == issue.id || c.link == issue.id)
39 if (c.open != null)
40 return c
41 if (c.issues) {
42 var mention
43 for (var i = 0; i < c.issues.length; i++) {
44 mention = getMention({value: {
45 timestamp: msg.value.timestamp,
46 author: msg.value.author,
47 content: c.issues[i]
48 }}, issue)
49 if (mention)
50 return mention
51 }
52 }
53}
54
55exports.init = function (ssb) {
56
57 var ssbGet = asyncMemo(ssb.get)
58 var liveStreams = []
59
60 var getIssue = asyncMemo(function (id, cb) {
61 var issue = {}
62 var issueMsg
63
64 ssbGet(id, function (err, msg) {
65 msg = {key: id, value: msg}
66 if (err) return cb(err)
67 issueMsg = msg
68 issue.id = msg.key
69 issue.msg = msg
70 issue.author = msg.value.author
71 var c = msg.value.content
72 issue.project = c.project
73 issue.text = c.text || c.title || JSON.stringify(msg, null, 2)
74 issue.created_at = issue.updated_at = msg.value.timestamp
75 if (c.project)
76 ssbGet(c.project, gotProjectMsg)
77 else
78 getLinks()
79 })
80
81 function gotProjectMsg(err, msg) {
82 if (err) return cb(err)
83 issue.projectAuthor = msg.author
84 getLinks()
85 }
86
87 function getLinks() {
88 var now = Date.now()
89 // compute the result from the past data
90 pull(
91 ssb.links({dest: id, reverse: true, values: true,
92 old: true, live: false, sync: false}),
93 pull.drain(onOldMsg, onOldEnd)
94 )
95 // keep the results up-to-date in the future
96 var read = ssb.links({dest: id, values: true,
97 old: false, live: true, sync: false})
98 liveStreams.push(read)
99 pull(
100 read,
101 pull.drain(onNewMsg, onNewEnd)
102 )
103 }
104
105 function onOldMsg(msg) {
106 if (!msg.value)
107 return
108 var c = msg.value.content
109
110 // handle updates to issue
111 if (msg.key == id || c.issue == id || c.link == id) {
112 if (c.open != null && issue.open == null)
113 issue.open = c.open
114 if (c.title != null && issue.title == null)
115 issue.title = c.title
116 if (msg.value.timestamp > issue.updated_at)
117 issue.updated_at = msg.value.timestamp
118 }
119
120 // handle updates via mention
121 if (c.issues) {
122 for (var i = 0; i < c.issues.length; i++)
123 onOldMsg({value: {
124 timestamp: msg.value.timestamp,
125 author: msg.value.author,
126 content: c.issues[i]
127 }})
128 }
129
130 checkReady()
131 }
132
133 function onNewMsg(msg) {
134 if (!msg.value)
135 return
136 var c = msg.value.content
137
138 // handle updates to issue
139 if (msg.key == id || c.issue == id || c.link == id) {
140 if (c.open != null)
141 issue.open = c.open
142 if (c.title != null)
143 issue.title = c.title
144 if (msg.value.timestamp > issue.updated_at)
145 issue.updated_at = msg.value.timestamp
146 }
147
148 // handle updates via mention
149 if (c.issues) {
150 for (var i = 0; i < c.issues.length; i++)
151 onNewMsg({value: {
152 timestamp: msg.value.timestamp,
153 author: msg.value.author,
154 content: c.issues[i]
155 }})
156 }
157 }
158
159 function checkReady() {
160 // call back once all the issue properties are set
161 if (issue.open != null && issue.title != null) {
162 var _cb = cb
163 delete cb
164 _cb(null, issue)
165 }
166 }
167
168 function onOldEnd(err) {
169 if (err) {
170 if (cb) cb(err)
171 else console.error(err)
172 return
173 }
174 // process the root message last
175 onOldMsg(issueMsg)
176 // if callback hasn't been called yet, the issue is missing a field
177 if (cb) {
178 if (issue.open == null)
179 issue.open = true
180 if (issue.title == null)
181 issue.title = truncate(issue.text.split('\n')[0], 250) || issue.id
182 checkReady()
183 }
184 }
185
186 function onNewEnd(err) {
187 if (err) {
188 if (cb) cb(err)
189 else console.error(err)
190 }
191 }
192 })
193
194 function deinit(cb) {
195 var done = multicb()
196 // cancel all live streams
197 liveStreams.forEach(function (read) {
198 read(true, done())
199 })
200 done(cb)
201 }
202
203 function listIssues(opts) {
204 opts.type = 'issue'
205 return pull(
206 // TODO: use links2 for this
207 ssb.messagesByType(opts),
208 pull.unique('key'),
209 pull.filter(function (msg) {
210 return (!opts.project || opts.project == msg.value.content.project)
211 && (!opts.author || opts.author == msg.value.author)
212 }),
213 paramap(function (msg, cb) {
214 getIssue(msg.key, cb)
215 }, 8),
216 pull.filter(opts.open != null && function (pr) {
217 return pr.open == opts.open
218 })
219 )
220 }
221
222 function editIssue(id, opts, cb) {
223 var msg
224 try { ssb.publish(issueSchemas.edit(id, opts), cb) }
225 catch(e) { return cb(e) }
226 }
227
228 function closeIssue(id, cb) {
229 var msg
230 try { ssb.publish(issueSchemas.close(id), cb) }
231 catch(e) { return cb(e) }
232 }
233
234 function reopenIssue(id, cb) {
235 var msg
236 try { msg = issueSchemas.reopen(id) }
237 catch(e) { return cb(e) }
238 ssb.publish(msg, cb)
239 }
240
241 function newIssue(opts, cb) {
242 var msg
243 try { msg = issueSchemas.new(opts.project, opts.title, opts.text) }
244 catch(e) { return cb(e) }
245 ssb.publish(msg, function (err, msg) {
246 if (err) return cb(err)
247 getIssue(msg.key, cb)
248 })
249 }
250
251 return {
252 deinit: deinit,
253 get: getIssue,
254 list: listIssues,
255 new: newIssue,
256 edit: editIssue,
257 close: closeIssue,
258 reopen: reopenIssue,
259 getMention: getMention,
260 isStatusChanged: isStatusChanged
261 }
262}
263

Built with git-ssb-web