git ssb

0+

cel / ssb-issues



Tree: 0e95f2aac8609383058d181c4903b7b858eaa2b8

Files: 0e95f2aac8609383058d181c4903b7b858eaa2b8 / index.js

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

Built with git-ssb-web