Commit 1e2d415297dc67684478280b7b309f1e3432d2e4
Add issue labels
cel committed on 5/23/2020, 9:23:28 PMParent: eb15fe70a933fb5906d227f9b4d88d570c179867
Files changed
README.md | changed |
index.js | changed |
lib/schemas.js | changed |
README.md | ||
---|---|---|
@@ -12,29 +12,50 @@ | ||
12 | 12 … | { |
13 | 13 … | type: 'issue', |
14 | 14 … | project: Link?, |
15 | 15 … | title: string?, |
16 | - text: string? | |
16 … | + text: string?, | |
17 … | + labels: [LabelRef]? | |
17 | 18 … | } |
18 | 19 … | ``` |
19 | 20 … | |
20 | 21 … | #### issue edits |
21 | 22 … | |
22 | -Edits to an issue may be done by any ssb message type. For example, you can | |
23 | -make a post that updates an issue, or push a git commit that updates an issue. | |
24 | -If the message does not have any content other than to update the issue, then | |
25 | -the type may be `issue-edit`. | |
23 … | +Edits to an issue open/closed state may be done by ssb message type | |
24 … | +`issue-edit`, `post`, or `git-update`. For example, you can make a post that | |
25 … | +updates an issue, or push a git commit that updates an issue. Other issue | |
26 … | +edits should be done with message type `issue-edit`. | |
26 | 27 … | |
27 | 28 … | ```js |
28 | 29 … | { |
29 | 30 … | type: 'issue-edit'|'post'|'git-update', |
30 | 31 … | issues: [{ |
31 | - link: IssueRef, | |
32 | - open: boolean, | |
32 … | + link: IssueRef, | |
33 … | + open: boolean?, | |
34 … | + labels: { | |
35 … | + add: [IssueRef]?, | |
36 … | + remove: [IssueRef]?, | |
37 … | + }? | |
33 | 38 … | }] |
34 | 39 … | } |
35 | 40 … | ``` |
36 | 41 … | |
42 … | +#### issue labels | |
43 … | + | |
44 … | +An issue label represents a tag that can be applied to issues in a repo/project. | |
45 … | +It can be renamed using `about` messages. Issue labels can be added to or removed from issues using `issue-edit` messages. | |
46 … | + | |
47 … | +```js | |
48 … | +{ | |
49 … | + type: 'issue-label', | |
50 … | + project: Link?, | |
51 … | + name: string?, | |
52 … | + issues: [issueRef]? | |
53 … | +} | |
54 … | +``` | |
55 … | + | |
56 … | +- `issues`: initial set of issues to add the label to | |
57 … | + | |
37 | 58 … | ## API |
38 | 59 … | |
39 | 60 … | ```js |
40 | 61 … | var Issues = require('ssb-issues') |
@@ -60,8 +81,9 @@ | ||
60 | 81 … | created_at: number, |
61 | 82 … | updated_at: number, |
62 | 83 … | open: boolean, |
63 | 84 … | msg: Msg, |
85 … | + labels: MsgRefs | |
64 | 86 … | } |
65 | 87 … | ``` |
66 | 88 … | |
67 | 89 … | - `id`: id of the issue |
@@ -73,8 +95,9 @@ | ||
73 | 95 … | - `project`: the project that the issue is for |
74 | 96 … | - `projectAuthor`: the author of the project |
75 | 97 … | - `msg`: ssb message object that created the issue (with |
76 | 98 … | `.key` and `.value`). |
99 … | +- `labels`: ids of issue-labels associated with the issue | |
77 | 100 … | |
78 | 101 … | #### list: source |
79 | 102 … | |
80 | 103 … | Get a stream of issues |
@@ -186,14 +209,24 @@ | ||
186 | 209 … | - `project` (Ref): id of project to associate the issue with |
187 | 210 … | - `title` (string): title to give the issue (deprecated) |
188 | 211 … | - `text` (string): text body for the issue |
189 | 212 … | |
213 … | +#### `issueSchemas.newLabel(project, name, issues)` | |
214 … | + | |
215 … | +Create a new issue label. | |
216 … | + | |
217 … | +- `project` (Ref): id of project to associate the issue/label with | |
218 … | +- `name` (string): name to give the issue (optional) | |
219 … | +- `issues` (IssueRefs): ids of issues to add the label to | |
220 … | + | |
190 | 221 … | #### `issueSchemas.edit(id, opts)` |
191 | 222 … | |
192 | 223 … | Edit an issue. |
193 | 224 … | |
194 | 225 … | - `opts.open` (boolean): open or close the issue |
195 | 226 … | - `opts.title` (string): set the title of the issue (deprecated) |
227 … | +- `opts.addLabels` (LabelRefs): ids of labels to add to issue | |
228 … | +- `opts.removeLabels` (LabelRefs): ids of labels to remove from issue | |
196 | 229 … | |
197 | 230 … | #### `issueSchemas.close(id)` |
198 | 231 … | |
199 | 232 … | Close an issue. |
index.js | ||
---|---|---|
@@ -95,11 +95,32 @@ | ||
95 | 95 … | var ssbGet = asyncMemo(ssb.get) |
96 | 96 … | var liveStreams = [] |
97 | 97 … | |
98 | 98 … | var getIssue = asyncMemo(function (id, cb) { |
99 | - var issue = {} | |
99 … | + var issue = { | |
100 … | + labels: [] | |
101 … | + } | |
102 … | + var labelCounts = {} | |
100 | 103 … | var issueMsg |
101 | 104 … | |
105 … | + function addLabel(id) { | |
106 … | + var count = labelCounts[id] || 0 | |
107 … | + labelCounts[id] = ++count | |
108 … | + if (count === 1) { | |
109 … | + var i = issue.labels.indexOf(id) | |
110 … | + if (i === -1) issue.labels.push(id) | |
111 … | + } | |
112 … | + } | |
113 … | + | |
114 … | + function removeLabel(id) { | |
115 … | + var count = labelCounts[id] || 0 | |
116 … | + labelCounts[id] = --count | |
117 … | + if (count === 0) { | |
118 … | + var i = issue.labels.indexOf(id) | |
119 … | + if (i > -1) issue.labels.splice(i, 1) | |
120 … | + } | |
121 … | + } | |
122 … | + | |
102 | 123 … | ssbGet(id, function (err, msg) { |
103 | 124 … | msg = {key: id, value: msg} |
104 | 125 … | if (err) return cb(err) |
105 | 126 … | issueMsg = msg |
@@ -109,8 +130,9 @@ | ||
109 | 130 … | var c = msg.value.content |
110 | 131 … | issue.project = c.project |
111 | 132 … | issue.text = c.text || c.title || JSON.stringify(msg, null, 2) |
112 | 133 … | issue.created_at = issue.updated_at = msg.value.timestamp |
134 … | + if (Array.isArray(c.labels)) c.labels.forEach(addLabel) | |
113 | 135 … | if (c.project) |
114 | 136 … | ssbGet(c.project, gotProjectMsg) |
115 | 137 … | else |
116 | 138 … | getLinks() |
@@ -150,20 +172,31 @@ | ||
150 | 172 … | if (c.open != null && issue.open == null) |
151 | 173 … | issue.open = c.open |
152 | 174 … | if (c.title != null && issue.title == null) |
153 | 175 … | issue.title = c.title |
176 … | + if (c.labels) { | |
177 … | + if (Array.isArray(c.labels.add)) | |
178 … | + c.labels.add.forEach(addLabel) | |
179 … | + if (Array.isArray(c.labels.remove)) | |
180 … | + c.labels.remove.forEach(removeLabel) | |
181 … | + } | |
154 | 182 … | if (msg.value.timestamp > issue.updated_at) |
155 | 183 … | issue.updated_at = msg.value.timestamp |
156 | 184 … | } |
157 | 185 … | |
158 | 186 … | // handle updates via mention |
159 | 187 … | if (c.issues) { |
160 | - for (var i = 0; i < c.issues.length; i++) | |
161 | - onOldMsg({value: { | |
162 | - timestamp: msg.value.timestamp, | |
163 | - author: msg.value.author, | |
164 | - content: c.issues[i] | |
165 | - }}) | |
188 … | + for (var i = 0; i < c.issues.length; i++) { | |
189 … | + if (c.type === 'issue-label') { | |
190 … | + if (c.issues[i] === id) addLabel(msg.key) | |
191 … | + } else { | |
192 … | + onOldMsg({value: { | |
193 … | + timestamp: msg.value.timestamp, | |
194 … | + author: msg.value.author, | |
195 … | + content: c.issues[i] | |
196 … | + }}) | |
197 … | + } | |
198 … | + } | |
166 | 199 … | } |
167 | 200 … | |
168 | 201 … | checkReady() |
169 | 202 … | } |
@@ -180,8 +213,14 @@ | ||
180 | 213 … | if (c.title != null) |
181 | 214 … | issue.title = c.title |
182 | 215 … | if (msg.value.timestamp > issue.updated_at) |
183 | 216 … | issue.updated_at = msg.value.timestamp |
217 … | + if (c.labels) { | |
218 … | + if (Array.isArray(c.labels.add)) | |
219 … | + c.labels.add.forEach(addLabel) | |
220 … | + if (Array.isArray(c.labels.remove)) | |
221 … | + c.labels.remove.forEach(removeLabel) | |
222 … | + } | |
184 | 223 … | } |
185 | 224 … | |
186 | 225 … | // handle updates via mention |
187 | 226 … | if (c.issues) { |
lib/schemas.js | ||
---|---|---|
@@ -23,8 +23,28 @@ | ||
23 | 23 … | } |
24 | 24 … | return msg |
25 | 25 … | } |
26 | 26 … | |
27 … | +exports.newLabel = function (project, name, issues) { | |
28 … | + if (!ssbRef.isLink(project)) | |
29 … | + throw new Error('invalid project id') | |
30 … | + | |
31 … | + var msg = { type: 'issue-label', project: project } | |
32 … | + if (name) { | |
33 … | + if (typeof name === 'string') | |
34 … | + msg.name = name | |
35 … | + else | |
36 … | + throw new Error('invalid issue label name') | |
37 … | + } | |
38 … | + if (issues) { | |
39 … | + if (Array.isArray(issues) && issues.every(ssbRef.isMsg)) | |
40 … | + msg.issues = issues | |
41 … | + else | |
42 … | + throw new Error('invalid issues list') | |
43 … | + } | |
44 … | + return msg | |
45 … | +} | |
46 … | + | |
27 | 47 … | exports.edit = function (id, opts) { |
28 | 48 … | if (!ssbRef.isMsg(id)) |
29 | 49 … | throw new Error('invalid issue id') |
30 | 50 … | var msg = { |
@@ -32,12 +52,30 @@ | ||
32 | 52 … | issues: [{ |
33 | 53 … | link: id |
34 | 54 … | }] |
35 | 55 … | } |
56 … | + var labels = {} | |
36 | 57 … | if (opts.open != null) |
37 | 58 … | msg.issues[0].open = opts.open |
38 | 59 … | if (opts.title != null) |
39 | 60 … | msg.issues[0].title = opts.title |
61 … | + if (opts.addLabels != null) { | |
62 … | + if (Array.isArray(opts.addLabels) && opts.addLabels.every(ssbRef.isMsg)) { | |
63 … | + msg.issues[0].labels = labels | |
64 … | + labels.add = opts.addLabels | |
65 … | + } else { | |
66 … | + throw new Error('invalid addLabels') | |
67 … | + } | |
68 … | + } | |
69 … | + if (opts.removeLabels != null) { | |
70 … | + if (Array.isArray(opts.removeLabels) | |
71 … | + && opts.removeLabels.every(ssbRef.isMsg)) { | |
72 … | + msg.issues[0].labels = labels | |
73 … | + labels.remove = opts.removeLabels | |
74 … | + } else { | |
75 … | + throw new Error('invalid removeLabels') | |
76 … | + } | |
77 … | + } | |
40 | 78 … | return msg |
41 | 79 … | } |
42 | 80 … | |
43 | 81 … | exports.close = function (id) { |
Built with git-ssb-web