git ssb

0+

cel / ssb-issues



Tree: 22e129844192c0ac7cf466780ed41402f70a273c

Files: 22e129844192c0ac7cf466780ed41402f70a273c / index.js

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

Built with git-ssb-web