/* global $rdf */ /* Tools for doing things with a message * Let is be cretiev here. Allow all sorts of things to * be done to a message - linking to new or old objects in an open way * * Ideas: Bookmark, Like, star, pin at top of chat, reply as new thread, * If you made it originally: edit, delete, attach */ const UI = { authn: require('../authn/authn'), icons: require('../iconBase'), log: require('../log'), ns: require('../ns'), media: require('../media-capture'), pad: require('../pad'), rdf: require('rdflib'), store: require('../store'), style: require('../style'), utils: require('../utils'), widgets: require('../widgets') } const bookmarks = require('./bookmarks') const dom = window.document const kb = UI.store const ns = UI.ns // const label = UI.utils.label // THE UNUSED ICONS are here as reminders for possible future functionality // const BOOKMARK_ICON = 'noun_45961.svg' // const HEART_ICON = 'noun_130259.svg' -> Add this to my (private) favorites // const MENU_ICON = 'noun_897914.svg' // const PAPERCLIP_ICON = 'noun_25830.svg' -> add attachments to this message // const PIN_ICON = 'noun_562340.svg' -> pin this message permanently in the chat UI // const PENCIL_ICON = 'noun_253504.svg' // const SPANNER_ICON = 'noun_344563.svg' -> settings const THUMBS_UP_ICON = 'noun_1384132.svg' const THUMBS_DOWN_ICON = 'noun_1384135.svg' // module.export = { messageTools, sentimentStripLinked, sentimentStrip } // @@@@ use the one in rdflib.js when it is avaiable and delete this function updatePromise (del, ins) { return new Promise(function (resolve, reject) { kb.updater.update(del, ins, function (uri, ok, errorBody) { if (!ok) { reject(new Error(errorBody)) } else { resolve() } }) // callback }) // promise } /* Emoji in Unicode */ var emoji = {} emoji[ns.schema('AgreeAction')] = '👍' emoji[ns.schema('DisagreeAction')] = '👎' emoji[ns.schema('EndorseAction')] = '⭐️' emoji[ns.schema('LikeAction')] = '❤️' /* Strip of sentiments expressed */ export function sentimentStrip (target, doc) { const actions = kb.each(null, ns.schema('target'), target, doc) const sentiments = actions.map(a => kb.any(a, ns.rdf('type'), null, doc)) sentiments.sort() const strings = sentiments.map(x => emoji[x] || '') return dom.createTextNode(strings.join(' ')) } /** Strip of sentiments expressed * * @param target {NamedNode} - The thing about which they are expressed * @param doc {NamedNode} - The document iun which they are expressed */ export function sentimentStripLinked (target, doc) { var strip = dom.createElement('span') function refresh () { strip.innerHTML = '' const actions = kb.each(null, ns.schema('target'), target, doc) const sentiments = actions.map(a => [ kb.any(a, ns.rdf('type'), null, doc), kb.any(a, ns.schema('agent'), null, doc) ]) sentiments.sort() sentiments.forEach(ss => { const [klass, agent] = ss var res if (agent) { res = dom.createElement('a') res.setAttribute('href', agent.uri) } else { res = dom.createTextNode('') } res.textContent = emoji[klass] || '*' strip.appendChild(res) }) } refresh() strip.refresh = refresh return strip } export function messageToolbar (message, messageRow, userContext) { const div = dom.createElement('div') function closeToolbar () { div.parentElement.parentElement.removeChild(div.parentElement) // remive the TR } async function deleteThingThen (x) { await updatePromise(kb.connectedStatements(x), []) } // Things only the original author can do let me = UI.authn.currentUser() // If already logged on if (me && kb.holds(message, ns.foaf('maker'), me)) { // button to delete the message const deleteButton = UI.widgets.deleteButtonWithCheck( dom, div, 'message', async function () { await kb.updater.update(kb.connectedStatements[message], []) closeToolbar() } ) div.appendChild(deleteButton) } // if mine // Things anyone can do if they have a bookmark list async /* var bookmarkButton = await bookmarks.renderBookmarksButton(userContext) if (bookmarkButton) { div.appendChild(bookmarkButton) } */ // Things anyone can do if they have a bookmark list bookmarks.renderBookmarksButton(userContext).then(bookmarkButton => { if (bookmarkButton) div.appendChild(bookmarkButton) }) /** Button to allow user to express a sentiment (like, endorse, etc) about a target * * @param context {Object} - Provide dom and me * @param target {NamedNode} - The thing the user expresses an opnion about * @param icon {uristring} - The icon to be used for the button * @param actionClass {NamedNode} - The RDF class - typically a subclass of schema:Action * @param doc - {NamedNode} - the Solid document iunto which the data should be written * @param mutuallyExclusive {Array} - Any RDF classes of sentimentswhich are mutiually exclusive */ function sentimentButton ( context, target, icon, actionClass, doc, mutuallyExclusive ) { function setColor () { button.style.backgroundColor = action ? 'yellow' : 'white' } var button = UI.widgets.button( dom, icon, UI.utils.label(actionClass), async function (_event) { if (action) { await deleteThingThen(action) action = null setColor() } else { // no action action = UI.widgets.newThing(doc) var insertMe = [ $rdf.st(action, ns.schema('agent'), context.me, doc), $rdf.st(action, ns.rdf('type'), actionClass, doc), $rdf.st(action, ns.schema('target'), target, doc) ] await updatePromise([], insertMe) setColor() if (mutuallyExclusive) { // Delete incompative sentiments var dirty = false for (let i = 0; i < mutuallyExclusive.length; i++) { const a = existingAction(mutuallyExclusive[i]) if (a) { await deleteThingThen(a) // but how refresh? refreshTree the parent? dirty = true } } if (dirty) { // UI.widgets.refreshTree(button.parentNode) // requires them all to be immediate siblings UI.widgets.refreshTree(messageRow) // requires them all to be immediate siblings } } } } ) function existingAction (actionClass) { var actions = kb .each(null, ns.schema('agent'), context.me, doc) .filter(x => kb.holds(x, ns.rdf('type'), actionClass, doc)) .filter(x => kb.holds(x, ns.schema('target'), target, doc)) return actions.length ? actions[0] : null } function refresh () { action = existingAction(actionClass) setColor() } var action button.refresh = refresh // If the file changes, refresh live refresh() return button } // THUMBS_UP_ICON // https://schema.org/AgreeAction me = UI.authn.currentUser() // If already logged on if (me) { // Things yo mnust be logged in fo var context1 = { me, dom, div } div.appendChild( sentimentButton( context1, message, // @@ use UI.widgets.sentimentButton UI.icons.iconBase + THUMBS_UP_ICON, ns.schema('AgreeAction'), message.doc(), [ns.schema('DisagreeAction')] ) ) // Thumbs down div.appendChild( sentimentButton( context1, message, UI.icons.iconBase + THUMBS_DOWN_ICON, ns.schema('DisagreeAction'), message.doc(), [ns.schema('AgreeAction')] ) ) } // X button to remove the tool UI itself const cancelButton = div.appendChild(UI.widgets.cancelButton(dom)) cancelButton.style.float = 'right' cancelButton.firstChild.style.opacity = '0.3' cancelButton.addEventListener('click', closeToolbar) return div }