diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/about.js | 59 | ||||
-rw-r--r-- | lib/app.js | 6 | ||||
-rw-r--r-- | lib/render-msg.js | 2 | ||||
-rw-r--r-- | lib/render.js | 2 | ||||
-rw-r--r-- | lib/serve.js | 108 | ||||
-rw-r--r-- | lib/util.js | 10 |
6 files changed, 162 insertions, 25 deletions
diff --git a/lib/about.js b/lib/about.js new file mode 100644 index 0000000..4f1d582 --- /dev/null +++ b/lib/about.js @@ -0,0 +1,59 @@ +var pull = require('pull-stream') +// var defer = require('pull-defer') +// var many = require('pull-many') +var multicb = require('multicb') +var u = require('./util') + +module.exports = About + +function About(sbot) { + if (!(this instanceof About)) return new About(sbot) + this.sbot = sbot +} + +About.prototype.createAboutOpStream = function (id) { + return pull( + this.sbot.links({dest: id, rel: 'about', values: true, reverse: true}), + pull.map(function (msg) { + var c = msg.value.content || {} + return Object.keys(c).filter(function (key) { + return key !== 'about' + && key !== 'type' + && key !== 'recps' + }).map(function (key) { + var value = c[key] + if (u.isRef(value)) value = {link: value} + return { + id: msg.key, + author: msg.value.author, + timestamp: msg.value.timestamp, + prop: key, + value: value, + remove: value && typeof value === 'object' && value.remove, + } + }) + }), + pull.flatten() + ) +} + +About.prototype.createAboutStreams = function (id) { + var ops = this.createAboutOpStream(id) + var scalars = {/* author: {prop: value} */} + var sets = {/* author: {prop: {link}} */} + + var setsDone = multicb({pluck: 1, spread: true}) + setsDone()(null, pull.values([])) + return { + scalars: pull( + ops, + pull.unique(function (op) { + return op.author + '-' + op.prop + '-' + (op.value ? op.value.link : '') + }), + pull.filter(function (op) { + return !op.remove + }) + ), + sets: u.readNext(setsDone) + } +} @@ -9,7 +9,7 @@ var hasher = require('pull-hash/ext/ssb') var multicb = require('multicb') var paramap = require('pull-paramap') var Contacts = require('ssb-contact') - +var About = require('./about') var Serve = require('./serve') var Render = require('./render') @@ -280,3 +280,7 @@ App.prototype.streamChannels = function (opts) { App.prototype.createContactStreams = function (id) { return new Contacts(this.sbot).createContactStreams(id) } + +App.prototype.createAboutStreams = function (id) { + return new About(this.sbot).createAboutStreams(id) +} diff --git a/lib/render-msg.js b/lib/render-msg.js index 2a0e7df..931b7a6 100644 --- a/lib/render-msg.js +++ b/lib/render-msg.js @@ -149,6 +149,8 @@ RenderMsg.prototype.actions = function () { this.msg.rel ? [this.msg.rel, ' '] : '', this.opts.withGt && this.msg.timestamp ? [ h('a', {href: '?gt=' + this.msg.timestamp}, '↓'), ' '] : '', + this.c.type === 'gathering' ? [ + h('a', {href: this.render.toUrl('/about/' + encodeURIComponent(this.msg.key))}, 'about'), ' '] : '', h('a', {href: this.toUrl(this.msg.key) + '?raw'}, 'raw'), ' ', this.voteFormInner('dig') ) : [ diff --git a/lib/render.js b/lib/render.js index 316ad2f..bc4ce5b 100644 --- a/lib/render.js +++ b/lib/render.js @@ -183,7 +183,7 @@ Render.prototype.prepareLink = function (link, cb) { if (link.name || !link.link) cb(null, link) else this.app.getAbout(link.link, function (err, about) { if (err) return cb(null, link) - link.name = about.name || (link.link.substr(0, 8) + '…') + link.name = about.name || about.title || (link.link.substr(0, 8) + '…') if (link.name && link.name[0] === link.link[0]) { link.name = link.name.substr(1) } diff --git a/lib/serve.js b/lib/serve.js index 4fe3cd6..9296c46 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -26,16 +26,6 @@ var emojiDir = path.join(require.resolve('emoji-named-characters'), '../pngs') var urlIdRegex = /^(?:\/+(([%&@]|%25)(?:[A-Za-z0-9\/+]|%2[Ff]|%2[Bb]){43}(?:=|%3D)\.(?:sha256|ed25519))(?:\.([^?]*))?|(\/.*?))(?:\?(.*))?$/ -function isMsgEncrypted(msg) { - var c = msg && msg.value.content - return typeof c === 'string' -} - -function isMsgReadable(msg) { - var c = msg && msg.value && msg.value.content - return typeof c === 'object' && c !== null -} - function ctype(name) { switch (name && /[^.\/]*$/.exec(name)[0] || 'html') { case 'html': return 'text/html' @@ -250,6 +240,7 @@ Serve.prototype.path = function (url) { case '/static': return this.static(m[2]) case '/emoji': return this.emoji(m[2]) case '/contacts': return this.contacts(m[2]) + case '/about': return this.about(m[2]) } return this.respond(404, 'Not found') } @@ -349,9 +340,9 @@ Serve.prototype.private = function (ext) { pull( this.app.createLogStream(opts), - pull.filter(isMsgEncrypted), + pull.filter(u.isMsgEncrypted), this.app.unboxMessages(), - pull.filter(isMsgReadable), + pull.filter(u.isMsgReadable), pull.take(limit), this.renderThreadPaginated(opts, null, q), this.wrapMessages(), @@ -549,6 +540,14 @@ Serve.prototype.channels = function (ext) { ) } +Serve.prototype.phIdLink = function (id) { + return pull( + pull.once(id), + pull.asyncMap(this.renderIdLink.bind(this)), + pull.map(u.toHTML) + ) +} + Serve.prototype.contacts = function (path) { var self = this var id = String(path).substr(1) @@ -570,18 +569,10 @@ Serve.prototype.contacts = function (path) { ) } - function idLink(id) { - return pull( - pull.once(id), - pull.asyncMap(self.renderIdLink.bind(self)), - pull.map(u.toHTML) - ) - } - pull( cat([ ph('section', {}, [ - ph('h3', {}, ['Contacts: ', idLink(id)]), + ph('h3', {}, ['Contacts: ', self.phIdLink(id)]), ph('h4', {}, 'Friends'), renderFriendsList()(contacts.friends), ph('h4', {}, 'Follows'), @@ -597,9 +588,79 @@ Serve.prototype.contacts = function (path) { ) } +Serve.prototype.about = function (path) { + var self = this + var id = decodeURIComponent(String(path).substr(1)) + var abouts = self.app.createAboutStreams(id) + var render = self.app.render + + function renderAboutOpImage(link) { + if (!link) return + if (!u.isRef(link.link)) return ph('code', {}, JSON.stringify(link)) + return ph('img', { + class: 'ssb-avatar-image', + src: render.imageUrl(link.link), + alt: link.link + + (link.size ? ' (' + render.formatSize(link.size) + ')' : '') + }) + } + + function renderAboutOpValue(value) { + if (!value) return + if (u.isRef(value.link)) return self.phIdLink(value.link) + if (value.epoch) return new Date(value.epoch).toUTCString() + return ph('code', {}, JSON.stringify(value)) + } + + function renderAboutOpContent(op) { + if (op.prop === 'image') + return renderAboutOpImage(op.value) + if (op.prop === 'description') + return h('div', {innerHTML: render.markdown(op.value)}).outerHTML + if (op.prop === 'title') + return h('strong', op.value).outerHTML + if (op.prop === 'name') + return h('u', op.value).outerHTML + return renderAboutOpValue(op.value) + } + + function renderAboutOp(op) { + return ph('tr', {}, [ + ph('td', self.phIdLink(op.author)), + ph('td', + ph('a', {href: render.toUrl(op.id)}, + htime(new Date(op.timestamp)))), + ph('td', op.prop), + ph('td', renderAboutOpContent(op)) + ]) + } + + pull( + cat([ + ph('section', {}, [ + ph('h3', {}, ['About: ', self.phIdLink(id)]), + ph('table', {}, + pull(abouts.scalars, pull.map(renderAboutOp)) + ), + pull( + abouts.sets, + pull.map(function (op) { + return h('pre', JSON.stringify(op, 0, 2)) + }), + pull.map(u.toHTML) + ) + ]) + ]), + this.wrapPage('about: ' + id), + this.respondSink(200, { + 'Content-Type': ctype('html') + }) + ) +} + Serve.prototype.type = function (path) { var q = this.query - var type = path.substr(1) + var type = decodeURIComponent(path.substr(1)) var opts = { reverse: !q.forwards, lt: Number(q.lt) || Date.now(), @@ -1096,7 +1157,8 @@ Serve.prototype.wrapUserFeed = function (isScrolled, id) { h('tr', h('td'), h('td', - h('a', {href: render.toUrl('/contacts/' + id)}, 'contacts') + h('a', {href: render.toUrl('/contacts/' + id)}, 'contacts'), ' ', + h('a', {href: render.toUrl('/about/' + id)}, 'about') ) ), h('tr', diff --git a/lib/util.js b/lib/util.js index 0a6356c..2c0cf03 100644 --- a/lib/util.js +++ b/lib/util.js @@ -103,3 +103,13 @@ u.extractFeedIds = function (str) { }) return ids } + +u.isMsgReadable = function (msg) { + var c = msg && msg.value && msg.value.content + return typeof c === 'object' && c !== null +} + +u.isMsgEncrypted = function (msg) { + var c = msg && msg.value.content + return typeof c === 'string' +} |