git ssb

0+

cel / ssb-issues



Tree: d289682bb0cccd80e293d737e325636a678dc70e

Files: d289682bb0cccd80e293d737e325636a678dc70e / index.js

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

Built with git-ssb-web