From 35a3dedeb8b9670721bd363031083cc2c90fa085 Mon Sep 17 00:00:00 2001 From: cel Date: Mon, 29 May 2017 06:38:49 -1000 Subject: Implement custom emoji rendering and uploading --- lib/app.js | 5 +++++ lib/render.js | 21 ++++++++++++++++++--- lib/serve.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- lib/util.js | 16 ++++++++++++++-- 4 files changed, 89 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/app.js b/lib/app.js index 825bd91..10a8a07 100644 --- a/lib/app.js +++ b/lib/app.js @@ -38,6 +38,7 @@ function App(sbot, config) { this._getAbout.bind(this)) this.unboxContent = memo({cache: lru(100)}, sbot.private.unbox) this.reverseNameCache = lru(500) + this.reverseEmojiNameCache = lru(500) this.unboxMsg = this.unboxMsg.bind(this) @@ -233,6 +234,10 @@ App.prototype.getReverseNameSync = function (name) { return id } +App.prototype.getReverseEmojiNameSync = function (name) { + return this.reverseEmojiNameCache.get(name) +} + App.prototype.getNameSync = function (name) { var about = this.aboutCache.get(name) return about && about.name diff --git a/lib/render.js b/lib/render.js index 2c2278c..0135205 100644 --- a/lib/render.js +++ b/lib/render.js @@ -88,13 +88,26 @@ function Render(app, opts) { Render.prototype.emoji = function (emoji) { var name = ':' + emoji + ':' - return emoji in emojis ? - h('img.ssb-emoji', { + if (emoji in emojis) { + return h('img.ssb-emoji', { src: this.opts.emoji_base + emoji + '.png', alt: name, height: 17, title: name, - }) : name + }) + } + var link = this._mentions[name] + if (link && link.link) { + this.app.reverseEmojiNameCache.set(emoji, link.link) + return h('img.ssb-emoji', { + src: this.opts.img_base + link.link, + alt: name + + (link.size != null ? ' (' + this.formatSize(link.size) + ')' : ''), + height: 17, + title: name, + }) + } + return name } /* disabled until it can be done safely without breaking html @@ -113,6 +126,8 @@ Render.prototype.markdown = function (text, mentions) { var mentionsByLink = this._mentionsByLink = {} if (Array.isArray(mentions)) mentions.forEach(function (link) { if (!link) return + else if (link.emoji) + mentionsObj[':' + link.name + ':'] = link else if (link.name) mentionsObj['@' + link.name] = link.link else if (link.host === 'http://localhost:7777') diff --git a/lib/serve.js b/lib/serve.js index 9d907e0..e0c089c 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -74,6 +74,11 @@ Serve.prototype.go = function () { filesCb(function (err) { gotData(err, data) }) + function addField(name, value) { + if (!(name in data)) data[name] = value + else if (Array.isArray(data[name])) data[name].push(value) + else data[name] = [data[name], value] + } busboy.on('file', function (fieldname, file, filename, encoding, mimetype) { var done = multicb({pluck: 1, spread: true}) var cb = filesCb() @@ -85,14 +90,13 @@ Serve.prototype.go = function () { done(function (err, size, id) { if (err) return cb(err) if (size === 0 && !filename) return cb() - data[fieldname] = {link: id, name: filename, type: mimetype, size: size} + addField(fieldname, + {link: id, name: filename, type: mimetype, size: size}) cb() }) }) busboy.on('field', function (fieldname, val, fieldnameTruncated, valTruncated, encoding, mimetype) { - if (!(fieldname in data)) data[fieldname] = val - else if (Array.isArray(data[fieldname])) data[fieldname].push(val) - else data[fieldname] = [data[fieldname], val] + addField(fieldname, val) }) this.req.pipe(busboy) } else { @@ -1921,6 +1925,20 @@ Serve.prototype.composer = function (opts, cb) { formNames[mentionNames[i]] = u.extractFeedIds(mentionIds[i])[0] } + var formEmojiNames = {} + var emojiIds = u.toArray(data.emoji_id) + var emojiNames = u.toArray(data.emoji_name) + var emojiUploads = u.toArray(data.emoji_upload) + for (var i = 0; i < emojiIds.length && i < emojiNames.length; i++) { + var upload = emojiUploads[i] + formEmojiNames[emojiNames[i]] = + (upload && upload.link) || u.extractBlobIds(emojiIds[i])[0] + if (upload) blobs[upload.link] = { + type: upload.type, + size: upload.size, + } + } + if (data.upload) { // TODO: be able to change the content-type var isImage = /^image\//.test(data.upload.type) @@ -1931,7 +1949,8 @@ Serve.prototype.composer = function (opts, cb) { // get bare feed names var unknownMentionNames = {} - var unknownMentions = ssbMentions(data.text, {bareFeedNames: true}) + var mentions = ssbMentions(data.text, {bareFeedNames: true, emoji: true}) + var unknownMentions = mentions .filter(function (mention) { return mention.link === '@' }) @@ -1944,6 +1963,15 @@ Serve.prototype.composer = function (opts, cb) { return {name: name, id: id} }) + var emoji = mentions + .filter(function (mention) { return mention.emoji }) + .map(function (mention) { return mention.name }) + .filter(uniques()) + .map(function (name) { + var id = formEmojiNames[name] || self.app.getReverseEmojiNameSync(name) + return {name: name, id: id} + }) + // strip content other than feed ids from the recps field if (data.recps) { data.recps = u.extractFeedIds(data.recps).filter(uniques()).join(', ') @@ -1982,7 +2010,19 @@ Serve.prototype.composer = function (opts, cb) { h('input', {name: 'mention_name', type: 'hidden', value: mention.name}), h('input.id-input', {name: 'mention_id', size: 60, - value: mention.id, placeholder: 'id'})) + value: mention.id, placeholder: '@id'})) + })) + ] : '', + emoji.length > 0 ? [ + h('div', h('em', 'emoji:')), + h('ul.mentions', emoji.map(function (link) { + return h('li', + h('code', link.name), ': ', + h('input', {name: 'emoji_name', type: 'hidden', + value: link.name}), + h('input.id-input', {name: 'emoji_id', size: 60, + value: link.id, placeholder: '&id'}), ' ', + h('input', {type: 'file', name: 'emoji_upload'})) })) ] : '', h('table.ssb-msgs', @@ -2013,8 +2053,13 @@ Serve.prototype.composer = function (opts, cb) { type: 'post', text: data.text, } - var mentions = ssbMentions(data.text, {bareFeedNames: true}) + var mentions = ssbMentions(data.text, {bareFeedNames: true, emoji: true}) .filter(function (mention) { + if (mention.emoji) { + mention.link = formEmojiNames[mention.name] + || self.app.getReverseEmojiNameSync(mention.name) + if (!mention.link) return false + } var blob = blobs[mention.link] if (blob) { if (!isNaN(blob.size)) diff --git a/lib/util.js b/lib/util.js index d25ecaf..f8da640 100644 --- a/lib/util.js +++ b/lib/util.js @@ -72,7 +72,7 @@ u.linkDest = function (link) { } u.toArray = function (x) { - return !x ? [] : Array.isArray(x) ? x : [x] + return x == null ? [] : Array.isArray(x) ? x : [x] } u.fromArray = function (arr) { @@ -102,7 +102,7 @@ u.tryDecodeJSON = function (json) { } } -u.extractFeedIds = function (str) { +u.extractRefs = function (str) { var ids = [] String(str).replace(u.ssbRefRegex, function (id) { ids.push(id) @@ -110,6 +110,18 @@ u.extractFeedIds = function (str) { return ids } +u.extractFeedIds = function (str) { + return u.extractRefs(str).filter(function (ref) { + return ref[0] === '@' + }) +} + +u.extractBlobIds = function (str) { + return u.extractRefs(str).filter(function (ref) { + return ref[0] === '&' + }) +} + u.isMsgReadable = function (msg) { var c = msg && msg.value && msg.value.content return typeof c === 'object' && c !== null -- cgit v1.2.3