git ssb

0+

cel / ssb-issues



Tree: 5ba5ed8f4b7cd0ca004b81ac3c4594eb91488aa1

Files: 5ba5ed8f4b7cd0ca004b81ac3c4594eb91488aa1 / index.js

5980 bytesRaw
1var pull = require('pull-stream')
2var paramap = require('pull-paramap')
3var asyncMemo = require('asyncmemo')
4var issueSchemas = require('./lib/schemas')
5
6function Cache(fn, ssb) {
7 return asyncMemo(fn)
8 return function (key, cb) { ac.get(key, cb) }
9}
10
11function isUpdateValid(issue, msg) {
12 return msg.value.author == issue.author
13 || msg.value.author == issue.projectAuthor
14}
15
16exports.name = 'issues'
17
18exports.manifest = {
19 get: 'async',
20 createFeedStream: '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
59 var getIssue = asyncMemo(function (id, cb) {
60 var issue = {}
61 var issueMsg
62
63 ssbGet(id, function (err, msg) {
64 msg = {key: id, value: msg}
65 if (err) return cb(err)
66 issueMsg = msg
67 issue.id = msg.key
68 issue.msg = msg
69 issue.author = msg.value.author
70 var c = msg.value.content
71 issue.project = c.project
72 issue.text = c.text
73 issue.created_at = issue.updated_at = msg.value.timestamp
74 if (c.project)
75 ssbGet(c.project, gotProjectMsg)
76 else
77 getLinks()
78 })
79
80 function gotProjectMsg(err, msg) {
81 if (err) return cb(err)
82 issue.projectAuthor = msg.author
83 getLinks()
84 }
85
86 function getLinks() {
87 var now = Date.now()
88 // compute the result from the past data
89 pull(
90 ssb.links({dest: id, lt: now, reverse: true, values: true}),
91 pull.drain(onOldMsg, onOldEnd)
92 )
93 // keep the results up-to-date in the future
94 pull(
95 ssb.links({dest: id, gte: now, values: true, live: true}),
96 pull.drain(onNewMsg, onNewEnd)
97 )
98 }
99
100 function onOldMsg(msg) {
101 if (!isUpdateValid(issue, msg))
102 return
103 var c = msg.value.content
104
105 // handle updates to issue
106 if (msg.key == id || c.issue == id || c.link == id) {
107 if (c.open != null && issue.open == null)
108 issue.open = c.open
109 if (c.title != null && issue.title == null)
110 issue.title = c.title
111 if (msg.value.timestamp > issue.updated_at)
112 issue.updated_at = msg.value.timestamp
113 }
114
115 // handle updates via mention
116 if (c.issues) {
117 for (var i = 0; i < c.issues.length; i++)
118 onOldMsg({value: {
119 timestamp: msg.value.timestamp,
120 author: msg.value.author,
121 content: c.issues[i]
122 }})
123 }
124
125 checkReady()
126 }
127
128 function onNewMsg(msg) {
129 if (!isUpdateValid(issue, msg))
130 return
131 var c = msg.value.content
132
133 // handle updates to issue
134 if (msg.key == id || c.issue == id || c.link == id) {
135 if (c.open != null)
136 issue.open = c.open
137 if (c.title != null)
138 issue.title = c.title
139 if (msg.value.timestamp > issue.updated_at)
140 issue.updated_at = msg.value.timestamp
141 }
142
143 // handle updates via mention
144 if (c.issues) {
145 for (var i = 0; i < c.issues.length; i++)
146 onNewMsg({value: {
147 timestamp: msg.value.timestamp,
148 author: msg.value.author,
149 content: c.issues[i]
150 }})
151 }
152 }
153
154 function checkReady() {
155 // call back once all the issue properties are set
156 if (issue.open != null && issue.title != null) {
157 var _cb = cb
158 delete cb
159 _cb(null, issue)
160 }
161 }
162
163 function onOldEnd(err) {
164 if (err) {
165 if (cb) cb(err)
166 else console.error(err)
167 return
168 }
169 // process the root message last
170 onOldMsg(issueMsg)
171 // if callback hasn't been called yet, the issue is missing a field
172 if (cb) {
173 if (issue.open == null)
174 issue.open = true
175 if (issue.title == null)
176 issue.title = issue.id
177 checkReady()
178 }
179 }
180
181 function onNewEnd(err) {
182 if (err) {
183 if (cb) cb(err)
184 else console.error(err)
185 }
186 }
187 })
188
189 function createFeedStream(opts) {
190 opts.type = 'issue'
191 return pull(
192 // TODO: use links2 for this
193 ssb.messagesByType(opts),
194 pull.filter(function (msg) {
195 return (!opts.project || opts.project == msg.value.content.project)
196 && (!opts.author || opts.author == msg.value.author)
197 }),
198 paramap(function (msg, cb) {
199 getIssue(msg.key, cb)
200 }, 8)
201 )
202 }
203
204 function editIssue(id, opts, cb) {
205 var msg
206 try { ssb.publish(issueSchemas.edit(id, opts), cb) }
207 catch(e) { return cb(e) }
208 }
209
210 function closeIssue(id, cb) {
211 var msg
212 try { ssb.publish(issueSchemas.close(id), cb) }
213 catch(e) { return cb(e) }
214 }
215
216 function reopenIssue(id, cb) {
217 var msg
218 try { msg = issueSchemas.reopen(id) }
219 catch(e) { return cb(e) }
220 ssb.publish(msg, cb)
221 }
222
223 function newIssue(opts, cb) {
224 var msg
225 try { msg = issueSchemas.new(opts.project, opts.title, opts.text) }
226 catch(e) { return cb(e) }
227 ssb.publish(msg, function (err, msg) {
228 if (err) return cb(err)
229 getIssue(msg.key, cb)
230 })
231 }
232
233 return {
234 get: getIssue,
235 createFeedStream: createFeedStream,
236 new: newIssue,
237 edit: editIssue,
238 close: closeIssue,
239 reopen: reopenIssue,
240 getMention: getMention,
241 isStatusChanged: isStatusChanged
242 }
243}
244

Built with git-ssb-web