issue-pane 2.6.1 → 3.0.0-a58a4367

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.
@@ -1,78 +0,0 @@
1
- @prefix : <#>.
2
- @prefix dc: <http://purl.org/dc/elements/1.1/>.
3
- @prefix dct: <http://purl.org/dc/terms/>.
4
- @prefix foaf: <http://xmlns.com/foaf/0.1/>.
5
- @prefix owl: <http://www.w3.org/2002/07/owl#>.
6
- @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
7
- @prefix ui: <http://www.w3.org/ns/ui#>.
8
- @prefix wf: <http://www.w3.org/2005/01/wf/flow#>.
9
- @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
10
- @prefix c: </profile/card#>.
11
-
12
- :Classification
13
- rdfs:label "Category";
14
- owl:disjointUnionOf
15
- ( :Specification :Tests :Servers :Clients :DeveloperTools :Vertical ).
16
- :Clients
17
- rdfs:comment "Client side frameworks, SDKs and operating systems";
18
- rdfs:label "Clients";
19
- rdfs:subClassOf :Classification.
20
- :DeveloperTools rdfs:label "Developer tools"; rdfs:subClassOf :Classification.
21
-
22
- :InProgress
23
- rdfs:label "In progress";
24
- rdfs:subClassOf wf:Open, :States;
25
- ui:backgroundColor "#f9ee71"^^xsd:color.
26
- :NextSession
27
- rdfs:label "To be done this month";
28
- rdfs:subClassOf wf:Open, :States;
29
- ui:backgroundColor "#91fdb9"^^xsd:color.
30
- :Released
31
- rdfs:label "Released";
32
- rdfs:subClassOf wf:Open, :States;
33
- ui:backgroundColor "#e2a7fb"^^xsd:color.
34
- :Research
35
- rdfs:label "Research";
36
- rdfs:subClassOf wf:Open, :States;
37
- ui:backgroundColor "#ffffff"^^xsd:color.
38
- :Servers
39
- rdfs:label "Solid Server implementations"; rdfs:subClassOf :Classification.
40
- :Someday
41
- rdfs:label "Someday pile";
42
- rdfs:subClassOf wf:Open, :States;
43
- ui:backgroundColor "#e3e3e3"^^xsd:color.
44
- :Specification
45
- rdfs:label "Parts of the Solid specification"; rdfs:subClassOf :Classification.
46
- :States
47
- rdfs:label "Task";
48
- owl:disjointUnionOf
49
- ( :Research :Someday :ToBeDone :NextSession :InProgress :Works :Released ).
50
- :Tests rdfs:label "Test suites"; rdfs:subClassOf :Classification.
51
-
52
- :this
53
- a wf:Tracker;
54
- dc:author c:me;
55
- dc:created "2020-07-21T14:00:20Z"^^xsd:dateTime;
56
- dct:title "Solid Ecosystem: Roadmap";
57
- wf:assigneeClass foaf:Person;
58
- wf:defaultView :Classification;
59
- wf:description
60
- """A roadmap for the whole Solid ecosystem.
61
- See also other linked roadmaps for parts of the ecosystem like the SolidOS, Spec, open source projects, etc.
62
-
63
-
64
- """;
65
- wf:initialState :Someday;
66
- wf:issueCategory :Classification;
67
- wf:issueClass :States;
68
- wf:stateStore <state.ttl>.
69
- :ToBeDone
70
- rdfs:label "To Be Done";
71
- rdfs:subClassOf wf:Open, :States;
72
- ui:backgroundColor "#e0ffeb"^^xsd:color.
73
- :Vertical rdfs:label "Apps and Verticals"; rdfs:subClassOf :Classification.
74
-
75
- :Works
76
- rdfs:label "Code runs";
77
- rdfs:subClassOf wf:Open, :States;
78
- ui:backgroundColor "#dcdbff"^^xsd:color.
@@ -1,64 +0,0 @@
1
- @prefix dc: <http://purl.org/dc/terms/> .
2
- @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
3
- @prefix con: <http://www.w3.org/2000/10/swap/pim/contact#> .
4
- @prefix dc: <http://purl.org/dc/elements/1.1/>.
5
- @prefix doap: <http://usefulinc.com/ns/doap#> .
6
- @prefix wf: <http://www.w3.org/2005/01/wf/flow#> .
7
- @prefix ical: <http://www.w3.org/2002/12/cal/ical#> .
8
- @prefix owl: <http://www.w3.org/2002/07/owl#> .
9
- @prefix ui: <http://www.w3.org/ns/ui#> .
10
- @prefix dct: <http://purl.org/dc/terms/> .
11
- @prefix : <big-tracker.ttl#> . # the config file
12
-
13
- <#Task1> wf:tracker :this ;
14
- dc:title "Test task 1";
15
- a :New , :Action;
16
- dct:created 2004-08-21;
17
- ical:location "London";
18
- wf:dateReceived 2000-01-12;
19
- wf:earliestDate 1999-12-12;
20
- wf:latestDate 2001-12-12;
21
-
22
- wf:description "Yadda Tadda yadda ";
23
- rdfs:comment "This is a comment".
24
-
25
- <#Task2> wf:tracker :this ;
26
- dc:title "Test task 2";
27
- a :Accepted , :Action;
28
- dct:created 2004-06-21;
29
- ical:location "London";
30
- wf:dateReceived 2000-01-12;
31
- wf:earliestDate 1999-12-12;
32
- wf:latestDate 2001-12-12;
33
-
34
- wf:description "Yadda Tadda yadda ";
35
- rdfs:comment "This is a comment".
36
-
37
- <#Task3> wf:tracker :this ;
38
- dc:title "Test task 3";
39
- a :New , :Book;
40
- dct:created 2004-04-21;
41
- ical:location "London";
42
- wf:dateReceived 2000-01-12;
43
- wf:earliestDate 1999-12-12;
44
- wf:latestDate 2001-12-12;
45
-
46
- wf:description "Yadda Tadda yadda ";
47
- rdfs:comment "This is a comment".
48
-
49
- <#Task4> wf:tracker :this ;
50
- dc:title "Test task 4";
51
- a :Accepted , :Book;
52
- dct:created 2004-09-21;
53
- ical:location "London";
54
- wf:dateReceived 2000-01-12;
55
- wf:earliestDate 1999-12-12;
56
- wf:latestDate 2001-12-12;
57
-
58
- wf:description "Yadda Tadda yadda ";
59
- rdfs:comment "This is a comment".
60
-
61
-
62
-
63
- # ENDS
64
-
package/eslint.config.mjs DELETED
@@ -1,32 +0,0 @@
1
- import globals from "globals";
2
- import js from "@eslint/js";
3
- import importPlugin from "eslint-plugin-import";
4
-
5
- export default [
6
- {
7
- languageOptions: {
8
- globals: {
9
- ...globals.browser,
10
- ...globals.node,
11
- Atomics: "readonly",
12
- SharedArrayBuffer: "readonly",
13
- },
14
- ecmaVersion: 2022,
15
- sourceType: "module",
16
- },
17
- plugins: {
18
- import: importPlugin,
19
- },
20
- rules: {
21
- ...js.configs.recommended.rules,
22
- ...importPlugin.configs.recommended.rules,
23
- "no-unused-vars": [
24
- "warn",
25
- {
26
- argsIgnorePattern: "^_",
27
- varsIgnorePattern: "^_",
28
- },
29
- ],
30
- },
31
- },
32
- ];
package/issue.js DELETED
@@ -1,506 +0,0 @@
1
- // All the UI for a single issue, without store load or listening for changes
2
- //
3
- import { icons, messageArea, ns, rdf, style, utils, widgets } from 'solid-ui'
4
- import { authn, store } from 'solid-logic'
5
- import { newIssueForm } from './newIssue'
6
-
7
- const $rdf = rdf
8
- const kb = store
9
-
10
- const SET_MODIFIED_DATES = false
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
-
16
- function complain (message, context) {
17
- console.warn(message)
18
- context.paneDiv.appendChild(widgets.errorMessageBlock(context.dom, message))
19
- }
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
- }
29
- export function getState (issue, classification) {
30
- const tracker = kb.the(issue, ns.wf('tracker'), null, issue.doc())
31
- const states = kb.any(tracker, ns.wf('issueClass'))
32
- classification = classification || states
33
- const types = kb.each(issue, ns.rdf('type'))
34
- .filter(ty => kb.holds(ty, ns.rdfs('subClassOf'), classification))
35
- if (types.length !== 1) {
36
- // const initialState = kb.any(tracker, ns.wf('initialState')) No do NOT default
37
- // if (initialState) return initialState
38
- throw new Error('Issue must have one type as state: ' + types.length)
39
- }
40
- return types[0]
41
- }
42
-
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)
46
-
47
- if (catColors.length) return catColors[0].value // pick first one
48
- return null
49
- }
50
-
51
- export function renderIssueCard (issue, context) {
52
- function refresh () {
53
- const backgroundColor = getBackgroundColorFromTypes(issue) || 'white'
54
- card.style.backgroundColor = backgroundColor
55
- editButton.style.backgroundColor = backgroundColor // Override white from style sheet
56
- }
57
- const dom = context.dom
58
- const uncategorized = !getBackgroundColorFromTypes(issue) // This is a suspect issue. Prompt to delete it
59
-
60
- const card = dom.createElement('div')
61
- const table = card.appendChild(dom.createElement('table'))
62
- table.style.width = '100%'
63
- const options = { draggable: false } // Let the board make the whole card draggable
64
- table.appendChild(widgets.personTR(dom, null, issue, options))
65
- table.subject = issue
66
- card.style = 'border-radius: 0.4em; border: 0.05em solid grey; margin: 0.3em;'
67
-
68
- const img = card.firstChild.firstChild.firstChild.firstChild // div/table/tr/td/img
69
- img.setAttribute('src', icons.iconBase + 'noun_Danger_1259514.svg') // override
70
- // Add a button for viewing the whole issue in overlay
71
- const buttonsCell = card.firstChild.firstChild.children[2] // right hand part of card
72
- const editButton = widgets.button(dom, icons.iconBase + 'noun_253504.svg', 'edit', async _event => {
73
- exposeOverlay(issue, context)
74
- })
75
- const editButtonImage = editButton.firstChild
76
- editButtonImage.style.width = editButtonImage.style.height = '1.5em'
77
- buttonsCell.appendChild(editButton)
78
-
79
- // If uncategorized, shortcut to delete issue
80
- if (uncategorized) {
81
- const deleteButton = widgets.deleteButtonWithCheck(dom, buttonsCell, 'issue', async function () { // noun?
82
- try {
83
- await kb.updater.update(kb.connectedStatements(issue))
84
- } catch (err) {
85
- complain(`Unable to delete issue: ${err}`, context)
86
- }
87
- console.log('User deleted issue ' + issue)
88
- card.parentNode.removeChild(card) // refresh doesn't work yet because it is not passed though tabs so short cut
89
- widgets.refreshTree(context.paneDiv) // Should delete the card if nec when tabs pass it though
90
- // complain('DELETED OK', context)
91
- })
92
- buttonsCell.appendChild(deleteButton)
93
- }
94
- card.style.maxWidth = '24em' // @@ User adjustable??
95
- card.refresh = refresh
96
- refresh()
97
- return card
98
- }
99
-
100
- export function exposeOverlay (subject, context) {
101
- function hideOverlay () {
102
- overlay.innerHTML = '' // clear overlay
103
- overlay.style.visibility = 'hidden'
104
- }
105
- const overlay = context.overlay
106
- overlay.innerHTML = '' // clear existing
107
- const button = overlay.appendChild(
108
- widgets.button(context.dom, icons.iconBase + 'noun_1180156.svg', 'close', hideOverlay))
109
- button.style.float = 'right'
110
- button.style.margin = '0.7em'
111
- delete button.style.backgroundColor // do not want white
112
- overlay.style.visibility = 'visible'
113
- overlay.appendChild(renderIssue(subject, context))
114
- overlay.firstChild.style.overflow = 'auto' // was scroll
115
- }
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
-
124
- export function renderIssue (issue, context) {
125
- // Don't bother changing the last modified dates of things: save time
126
- function setModifiedDate (subj, kb, doc) {
127
- if (SET_MODIFIED_DATES) {
128
- if (!getOption(tracker, 'trackLastModified')) return
129
- const deletions = kb.statementsMatching(issue, ns.dct('modified'))
130
- .concat(kb.statementsMatching(issue, ns.wf('modifiedBy'))
131
- )
132
- const insertions = [$rdf.st(issue, ns.dct('modified'), new Date(), doc)]
133
- if (me) insertions.push($rdf.st(issue, ns.wf('modifiedBy'), me, doc))
134
- kb.updater.update(deletions, insertions, function (_uri, _ok, _body) {})
135
- }
136
- }
137
-
138
- function say (message, style) {
139
- const pre = dom.createElement('pre')
140
- pre.setAttribute('style', style || 'color: grey')
141
- issueDiv.appendChild(pre)
142
- pre.appendChild(dom.createTextNode(message))
143
- return pre
144
- }
145
-
146
- function timestring () {
147
- const now = new Date()
148
- return '' + now.getTime()
149
- // http://www.w3schools.com/jsref/jsref_obj_date.asp
150
- }
151
-
152
- function complain (message) {
153
- console.warn(message)
154
- issueDiv.appendChild(widgets.errorMessageBlock(dom, message))
155
- }
156
-
157
- function complainIfBad (ok, body) {
158
- if (!ok) {
159
- complain(
160
- 'Sorry, failed to save your change:\n' + body,
161
- 'background-color: pink;', context
162
- )
163
- }
164
- }
165
- function getOption (tracker, option) {
166
- // eg 'allowSubIssues'
167
- const opt = kb.any(tracker, ns.ui(option))
168
- return !!(opt && opt.value)
169
- }
170
-
171
- function setPaneStyle () {
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 + '; '
175
- issueDiv.setAttribute('style', mystyle)
176
- issueDiv.style.backgroundColor = 'white'
177
- }
178
-
179
- /// ////////////// Body of renderIssue
180
-
181
- const dom = context.dom
182
- // eslint-disable-next-line no-use-before-define
183
- const tracker = kb.the(issue, ns.wf('tracker'), null, issue.doc())
184
- if (!tracker) throw new Error('No tracker')
185
- // eslint-disable-next-line no-use-before-define
186
- const stateStore = kb.any(tracker, ns.wf('stateStore'))
187
- const store = issue.doc()
188
-
189
- const issueDiv = dom.createElement('div')
190
- const me = authn.currentUser()
191
- const backgroundColor = getBackgroundColorFromTypes(issue) || 'white'
192
-
193
- setPaneStyle()
194
-
195
- authn.checkUser() // kick off async operation
196
-
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'))
201
- if (!states) { throw new Error('This tracker ' + tracker + ' has no issueClass') }
202
- const select = widgets.makeSelectForCategory(
203
- dom,
204
- kb,
205
- issue,
206
- states,
207
- stateStore,
208
- function (ok, body) {
209
- if (ok) {
210
- setModifiedDate(store, kb, store)
211
- widgets.refreshTree(issueDiv)
212
- } else {
213
- console.log('Failed to change state:\n' + body)
214
- }
215
- }
216
- )
217
- issueDiv.appendChild(select)
218
-
219
- const cats = kb.each(tracker, ns.wf('issueCategory')) // zero or more
220
- for (const cat of cats) {
221
- issueDiv.appendChild(
222
- widgets.makeSelectForCategory(
223
- dom,
224
- kb,
225
- issue,
226
- cat,
227
- stateStore,
228
- function (ok, body) {
229
- if (ok) {
230
- setModifiedDate(store, kb, store)
231
- widgets.refreshTree(issueDiv)
232
- } else {
233
- console.log('Failed to change category:\n' + body)
234
- }
235
- }
236
- )
237
- )
238
- }
239
-
240
- // For when issue is the main solo subject, include link to tracker itself.
241
- const a = dom.createElement('a')
242
- a.setAttribute('href', tracker.uri)
243
- a.setAttribute('style', 'float:right')
244
- issueDiv.appendChild(a).textContent = utils.label(tracker)
245
- a.addEventListener('click', widgets.openHrefInOutlineMode, true)
246
-
247
- // Main Form for Title, description only
248
- const coreIssueFormText = `
249
- @prefix : <http://www.w3.org/ns/ui#> .
250
- @prefix core: <http://www.w3.org/2005/01/wf/flow#>.
251
- @prefix dc: <http://purl.org/dc/elements/1.1/>.
252
- @prefix wf: <http://www.w3.org/2005/01/wf/flow#> .
253
-
254
- core:coreIsueForm a :Form;
255
- <http://purl.org/dc/elements/1.1/title> "Core issue data";
256
- :parts (
257
- core:titleField
258
- core:descriptionField ) .
259
-
260
- core:descriptionField a :MultiLineTextField;
261
- :label "Description";
262
- :property wf:description;
263
- :size "40" .
264
-
265
- core:titleField a :SingleLineTextField;
266
- :label "Title";
267
- :maxLength "128";
268
- :property dc:title; # @@ Should move to dct or schema
269
- :size "40" .
270
-
271
- wf:Task :creationForm core:coreIsueForm .
272
- `
273
- const CORE_ISSUE_FORM = ns.wf('coreIsueForm')
274
- $rdf.parse(coreIssueFormText, kb, CORE_ISSUE_FORM.doc().uri, 'text/turtle')
275
- const form = widgets.appendForm(
276
- dom,
277
- null, // was: container
278
- {},
279
- issue,
280
- CORE_ISSUE_FORM,
281
- stateStore,
282
- complainIfBad
283
- )
284
- issueDiv.appendChild(form)
285
- form.style.backgroundColor = backgroundColor
286
-
287
- // Assigned to whom?
288
-
289
- const assignments = kb.statementsMatching(issue, ns.wf('assignee'))
290
- if (assignments.length > 1) {
291
- say('Weird, was assigned to more than one person. Fixing ..')
292
- const deletions = assignments.slice(1)
293
- kb.updater.update(deletions, [], function (uri, ok, body) {
294
- if (ok) {
295
- say('Now fixed.')
296
- } else {
297
- complain('Fixed failed: ' + body, context)
298
- }
299
- })
300
- }
301
-
302
- // Who could be assigned to this?
303
- // Anyone assigned to any issue we know about
304
-
305
- async function getPossibleAssignees () {
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
- // Anyone who is a developer of any project which uses this tracker
310
- const proj = kb.any(null, ns.doap('bug-database'), tracker) // What project?
311
- if (proj) {
312
- await kb.fetcher.load(proj)
313
- }
314
- const projectDevs = proj ? kb.each(proj, ns.doap('developer')) : []
315
- return groupDevs.concat(projectDevs)
316
- }
317
-
318
- // Super issues first - like parent directories .. maybe use breadcrums from?? @@
319
- function renderSubIssue (issue) {
320
- const options = { link: false }
321
- return widgets.personTR(dom, ns.wf('dependent'), issue, options)
322
- }
323
-
324
- getPossibleAssignees().then(devs => {
325
- if (devs.length) {
326
- devs.forEach(function (person) {
327
- kb.fetcher.lookUpThing(person)
328
- }) // best effort async for names etc
329
- const opts = {
330
- // 'mint': '** Add new person **',
331
- nullLabel: '(unassigned)'
332
- /* 'mintStatementsFun': function (newDev) {
333
- var sts = [ $rdf.st(newDev, ns.rdf('type'), ns.foaf('Person')) ]
334
- if (proj) sts.push($rdf.st(proj, ns.doap('developer'), newDev))
335
- return sts
336
- }
337
- */
338
- }
339
- issueDiv.appendChild(
340
- widgets.makeSelectForOptions(
341
- dom,
342
- kb,
343
- issue,
344
- ns.wf('assignee'),
345
- devs,
346
- opts,
347
- store,
348
- function (ok, body) {
349
- if (ok) setModifiedDate(store, kb, store)
350
- else console.log('Failed to change assignee:\n' + body)
351
- }
352
- )
353
- )
354
- }
355
- })
356
-
357
- /* The trees of super-issues and sub-issues
358
- */
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
- }
365
- if (getOption(tracker, 'allowSubIssues')) {
366
- const subIssuePanel = issueDiv.appendChild(dom.createElement('div'))
367
- subIssuePanel.style = 'margin: 1em; padding: 1em;'
368
-
369
- subIssuePanel.appendChild(dom.createElement('h4')).textContent = 'Super Issues'
370
- const listOfSupers = subIssuePanel.appendChild(dom.createElement('div'))
371
- listOfSupers.style.display = 'flex'
372
- listOfSupers.refresh = function () {
373
- // const supers = kb.each(null, ns.wf('dependent'), issue, issue.doc())
374
- const supers = supersOver(issue)
375
- utils.syncTableToArrayReOrdered(listOfSupers, supers, renderSubIssue)
376
- }
377
- listOfSupers.refresh()
378
-
379
- // Sub issues
380
- subIssuePanel.appendChild(dom.createElement('h4')).textContent = 'Sub Issues'
381
- const listOfSubs = subIssuePanel.appendChild(dom.createElement('div'))
382
- listOfSubs.style.display = 'flex'
383
- listOfSubs.style.flexDirection = 'reverse' // Or center
384
- listOfSubs.refresh = function () {
385
- const subs = kb.each(issue, ns.wf('dependent'), null, issue.doc())
386
- utils.syncTableToArrayReOrdered(listOfSubs, subs, renderSubIssue)
387
- }
388
- listOfSubs.refresh()
389
-
390
- const b = dom.createElement('button')
391
- b.setAttribute('type', 'button')
392
- subIssuePanel.appendChild(b)
393
- const classLabel = utils.label(states)
394
- b.innerHTML = 'New sub ' + classLabel
395
- b.setAttribute('style', 'float: right; margin: 0.5em 1em;')
396
- b.addEventListener(
397
- 'click',
398
- function (_event) {
399
- subIssuePanel.insertBefore(newIssueForm(dom, kb, tracker, issue, listOfSubs.refresh), b.nextSibling) // Pop form just after button
400
- },
401
- false
402
- )
403
- }
404
-
405
- issueDiv.appendChild(dom.createElement('br'))
406
-
407
- // Extras are stored centrally to the tracker
408
- const extrasForm = kb.any(tracker, ns.wf('extrasEntryForm'))
409
- if (extrasForm) {
410
- widgets.appendForm(
411
- dom,
412
- issueDiv,
413
- {},
414
- issue,
415
- extrasForm,
416
- stateStore,
417
- complainIfBad
418
- )
419
- // issueDiv.appendChild(renderSpacer(backgroundColor))
420
- }
421
-
422
- // Comment/discussion area
423
-
424
- const spacer = issueDiv.appendChild(renderSpacer(dom, backgroundColor))
425
-
426
- const template = kb.anyValue(tracker, ns.wf('issueURITemplate'))
427
- /*
428
- var chatDocURITemplate = kb.anyValue(tracker, ns.wf('chatDocURITemplate')) // relaive to issue
429
- var chat
430
- if (chatDocURITemplate) {
431
- let template = $rdf.uri.join(chatDocURITemplate, issue.uri) // Template is relative to issue
432
- chat = kb.sym(expandTemplate(template))
433
- } else
434
- */
435
- let messageStore
436
- if (template) {
437
- messageStore = issue.doc() // for now. Could go deeper
438
- } else {
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 =
442
- }
443
-
444
- kb.fetcher.nowOrWhenFetched(messageStore, function (ok, body, _xhr) {
445
- if (!ok) {
446
- const er = dom.createElement('p')
447
- er.textContent = body // @@ use nice error message
448
- issueDiv.insertBefore(er, spacer)
449
- } else {
450
- const discussion = messageArea(dom, kb, issue, messageStore)
451
- issueDiv.insertBefore(discussion, spacer)
452
- issueDiv.insertBefore(renderSpacer(dom, backgroundColor), discussion)
453
- } // Not sure why e stuck this in upwards rather than downwards
454
- })
455
-
456
- // Draggable attachment list
457
- const attachmentHint = issueDiv.appendChild(dom.createElement('div'))
458
- attachmentHint.innerHTML = `<h4>Attachments</h4>
459
- <p>Drag files, emails,
460
- web pages onto the paper clip, or click the file upload button.</p>`
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
-
466
- widgets.attachmentList(dom, issue, issueDiv, {
467
- doc: stateStore,
468
- promptIcon: icons.iconBase + 'noun_25830.svg',
469
- uploadFolder: kb.sym(uploadFolderURI), // Allow local files to be uploaded
470
- predicate: ns.wf('attachment')
471
- })
472
-
473
- // Delete button to delete the issue
474
- const deleteButton = widgets.deleteButtonWithCheck(dom, issueDiv, 'issue', async function () {
475
- try {
476
- await kb.updater.update(kb.connectedStatements(issue))
477
- } catch (err) {
478
- complain(`Unable to delete issue: ${err}`, context)
479
- }
480
- // @@ refreshTree
481
- complain('DELETED OK', context)
482
- issueDiv.style.backgroundColor = '#eee'
483
- issueDiv.style.fontColor = 'orange'
484
- })
485
- deleteButton.style.float = 'right'
486
-
487
- // Refresh button
488
- const refreshButton = dom.createElement('button')
489
- refreshButton.textContent = 'refresh messages'
490
- refreshButton.addEventListener(
491
- 'click',
492
- async function (_event) {
493
- try {
494
- await kb.fetcher.load(messageStore, { force: true, clearPreviousData: true })
495
- } catch (err) {
496
- alert(err)
497
- return
498
- }
499
- widgets.refreshTree(issueDiv)
500
- },
501
- false
502
- )
503
- refreshButton.setAttribute('style', style.button)
504
- issueDiv.appendChild(refreshButton)
505
- return issueDiv
506
- } // renderIssue