UNPKG

meeting-pane

Version:

Solid-compatible Panes: meeting collaborative tool

950 lines (919 loc) 41.2 kB
"use strict"; /* Meeting materials and tools Pane ** ** Putting together some of the tools we have to manage a Meeting */ // const VideoRoomPrefix = 'https://appear.in/' var logic = require('solid-logic'); var VideoRoomPrefix = 'https://meet.jit.si/'; var UI = require('solid-ui'); var ns = UI.ns; var $rdf = require('rdflib'); var meetingDetailsFormText = require('./meetingDetailsForm.js'); module.exports = { icon: UI.icons.iconBase + 'noun_66617.svg', name: 'meeting', audience: [ns.solid('PowerUser')], label: function label(subject, context) { var kb = context.session.store; var ns = UI.ns; if (kb.holds(subject, ns.rdf('type'), ns.meeting('Meeting'))) { return 'Meeting'; } return null; // Suppress pane otherwise }, // Create a new Meeting thing // // returns: A promise of a meeting object // mintClass: UI.ns.meeting('Meeting'), mintNew: function mintNew(context, options) { return new Promise(function (resolve, reject) { var kb = context.session.store; var ns = UI.ns; options.newInstance = options.newInstance || kb.sym(options.newBase + 'index.ttl#this'); var meeting = options.newInstance; var meetingDoc = meeting.doc(); var me = logic.authn.currentUser(); if (me) { kb.add(meeting, ns.dc('author'), me, meetingDoc); } kb.add(meeting, ns.rdf('type'), ns.meeting('Meeting'), meetingDoc); kb.add(meeting, ns.dc('created'), new Date(), meetingDoc); kb.add(meeting, ns.ui('backgroundColor'), new $rdf.Literal('#ddddcc', undefined, ns.xsd('color')), meetingDoc); var toolList = new $rdf.Collection(); kb.add(meeting, ns.meeting('toolList'), toolList, meetingDoc); toolList.elements.push(meeting); // Add the meeting itself - see renderMain() kb.updater.put(meetingDoc, kb.statementsMatching(undefined, undefined, undefined, meetingDoc), 'text/turtle', function (uri2, ok, message) { if (ok) { resolve(options); } else { reject(new Error('Error writing meeting configuration: ' + message)); } }); }); }, // Returns a div render: function render(subject, dataBrowserContext) { var dom = dataBrowserContext.dom; var kb = dataBrowserContext.session.store; var ns = UI.ns; var updater = kb.updater; var thisPane = this; var complain = function complain(message, color) { console.log(message); var pre = dom.createElement('pre'); pre.setAttribute('style', 'background-color: ' + color || '#eed' + ';'); div.appendChild(pre); pre.appendChild(dom.createTextNode(message)); }; var complainIfBad = function complainIfBad(ok, message) { if (!ok) complain(message); }; var meeting = subject; var meetingDoc = subject.doc(); var meetingBase = subject.dir().uri; var div = dom.createElement('div'); var table = div.appendChild(dom.createElement('table')); table.style = 'width: 100%; height: 100%; margin:0;'; var topTR = table.appendChild(dom.createElement('tr')); topTR.appendChild(dom.createElement('div')); // topDiv var mainTR = table.appendChild(dom.createElement('tr')); var toolBar0 = table.appendChild(dom.createElement('td')); var toolBar1 = toolBar0.appendChild(dom.createElement('table')); var toolBar = toolBar1.appendChild(dom.createElement('tr')); topTR.setAttribute('style', 'height: 2em;'); // spacer if notthing else var me = null; // @@ Put code to find out logged in person var saveBackMeetingDoc = function saveBackMeetingDoc() { updater.put(meetingDoc, kb.statementsMatching(undefined, undefined, undefined, meetingDoc), 'text/turtle', function (uri2, ok, message) { if (ok) { tabs.refresh(); resetTools(); } else { message = 'FAILED to save new thing at: ' + meetingDoc + ' : ' + message; complain(message); } }); }; var saveAppDocumentLinkAndAddNewThing = function saveAppDocumentLinkAndAddNewThing(tool, thing, pred) { var appDoc = thing.doc(); if (pred) { kb.add(meeting, pred, thing, appDoc); // Specific Link back to meeting } kb.add(thing, ns.meeting('parentMeeting'), meeting, appDoc); // Generic link back to meeting updater.put(appDoc, kb.statementsMatching(undefined, undefined, undefined, appDoc), 'text/turtle', function (uri2, ok, message) { if (ok) { saveBackMeetingDoc(); } else { complain('FAILED to save new tool at: ' + thing + ' : ' + message); } }); }; var makeToolNode = function makeToolNode(target, pred, label, iconURI) { if (pred) { kb.add(meeting, pred, target, meetingDoc); } var x = UI.widgets.newThing(meetingDoc); if (label) kb.add(x, ns.rdfs('label'), label, meetingDoc); if (iconURI) kb.add(x, ns.meeting('icon'), kb.sym(iconURI), meetingDoc); kb.add(x, ns.rdf('type'), ns.meeting('Tool'), meetingDoc); kb.add(x, ns.meeting('target'), target, meetingDoc); var toolList = kb.the(meeting, ns.meeting('toolList')); toolList.elements.push(x); return x; }; // Map from end-user non-iframeable Google maps URI to G Maps API // Input: like https://www.google.co.uk/maps/place/Mastercard/@53.2717971,-6.2042699,17z/... // Output: function googleMapsSpecial(page) { var initialPrefix = /https:\/\/www\.google\..*\/maps\//; var finalPrefix = 'https://www.google.com/maps/embed/v1/'; var myPersonalApiKEY = 'AIzaSyB8aaT6bY9tcLCmc2oPCkdUYLmTOWM8R54'; // Get your own key! // GET YOUR KEY AT https://developers.google.com/maps/documentation/javascript/ var uri = page.uri; if (!uri.match(initialPrefix)) return page; if (uri.startsWith(finalPrefix)) return page; // Already done var map = uri.replace(initialPrefix, finalPrefix) + '&key=' + myPersonalApiKEY; console.log('Converted Google Map URI! ' + map); return $rdf.sym(map); } // //////////////////// DRAG and Drop var handleDroppedThing = function handleDroppedThing(target) { // @@ idea: look return new Promise(function (resolve) { // Add a meeting tab for a web resource. Alas many resource canot be framed // as they block framing, or are insecure. var addIframeTool = function addIframeTool(target) { var tool = makeToolNode(target, UI.ns.wf('attachment'), UI.utils.label(target), null); kb.add(tool, UI.ns.meeting('view'), 'iframe', meetingDoc); }; var addLink = function addLink(target) { var pred = ns.wf('attachment'); kb.add(subject, pred, target, subject.doc()); var toolObject = { icon: 'noun_160581.svg', // right arrow "link" limit: 1, shareTab: true // but many things behind it }; var newPaneOptions = { newInstance: subject, // kb.sym(subject.doc().uri + '#LinkListTool'), pane: dataBrowserContext.session.paneRegistry.byName('link'), // the pane to be used to mint a new thing predicate: ns.meeting('attachmentTool'), tabTitle: 'Links', view: 'link', // The pane to be used when it is viewed noIndexHTML: true }; return makeNewPaneTool(toolObject, newPaneOptions); }; // When paerson added to he meeting, make an ad hoc group // of meeting participants is one does not already exist, and add them var addParticipant = function addParticipant(target) { var pref = kb.any(target, ns.foaf('preferredURI')); var obj = pref ? kb.sym(pref) : target; var group = kb.any(meeting, ns.meeting('attendeeGroup')); var addPersonToGroup = function addPersonToGroup(obj, group) { var ins = [$rdf.st(group, UI.ns.vcard('hasMember'), obj, group.doc())]; // @@@ Complex rules about webid? var name = kb.any(obj, ns.vcard('fn')) || kb.any(obj, ns.foaf('name')); if (name) { ins.push($rdf.st(obj, UI.ns.vcard('fn'), name, group.doc())); } kb.fetcher.nowOrWhenFetched(group.doc(), undefined, function (ok, _body) { if (!ok) { complain("Can't read group to add person" + group); return; } kb.updater.update([], ins, function (uri, ok, body) { complainIfBad(ok, body); if (ok) { console.log('Addded to particpants OK: ' + obj); } }); }); }; if (group) { addPersonToGroup(obj, group); return; } makeParticipantsGroup().then(function (options) { var group = options.newInstance; addPersonToGroup(obj, group); kb.fetcher.putBack(meetingDoc, { contentType: 'text/turtle' }).then(function (_xhr) { console.log('Particiants Group created: ' + group); }); })["catch"](function (err) { complain(err); }); }; console.log('Dropped on thing ' + target); // icon was: UI.icons.iconBase + 'noun_25830.svg' var u = target.uri; if (u.startsWith('http:') && u.indexOf('#') < 0) { // insecure Plain document addLink(target); return resolve(target); } kb.fetcher.nowOrWhenFetched(target, function (ok, mess) { function addAttachmentTab(target) { target = googleMapsSpecial(target); console.log('make web page attachement tab ' + target); // icon was: UI.icons.iconBase + 'noun_25830.svg' var tool = makeToolNode(target, UI.ns.wf('attachment'), UI.utils.label(target), null); kb.add(tool, UI.ns.meeting('view'), 'iframe', meetingDoc); return resolve(target); } if (!ok) { console.log('Error looking up dropped thing, will just add it anyway. ' + target + ': ' + mess); return addAttachmentTab(target); // You can still try iframing it. (Could also add to list of links in PersonTR widgets) } else { var obj = target; var types = kb.findTypeURIs(obj); for (var ty in types) { console.log(' drop object type includes: ' + ty); } if (ns.vcard('Individual').uri in types || ns.foaf('Person').uri in types || ns.foaf('Agent').uri in types) { addParticipant(target); return resolve(target); } if (u.startsWith('https:') && u.indexOf('#') < 0) { // Plain secure document // can we iframe it? var hh = kb.fetcher.getHeader(target, 'x-frame-options'); var ok2 = true; if (hh) { for (var j = 0; j < hh.length; j++) { console.log('x-frame-options: ' + hh[j]); if (hh[j].indexOf('sameorigin') < 0) { // (and diff origin @@) ok2 = false; } if (hh[j].indexOf('deny') < 0) { ok2 = false; } } } if (ok2) { target = googleMapsSpecial(target); // tweak Google maps to embed OK addIframeTool(target); // Something we can maybe iframe return resolve(target); } } // Something we cannot iframe, and must link to: console.log('Default: assume web page attachement ' + target); // icon was: UI.icons.iconBase + 'noun_25830.svg' return addAttachmentTab(target); } }); }); // promise }; // When a set of URIs are dropped on the tabs var droppedURIHandler = function droppedURIHandler(uris) { Promise.all(uris.map(function (u) { var target = $rdf.sym(u); // Attachment needs text label to disinguish I think not icon. return handleDroppedThing(target); // can add to meetingDoc but must be sync })).then(function (_a) { saveBackMeetingDoc(); }); }; var droppedFileHandler = function droppedFileHandler(files) { UI.widgets.uploadFiles(kb.fetcher, files, meeting.dir().uri + 'Files', meeting.dir().uri + 'Pictures', function (theFile, _destURI) { if (theFile.type.startsWith('image/')) { makePicturesFolder('Files'); // If necessary } else { makeMaterialsFolder('Pictures'); } }); }; // ////////////////////////////////////////////////////// end of drag drop var makeGroup = function makeGroup(_toolObject) { var newBase = meetingBase + 'Group/'; var kb = dataBrowserContext.session.store; var group = kb.any(meeting, ns.meeting('particpants')); if (!group) { group = $rdf.sym(newBase + 'index.ttl#this'); } console.log('Participant group: ' + group); var tool = makeToolNode(group, ns.meeting('particpants'), 'Particpants', UI.icons.iconBase + 'noun_339237.svg'); // group: noun_339237.svg 'noun_15695.svg' kb.add(tool, UI.ns.meeting('view'), 'peoplePicker', meetingDoc); saveBackMeetingDoc(); }; /* var makeAddressBook = function (toolObject) { var newBase = meetingBase + 'Group/' var kb = store var group = kb.any(meeting, ns.meeting('addressBook')) if (!group) { group = $rdf.sym(newBase + 'index.ttl#this') } // Create a tab for the addressbook var div = dom.createElement('div') var context = { dom: dom, div: div } var book UI.login.findAppInstances(context, ns.vcard('AddressBook')).then( function (context) { if (context.instances.length === 0) { complain('You have no solid address book. It is really handy to have one to keep track of people and groups') } else if (context.instances.length > 1) { var s = context.instances.map(function (x) { return '' + x }).join(', ') complain('You have more than one solid address book: ' + s + ' Not supported yet.') } else { // addressbook book = context.instances[0] var tool = makeToolNode(book, ns.meeting('addressBook'), 'Address Book', UI.icons.iconBase + 'noun_15695.svg') // group: noun_339237.svg kb.add(tool, UI.ns.meeting('view'), 'contact', meetingDoc) saveBackMeetingDoc() } } ) } */ var makePoll = function makePoll(toolObject) { var newPaneOptions = { useExisting: meeting, // Regard the meeting as being the schedulable event itself. // newInstance: meeting, pane: dataBrowserContext.session.paneRegistry.byName('schedule'), view: 'schedule', // predicate: ns.meeting('schedulingPoll'), // newBase: meetingBase + 'Schedule/', Not needed as uses existing meeting tabTitle: 'Schedule poll', noIndexHTML: true }; return makeNewPaneTool(toolObject, newPaneOptions); }; var makePicturesFolder = function makePicturesFolder(folderName) { var toolObject = { icon: 'noun_598334.svg', // Slideshow @@ find a "picture" icon? limit: 1, shareTab: true // but many things behind it }; var newPaneOptions = { newInstance: kb.sym(meeting.dir().uri + folderName + '/'), pane: dataBrowserContext.session.paneRegistry.byName('folder'), // @@ slideshow?? predicate: ns.meeting('pictures'), shareTab: true, tabTitle: folderName, view: 'slideshow', noIndexHTML: true }; return makeNewPaneTool(toolObject, newPaneOptions); }; var makeMaterialsFolder = function makeMaterialsFolder(_folderName) { var toolObject = { icon: 'noun_681601.svg', // Document limit: 1, shareTab: true // but many things behind it }; var options = { newInstance: kb.sym(meeting.dir().uri + 'Files/'), pane: dataBrowserContext.session.paneRegistry.byName('folder'), predicate: ns.meeting('materialsFolder'), tabTitle: 'Materials', noIndexHTML: true }; return makeNewPaneTool(toolObject, options); }; var makeParticipantsGroup = function makeParticipantsGroup() { var toolObject = { icon: 'noun_339237.svg', // Group of people limit: 1, // Only one tab shareTab: true // but many things behind it }; var options = { newInstance: kb.sym(meeting.dir().uri + 'Attendees/index.ttl#this'), pane: dataBrowserContext.session.paneRegistry.byName('contact'), predicate: ns.meeting('attendeeGroup'), tabTitle: 'Attendees', instanceClass: ns.vcard('Group'), instanceName: UI.utils.label(subject) + ' attendees', noIndexHTML: true }; return makeNewPaneTool(toolObject, options); }; // Make Pad for notes of meeting var makePad = function makePad(toolObject) { var newPaneOptions = { newBase: meetingBase + 'SharedNotes/', predicate: UI.ns.meeting('sharedNotes'), tabTitle: 'Shared Notes', pane: dataBrowserContext.session.paneRegistry.byName('pad') }; return makeNewPaneTool(toolObject, newPaneOptions); }; // Make Sub-meeting of meeting var makeMeeting = function makeMeeting(toolObject) { UI.widgets.askName(dom, kb, parameterCell, ns.foaf('name'), UI.ns.meeting('Meeting')).then(function (name) { if (!name) { return resetTools(); } var URIsegment = encodeURIComponent(name); var options = { newBase: meetingBase + URIsegment + '/', // @@@ sanitize predicate: UI.ns.meeting('subMeeting'), tabTitle: name, pane: dataBrowserContext.session.paneRegistry.byName('meeting') }; return makeNewPaneTool(toolObject, options); })["catch"](function (e) { complain('Error making new sub-meeting: ' + e); }); }; // Returns promise of newPaneOptions // In: options. // me?, predicate, newInstance ?, newBase, instanceClass // out: options. the above plus // me, newInstance function makeNewPaneTool(toolObject, options) { return new Promise(function (resolve, reject) { var kb = dataBrowserContext.session.store; if (!options.useExisting) { // useExisting means use existing object in new role var existing = kb.any(meeting, options.predicate); if (existing) { if (toolObject.limit && toolObject.limit === 1 && !toolObject.shareTab) { complain('Already have ' + existing + ' as ' + UI.utils.label(options.predicate)); complain('Cant have two'); return resolve(null); } if (toolObject.shareTab) { // return existing one console.log('Using existing ' + existing + ' as ' + UI.utils.label(options.predicate)); return resolve({ me: me, newInstance: existing, instanceClass: options.instanceClass }); } } } if (!me && !options.me) { reject(new Error('Username not defined for new tool')); } options.me = options.me || me; options.newInstance = options.useExisting || options.newInstance || kb.sym(options.newBase + 'index.ttl#this'); options.pane.mintNew(dataBrowserContext, options).then(function (options) { var tool = makeToolNode(options.newInstance, options.predicate, options.tabTitle, options.pane.icon); if (options.view) { kb.add(tool, UI.ns.meeting('view'), options.view, meetingDoc); } saveBackMeetingDoc(); kb.fetcher.putBack(meetingDoc, { contentType: 'text/turtle' }).then(function (_xhr) { resolve(options); })["catch"](function (err) { reject(err); }); })["catch"](function (err) { complain(err); reject(err); }); }); } var makeAgenda = function makeAgenda(_toolObject) { // selectTool(icon) }; var makeActions = function makeActions(_toolObject) { var newBase = meetingBase + 'Actions/'; var kb = dataBrowserContext.session.store; if (kb.holds(meeting, ns.meeting('actions'))) { console.log('Ignored - already have actions'); return; // already got one } var appDoc = kb.sym(newBase + 'config.ttl'); var newInstance = kb.sym(newBase + 'config.ttl#this'); var stateStore = kb.sym(newBase + 'state.ttl'); kb.add(newInstance, ns.dc('title'), (kb.anyValue(meeting, ns.cal('summary')) || 'Meeting ') + ' actions', appDoc); kb.add(newInstance, ns.wf('issueClass'), ns.wf('Task'), appDoc); kb.add(newInstance, ns.wf('initialState'), ns.wf('Open'), appDoc); kb.add(newInstance, ns.wf('stateStore'), stateStore, appDoc); kb.add(newInstance, ns.wf('assigneeClass'), ns.foaf('Person'), appDoc); // @@ set to people in the meeting? kb.add(newInstance, ns.rdf('type'), ns.wf('Tracker'), appDoc); // Flag its type in the chat itself as well as in the master meeting config file kb.add(newInstance, ns.rdf('type'), ns.wf('Tracker'), appDoc); var tool = makeToolNode(newInstance, ns.meeting('actions'), 'Actions', UI.icons.iconBase + 'noun_17020.svg'); saveAppDocumentLinkAndAddNewThing(tool, newInstance, ns.meeting('actions')); }; var makeChat = function makeChat(_toolObject) { var newBase = meetingBase + 'Chat/'; var kb = dataBrowserContext.session.store; if (kb.holds(meeting, ns.meeting('chat'))) { console.log('Ignored - already have chat'); return; // already got one } var messageStore = kb.sym(newBase + 'chat.ttl'); kb.add(messageStore, ns.rdf('type'), ns.meeting('Chat'), messageStore); var tool = makeToolNode(messageStore, ns.meeting('chat'), 'Chat', UI.icons.iconBase + 'noun_346319.svg'); saveAppDocumentLinkAndAddNewThing(tool, messageStore, ns.meeting('chat')); }; var makeVideoCall = function makeVideoCall(_toolObject) { var kb = dataBrowserContext.session.store; var newInstance = $rdf.sym(VideoRoomPrefix + UI.utils.genUuid()); if (kb.holds(meeting, ns.meeting('videoCallPage'))) { console.log('Ignored - already have a videoCallPage'); return; // already got one } kb.add(newInstance, ns.rdf('type'), ns.meeting('VideoCallPage'), meetingDoc); var tool = makeToolNode(newInstance, ns.meeting('videoCallPage'), 'Video call', UI.icons.iconBase + 'noun_260227.svg'); kb.add(tool, ns.meeting('view'), 'iframe', meetingDoc); saveBackMeetingDoc(); }; var makeAttachment = function makeAttachment(_toolObject) { UI.widgets.askName(dom, kb, parameterCell, ns.log('uri'), UI.ns.rdf('Resource')).then(function (uri) { if (!uri) { return resetTools(); } var kb = dataBrowserContext.session.store; var ns = UI.ns; var target = kb.sym(uri); var tool = makeToolNode(target, ns.wf('attachment'), UI.utils.label(target), null); kb.add(tool, ns.meeting('view'), 'iframe', meetingDoc); saveBackMeetingDoc(); })["catch"](function (e) { complain('Error making new sub-meeting: ' + e); }); }; var makeSharing = function makeSharing(toolObject) { var kb = dataBrowserContext.session.store; var ns = UI.ns; var target = meeting.dir(); if (toolObject.limit && toolObject.limit === 1 && kb.holds(meeting, ns.wf('sharingControl'))) { complain('Ignored - already have ' + UI.utils.label(options.predicate)); return; } var tool = makeToolNode(target, ns.wf('sharingControl'), 'Sharing', UI.icons.iconBase + 'noun_123691.svg'); kb.add(tool, ns.meeting('view'), 'sharing', meetingDoc); saveBackMeetingDoc(); }; var makeNewMeeting = function makeNewMeeting() { // @@@ make option of continuing series var appDetails = { noun: 'meeting' }; var gotWS = function gotWS(ws, base) { thisPane.mintNew(dataBrowserContext, { newBase: base }).then(function (options) { var newInstance = options.newInstance; parameterCell.removeChild(mintUI); var p = parameterCell.appendChild(dom.createElement('p')); p.setAttribute('style', 'font-size: 140%;'); p.innerHTML = "Your <a target='_blank' href='" + newInstance.uri + "'><b>new meeting</b></a> is ready to be set up. " + "<br/><br/><a target='_blank' href='" + newInstance.uri + "'>Go to your new meeting.</a>"; })["catch"](function (err) { parameterCell.removeChild(mintUI); parameterCell.appendChild(UI.widgets.errorMessageBlock(dom, err)); }); }; var mintUI = UI.login.selectWorkspace(dom, appDetails, gotWS); parameterCell.appendChild(mintUI); }; // //////////////////////////////////////////////////////////// end of new tab creation functions var toolIcons = [{ icon: 'noun_339237.svg', maker: makeGroup, hint: 'Make a group of people', limit: 1 }, { icon: 'noun_346777.svg', maker: makePoll, hint: 'Make a poll to schedule the meeting' }, // When meet THIS or NEXT time { icon: 'noun_48218.svg', maker: makeAgenda, limit: 1, hint: 'Add an agenda list', disabled: true }, // When meet THIS or NEXT time { icon: 'noun_79217.svg', maker: makePad, hint: 'Add a shared notepad' }, { icon: 'noun_346319.svg', maker: makeChat, limit: 1, hint: 'Add a chat channel for the meeting' }, { icon: 'noun_17020.svg', maker: makeActions, limit: 1, hint: 'Add a list of action items' }, // When meet THIS or NEXT time { icon: 'noun_260227.svg', maker: makeVideoCall, limit: 1, hint: 'Add a video call for the meeting' }, { icon: 'noun_25830.svg', maker: makeAttachment, hint: 'Attach meeting materials', disabled: false }, { icon: 'noun_123691.svg', maker: makeSharing, limit: 1, hint: 'Control Sharing', disabled: false }, { icon: 'noun_66617.svg', maker: makeMeeting, hint: 'Make a sub meeting', disabled: false }]; // 'noun_66617.svg' var settingsForm = $rdf.sym('https://solid.github.io/solid-panes/meeting/meetingDetailsForm.ttl#settings'); $rdf.parse(meetingDetailsFormText, kb, settingsForm.doc().uri, 'text/turtle'); // Load form directly var iconStyle = 'padding: 1em; width: 3em; height: 3em;'; var iconCell = toolBar.appendChild(dom.createElement('td')); var parameterCell = toolBar.appendChild(dom.createElement('td')); var star = iconCell.appendChild(dom.createElement('img')); var visible = false; // the inividual tools tools star.setAttribute('src', UI.icons.iconBase + 'noun_19460_green.svg'); // noun_272948.svg star.setAttribute('style', iconStyle + 'opacity: 50%;'); star.setAttribute('title', 'Add another tool to the meeting'); var selectNewTool = function selectNewTool(_event) { visible = !visible; star.setAttribute('style', iconStyle + (visible ? 'background-color: yellow;' : '')); styleTheIcons(visible ? '' : 'display: none;'); }; var loginOutButton; logic.authn.checkUser().then(function (webId) { if (webId) { me = webId; star.addEventListener('click', selectNewTool); star.setAttribute('style', iconStyle); return; } loginOutButton = UI.login.loginStatusBox(dom, function (webIdUri) { if (webIdUri) { me = kb.sym(webIdUri); parameterCell.removeChild(loginOutButton); // loginOutButton.setAttribute('',iconStyle) // make it match the icons star.addEventListener('click', selectNewTool); star.setAttribute('style', iconStyle); } else { console.log('(Logged out)'); me = null; } }); loginOutButton.setAttribute('style', 'margin: 0.5em 1em;'); parameterCell.appendChild(loginOutButton); }); var iconArray = []; for (var i = 0; i < toolIcons.length; i++) { var foo = function foo() { var toolObject = toolIcons[i]; var icon = iconCell.appendChild(dom.createElement('img')); icon.setAttribute('src', UI.icons.iconBase + toolObject.icon); icon.setAttribute('style', iconStyle + 'display: none;'); iconArray.push(icon); icon.tool = toolObject; var maker = toolObject.maker; if (!toolObject.disabled) { icon.addEventListener('click', function (_event) { selectTool(icon); maker(toolObject); }); } }; foo(); } var styleTheIcons = function styleTheIcons(style) { for (var i = 0; i < iconArray.length; i++) { var st = iconStyle + style; if (toolIcons[i].disabled) { st += 'opacity: 0.3;'; } iconArray[i].setAttribute('style', st); // eg 'background-color: #ccc;' } }; var resetTools = function resetTools() { styleTheIcons('display: none;'); star.setAttribute('style', iconStyle); }; var selectTool = function selectTool(icon) { styleTheIcons('display: none;'); // 'background-color: #ccc;' icon.setAttribute('style', iconStyle + 'background-color: yellow;'); }; // ////////////////////////////// var renderTab = function renderTab(div, item) { if (kb.holds(item, ns.rdf('type'), ns.meeting('Tool'))) { var target = kb.any(item, ns.meeting('target')); var label = kb.any(item, ns.rdfs('label')); label = label ? label.value : UI.utils.label(target); var s = div.appendChild(dom.createElement('div')); s.textContent = label; s.setAttribute('style', 'margin-left: 0.7em'); var icon = kb.any(item, ns.meeting('icon')); if (icon) { // Make sure the icon is cleanly on the left of the label var table = div.appendChild(dom.createElement('table')); var tr = table.appendChild(dom.createElement('tr')); var left = tr.appendChild(dom.createElement('td')); var right = tr.appendChild(dom.createElement('td')); // var img = div.appendChild(dom.createElement('img')) var img = left.appendChild(dom.createElement('img')); img.setAttribute('src', icon.uri); // img.setAttribute('style', 'max-width: 1.5em; max-height: 1.5em;') // @@ SVG shrinks to 0 img.setAttribute('style', 'width: 1.5em; height: 1.5em;'); // @ img.setAttribute('title', label); right.appendChild(s); } else { div.appendChild(s); } } else { div.textContent = UI.utils.label(item); } }; var tipDiv = function tipDiv(text) { var d = dom.createElement('div'); var p = d.appendChild(dom.createElement('p')); p.setAttribute('style', 'margin: 0em; padding:3em; color: #888;'); p.textContent = 'Tip: ' + text; return d; }; var renderTabSettings = function renderTabSettings(containerDiv, subject) { containerDiv.innerHTML = ''; containerDiv.style += 'border-color: #eed;'; containerDiv.appendChild(dom.createElement('h3')).textContent = 'Adjust this tab'; if (kb.holds(subject, ns.rdf('type'), ns.meeting('Tool'))) { var form = $rdf.sym('https://solid.github.io/solid-panes/meeting/meetingDetailsForm.ttl#settings'); UI.widgets.appendForm(document, containerDiv, {}, subject, form, meeting.doc(), complainIfBad); var delButton = UI.widgets.deleteButtonWithCheck(dom, containerDiv, 'tab', function () { var toolList = kb.the(meeting, ns.meeting('toolList')); for (var i = 0; i < toolList.elements.length; i++) { if (toolList.elements[i].sameTerm(subject)) { toolList.elements.splice(i, 1); break; } } var target = kb.any(subject, ns.meeting('target')); var ds = kb.statementsMatching(subject).concat(kb.statementsMatching(undefined, undefined, subject)).concat(kb.statementsMatching(meeting, undefined, target)); kb.remove(ds); // Remove all links to and from the tab node saveBackMeetingDoc(); }); delButton.setAttribute('style', 'width: 1.5em; height: 1.5em;'); // delButton.setAttribute('class', '') // delButton.setAttribute('style', 'height: 2em; width: 2em; margin: 1em; border-radius: 0.5em; padding: 1em; font-size: 120%; background-color: red; color: white;') // delButton.textContent = 'Delete this tab' } else { containerDiv.appendChild(dom.createElement('h4')).textContent = '(No adjustments available)'; } }; var renderMain = function renderMain(containerDiv, subject) { var pane = null; var table; var selectedGroup = null; containerDiv.innerHTML = ''; var complainIfBad = function complainIfBad(ok, message) { if (!ok) { containerDiv.textContent = '' + message; } }; var showIframe = function showIframe(target) { var iframe = containerDiv.appendChild(dom.createElement('iframe')); // iframe.setAttribute('sandbox', '') // All restrictions iframe.setAttribute('src', target.uri); // See https://stackoverflow.com/questions/325273/make-iframe-to-fit-100-of-containers-remaining-height // Set the container position (sic) so it becaomes a 100% reference for the size of the iframe height 100% /* For now at least , leave the container style as set by the tab system. 20200115b containerDiv.setAttribute( 'style', 'position: relative; top: 0px; left:0px; right:0px; resize: both; overflow:scroll; min-width: 30em; min-height: 30em;' ) */ // iframe.setAttribute('style', 'height: 350px; border: 0; margin: 0; padding: 0; resize:both; overflow:scroll; width: 100%;') // iframe.setAttribute('style', 'border: none; margin: 0; padding: 0; height: 100%; width: 100%; resize: both; overflow:scroll;') iframe.setAttribute('style', 'border: none; margin: 0; padding: 0; height: 100%; width: 100%;'); // Following https://dev.chromium.org/Home/chromium-security/deprecating-permissions-in-cross-origin-iframes : iframe.setAttribute('allow', 'microphone camera'); // Allow iframe to request camera and mic // containerDiv.style.resize = 'none' // Remove scroll bars on outer div - don't seem to work so well iframe.setAttribute('name', 'disable-x-frame-options'); // For electron: see https://github.com/electron/electron/pull/573 containerDiv.style.padding = 0; }; var renderPeoplePicker = function renderPeoplePicker() { var context = { div: containerDiv, dom: dom }; containerDiv.appendChild(dom.createElement('h4')).textContent = 'Meeting Participants'; var groupPickedCb = function groupPickedCb(group) { var toIns = [$rdf.st(meeting, ns.meeting('particpantGroup'), group, meeting.doc())]; kb.updater.update([], toIns, function (uri, ok, message) { if (ok) { selectedGroup = group; } else { complain('Cant save participants group: ' + message); } }); }; selectedGroup = kb.any(meeting, ns.meeting('particpantGroup')); logic.loadTypeIndexes(context).then(function () { // Assumes that the type index has an entry for addressbook var options = { defaultNewGroupName: 'Meeting Participants', selectedGroup: selectedGroup }; var picker = new UI.widgets.PeoplePicker(context.div, context.index["private"][0], groupPickedCb, options); picker.render(); }); }; var renderDetails = function renderDetails() { containerDiv.appendChild(dom.createElement('h3')).textContent = 'Details of meeting'; var form = $rdf.sym('https://solid.github.io/solid-panes/meeting/meetingDetailsForm.ttl#main'); UI.widgets.appendForm(document, containerDiv, {}, meeting, form, meeting.doc(), complainIfBad); containerDiv.appendChild(tipDiv('Drag URL-bar icons of web pages into the tab bar on the left to add new meeting materials.')); me = logic.authn.currentUser(); if (me) { kb.add(meeting, ns.dc('author'), me, meetingDoc); // @@ should nly be on initial creation? } var context = { noun: 'meeting', me: me, statusArea: containerDiv, div: containerDiv, dom: dom }; UI.login.registrationControl(context, meeting, ns.meeting('Meeting')).then(function (_context) { console.log('Registration control finsished.'); }); var options = {}; UI.pad.manageParticipation(dom, containerDiv, meetingDoc, meeting, me, options); // "Make a new meeting" button var imageStyle = 'height: 2em; width: 2em; margin:0.5em;'; var detailsBottom = containerDiv.appendChild(dom.createElement('div')); var spawn = detailsBottom.appendChild(dom.createElement('img')); spawn.setAttribute('src', UI.icons.iconBase + 'noun_145978.svg'); spawn.setAttribute('title', 'Make a fresh new meeting'); spawn.addEventListener('click', makeNewMeeting); spawn.setAttribute('style', imageStyle); // "Fork me on Github" button var forka = detailsBottom.appendChild(dom.createElement('a')); forka.setAttribute('href', 'https://github.com/solid/solid-panes'); // @@ Move when code moves forka.setAttribute('target', '_blank'); var fork = forka.appendChild(dom.createElement('img')); fork.setAttribute('src', UI.icons.iconBase + 'noun_368567.svg'); fork.setAttribute('title', 'Fork me on github'); fork.setAttribute('style', imageStyle + 'opacity: 50%;'); }; if (kb.holds(subject, ns.rdf('type'), ns.meeting('Tool'))) { var target = kb.any(subject, ns.meeting('target')); if (target.sameTerm(meeting) && !kb.any(subject, ns.meeting('view'))) { // self reference? force details form renderDetails(); // Legacy meeting instances } else { var view = kb.any(subject, ns.meeting('view')); view = view ? view.value : null; if (view === 'details') { renderDetails(); } else if (view === 'peoplePicker') { renderPeoplePicker(); } else if (view === 'iframe') { showIframe(target); } else { pane = view ? dataBrowserContext.session.paneRegistry.byName(view) : null; table = containerDiv.appendChild(dom.createElement('table')); table.style.width = '100%'; dataBrowserContext.getOutliner(dom).GotoSubject(target, true, pane, false, undefined, table); } } } else if (subject.sameTerm(meeting)) { // self reference? force details form renderDetails(); } else if (subject.sameTerm(subject.doc()) && !kb.holds(subject, UI.ns.rdf('type'), UI.ns.meeting('Chat')) && !kb.holds(subject, UI.ns.rdf('type'), UI.ns.meeting('PaneView')) // eslint-disable-next-line no-empty ) {} else { table = containerDiv.appendChild(dom.createElement('table')); dataBrowserContext.getOutliner(dom).GotoSubject(subject, true, undefined, false, undefined, table); } }; var options = { dom: dom }; options.predicate = ns.meeting('toolList'); options.subject = subject; options.ordered = true; options.orientation = 1; // tabs on LHS options.renderMain = renderMain; options.renderTab = renderTab; options.renderTabSettings = renderTabSettings; options.backgroundColor = kb.anyValue(subject, ns.ui('backgroundColor')) || '#ddddcc'; var tabs = mainTR.appendChild(UI.tabs.tabWidget(options)); UI.aclControl.preventBrowserDropEvents(dom); UI.widgets.makeDropTarget(tabs.tabContainer, droppedURIHandler, droppedFileHandler); UI.widgets.makeDropTarget(iconCell, droppedURIHandler, droppedFileHandler); return div; } }; // ends //# sourceMappingURL=meetingPane.js.map