issue-pane 2.4.10 → 2.4.11-4fce62b6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
+ "root": true,
2
3
  "env": {
3
4
  "browser": true,
4
5
  "es6": true,
5
6
  "node": true
6
7
  },
7
- "extends": "standard",
8
8
  "globals": {
9
9
  "Atomics": "readonly",
10
10
  "SharedArrayBuffer": "readonly"
@@ -14,5 +14,6 @@
14
14
  "argsIgnorePattern": "^_",
15
15
  "varsIgnorePattern": "^_"
16
16
  }]
17
- }
17
+ },
18
+ "extends": ["eslint:recommended", "plugin:import/recommended"]
18
19
  }
@@ -15,7 +15,6 @@ jobs:
15
15
  strategy:
16
16
  matrix:
17
17
  node-version:
18
- - 12.x
19
18
  - 14.x
20
19
  - 16.x
21
20
  steps:
package/.nvmrc CHANGED
@@ -1 +1 @@
1
- v12.7.0
1
+ v16.14.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/LICENSE.md CHANGED
File without changes
package/Makefile CHANGED
File without changes
package/README.md CHANGED
File without changes
package/board.js CHANGED
@@ -10,10 +10,9 @@
10
10
  * @returns dom:Element
11
11
  */
12
12
 
13
- import * as UI from 'solid-ui'
13
+ import { ns, rdf, utils, widgets } from 'solid-ui'
14
14
  import { store } from 'solid-logic'
15
- const ns = UI.ns
16
- const $rdf = UI.rdf
15
+ const $rdf = rdf
17
16
 
