aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/app.js15
-rw-r--r--lib/serve.js95
-rw-r--r--lib/util.js17
-rw-r--r--package.json2
-rw-r--r--static/styles.css8
5 files changed, 118 insertions, 19 deletions
diff --git a/lib/app.js b/lib/app.js
index faed5fc..5d8af70 100644
--- a/lib/app.js
+++ b/lib/app.js
@@ -5,6 +5,8 @@ var pkg = require('../package')
var u = require('./util')
var pull = require('pull-stream')
var ssbAvatar = require('ssb-avatar')
+var hasher = require('pull-hash/ext/ssb')
+var multicb = require('multicb')
var Serve = require('./serve')
var Render = require('./render')
@@ -94,6 +96,19 @@ App.prototype.publish = function (content, cb) {
}
}
+App.prototype.addBlob = function (cb) {
+ var done = multicb({pluck: 1, spread: true})
+ var hashCb = done()
+ var addCb = done()
+ done(function (err, hash, add) {
+ cb(err, hash)
+ })
+ return pull(
+ hasher(hashCb),
+ this.sbot.blobs.add(addCb)
+ )
+}
+
function getMsgWithValue(sbot, id, cb) {
sbot.get(id, function (err, value) {
if (err) return cb(err)
diff --git a/lib/serve.js b/lib/serve.js
index 5836965..0a81fa1 100644
--- a/lib/serve.js
+++ b/lib/serve.js
@@ -14,6 +14,7 @@ var paginate = require('pull-paginate')
var ssbMentions = require('ssb-mentions')
var multicb = require('multicb')
var pkg = require('../package')
+var Busboy = require('busboy')
module.exports = Serve
@@ -53,18 +54,47 @@ Serve.prototype.go = function () {
var self = this
if (this.req.method === 'POST' || this.req.method === 'PUT') {
- pull(
- toPull(this.req),
- pull.collect(function (err, bufs) {
- var data
- if (!err) try {
- data = qs.parse(Buffer.concat(bufs).toString('ascii'))
- } catch(e) {
- err = e
- }
+ if (/^multipart\/form-data/.test(this.req.headers['content-type'])) {
+ var data = {}
+ var erred
+ var busboy = new Busboy({headers: this.req.headers})
+ var filesCb = multicb({pluck: 1})
+ busboy.on('finish', filesCb())
+ filesCb(function (err) {
gotData(err, data)
})
- )
+ busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
+ var done = multicb({pluck: 1, spread: true})
+ var cb = filesCb()
+ pull(
+ toPull(file),
+ u.pullLength(done()),
+ self.app.addBlob(done())
+ )
+ done(function (err, size, id) {
+ if (err) return cb(err)
+ data[fieldname] = {link: id, name: filename, type: mimetype, size: size}
+ cb()
+ })
+ })
+ busboy.on('field', function (fieldname, val, fieldnameTruncated, valTruncated, encoding, mimetype) {
+ data[fieldname] = val
+ })
+ this.req.pipe(busboy)
+ } else {
+ pull(
+ toPull(this.req),
+ pull.collect(function (err, bufs) {
+ var data
+ if (!err) try {
+ data = qs.parse(Buffer.concat(bufs).toString('ascii'))
+ } catch(e) {
+ err = e
+ }
+ gotData(err, data)
+ })
+ )
+ }
} else {
gotData(null, {})
}
@@ -768,9 +798,20 @@ Serve.prototype.composer = function (opts, cb) {
opts = opts || {}
var data = self.data
+ var blobs = u.tryDecodeJSON(data.blobs) || {}
+ if (data.upload && typeof data.upload === 'object') {
+ blobs[data.upload.link] = {
+ type: data.upload.type,
+ size: data.upload.size,
+ }
+ }
+
var done = multicb({pluck: 1, spread: true})
done()(null, h('section.composer',
- h('form', {method: 'post', action: opts.id ? '#' + opts.id : ''},
+ h('form', {method: 'post', action: opts.id ? '#' + opts.id : '',
+ enctype: 'multipart/form-data'},
+ h('input', {type: 'hidden', name: 'blobs',
+ value: JSON.stringify(blobs)}),
opts.recps ? self.app.render.privateLine(opts.recps, done()) :
opts.private ? h('div', h('input.recps-input', {name: 'recps',
value: data.recps || '', placeholder: 'recipient ids'})) : '',
@@ -784,9 +825,22 @@ Serve.prototype.composer = function (opts, cb) {
cols: 70,
placeholder: opts.placeholder || 'public message',
}, data.text || ''),
- h('div.composer-actions',
- h('input', {type: 'submit', name: 'action', value: 'raw'}), ' ',
- h('input', {type: 'submit', name: 'action', value: 'preview'})),
+ h('table.ssb-msgs',
+ h('tr.msg-row',
+ h('td.msg-left', {colspan: 2},
+ h('input', {type: 'file', name: 'upload'}), ' ',
+ h('input', {type: 'submit', name: 'action', value: 'attach'})
+ ),
+ h('td.msg-right',
+ h('input', {type: 'submit', name: 'action', value: 'raw'}), ' ',
+ h('input', {type: 'submit', name: 'action', value: 'preview'})
+ )
+ )
+ ),
+ data.upload ? [
+ h('div', h('em', 'attach:')),
+ h('pre', '[' + data.upload.name + '](' + data.upload.link + ')')
+ ] : '',
data.action === 'preview' ? preview(false, done()) :
data.action === 'raw' ? preview(true, done()) :
data.action === 'publish' ? publish(done()) : ''
@@ -806,7 +860,18 @@ Serve.prototype.composer = function (opts, cb) {
text: data.text,
}
var mentions = ssbMentions(data.text)
- if (mentions.length) content.mentions = mentions
+ if (mentions.length) {
+ content.mentions = mentions.map(function (mention) {
+ var blob = blobs[mention.link]
+ if (blob) {
+ if (!isNaN(blob.size))
+ mention.size = blob.size
+ if (blob.type && blob.type !== 'application/octet-stream')
+ mention.type = blob.type
+ }
+ return mention
+ })
+ }
if (data.recps != null) {
if (opts.recps) return cb(new Error('got recps in opts and data'))
content.recps = [myId]
diff --git a/lib/util.js b/lib/util.js
index d6c3133..fe7a335 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -78,3 +78,20 @@ u.renderError = function(err) {
h('h3', err.name),
h('pre', err.stack))
}
+
+u.pullLength = function (cb) {
+ var len = 0
+ return pull.through(function (data) {
+ len += data.length
+ }, function (err) {
+ cb(err, len)
+ })
+}
+
+u.tryDecodeJSON = function (json) {
+ try {
+ return JSON.parse(json)
+ } catch(e) {
+ return null
+ }
+}
diff --git a/package.json b/package.json
index 8b769eb..4d47f7f 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
"description": "plain ssb web ui",
"dependencies": {
"asyncmemo": "^1.0.0",
+ "busboy": "^0.2.14",
"emoji-named-characters": "^1.0.2",
"emoji-server": "^1.0.0",
"human-time": "^0.0.1",
@@ -11,6 +12,7 @@
"lrucache": "^1.0.2",
"multicb": "^1.2.1",
"pull-cat": "^1.1.11",
+ "pull-hash": "^1.0.0",
"pull-paginate": "^1.0.0",
"pull-paramap": "^1.2.1",
"pull-stream": "^3.5.0",
diff --git a/static/styles.css b/static/styles.css
index 3413be8..d27ca0a 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -8,10 +8,6 @@ section {
padding: 1ex;
}
-.composer-actions {
- text-align: right;
-}
-
.ssb-post img {
max-width: 100%;
width: 640px;
@@ -120,6 +116,10 @@ td {
width: 100%;
}
+.msg-right {
+ text-align: right;
+}
+
.feed-name {
padding: .75ex;
}