git ssb

0+

Kira / %V53yIAO6ZNGv1Lx9tCP…



forked from cel / ssb-issues

Tree: ea7483671e4c557ff8a2d71717148f1ea356c1f4

Files: ea7483671e4c557ff8a2d71717148f1ea356c1f4 / index.js

6563 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.filter(function (msg) {
214 return (!opts.project || opts.project == msg.value.content.project)
215 && (!opts.author || opts.author == msg.value.author)
216 }),
217 paramap(function (msg, cb) {
218 getIssue(msg.key, cb)
219 }, 8),
220 pull.filter(opts.open != null && function (pr) {
221 return pr.open == opts.open
222 })
223 )
224 }
225
226 function editIssue(id, opts, cb) {
227 var msg
228 try { ssb.publish(issueSchemas.edit(id, opts), cb) }
229 catch(e) { return cb(e) }
230 }
231
232 function closeIssue(id, cb) {
233 var msg
234 try { ssb.publish(issueSchemas.close(id), cb) }
235 catch(e) { return cb(e) }
236 }
237
238 function reopenIssue(id, cb) {
239 var msg
240 try { msg = issueSchemas.reopen(id) }
241 catch(e) { return cb(e) }
242 ssb.publish(msg, cb)
243 }
244
245 function newIssue(opts, cb) {
246 var msg
247 try { msg = issueSchemas.new(opts.project, opts.title, opts.text) }
248 catch(e) { return cb(e) }
249 ssb.publish(msg, function (err, msg) {
250 if (err) return cb(err)
251 getIssue(msg.key, cb)
252 })
253 }
254
255 return {
256 deinit: deinit,
257 get: getIssue,
258 list: listIssues,
259 new: newIssue,
260 edit: editIssue,
261 close: closeIssue,
262 reopen: reopenIssue,
263 getMention: getMention,
264 isStatusChanged: isStatusChanged
265 }
266}
267

Built with git-ssb-web