18
17
  export function board (dom, columnValues, renderItem, options) {
19
18
  const board = dom.createElement('div')
@@ -27,7 +26,7 @@ export function board (dom, columnValues, renderItem, options) {
27
26
  const mainRow = table.appendChild(dom.createElement('tr'))
28
27
  columnValues.forEach(x => {
29
28
  const cell = headerRow.appendChild(dom.createElement('th'))
30
- cell.textContent = UI.utils.label(x, true) // Initial capital
29
+ cell.textContent = utils.label(x, true) // Initial capital
31
30
  cell.subject = x
32
31
  cell.style = 'margin: 0.3em; padding: 0.5em 1em; font-treatment: bold; font-size: 120%;'
33
32
 
@@ -44,7 +43,7 @@ export function board (dom, columnValues, renderItem, options) {
44
43
  }
45
44
 
46
45
  if (options.columnDropHandler) {
47
- UI.widgets.makeDropTarget(column, droppedURIHandler)
46
+ widgets.makeDropTarget(column, droppedURIHandler)
48
47
  }
49
48
  })
50
49
 
@@ -57,7 +56,7 @@ export function board (dom, columnValues, renderItem, options) {
57
56
  const classes = store.each(item, ns.rdf('type'))
58
57
  const catColors = classes.map(cat => store.any(cat, ns.ui('backgroundColor'))).filter(c => c)
59
58
 
60
- table.appendChild(UI.widgets.personTR(dom, null, item))
59
+ table.appendChild(widgets.personTR(dom, null, item))
61
60
  table.subject = item
62
61
  table.style = 'margin: 1em;' // @@ use style.js
63
62
  const backgroundColor = catColors[0] || store.any(category, ns.ui('backgroundColor'))
@@ -76,7 +75,7 @@ export function board (dom, columnValues, renderItem, options) {
76
75
  const actualRenderItem = renderItem || options.renderItem || defaultRenderItem
77
76
  function localRenderItem (subject) {
78
77
  const ele = actualRenderItem(subject)
79
- UI.widgets.makeDraggable(ele, subject)
78
+ widgets.makeDraggable(ele, subject)
80
79
  ele.subject = subject
81
80
  return ele
82
81
  }
@@ -88,7 +87,7 @@ export function board (dom, columnValues, renderItem, options) {
88
87
  items = items.filter(options.filter)
89
88
  }
90
89
  const sortedItems = sortedBy(items, sortBy, now, true)
91
- UI.utils.syncTableToArrayReOrdered(col, sortedItems, localRenderItem)
90
+ utils.syncTableToArrayReOrdered(col, sortedItems, localRenderItem)
92
91
  }
93
92
  }
94
93
 
package/issue.js CHANGED
@@ -5,20 +5,33 @@ import { authn, store } from 'solid-logic'
5
5
  import { newIssueForm } from './newIssue'
6
6
 
7
7
  const $rdf = rdf
8
+ const kb = store
8
9
 
9
10
  const SET_MODIFIED_DATES = false
10
11
 
12
+ export const TASK_ICON = icons.iconBase + 'noun_17020_gray-tick.svg'
13
+ export const OPEN_TASK_ICON = icons.iconBase + 'noun_17020_sans-tick.svg'
14
+ export const CLOSED_TASK_ICON = icons.iconBase + 'noun_17020.svg'
15
+
11
16
  function complain (message, context) {
12
17
  console.warn(message)
13
18
  context.paneDiv.appendChild(widgets.errorMessageBlock(context.dom, message))
14
19
  }
15
20
 
21
+ export function isOpen (issue) {
22
+ const types = kb.findTypeURIs(issue)
23
+ return !!types[ns.wf('Open').uri]
24
+ }
25
+
26
+ export function iconForIssue (issue) {
27
+ return isOpen(issue) ? TASK_ICON : CLOSED_TASK_ICON
28
+ }
16
29
  export function getState (issue, classification) {
17
- const tracker = store.the(issue, ns.wf('tracker'), null, issue.doc())
18
- const states = store.any(tracker, ns.wf('issueClass'))
30
+ const tracker = kb.the(issue, ns.wf('tracker'), null, issue.doc())
31
+ const states = kb.any(tracker, ns.wf('issueClass'))
19
32
  classification = classification || states
20
- const types = store.each(issue, ns.rdf('type'))
21
- .filter(ty => store.holds(ty, ns.rdfs('subClassOf'), classification))
33
+ const types = kb.each(issue, ns.rdf('type'))
34
+ .filter(ty => kb.holds(ty, ns.rdfs('subClassOf'), classification))
22
35
  if (types.length !== 1) {
23
36
  // const initialState = kb.any(tracker, ns.wf('initialState')) No do NOT default
24
37
  // if (initialState) return initialState
@@ -27,26 +40,27 @@ export function getState (issue, classification) {
27
40
  return types[0]
28
41
  }
29
42
 
30
- export function renderIssueCard (issue, context) {
31
- function getBackgroundColor () {
32
- const classes = store.each(issue, ns.rdf('type')) // @@ pick cats in order then state
33
- const catColors = classes.map(cat => store.any(cat, ns.ui('backgroundColor'))).filter(c => !!c)
43
+ export function getBackgroundColorFromTypes (issue) {
44
+ const classes = kb.each(issue, ns.rdf('type')) // @@ pick cats in order then state
45
+ const catColors = classes.map(cat => kb.any(cat, ns.ui('backgroundColor'))).filter(c => !!c)
34
46
 
35
- if (catColors.length) return catColors[0].value // pick first one
36
- return null
37
- }
47
+ if (catColors.length) return catColors[0].value // pick first one
48
+ return null
49
+ }
50
+
51
+ export function renderIssueCard (issue, context) {
38
52
  function refresh () {
39
- const backgroundColor = getBackgroundColor() || 'white'
53
+ const backgroundColor = getBackgroundColorFromTypes(issue) || 'white'
40
54
  card.style.backgroundColor = backgroundColor
41
55
  editButton.style.backgroundColor = backgroundColor // Override white from style sheet
42
56
  }
43
57
  const dom = context.dom
44
- const uncategorized = !getBackgroundColor() // This is a suspect issue. Prompt to delete it
58
+ const uncategorized = !getBackgroundColorFromTypes(issue) // This is a suspect issue. Prompt to delete it
45
59
 
46
60
  const card = dom.createElement('div')
47
61
  const table = card.appendChild(dom.createElement('table'))
48
62
  table.style.width = '100%'
49
- const options = { draggable: false } // Let the baord make th ewhole card draggable
63
+ const options = { draggable: false } // Let the board make the whole card draggable
50
64
  table.appendChild(widgets.personTR(dom, null, issue, options))
51
65
  table.subject = issue
52
66
  card.style = 'border-radius: 0.4em; border: 0.05em solid grey; margin: 0.3em;'
@@ -66,7 +80,7 @@ export function renderIssueCard (issue, context) {
66
80
  if (uncategorized) {
67
81
  const deleteButton = widgets.deleteButtonWithCheck(dom, buttonsCell, 'issue', async function () { // noun?
68
82
  try {
69
- await store.updater.update(store.connectedStatements(issue))
83
+ await kb.updater.update(kb.connectedStatements(issue))
70
84
  } catch (err) {
71
85
  complain(`Unable to delete issue: ${err}`, context)
72
86
  }
@@ -100,18 +114,24 @@ export function exposeOverlay (subject, context) {
100
114
  overlay.firstChild.style.overflow = 'auto' // was scroll
101
115
  }
102
116
 
117
+ function renderSpacer (dom, backgroundColor) {
118
+ const spacer = dom.createElement('div')
119
+ spacer.setAttribute('style', 'height: 1em; margin: 0.5em;') // spacer and placeHolder
120
+ spacer.style.backgroundColor = backgroundColor // try that
121
+ return spacer
122
+ }
123
+
103
124
  export function renderIssue (issue, context) {
104
125
  // Don't bother changing the last modified dates of things: save time
105
- function setModifiedDate (subj, store, doc) {
126
+ function setModifiedDate (subj, kb, doc) {
106
127
  if (SET_MODIFIED_DATES) {
107
128
  if (!getOption(tracker, 'trackLastModified')) return
108
- let deletions = store.statementsMatching(issue, ns.dct('modified'))
109
- deletions = deletions.concat(
110
- store.statementsMatching(issue, ns.wf('modifiedBy'))
111
- )
129
+ const deletions = kb.statementsMatching(issue, ns.dct('modified'))
130
+ .concat(kb.statementsMatching(issue, ns.wf('modifiedBy'))
131
+ )
112
132
  const insertions = [$rdf.st(issue, ns.dct('modified'), new Date(), doc)]
113
133
  if (me) insertions.push($rdf.st(issue, ns.wf('modifiedBy'), me, doc))
114
- store.updater.update(deletions, insertions, function (_uri, _ok, _body) {})
134
+ kb.updater.update(deletions, insertions, function (_uri, _ok, _body) {})
115
135
  }
116
136
  }
117
137
 
@@ -123,7 +143,7 @@ export function renderIssue (issue, context) {
123
143
  return pre
124
144
  }
125
145
 
126
- const timestring = function () {
146
+ function timestring () {
127
147
  const now = new Date()
128
148
  return '' + now.getTime()
129
149
  // http://www.w3schools.com/jsref/jsref_obj_date.asp
@@ -144,52 +164,50 @@ export function renderIssue (issue, context) {
144
164
  }
145
165
  function getOption (tracker, option) {
146
166
  // eg 'allowSubIssues'
147
- const opt = store.any(tracker, ns.ui(option))
167
+ const opt = kb.any(tracker, ns.ui(option))
148
168
  return !!(opt && opt.value)
149
169
  }
150
170
 
151
171
  function setPaneStyle () {
152
- const types = store.findTypeURIs(issue)
153
- let mystyle = 'padding: 0.5em 1.5em 1em 1.5em; '
154
- let backgroundColor = null
155
- for (const uri in types) {
156
- backgroundColor = store.any(
157
- store.sym(uri),
158
- store.sym('http://www.w3.org/ns/ui#backgroundColor')
159
- )
160
- if (backgroundColor) break
161
- }
162
- backgroundColor = backgroundColor ? backgroundColor.value : '#eee' // default grey
163
- mystyle += 'background-color: ' + backgroundColor + '; '
172
+ const backgroundColor = getBackgroundColorFromTypes(issue) || '#eee' // default grey
173
+ const mystyle0 = 'padding: 0.5em 1.5em 1em 1.5em; border: 0.7em;'
174
+ const mystyle = mystyle0 + 'border-color: ' + backgroundColor + '; '
164
175
  issueDiv.setAttribute('style', mystyle)
176
+ issueDiv.style.backgroundColor = 'white'
165
177
  }
166
178
 
179
+ /// ////////////// Body of renderIssue
180
+
167
181
  const dom = context.dom
168
182
  // eslint-disable-next-line no-use-before-define
169
- const tracker = store.the(issue, ns.wf('tracker'), null, issue.doc())
183
+ const tracker = kb.the(issue, ns.wf('tracker'), null, issue.doc())
170
184
  if (!tracker) throw new Error('No tracker')
171
185
  // eslint-disable-next-line no-use-before-define
172
- const stateStore = store.any(tracker, ns.wf('stateStore'))
186
+ const stateStore = kb.any(tracker, ns.wf('stateStore'))
173
187
  const store = issue.doc()
174
188
 
175
189
  const issueDiv = dom.createElement('div')
176
190
  const me = authn.currentUser()
191
+ const backgroundColor = getBackgroundColorFromTypes(issue) || 'white'
177
192
 
178
193
  setPaneStyle()
179
194
 
180
195
  authn.checkUser() // kick off async operation
181
196
 
182
- const states = store.any(tracker, ns.wf('issueClass'))
197
+ const iconButton = issueDiv.appendChild(widgets.button(dom, iconForIssue(issue)))
198
+ widgets.makeDraggable(iconButton, issue) // Drag me wherever you need to do stuff with this issue
199
+
200
+ const states = kb.any(tracker, ns.wf('issueClass'))
183
201
  if (!states) { throw new Error('This tracker ' + tracker + ' has no issueClass') }
184
202
  const select = widgets.makeSelectForCategory(
185
203
  dom,
186
- store,
204
+ kb,
187
205
  issue,
188
206
  states,
189
207
  stateStore,
190
208
  function (ok, body) {
191
209
  if (ok) {
192
- setModifiedDate(store, store, store)
210
+ setModifiedDate(store, kb, store)
193
211
  widgets.refreshTree(issueDiv)
194
212
  } else {
195
213
  console.log('Failed to change state:\n' + body)
@@ -198,18 +216,18 @@ export function renderIssue (issue, context) {
198
216
  )
199
217
  issueDiv.appendChild(select)
200
218
 
201
- const cats = store.each(tracker, ns.wf('issueCategory')) // zero or more
202
- for (let i = 0; i < cats.length; i++) {
219
+ const cats = kb.each(tracker, ns.wf('issueCategory')) // zero or more
220
+ for (const cat of cats) {
203
221
  issueDiv.appendChild(
204
222
  widgets.makeSelectForCategory(
205
223
  dom,
206
- store,
224
+ kb,
207
225
  issue,
208
- cats[i],
226
+ cat,
209
227
  stateStore,
210
228
  function (ok, body) {
211
229
  if (ok) {
212
- setModifiedDate(store, store, store)
230
+ setModifiedDate(store, kb, store)
213
231
  widgets.refreshTree(issueDiv)
214
232
  } else {
215
233
  console.log('Failed to change category:\n' + body)
@@ -253,40 +271,26 @@ export function renderIssue (issue, context) {
253
271
  wf:Task :creationForm core:coreIsueForm .
254
272
  `
255
273
  const CORE_ISSUE_FORM = ns.wf('coreIsueForm')
256
- $rdf.parse(coreIssueFormText, store, CORE_ISSUE_FORM.doc().uri, 'text/turtle')
257
- widgets.appendForm(
274
+ $rdf.parse(coreIssueFormText, kb, CORE_ISSUE_FORM.doc().uri, 'text/turtle')
275
+ const form = widgets.appendForm(
258
276
  dom,
259
- issueDiv,
277
+ null, // was: container
260
278
  {},
261
279
  issue,
262
280
  CORE_ISSUE_FORM,
263
281
  stateStore,
264
282
  complainIfBad
265
283
  )
266
-
267
- // Descriptions can be long and are stored local to the issue
268
- /*
269
- issueDiv.appendChild(
270
- widgets.makeDescription(
271
- dom,
272
- kb,
273
- issue,
274
- ns.wf('description'),
275
- store,
276
- function (ok, body) {
277
- if (ok) setModifiedDate(store, kb, store)
278
- else console.log('Failed to change description:\n' + body)
279
- }
280
- )
281
- ) */
284
+ issueDiv.appendChild(form)
285
+ form.style.backgroundColor = backgroundColor
282
286
 
283
287
  // Assigned to whom?
284
288
 
285
- const assignments = store.statementsMatching(issue, ns.wf('assignee'))
289
+ const assignments = kb.statementsMatching(issue, ns.wf('assignee'))
286
290
  if (assignments.length > 1) {
287
291
  say('Weird, was assigned to more than one person. Fixing ..')
288
292
  const deletions = assignments.slice(1)
289
- store.updater.update(deletions, [], function (uri, ok, body) {
293
+ kb.updater.update(deletions, [], function (uri, ok, body) {
290
294
  if (ok) {
291
295
  say('Now fixed.')
292
296
  } else {
@@ -299,20 +303,16 @@ export function renderIssue (issue, context) {
299
303
  // Anyone assigned to any issue we know about
300
304
 
301
305
  async function getPossibleAssignees () {
302
- let devs = []
303
- const devGroups = store.each(issue, ns.wf('assigneeGroup'))
304
- for (let i = 0; i < devGroups.length; i++) {
305
- const group = devGroups[i]
306
- await store.fetcher.load()
307
- devs = devs.concat(store.each(group, ns.vcard('member')))
308
- }
306
+ const devGroups = kb.each(issue, ns.wf('assigneeGroup'))
307
+ await kb.fetcher.load(devGroups) // Load them all
308
+ const groupDevs = devGroups.map(group => kb.each(group, ns.vcard('member'), null, group.doc())).flat()
309
309
  // Anyone who is a developer of any project which uses this tracker
310
- const proj = store.any(null, ns.doap('bug-database'), tracker) // What project?
310
+ const proj = kb.any(null, ns.doap('bug-database'), tracker) // What project?
311
311
  if (proj) {
312
- await store.fetcher.load(proj)
313
- devs = devs.concat(store.each(proj, ns.doap('developer')))
312
+ await kb.fetcher.load(proj)
314
313
  }
315
- return devs
314
+ const projectDevs = proj ? kb.each(proj, ns.doap('developer')) : []
315
+ return groupDevs.concat(projectDevs)
316
316
  }
317
317
 
318
318
  // Super issues first - like parent directories .. maybe use breadcrums from?? @@
@@ -324,7 +324,7 @@ export function renderIssue (issue, context) {
324
324
  getPossibleAssignees().then(devs => {
325
325
  if (devs.length) {
326
326
  devs.forEach(function (person) {
327
- store.fetcher.lookUpThing(person)
327
+ kb.fetcher.lookUpThing(person)
328
328
  }) // best effort async for names etc
329
329
  const opts = {
330
330
  // 'mint': '** Add new person **',
@@ -339,14 +339,14 @@ export function renderIssue (issue, context) {
339
339
  issueDiv.appendChild(
340
340
  widgets.makeSelectForOptions(
341
341
  dom,
342
- store,
342
+ kb,
343
343
  issue,
344
344
  ns.wf('assignee'),
345
345
  devs,
346
346
  opts,
347
347
  store,
348
348
  function (ok, body) {
349
- if (ok) setModifiedDate(store, store, store)
349
+ if (ok) setModifiedDate(store, kb, store)
350
350
  else console.log('Failed to change assignee:\n' + body)
351
351
  }
352
352
  )
@@ -354,27 +354,36 @@ export function renderIssue (issue, context) {
354
354
  }
355
355
  })
356
356
 
357
- /* The trees of super issues and subissues
357
+ /* The trees of super-issues and sub-issues
358
358
  */
359
- let subIssuePanel
359
+ function supersOver (issue, stack) {
360
+ stack = stack || []
361
+ const sup = kb.any(null, ns.wf('dependent'), issue, issue.doc())
362
+ if (sup) return supersOver(sup, [sup].concat(stack))
363
+ return stack
364
+ }
360
365
  if (getOption(tracker, 'allowSubIssues')) {
361
- if (!subIssuePanel) {
362
- subIssuePanel = issueDiv.appendChild(dom.createElement('div'))
363
- subIssuePanel.style = 'margin: 1em; padding: 1em;'
364
- }
366
+ const subIssuePanel = issueDiv.appendChild(dom.createElement('div'))
367
+ subIssuePanel.style = 'margin: 1em; padding: 1em;'
365
368
 
366
369
  subIssuePanel.appendChild(dom.createElement('h4')).textContent = 'Super Issues'
367
370
  const listOfSupers = subIssuePanel.appendChild(dom.createElement('div'))
371
+ listOfSupers.style.display = 'flex'
368
372
  listOfSupers.refresh = function () {
369
- utils.syncTableToArrayReOrdered(listOfSupers, store.each(null, ns.wf('dependent'), issue), renderSubIssue)
373
+ // const supers = kb.each(null, ns.wf('dependent'), issue, issue.doc())
374
+ const supers = supersOver(issue)
375
+ utils.syncTableToArrayReOrdered(listOfSupers, supers, renderSubIssue)
370
376
  }
371
377
  listOfSupers.refresh()
372
378
 
373
379
  // Sub issues
374
380
  subIssuePanel.appendChild(dom.createElement('h4')).textContent = 'Sub Issues'
375
381
  const listOfSubs = subIssuePanel.appendChild(dom.createElement('div'))
382
+ listOfSubs.style.display = 'flex'
383
+ listOfSubs.style.flexDirection = 'reverse' // Or center
376
384
  listOfSubs.refresh = function () {
377
- utils.syncTableToArrayReOrdered(listOfSubs, store.each(issue, ns.wf('dependent')), renderSubIssue)
385
+ const subs = kb.each(issue, ns.wf('dependent'), null, issue.doc())
386
+ utils.syncTableToArrayReOrdered(listOfSubs, subs, renderSubIssue)
378
387
  }
379
388
  listOfSubs.refresh()
380
389
 
@@ -387,7 +396,7 @@ export function renderIssue (issue, context) {
387
396
  b.addEventListener(
388
397
  'click',
389
398
  function (_event) {
390
- subIssuePanel.insertBefore(newIssueForm(dom, store, tracker, issue, listOfSubs.refresh), b.nextSibling) // Pop form just after button
399
+ subIssuePanel.insertBefore(newIssueForm(dom, kb, tracker, issue, listOfSubs.refresh), b.nextSibling) // Pop form just after button
391
400
  },
392
401
  false
393
402
  )
@@ -396,7 +405,7 @@ export function renderIssue (issue, context) {
396
405
  issueDiv.appendChild(dom.createElement('br'))
397
406
 
398
407
  // Extras are stored centrally to the tracker
399
- const extrasForm = store.any(tracker, ns.wf('extrasEntryForm'))
408
+ const extrasForm = kb.any(tracker, ns.wf('extrasEntryForm'))
400
409
  if (extrasForm) {
401
410
  widgets.appendForm(
402
411
  dom,
@@ -407,14 +416,14 @@ export function renderIssue (issue, context) {
407
416
  stateStore,
408
417
  complainIfBad
409
418
  )
419
+ // issueDiv.appendChild(renderSpacer(backgroundColor))
410
420
  }
411
421
 
412
422
  // Comment/discussion area
413
423
 
414
- const spacer = issueDiv.appendChild(dom.createElement('tr'))
415
- spacer.setAttribute('style', 'height: 1em') // spacer and placeHolder
424
+ const spacer = issueDiv.appendChild(renderSpacer(dom, backgroundColor))
416
425
 
417
- const template = store.anyValue(tracker, ns.wf('issueURITemplate'))
426
+ const template = kb.anyValue(tracker, ns.wf('issueURITemplate'))
418
427
  /*
419
428
  var chatDocURITemplate = kb.anyValue(tracker, ns.wf('chatDocURITemplate')) // relaive to issue
420
429
  var chat
@@ -427,20 +436,21 @@ export function renderIssue (issue, context) {
427
436
  if (template) {
428
437
  messageStore = issue.doc() // for now. Could go deeper
429
438
  } else {
430
- messageStore = store.any(tracker, ns.wf('messageStore'))
431
- if (!messageStore) messageStore = store.any(tracker, ns.wf('stateStore'))
432
- store.sym(messageStore.uri + '#' + 'Chat' + timestring()) // var chat =
439
+ messageStore = kb.any(tracker, ns.wf('messageStore'))
440
+ if (!messageStore) messageStore = kb.any(tracker, ns.wf('stateStore'))
441
+ kb.sym(messageStore.uri + '#' + 'Chat' + timestring()) // var chat =
433
442
  }
434
443
 
435
- store.fetcher.nowOrWhenFetched(messageStore, function (ok, body, _xhr) {
444
+ kb.fetcher.nowOrWhenFetched(messageStore, function (ok, body, _xhr) {
436
445
  if (!ok) {
437
446
  const er = dom.createElement('p')
438
447
  er.textContent = body // @@ use nice error message
439
448
  issueDiv.insertBefore(er, spacer)
440
449
  } else {
441
- const discussion = messageArea(dom, store, issue, messageStore)
450
+ const discussion = messageArea(dom, kb, issue, messageStore)
442
451
  issueDiv.insertBefore(discussion, spacer)
443
- }
452
+ issueDiv.insertBefore(renderSpacer(dom, backgroundColor), discussion)
453
+ } // Not sure why e stuck this in upwards rather than downwards
444
454
  })
445
455
 
446
456
  // Draggable attachment list
@@ -448,23 +458,22 @@ export function renderIssue (issue, context) {
448
458
  attachmentHint.innerHTML = `<h4>Attachments</h4>
449
459
  <p>Drag files, emails,
450
460
  web pages onto the paper clip, or click the file upload button.</p>`
451
- let uploadFolderURI
452
- if (issue.uri.endsWith('/index.ttl#this')) { // This has a whole folder to itself
453
- uploadFolderURI = issue.uri.slice(0, 14) + 'Files/' // back to slash
454
- } else { // like state.ttl#Iss1587852322438
455
- uploadFolderURI = issue.dir().uri + 'Files/' + issue.uri.split('#')[1] + '/' // New folder for issue in file with others
456
- }
461
+ const uploadFolderURI =
462
+ issue.uri.endsWith('/index.ttl#this') // This has a whole folder to itself
463
+ ? issue.uri.slice(0, 14) + 'Files/' // back to slash
464
+ : issue.dir().uri + 'Files/' + issue.uri.split('#')[1] + '/' // New folder for issue in file with others
465
+
457
466
  widgets.attachmentList(dom, issue, issueDiv, {
458
467
  doc: stateStore,
459
468
  promptIcon: icons.iconBase + 'noun_25830.svg',
460
- uploadFolder: store.sym(uploadFolderURI), // Allow local files to be uploaded
469
+ uploadFolder: kb.sym(uploadFolderURI), // Allow local files to be uploaded
461
470
  predicate: ns.wf('attachment')
462
471
  })
463
472
 
464
473
  // Delete button to delete the issue
465
474
  const deleteButton = widgets.deleteButtonWithCheck(dom, issueDiv, 'issue', async function () {
466
475
  try {
467
- await store.updater.update(store.connectedStatements(issue))
476
+ await kb.updater.update(kb.connectedStatements(issue))
468
477
  } catch (err) {
469
478
  complain(`Unable to delete issue: ${err}`, context)
470
479
  }
@@ -482,7 +491,7 @@ export function renderIssue (issue, context) {
482
491
  'click',
483
492
  async function (_event) {
484
493
  try {
485
- await store.fetcher.load(messageStore, { force: true, clearPreviousData: true })
494
+ await kb.fetcher.load(messageStore, { force: true, clearPreviousData: true })
486
495
  } catch (err) {
487
496
  alert(err)
488
497
  return