meeting-pane 2.5.1-b5aaef32 → 2.5.1-f652b85a

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,1220 +0,0 @@
1
- /* Meeting materials and tools Pane
2
- **
3
- ** Putting together some of the tools we have to manage a Meeting
4
- */
5
-
6
- // const VideoRoomPrefix = 'https://appear.in/'
7
- const logic = require('solid-logic')
8
- const VideoRoomPrefix = 'https://meet.jit.si/'
9
-
10
- const UI = require('solid-ui')
11
- const ns = UI.ns
12
- const $rdf = require('rdflib')
13
-
14
- const meetingDetailsFormText = require('./meetingDetailsForm.js')
15
-
16
- module.exports = {
17
- icon: UI.icons.iconBase + 'noun_66617.svg',
18
-
19
- name: 'meeting',
20
-
21
- audience: [ns.solid('PowerUser')],
22
-
23
- label: function (subject, context) {
24
- var kb = context.session.store
25
- var ns = UI.ns
26
- if (kb.holds(subject, ns.rdf('type'), ns.meeting('Meeting'))) {
27
- return 'Meeting'
28
- }
29
- return null // Suppress pane otherwise
30
- },
31
-
32
- // Create a new Meeting thing
33
- //
34
- // returns: A promise of a meeting object
35
- //
36
-
37
- mintClass: UI.ns.meeting('Meeting'),
38
-
39
- mintNew: function (context, options) {
40
- return new Promise(function (resolve, reject) {
41
- var kb = context.session.store
42
- var ns = UI.ns
43
- options.newInstance =
44
- options.newInstance || kb.sym(options.newBase + 'index.ttl#this')
45
- var meeting = options.newInstance
46
- var meetingDoc = meeting.doc()
47
-
48
- var me = logic.authn.currentUser()
49
-
50
- if (me) {
51
- kb.add(meeting, ns.dc('author'), me, meetingDoc)
52
- }
53
-
54
- kb.add(meeting, ns.rdf('type'), ns.meeting('Meeting'), meetingDoc)
55
- kb.add(meeting, ns.dc('created'), new Date(), meetingDoc)
56
- kb.add(
57
- meeting,
58
- ns.ui('backgroundColor'),
59
- new $rdf.Literal('#ddddcc', undefined, ns.xsd('color')),
60
- meetingDoc
61
- )
62
- var toolList = new $rdf.Collection()
63
- kb.add(meeting, ns.meeting('toolList'), toolList, meetingDoc)
64
-
65
- toolList.elements.push(meeting) // Add the meeting itself - see renderMain()
66
-
67
- kb.updater.put(
68
- meetingDoc,
69
- kb.statementsMatching(undefined, undefined, undefined, meetingDoc),
70
- 'text/turtle',
71
- function (uri2, ok, message) {
72
- if (ok) {
73
- resolve(options)
74
- } else {
75
- reject(new Error('Error writing meeting configuration: ' + message))
76
- }
77
- }
78
- )
79
- })
80
- },
81
-
82
- // Returns a div
83
-
84
- render: function (subject, dataBrowserContext) {
85
- const dom = dataBrowserContext.dom
86
- var kb = dataBrowserContext.session.store
87
- var ns = UI.ns
88
- var updater = kb.updater
89
- var thisPane = this
90
-
91
- var complain = function complain (message, color) {
92
- console.log(message)
93
- var pre = dom.createElement('pre')
94
- pre.setAttribute('style', 'background-color: ' + color || '#eed' + ';')
95
- div.appendChild(pre)
96
- pre.appendChild(dom.createTextNode(message))
97
- }
98
-
99
- var complainIfBad = function (ok, message) {
100
- if (!ok) complain(message)
101
- }
102
-
103
- var meeting = subject
104
- var meetingDoc = subject.doc()
105
- var meetingBase = subject.dir().uri
106
- var div = dom.createElement('div')
107
- var table = div.appendChild(dom.createElement('table'))
108
- table.style = 'width: 100%; height: 100%; margin:0;'
109
- var topTR = table.appendChild(dom.createElement('tr'))
110
- topTR.appendChild(dom.createElement('div')) // topDiv
111
- var mainTR = table.appendChild(dom.createElement('tr'))
112
-
113
- var toolBar0 = table.appendChild(dom.createElement('td'))
114
- var toolBar1 = toolBar0.appendChild(dom.createElement('table'))
115
- var toolBar = toolBar1.appendChild(dom.createElement('tr'))
116
-
117
- topTR.setAttribute('style', 'height: 2em;') // spacer if notthing else
118
-
119
- var me = null // @@ Put code to find out logged in person
120
-
121
- var saveBackMeetingDoc = function () {
122
- updater.put(
123
- meetingDoc,
124
- kb.statementsMatching(undefined, undefined, undefined, meetingDoc),
125
- 'text/turtle',
126
- function (uri2, ok, message) {
127
- if (ok) {
128
- tabs.refresh()
129
- resetTools()
130
- } else {
131
- message =
132
- 'FAILED to save new thing at: ' + meetingDoc + ' : ' + message
133
- complain(message)
134
- }
135
- }
136
- )
137
- }
138
-
139
- var saveAppDocumentLinkAndAddNewThing = function (tool, thing, pred) {
140
- var appDoc = thing.doc()
141
- if (pred) {
142
- kb.add(meeting, pred, thing, appDoc) // Specific Link back to meeting
143
- }
144
- kb.add(thing, ns.meeting('parentMeeting'), meeting, appDoc) // Generic link back to meeting
145
- updater.put(
146
- appDoc,
147
- kb.statementsMatching(undefined, undefined, undefined, appDoc),
148
- 'text/turtle',
149
- function (uri2, ok, message) {
150
- if (ok) {
151
- saveBackMeetingDoc()
152
- } else {
153
- complain('FAILED to save new tool at: ' + thing + ' : ' + message)
154
- }
155
- }
156
- )
157
- }
158
-
159
- var makeToolNode = function (target, pred, label, iconURI) {
160
- if (pred) {
161
- kb.add(meeting, pred, target, meetingDoc)
162
- }
163
- var x = UI.widgets.newThing(meetingDoc)
164
- if (label) kb.add(x, ns.rdfs('label'), label, meetingDoc)
165
- if (iconURI) kb.add(x, ns.meeting('icon'), kb.sym(iconURI), meetingDoc)
166
- kb.add(x, ns.rdf('type'), ns.meeting('Tool'), meetingDoc)
167
- kb.add(x, ns.meeting('target'), target, meetingDoc)
168
- var toolList = kb.the(meeting, ns.meeting('toolList'))
169
- toolList.elements.push(x)
170
- return x
171
- }
172
-
173
- // Map from end-user non-iframeable Google maps URI to G Maps API
174
- // Input: like https://www.google.co.uk/maps/place/Mastercard/@53.2717971,-6.2042699,17z/...
175
- // Output:
176
- function googleMapsSpecial (page) {
177
- const initialPrefix = /https:\/\/www\.google\..*\/maps\//
178
- const finalPrefix = 'https://www.google.com/maps/embed/v1/'
179
- const myPersonalApiKEY = 'AIzaSyB8aaT6bY9tcLCmc2oPCkdUYLmTOWM8R54' // Get your own key!
180
- // GET YOUR KEY AT https://developers.google.com/maps/documentation/javascript/
181
- const uri = page.uri
182
- if (!uri.match(initialPrefix)) return page
183
- if (uri.startsWith(finalPrefix)) return page // Already done
184
- const map =
185
- uri.replace(initialPrefix, finalPrefix) + '&key=' + myPersonalApiKEY
186
- console.log('Converted Google Map URI! ' + map)
187
- return $rdf.sym(map)
188
- }
189
-
190
- // //////////////////// DRAG and Drop
191
-
192
- var handleDroppedThing = function (target) {
193
- // @@ idea: look
194
- return new Promise(function (resolve) {
195
- // Add a meeting tab for a web resource. Alas many resource canot be framed
196
- // as they block framing, or are insecure.
197
- var addIframeTool = function (target) {
198
- var tool = makeToolNode(
199
- target,
200
- UI.ns.wf('attachment'),
201
- UI.utils.label(target),
202
- null
203
- )
204
- kb.add(tool, UI.ns.meeting('view'), 'iframe', meetingDoc)
205
- }
206
-
207
- var addLink = function (target) {
208
- const pred = ns.wf('attachment')
209
- kb.add(subject, pred, target, subject.doc())
210
- var toolObject = {
211
- icon: 'noun_160581.svg', // right arrow "link"
212
- limit: 1,
213
- shareTab: true // but many things behind it
214
- }
215
- var newPaneOptions = {
216
- newInstance: subject, // kb.sym(subject.doc().uri + '#LinkListTool'),
217
- pane: dataBrowserContext.session.paneRegistry.byName('link'), // the pane to be used to mint a new thing
218
- predicate: ns.meeting('attachmentTool'),
219
- tabTitle: 'Links',
220
- view: 'link', // The pane to be used when it is viewed
221
- noIndexHTML: true
222
- }
223
- return makeNewPaneTool(toolObject, newPaneOptions)
224
- }
225
-
226
- // When paerson added to he meeting, make an ad hoc group
227
- // of meeting participants is one does not already exist, and add them
228
- var addParticipant = function (target) {
229
- var pref = kb.any(target, ns.foaf('preferredURI'))
230
- var obj = pref ? kb.sym(pref) : target
231
- var group = kb.any(meeting, ns.meeting('attendeeGroup'))
232
- var addPersonToGroup = function (obj, group) {
233
- var ins = [
234
- $rdf.st(group, UI.ns.vcard('hasMember'), obj, group.doc())
235
- ] // @@@ Complex rules about webid?
236
- var name =
237
- kb.any(obj, ns.vcard('fn')) || kb.any(obj, ns.foaf('name'))
238
- if (name) {
239
- ins.push($rdf.st(obj, UI.ns.vcard('fn'), name, group.doc()))
240
- }
241
- kb.fetcher.nowOrWhenFetched(group.doc(), undefined, function (
242
- ok,
243
- _body
244
- ) {
245
- if (!ok) {
246
- complain("Can't read group to add person" + group)
247
- return
248
- }
249
- kb.updater.update([], ins, function (uri, ok, body) {
250
- complainIfBad(ok, body)
251
- if (ok) {
252
- console.log('Addded to particpants OK: ' + obj)
253
- }
254
- })
255
- })
256
- }
257
- if (group) {
258
- addPersonToGroup(obj, group)
259
- return
260
- }
261
- makeParticipantsGroup()
262
- .then(function (options) {
263
- var group = options.newInstance
264
- addPersonToGroup(obj, group)
265
- kb.fetcher
266
- .putBack(meetingDoc, { contentType: 'text/turtle' })
267
- .then(function (_xhr) {
268
- console.log('Particiants Group created: ' + group)
269
- })
270
- })
271
- .catch(function (err) {
272
- complain(err)
273
- })
274
- }
275
-
276
- console.log('Dropped on thing ' + target) // icon was: UI.icons.iconBase + 'noun_25830.svg'
277
- var u = target.uri
278
- if (u.startsWith('http:') && u.indexOf('#') < 0) {
279
- // insecure Plain document
280
- addLink(target)
281
- return resolve(target)
282
- }
283
- kb.fetcher.nowOrWhenFetched(target, function (ok, mess) {
284
- function addAttachmentTab (target) {
285
- target = googleMapsSpecial(target)
286
- console.log('make web page attachement tab ' + target) // icon was: UI.icons.iconBase + 'noun_25830.svg'
287
- var tool = makeToolNode(
288
- target,
289
- UI.ns.wf('attachment'),
290
- UI.utils.label(target),
291
- null
292
- )
293
- kb.add(tool, UI.ns.meeting('view'), 'iframe', meetingDoc)
294
- return resolve(target)
295
- }
296
- if (!ok) {
297
- console.log(
298
- 'Error looking up dropped thing, will just add it anyway. ' +
299
- target +
300
- ': ' +
301
- mess
302
- )
303
- return addAttachmentTab(target) // You can still try iframing it. (Could also add to list of links in PersonTR widgets)
304
- } else {
305
- var obj = target
306
- var types = kb.findTypeURIs(obj)
307
- for (var ty in types) {
308
- console.log(' drop object type includes: ' + ty)
309
- }
310
- if (
311
- ns.vcard('Individual').uri in types ||
312
- ns.foaf('Person').uri in types ||
313
- ns.foaf('Agent').uri in types
314
- ) {
315
- addParticipant(target)
316
- return resolve(target)
317
- }
318
- if (u.startsWith('https:') && u.indexOf('#') < 0) {
319
- // Plain secure document
320
- // can we iframe it?
321
- var hh = kb.fetcher.getHeader(target, 'x-frame-options')
322
- var ok2 = true
323
- if (hh) {
324
- for (var j = 0; j < hh.length; j++) {
325
- console.log('x-frame-options: ' + hh[j])
326
- if (hh[j].indexOf('sameorigin') < 0) {
327
- // (and diff origin @@)
328
- ok2 = false
329
- }
330
- if (hh[j].indexOf('deny') < 0) {
331
- ok2 = false
332
- }
333
- }
334
- }
335
- if (ok2) {
336
- target = googleMapsSpecial(target) // tweak Google maps to embed OK
337
- addIframeTool(target) // Something we can maybe iframe
338
- return resolve(target)
339
- }
340
- } // Something we cannot iframe, and must link to:
341
- console.log('Default: assume web page attachement ' + target) // icon was: UI.icons.iconBase + 'noun_25830.svg'
342
- return addAttachmentTab(target)
343
- }
344
- })
345
- }) // promise
346
- }
347
-
348
- // When a set of URIs are dropped on the tabs
349
- var droppedURIHandler = function (uris) {
350
- Promise.all(
351
- uris.map(function (u) {
352
- var target = $rdf.sym(u) // Attachment needs text label to disinguish I think not icon.
353
- return handleDroppedThing(target) // can add to meetingDoc but must be sync
354
- })
355
- ).then(function (_a) {
356
- saveBackMeetingDoc()
357
- })
358
- }
359
-
360
- var droppedFileHandler = function (files) {
361
- UI.widgets.uploadFiles(
362
- kb.fetcher,
363
- files,
364
- meeting.dir().uri + 'Files',
365
- meeting.dir().uri + 'Pictures',
366
- function (theFile, _destURI) {
367
- if (theFile.type.startsWith('image/')) {
368
- makePicturesFolder('Files') // If necessary
369
- } else {
370
- makeMaterialsFolder('Pictures')
371
- }
372
- }
373
- )
374
- }
375
-
376
- // ////////////////////////////////////////////////////// end of drag drop
377
-
378
- var makeGroup = function (_toolObject) {
379
- var newBase = meetingBase + 'Group/'
380
- var kb = dataBrowserContext.session.store
381
- var group = kb.any(meeting, ns.meeting('particpants'))
382
- if (!group) {
383
- group = $rdf.sym(newBase + 'index.ttl#this')
384
- }
385
- console.log('Participant group: ' + group)
386
-
387
- var tool = makeToolNode(
388
- group,
389
- ns.meeting('particpants'),
390
- 'Particpants',
391
- UI.icons.iconBase + 'noun_339237.svg'
392
- ) // group: noun_339237.svg 'noun_15695.svg'
393
- kb.add(tool, UI.ns.meeting('view'), 'peoplePicker', meetingDoc)
394
- saveBackMeetingDoc()
395
- }
396
- /*
397
- var makeAddressBook = function (toolObject) {
398
- var newBase = meetingBase + 'Group/'
399
- var kb = store
400
- var group = kb.any(meeting, ns.meeting('addressBook'))
401
- if (!group) {
402
- group = $rdf.sym(newBase + 'index.ttl#this')
403
- }
404
-
405
- // Create a tab for the addressbook
406
- var div = dom.createElement('div')
407
- var context = { dom: dom, div: div }
408
- var book
409
- UI.login.findAppInstances(context, ns.vcard('AddressBook')).then(
410
- function (context) {
411
- if (context.instances.length === 0) {
412
- complain('You have no solid address book. It is really handy to have one to keep track of people and groups')
413
- } else if (context.instances.length > 1) {
414
- var s = context.instances.map(function (x) { return '' + x }).join(', ')
415
- complain('You have more than one solid address book: ' + s + ' Not supported yet.')
416
- } else { // addressbook
417
- book = context.instances[0]
418
- var tool = makeToolNode(book, ns.meeting('addressBook'), 'Address Book', UI.icons.iconBase + 'noun_15695.svg') // group: noun_339237.svg
419
- kb.add(tool, UI.ns.meeting('view'), 'contact', meetingDoc)
420
- saveBackMeetingDoc()
421
- }
422
- }
423
- )
424
- }
425
- */
426
- var makePoll = function (toolObject) {
427
- var newPaneOptions = {
428
- useExisting: meeting, // Regard the meeting as being the schedulable event itself.
429
- // newInstance: meeting,
430
- pane: dataBrowserContext.session.paneRegistry.byName('schedule'),
431
- view: 'schedule',
432
- // predicate: ns.meeting('schedulingPoll'),
433
- // newBase: meetingBase + 'Schedule/', Not needed as uses existing meeting
434
- tabTitle: 'Schedule poll',
435
- noIndexHTML: true
436
- }
437
- return makeNewPaneTool(toolObject, newPaneOptions)
438
- }
439
-
440
- var makePicturesFolder = function (folderName) {
441
- var toolObject = {
442
- icon: 'noun_598334.svg', // Slideshow @@ find a "picture" icon?
443
- limit: 1,
444
- shareTab: true // but many things behind it
445
- }
446
- var newPaneOptions = {
447
- newInstance: kb.sym(meeting.dir().uri + folderName + '/'),
448
- pane: dataBrowserContext.session.paneRegistry.byName('folder'), // @@ slideshow??
449
- predicate: ns.meeting('pictures'),
450
- shareTab: true,
451
- tabTitle: folderName,
452
- view: 'slideshow',
453
- noIndexHTML: true
454
- }
455
- return makeNewPaneTool(toolObject, newPaneOptions)
456
- }
457
-
458
- var makeMaterialsFolder = function (_folderName) {
459
- var toolObject = {
460
- icon: 'noun_681601.svg', // Document
461
- limit: 1,
462
- shareTab: true // but many things behind it
463
- }
464
- var options = {
465
- newInstance: kb.sym(meeting.dir().uri + 'Files/'),
466
- pane: dataBrowserContext.session.paneRegistry.byName('folder'),
467
- predicate: ns.meeting('materialsFolder'),
468
- tabTitle: 'Materials',
469
- noIndexHTML: true
470
- }
471
- return makeNewPaneTool(toolObject, options)
472
- }
473
-
474
- var makeParticipantsGroup = function () {
475
- var toolObject = {
476
- icon: 'noun_339237.svg', // Group of people
477
- limit: 1, // Only one tab
478
- shareTab: true // but many things behind it
479
- }
480
- var options = {
481
- newInstance: kb.sym(meeting.dir().uri + 'Attendees/index.ttl#this'),
482
- pane: dataBrowserContext.session.paneRegistry.byName('contact'),
483
- predicate: ns.meeting('attendeeGroup'),
484
- tabTitle: 'Attendees',
485
- instanceClass: ns.vcard('Group'),
486
- instanceName: UI.utils.label(subject) + ' attendees',
487
- noIndexHTML: true
488
- }
489
-
490
- return makeNewPaneTool(toolObject, options)
491
- }
492
-
493
- // Make Pad for notes of meeting
494
-
495
- var makePad = function (toolObject) {
496
- var newPaneOptions = {
497
- newBase: meetingBase + 'SharedNotes/',
498
- predicate: UI.ns.meeting('sharedNotes'),
499
- tabTitle: 'Shared Notes',
500
- pane: dataBrowserContext.session.paneRegistry.byName('pad')
501
- }
502
- return makeNewPaneTool(toolObject, newPaneOptions)
503
- }
504
-
505
- // Make Sub-meeting of meeting
506
-
507
- var makeMeeting = function (toolObject) {
508
- UI.widgets
509
- .askName(
510
- dom,
511
- kb,
512
- parameterCell,
513
- ns.foaf('name'),
514
- UI.ns.meeting('Meeting')
515
- )
516
- .then(function (name) {
517
- if (!name) {
518
- return resetTools()
519
- }
520
- var URIsegment = encodeURIComponent(name)
521
- var options = {
522
- newBase: meetingBase + URIsegment + '/', // @@@ sanitize
523
- predicate: UI.ns.meeting('subMeeting'),
524
- tabTitle: name,
525
- pane: dataBrowserContext.session.paneRegistry.byName('meeting')
526
- }
527
- return makeNewPaneTool(toolObject, options)
528
- })
529
- .catch(function (e) {
530
- complain('Error making new sub-meeting: ' + e)
531
- })
532
- }
533
-
534
- // Returns promise of newPaneOptions
535
- // In: options.
536
- // me?, predicate, newInstance ?, newBase, instanceClass
537
- // out: options. the above plus
538
- // me, newInstance
539
-
540
- function makeNewPaneTool (toolObject, options) {
541
- return new Promise(function (resolve, reject) {
542
- var kb = dataBrowserContext.session.store
543
- if (!options.useExisting) {
544
- // useExisting means use existing object in new role
545
- var existing = kb.any(meeting, options.predicate)
546
- if (existing) {
547
- if (
548
- toolObject.limit &&
549
- toolObject.limit === 1 &&
550
- !toolObject.shareTab
551
- ) {
552
- complain(
553
- 'Already have ' +
554
- existing +
555
- ' as ' +
556
- UI.utils.label(options.predicate)
557
- )
558
- complain('Cant have two')
559
- return resolve(null)
560
- }
561
- if (toolObject.shareTab) {
562
- // return existing one
563
- console.log(
564
- 'Using existing ' +
565
- existing +
566
- ' as ' +
567
- UI.utils.label(options.predicate)
568
- )
569
- return resolve({
570
- me: me,
571
- newInstance: existing,
572
- instanceClass: options.instanceClass
573
- })
574
- }
575
- }
576
- }
577
- if (!me && !options.me) { reject(new Error('Username not defined for new tool')) }
578
- options.me = options.me || me
579
- options.newInstance =
580
- options.useExisting ||
581
- options.newInstance ||
582
- kb.sym(options.newBase + 'index.ttl#this')
583
-
584
- options.pane
585
- .mintNew(dataBrowserContext, options)
586
- .then(function (options) {
587
- var tool = makeToolNode(
588
- options.newInstance,
589
- options.predicate,
590
- options.tabTitle,
591
- options.pane.icon
592
- )
593
- if (options.view) {
594
- kb.add(tool, UI.ns.meeting('view'), options.view, meetingDoc)
595
- }
596
- saveBackMeetingDoc()
597
- kb.fetcher
598
- .putBack(meetingDoc, { contentType: 'text/turtle' })
599
- .then(function (_xhr) {
600
- resolve(options)
601
- })
602
- .catch(function (err) {
603
- reject(err)
604
- })
605
- })
606
- .catch(function (err) {
607
- complain(err)
608
- reject(err)
609
- })
610
- })
611
- }
612
-
613
- var makeAgenda = function (_toolObject) {
614
- // selectTool(icon)
615
- }
616
-
617
- var makeActions = function (_toolObject) {
618
- var newBase = meetingBase + 'Actions/'
619
- var kb = dataBrowserContext.session.store
620
- if (kb.holds(meeting, ns.meeting('actions'))) {
621
- console.log('Ignored - already have actions')
622
- return // already got one
623
- }
624
- var appDoc = kb.sym(newBase + 'config.ttl')
625
- var newInstance = kb.sym(newBase + 'config.ttl#this')
626
- var stateStore = kb.sym(newBase + 'state.ttl')
627
-
628
- kb.add(
629
- newInstance,
630
- ns.dc('title'),
631
- (kb.anyValue(meeting, ns.cal('summary')) || 'Meeting ') + ' actions',
632
- appDoc
633
- )
634
- kb.add(newInstance, ns.wf('issueClass'), ns.wf('Task'), appDoc)
635
- kb.add(newInstance, ns.wf('initialState'), ns.wf('Open'), appDoc)
636
- kb.add(newInstance, ns.wf('stateStore'), stateStore, appDoc)
637
- kb.add(newInstance, ns.wf('assigneeClass'), ns.foaf('Person'), appDoc) // @@ set to people in the meeting?
638
-
639
- kb.add(newInstance, ns.rdf('type'), ns.wf('Tracker'), appDoc)
640
-
641
- // Flag its type in the chat itself as well as in the master meeting config file
642
- kb.add(newInstance, ns.rdf('type'), ns.wf('Tracker'), appDoc)
643
- var tool = makeToolNode(
644
- newInstance,
645
- ns.meeting('actions'),
646
- 'Actions',
647
- UI.icons.iconBase + 'noun_17020.svg'
648
- )
649
- saveAppDocumentLinkAndAddNewThing(
650
- tool,
651
- newInstance,
652
- ns.meeting('actions')
653
- )
654
- }
655
-
656
- var makeChat = function (_toolObject) {
657
- var newBase = meetingBase + 'Chat/'
658
- var kb = dataBrowserContext.session.store
659
- if (kb.holds(meeting, ns.meeting('chat'))) {
660
- console.log('Ignored - already have chat')
661
- return // already got one
662
- }
663
- var messageStore = kb.sym(newBase + 'chat.ttl')
664
-
665
- kb.add(messageStore, ns.rdf('type'), ns.meeting('Chat'), messageStore)
666
-
667
- var tool = makeToolNode(
668
- messageStore,
669
- ns.meeting('chat'),
670
- 'Chat',
671
- UI.icons.iconBase + 'noun_346319.svg'
672
- )
673
- saveAppDocumentLinkAndAddNewThing(tool, messageStore, ns.meeting('chat'))
674
- }
675
-
676
- var makeVideoCall = function (_toolObject) {
677
- var kb = dataBrowserContext.session.store
678
- var newInstance = $rdf.sym(VideoRoomPrefix + UI.utils.genUuid())
679
-
680
- if (kb.holds(meeting, ns.meeting('videoCallPage'))) {
681
- console.log('Ignored - already have a videoCallPage')
682
- return // already got one
683
- }
684
- kb.add(
685
- newInstance,
686
- ns.rdf('type'),
687
- ns.meeting('VideoCallPage'),
688
- meetingDoc
689
- )
690
- var tool = makeToolNode(
691
- newInstance,
692
- ns.meeting('videoCallPage'),
693
- 'Video call',
694
- UI.icons.iconBase + 'noun_260227.svg'
695
- )
696
- kb.add(tool, ns.meeting('view'), 'iframe', meetingDoc)
697
- saveBackMeetingDoc()
698
- }
699
-
700
- var makeAttachment = function (_toolObject) {
701
- UI.widgets
702
- .askName(dom, kb, parameterCell, ns.log('uri'), UI.ns.rdf('Resource'))
703
- .then(function (uri) {
704
- if (!uri) {
705
- return resetTools()
706
- }
707
- var kb = dataBrowserContext.session.store
708
- var ns = UI.ns
709
- var target = kb.sym(uri)
710
- var tool = makeToolNode(
711
- target,
712
- ns.wf('attachment'),
713
- UI.utils.label(target),
714
- null
715
- )
716
- kb.add(tool, ns.meeting('view'), 'iframe', meetingDoc)
717
- saveBackMeetingDoc()
718
- })
719
- .catch(function (e) {
720
- complain('Error making new sub-meeting: ' + e)
721
- })
722
- }
723
-
724
- var makeSharing = function (toolObject) {
725
- var kb = dataBrowserContext.session.store
726
- var ns = UI.ns
727
- var target = meeting.dir()
728
- if (
729
- toolObject.limit &&
730
- toolObject.limit === 1 &&
731
- kb.holds(meeting, ns.wf('sharingControl'))
732
- ) {
733
- complain('Ignored - already have ' + UI.utils.label(options.predicate))
734
- return
735
- }
736
- var tool = makeToolNode(
737
- target,
738
- ns.wf('sharingControl'),
739
- 'Sharing',
740
- UI.icons.iconBase + 'noun_123691.svg'
741
- )
742
- kb.add(tool, ns.meeting('view'), 'sharing', meetingDoc)
743
- saveBackMeetingDoc()
744
- }
745
-
746
- var makeNewMeeting = function () {
747
- // @@@ make option of continuing series
748
- var appDetails = { noun: 'meeting' }
749
- var gotWS = function (ws, base) {
750
- thisPane
751
- .mintNew(dataBrowserContext, { newBase: base })
752
- .then(function (options) {
753
- var newInstance = options.newInstance
754
- parameterCell.removeChild(mintUI)
755
- var p = parameterCell.appendChild(dom.createElement('p'))
756
- p.setAttribute('style', 'font-size: 140%;')
757
- p.innerHTML =
758
- "Your <a target='_blank' href='" +
759
- newInstance.uri +
760
- "'><b>new meeting</b></a> is ready to be set up. " +
761
- "<br/><br/><a target='_blank' href='" +
762
- newInstance.uri +
763
- "'>Go to your new meeting.</a>"
764
- })
765
- .catch(function (err) {
766
- parameterCell.removeChild(mintUI)
767
- parameterCell.appendChild(UI.widgets.errorMessageBlock(dom, err))
768
- })
769
- }
770
- var mintUI = UI.login.selectWorkspace(dom, appDetails, gotWS)
771
- parameterCell.appendChild(mintUI)
772
- }
773
-
774
- // //////////////////////////////////////////////////////////// end of new tab creation functions
775
-
776
- var toolIcons = [
777
- {
778
- icon: 'noun_339237.svg',
779
- maker: makeGroup,
780
- hint: 'Make a group of people',
781
- limit: 1
782
- },
783
- {
784
- icon: 'noun_346777.svg',
785
- maker: makePoll,
786
- hint: 'Make a poll to schedule the meeting'
787
- }, // When meet THIS or NEXT time
788
- {
789
- icon: 'noun_48218.svg',
790
- maker: makeAgenda,
791
- limit: 1,
792
- hint: 'Add an agenda list',
793
- disabled: true
794
- }, // When meet THIS or NEXT time
795
- { icon: 'noun_79217.svg', maker: makePad, hint: 'Add a shared notepad' },
796
- {
797
- icon: 'noun_346319.svg',
798
- maker: makeChat,
799
- limit: 1,
800
- hint: 'Add a chat channel for the meeting'
801
- },
802
- {
803
- icon: 'noun_17020.svg',
804
- maker: makeActions,
805
- limit: 1,
806
- hint: 'Add a list of action items'
807
- }, // When meet THIS or NEXT time
808
- {
809
- icon: 'noun_260227.svg',
810
- maker: makeVideoCall,
811
- limit: 1,
812
- hint: 'Add a video call for the meeting'
813
- },
814
- {
815
- icon: 'noun_25830.svg',
816
- maker: makeAttachment,
817
- hint: 'Attach meeting materials',
818
- disabled: false
819
- },
820
- {
821
- icon: 'noun_123691.svg',
822
- maker: makeSharing,
823
- limit: 1,
824
- hint: 'Control Sharing',
825
- disabled: false
826
- },
827
- {
828
- icon: 'noun_66617.svg',
829
- maker: makeMeeting,
830
- hint: 'Make a sub meeting',
831
- disabled: false
832
- }
833
- ] // 'noun_66617.svg'
834
-
835
- var settingsForm = $rdf.sym(
836
- 'https://solid.github.io/solid-panes/meeting/meetingDetailsForm.ttl#settings'
837
- )
838
- $rdf.parse(
839
- meetingDetailsFormText,
840
- kb,
841
- settingsForm.doc().uri,
842
- 'text/turtle'
843
- ) // Load form directly
844
-
845
- var iconStyle = 'padding: 1em; width: 3em; height: 3em;'
846
- var iconCell = toolBar.appendChild(dom.createElement('td'))
847
- var parameterCell = toolBar.appendChild(dom.createElement('td'))
848
- var star = iconCell.appendChild(dom.createElement('img'))
849
- var visible = false // the inividual tools tools
850
-
851
- star.setAttribute('src', UI.icons.iconBase + 'noun_19460_green.svg') // noun_272948.svg
852
- star.setAttribute('style', iconStyle + 'opacity: 50%;')
853
- star.setAttribute('title', 'Add another tool to the meeting')
854
-
855
- var selectNewTool = function (_event) {
856
- visible = !visible
857
- star.setAttribute(
858
- 'style',
859
- iconStyle + (visible ? 'background-color: yellow;' : '')
860
- )
861
- styleTheIcons(visible ? '' : 'display: none;')
862
- }
863
-
864
- var loginOutButton
865
- logic.authn.checkUser().then(webId => {
866
- if (webId) {
867
- me = webId
868
- star.addEventListener('click', selectNewTool)
869
- star.setAttribute('style', iconStyle)
870
- return
871
- }
872
-
873
- loginOutButton = UI.login.loginStatusBox(dom, webIdUri => {
874
- if (webIdUri) {
875
- me = kb.sym(webIdUri)
876
- parameterCell.removeChild(loginOutButton)
877
- // loginOutButton.setAttribute('',iconStyle) // make it match the icons
878
- star.addEventListener('click', selectNewTool)
879
- star.setAttribute('style', iconStyle)
880
- } else {
881
- console.log('(Logged out)')
882
- me = null
883
- }
884
- })
885
- loginOutButton.setAttribute('style', 'margin: 0.5em 1em;')
886
- parameterCell.appendChild(loginOutButton)
887
- })
888
-
889
- var iconArray = []
890
- for (var i = 0; i < toolIcons.length; i++) {
891
- var foo = function () {
892
- var toolObject = toolIcons[i]
893
- var icon = iconCell.appendChild(dom.createElement('img'))
894
- icon.setAttribute('src', UI.icons.iconBase + toolObject.icon)
895
- icon.setAttribute('style', iconStyle + 'display: none;')
896
- iconArray.push(icon)
897
- icon.tool = toolObject
898
- var maker = toolObject.maker
899
- if (!toolObject.disabled) {
900
- icon.addEventListener('click', function (_event) {
901
- selectTool(icon)
902
- maker(toolObject)
903
- })
904
- }
905
- }
906
- foo()
907
- }
908
-
909
- var styleTheIcons = function (style) {
910
- for (var i = 0; i < iconArray.length; i++) {
911
- var st = iconStyle + style
912
- if (toolIcons[i].disabled) {
913
- st += 'opacity: 0.3;'
914
- }
915
- iconArray[i].setAttribute('style', st) // eg 'background-color: #ccc;'
916
- }
917
- }
918
- var resetTools = function () {
919
- styleTheIcons('display: none;')
920
- star.setAttribute('style', iconStyle)
921
- }
922
-
923
- var selectTool = function (icon) {
924
- styleTheIcons('display: none;') // 'background-color: #ccc;'
925
- icon.setAttribute('style', iconStyle + 'background-color: yellow;')
926
- }
927
-
928
- // //////////////////////////////
929
-
930
- var renderTab = function (div, item) {
931
- if (kb.holds(item, ns.rdf('type'), ns.meeting('Tool'))) {
932
- var target = kb.any(item, ns.meeting('target'))
933
- var label = kb.any(item, ns.rdfs('label'))
934
- label = label ? label.value : UI.utils.label(target)
935
- var s = div.appendChild(dom.createElement('div'))
936
- s.textContent = label
937
- s.setAttribute('style', 'margin-left: 0.7em')
938
- var icon = kb.any(item, ns.meeting('icon'))
939
- if (icon) {
940
- // Make sure the icon is cleanly on the left of the label
941
- var table = div.appendChild(dom.createElement('table'))
942
- var tr = table.appendChild(dom.createElement('tr'))
943
- var left = tr.appendChild(dom.createElement('td'))
944
- var right = tr.appendChild(dom.createElement('td'))
945
- // var img = div.appendChild(dom.createElement('img'))
946
- var img = left.appendChild(dom.createElement('img'))
947
- img.setAttribute('src', icon.uri)
948
- // img.setAttribute('style', 'max-width: 1.5em; max-height: 1.5em;') // @@ SVG shrinks to 0
949
- img.setAttribute('style', 'width: 1.5em; height: 1.5em;') // @
950
- img.setAttribute('title', label)
951
- right.appendChild(s)
952
- } else {
953
- div.appendChild(s)
954
- }
955
- } else {
956
- div.textContent = UI.utils.label(item)
957
- }
958
- }
959
-
960
- var tipDiv = function (text) {
961
- var d = dom.createElement('div')
962
- var p = d.appendChild(dom.createElement('p'))
963
- p.setAttribute('style', 'margin: 0em; padding:3em; color: #888;')
964
- p.textContent = 'Tip: ' + text
965
- return d
966
- }
967
-
968
- var renderTabSettings = function (containerDiv, subject) {
969
- containerDiv.innerHTML = ''
970
- containerDiv.style += 'border-color: #eed;'
971
- containerDiv.appendChild(dom.createElement('h3')).textContent =
972
- 'Adjust this tab'
973
- if (kb.holds(subject, ns.rdf('type'), ns.meeting('Tool'))) {
974
- var form = $rdf.sym(
975
- 'https://solid.github.io/solid-panes/meeting/meetingDetailsForm.ttl#settings'
976
- )
977
- UI.widgets.appendForm(
978
- document,
979
- containerDiv,
980
- {},
981
- subject,
982
- form,
983
- meeting.doc(),
984
- complainIfBad
985
- )
986
- var delButton = UI.widgets.deleteButtonWithCheck(
987
- dom,
988
- containerDiv,
989
- 'tab',
990
- function () {
991
- var toolList = kb.the(meeting, ns.meeting('toolList'))
992
- for (var i = 0; i < toolList.elements.length; i++) {
993
- if (toolList.elements[i].sameTerm(subject)) {
994
- toolList.elements.splice(i, 1)
995
- break
996
- }
997
- }
998
- var target = kb.any(subject, ns.meeting('target'))
999
- var ds = kb
1000
- .statementsMatching(subject)
1001
- .concat(kb.statementsMatching(undefined, undefined, subject))
1002
- .concat(kb.statementsMatching(meeting, undefined, target))
1003
- kb.remove(ds) // Remove all links to and from the tab node
1004
- saveBackMeetingDoc()
1005
- }
1006
- )
1007
- delButton.setAttribute('style', 'width: 1.5em; height: 1.5em;')
1008
- // delButton.setAttribute('class', '')
1009
- // delButton.setAttribute('style', 'height: 2em; width: 2em; margin: 1em; border-radius: 0.5em; padding: 1em; font-size: 120%; background-color: red; color: white;')
1010
- // delButton.textContent = 'Delete this tab'
1011
- } else {
1012
- containerDiv.appendChild(dom.createElement('h4')).textContent =
1013
- '(No adjustments available)'
1014
- }
1015
- }
1016
-
1017
- const renderMain = function (containerDiv, subject) {
1018
- var pane = null
1019
- var table
1020
- var selectedGroup = null
1021
- containerDiv.innerHTML = ''
1022
- var complainIfBad = function (ok, message) {
1023
- if (!ok) {
1024
- containerDiv.textContent = '' + message
1025
- }
1026
- }
1027
- var showIframe = function (target) {
1028
- var iframe = containerDiv.appendChild(dom.createElement('iframe'))
1029
- // iframe.setAttribute('sandbox', '') // All restrictions
1030
- iframe.setAttribute('src', target.uri)
1031
- // See https://stackoverflow.com/questions/325273/make-iframe-to-fit-100-of-containers-remaining-height
1032
- // Set the container position (sic) so it becaomes a 100% reference for the size of the iframe height 100%
1033
- /* For now at least , leave the container style as set by the tab system. 20200115b
1034
- containerDiv.setAttribute(
1035
- 'style',
1036
- 'position: relative; top: 0px; left:0px; right:0px; resize: both; overflow:scroll; min-width: 30em; min-height: 30em;'
1037
- )
1038
- */
1039
- // iframe.setAttribute('style', 'height: 350px; border: 0; margin: 0; padding: 0; resize:both; overflow:scroll; width: 100%;')
1040
- // iframe.setAttribute('style', 'border: none; margin: 0; padding: 0; height: 100%; width: 100%; resize: both; overflow:scroll;')
1041
- iframe.setAttribute(
1042
- 'style',
1043
- 'border: none; margin: 0; padding: 0; height: 100%; width: 100%;'
1044
- )
1045
- // Following https://dev.chromium.org/Home/chromium-security/deprecating-permissions-in-cross-origin-iframes :
1046
- iframe.setAttribute('allow', 'microphone camera') // Allow iframe to request camera and mic
1047
- // containerDiv.style.resize = 'none' // Remove scroll bars on outer div - don't seem to work so well
1048
- iframe.setAttribute('name', 'disable-x-frame-options') // For electron: see https://github.com/electron/electron/pull/573
1049
- containerDiv.style.padding = 0
1050
- }
1051
- var renderPeoplePicker = function () {
1052
- var context = { div: containerDiv, dom: dom }
1053
- containerDiv.appendChild(dom.createElement('h4')).textContent =
1054
- 'Meeting Participants'
1055
- var groupPickedCb = function (group) {
1056
- var toIns = [
1057
- $rdf.st(
1058
- meeting,
1059
- ns.meeting('particpantGroup'),
1060
- group,
1061
- meeting.doc()
1062
- )
1063
- ]
1064
- kb.updater.update([], toIns, function (uri, ok, message) {
1065
- if (ok) {
1066
- selectedGroup = group
1067
- } else {
1068
- complain('Cant save participants group: ' + message)
1069
- }
1070
- })
1071
- }
1072
- selectedGroup = kb.any(meeting, ns.meeting('particpantGroup'))
1073
-
1074
- logic.loadTypeIndexes(context).then(function () {
1075
- // Assumes that the type index has an entry for addressbook
1076
- var options = {
1077
- defaultNewGroupName: 'Meeting Participants',
1078
- selectedGroup: selectedGroup
1079
- }
1080
- var picker = new UI.widgets.PeoplePicker(
1081
- context.div,
1082
- context.index.private[0],
1083
- groupPickedCb,
1084
- options
1085
- )
1086
- picker.render()
1087
- })
1088
- }
1089
-
1090
- var renderDetails = function () {
1091
- containerDiv.appendChild(dom.createElement('h3')).textContent =
1092
- 'Details of meeting'
1093
- var form = $rdf.sym(
1094
- 'https://solid.github.io/solid-panes/meeting/meetingDetailsForm.ttl#main'
1095
- )
1096
- UI.widgets.appendForm(
1097
- document,
1098
- containerDiv,
1099
- {},
1100
- meeting,
1101
- form,
1102
- meeting.doc(),
1103
- complainIfBad
1104
- )
1105
- containerDiv.appendChild(
1106
- tipDiv(
1107
- 'Drag URL-bar icons of web pages into the tab bar on the left to add new meeting materials.'
1108
- )
1109
- )
1110
- me = logic.authn.currentUser()
1111
- if (me) {
1112
- kb.add(meeting, ns.dc('author'), me, meetingDoc) // @@ should nly be on initial creation?
1113
- }
1114
- var context = {
1115
- noun: 'meeting',
1116
- me: me,
1117
- statusArea: containerDiv,
1118
- div: containerDiv,
1119
- dom: dom
1120
- }
1121
- UI.login
1122
- .registrationControl(context, meeting, ns.meeting('Meeting'))
1123
- .then(function (_context) {
1124
- console.log('Registration control finsished.')
1125
- })
1126
- var options = {}
1127
- UI.pad.manageParticipation(
1128
- dom,
1129
- containerDiv,
1130
- meetingDoc,
1131
- meeting,
1132
- me,
1133
- options
1134
- )
1135
-
1136
- // "Make a new meeting" button
1137
- var imageStyle = 'height: 2em; width: 2em; margin:0.5em;'
1138
- var detailsBottom = containerDiv.appendChild(dom.createElement('div'))
1139
- var spawn = detailsBottom.appendChild(dom.createElement('img'))
1140
- spawn.setAttribute('src', UI.icons.iconBase + 'noun_145978.svg')
1141
- spawn.setAttribute('title', 'Make a fresh new meeting')
1142
- spawn.addEventListener('click', makeNewMeeting)
1143
- spawn.setAttribute('style', imageStyle)
1144
-
1145
- // "Fork me on Github" button
1146
- var forka = detailsBottom.appendChild(dom.createElement('a'))
1147
- forka.setAttribute('href', 'https://github.com/solid/solid-panes') // @@ Move when code moves
1148
- forka.setAttribute('target', '_blank')
1149
- var fork = forka.appendChild(dom.createElement('img'))
1150
- fork.setAttribute('src', UI.icons.iconBase + 'noun_368567.svg')
1151
- fork.setAttribute('title', 'Fork me on github')
1152
- fork.setAttribute('style', imageStyle + 'opacity: 50%;')
1153
- }
1154
-
1155
- if (kb.holds(subject, ns.rdf('type'), ns.meeting('Tool'))) {
1156
- var target = kb.any(subject, ns.meeting('target'))
1157
- if (target.sameTerm(meeting) && !kb.any(subject, ns.meeting('view'))) {
1158
- // self reference? force details form
1159
- renderDetails() // Legacy meeting instances
1160
- } else {
1161
- var view = kb.any(subject, ns.meeting('view'))
1162
- view = view ? view.value : null
1163
- if (view === 'details') {
1164
- renderDetails()
1165
- } else if (view === 'peoplePicker') {
1166
- renderPeoplePicker()
1167
- } else if (view === 'iframe') {
1168
- showIframe(target)
1169
- } else {
1170
- pane = view
1171
- ? dataBrowserContext.session.paneRegistry.byName(view)
1172
- : null
1173
- table = containerDiv.appendChild(dom.createElement('table'))
1174
- table.style.width = '100%'
1175
- dataBrowserContext
1176
- .getOutliner(dom)
1177
- .GotoSubject(target, true, pane, false, undefined, table)
1178
- }
1179
- }
1180
- } else if (subject.sameTerm(meeting)) {
1181
- // self reference? force details form
1182
- renderDetails()
1183
- } else if (
1184
- subject.sameTerm(subject.doc()) &&
1185
- !kb.holds(subject, UI.ns.rdf('type'), UI.ns.meeting('Chat')) &&
1186
- !kb.holds(subject, UI.ns.rdf('type'), UI.ns.meeting('PaneView'))
1187
- // eslint-disable-next-line no-empty
1188
- ) { } else {
1189
- table = containerDiv.appendChild(dom.createElement('table'))
1190
- dataBrowserContext
1191
- .getOutliner(dom)
1192
- .GotoSubject(subject, true, undefined, false, undefined, table)
1193
- }
1194
- }
1195
-
1196
- var options = { dom: dom }
1197
- options.predicate = ns.meeting('toolList')
1198
- options.subject = subject
1199
- options.ordered = true
1200
- options.orientation = 1 // tabs on LHS
1201
- options.renderMain = renderMain
1202
- options.renderTab = renderTab
1203
- options.renderTabSettings = renderTabSettings
1204
- options.backgroundColor =
1205
- kb.anyValue(subject, ns.ui('backgroundColor')) || '#ddddcc'
1206
- var tabs = mainTR.appendChild(UI.tabs.tabWidget(options))
1207
-
1208
- UI.aclControl.preventBrowserDropEvents(dom)
1209
-
1210
- UI.widgets.makeDropTarget(
1211
- tabs.tabContainer,
1212
- droppedURIHandler,
1213
- droppedFileHandler
1214
- )
1215
- UI.widgets.makeDropTarget(iconCell, droppedURIHandler, droppedFileHandler)
1216
-
1217
- return div
1218
- }
1219
- }
1220
- // ends