From a06789afcc5b53c34d19d2d9d35c3b91c705d91c Mon Sep 17 00:00:00 2001 From: cel Date: Fri, 5 May 2017 18:52:46 -1000 Subject: check ref is string --- lib/util.js | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/util.js b/lib/util.js index 0a6356c..90fc55a 100644 --- a/lib/util.js +++ b/lib/util.js @@ -6,6 +6,7 @@ var u = exports u.ssbRefRegex = /((?:@|%|&|ssb:\/\/%)[A-Za-z0-9\/+]{43}=\.[\w\d]+)/g u.isRef = function (str) { + if (!str) return false u.ssbRefRegex.lastIndex = 0 return u.ssbRefRegex.test(str) } -- cgit v1.2.3 From d81825f4b4da83bd0376ac916107f425872eee7b Mon Sep 17 00:00:00 2001 From: cel Date: Sat, 6 May 2017 15:15:56 -1000 Subject: Serve channels under /channel/ instead of /%23 --- lib/render.js | 3 ++- lib/serve.js | 23 +++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/render.js b/lib/render.js index 316ad2f..36dba2f 100644 --- a/lib/render.js +++ b/lib/render.js @@ -146,7 +146,8 @@ Render.prototype.toUrl = function (href) { case '&': if (!u.isRef(href)) return false return this.opts.blob_base + href - case '#': return this.opts.base + encodeURIComponent(href) + case '#': return this.opts.base + 'channel/' + + encodeURIComponent(href.substr(1)) case '/': return this.opts.base + href.substr(1) case '?': return this.opts.base + 'search?q=' + encodeURIComponent(href) } diff --git a/lib/serve.js b/lib/serve.js index 4fe3cd6..bce474c 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -219,6 +219,13 @@ Serve.prototype.respondSink = function (status, headers, cb) { }) } +Serve.prototype.redirect = function (dest) { + this.res.writeHead(302, { + Location: dest + }) + this.res.end() +} + Serve.prototype.path = function (url) { var m url = url.replace(/^\/+/, '/') @@ -227,7 +234,8 @@ Serve.prototype.path = function (url) { case '/robots.txt': return this.res.end('User-agent: *') } if (m = /^\/%23(.*)/.exec(url)) { - return this.channel(decodeURIComponent(m[1])) + return this.redirect(this.app.render.toUrl('/channel/' + + decodeURIComponent(m[1]))) } m = /^([^.]*)(?:\.(.*))?$/.exec(url) switch (m[1]) { @@ -245,6 +253,7 @@ Serve.prototype.path = function (url) { } m = /^(\/?[^\/]*)(\/.*)?$/.exec(url) switch (m[1]) { + case '/channel': return this.channel(m[2]) case '/type': return this.type(m[2]) case '/links': return this.links(m[2]) case '/static': return this.static(m[2]) @@ -373,10 +382,7 @@ Serve.prototype.search = function (ext) { } if (u.isRef(searchQ) || searchQ[0] === '#') { - self.res.writeHead(302, { - Location: self.app.render.toUrl(searchQ) - }) - return self.res.end() + return self.redirect(self.app.render.toUrl(searchQ)) } pull( @@ -536,7 +542,7 @@ Serve.prototype.channels = function (ext) { paramap(function (channel, cb) { var subscribed = false cb(null, [ - h('a', {href: self.app.render.toUrl('#' + channel)}, '#' + channel), + h('a', {href: self.app.render.toUrl('/channel/' + channel)}, '#' + channel), ' ' ]) }, 8), @@ -660,7 +666,8 @@ Serve.prototype.rawId = function (id) { }) } -Serve.prototype.channel = function (channel) { +Serve.prototype.channel = function (path) { + var channel = decodeURIComponent(String(path).substr(1)) var q = this.query var gt = Number(q.gt) || -Infinity var lt = Number(q.lt) || Date.now() @@ -1220,7 +1227,7 @@ Serve.prototype.wrapChannel = function (channel) { cb(null, [ h('section', h('h3.feed-name', - h('a', {href: self.app.render.toUrl('#' + channel)}, '#' + channel) + h('a', {href: self.app.render.toUrl('/channel/' + channel)}, '#' + channel) ) ), composer, -- cgit v1.2.3 From bc37b05926f3b5acff7905748607f4f0da5ac14e Mon Sep 17 00:00:00 2001 From: cel Date: Sat, 6 May 2017 17:37:42 -1000 Subject: Put shortcut icon in correct place --- lib/serve.js | 1 + lib/util.js | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/serve.js b/lib/serve.js index bce474c..d9cbfe7 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -987,6 +987,7 @@ Serve.prototype.wrapPage = function (title, searchQ) { h('meta', {charset: 'utf-8'}), h('title', title), h('meta', {name: 'viewport', content: 'width=device-width,initial-scale=1'}), + h('link', {rel: 'icon', href: render.toUrl('/static/hermie.ico'), type: 'image/x-icon'}), h('style', styles()) ), h('body', diff --git a/lib/util.js b/lib/util.js index 90fc55a..6a419f9 100644 --- a/lib/util.js +++ b/lib/util.js @@ -38,9 +38,7 @@ u.toHTML = function (el) { if (typeof el === 'string' || Array.isArray(el)) { return h('div', el).innerHTML } - var html = el.outerHTML || String(el) - if (el.nodeName === 'html') html = '' + html + '\n' - return html + return el.outerHTML || String(el) } u.hyperwrap = function (fn) { -- cgit v1.2.3 From d437d4de396b37033f03626660b510d7b32e7377 Mon Sep 17 00:00:00 2001 From: cel Date: Sat, 6 May 2017 18:20:57 -1000 Subject: Implement thread forking option --- lib/serve.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/serve.js b/lib/serve.js index d9cbfe7..183c8c3 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -741,7 +741,9 @@ Serve.prototype.id = function (id, ext) { self.wrapThread({ recps: recps, root: threadRootId, - branches: id === threadRootId ? threadHeads(links, id) : id, + post: id, + branches: threadHeads(links, threadRootId), + postBranches: threadRootId !== id && threadHeads(links, id), channel: channel, }), self.wrapPage(id), @@ -1179,8 +1181,10 @@ Serve.prototype.wrapThread = function (opts) { placeholder: recps ? 'private reply' : 'reply', id: 'reply', root: opts.root, + post: opts.post, channel: opts.channel || '', branches: opts.branches, + postBranches: opts.postBranches, recps: recps, }, function (err, composer) { if (err) return cb(err) @@ -1360,6 +1364,12 @@ Serve.prototype.composer = function (opts, cb) { channel != null ? h('div', '#', h('input', {name: 'channel', placeholder: 'channel', value: channel})) : '', + opts.root !== opts.post ? h('div', + h('label', {for: 'fork_thread'}, + h('input', {id: 'fork_thread', type: 'checkbox', name: 'fork_thread', value: 'post', checked: data.fork_thread || undefined}), + ' fork thread' + ) + ) : '', h('textarea', { id: opts.id, name: 'text', @@ -1433,8 +1443,13 @@ Serve.prototype.composer = function (opts, cb) { } else { if (opts.recps) content.recps = opts.recps } - if (opts.root) content.root = opts.root - if (opts.branches) content.branch = u.fromArray(opts.branches) + if (data.fork_thread) { + content.root = opts.post || undefined + content.branch = u.fromArray(opts.postBranches) || undefined + } else { + content.root = opts.root || undefined + content.branch = u.fromArray(opts.branches) || undefined + } if (channel) content.channel = data.channel } var msg = { -- cgit v1.2.3 From 83c447c64b6339b9b2b98e3e35d22d2ef95ea611 Mon Sep 17 00:00:00 2001 From: cel Date: Sat, 6 May 2017 20:27:38 -1000 Subject: Add fallback image sizes --- lib/render.js | 2 ++ lib/serve.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/render.js b/lib/render.js index 36dba2f..aa2a6da 100644 --- a/lib/render.js +++ b/lib/render.js @@ -76,6 +76,7 @@ Render.prototype.emoji = function (emoji) { h('img.ssb-emoji', { src: this.opts.emoji_base + emoji + '.png', alt: name, + height: 17, title: name, }) : name } @@ -166,6 +167,7 @@ Render.prototype.avatarImage = function (link, cb) { if (!link) return cb(), '' if (typeof link === 'string') link = {link: link} var img = h('img.ssb-avatar-image', { + width: 72, alt: ' ' }) if (link.image) gotAbout(null, link) diff --git a/lib/serve.js b/lib/serve.js index 183c8c3..8e63ff5 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1384,7 +1384,7 @@ Serve.prototype.composer = function (opts, cb) { h('code', '@' + mention.name), ': ', h('input', {name: 'mention_name', type: 'hidden', value: mention.name}), - h('input.id-input', {name: 'mention_id', + h('input.id-input', {name: 'mention_id', size: 60, value: mention.id, placeholder: 'id'})) })) ] : '', -- cgit v1.2.3 From f13fbed3ce0247e55561b3dbe35819c672958cd7 Mon Sep 17 00:00:00 2001 From: cel Date: Sun, 7 May 2017 09:29:25 -1000 Subject: Add back doctype --- lib/util.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/util.js b/lib/util.js index 6a419f9..fe5b4f3 100644 --- a/lib/util.js +++ b/lib/util.js @@ -38,7 +38,9 @@ u.toHTML = function (el) { if (typeof el === 'string' || Array.isArray(el)) { return h('div', el).innerHTML } - return el.outerHTML || String(el) + var html = el.outerHTML || String(el) + if (el.nodeName === 'html') html = '' + html + '\n' + return html } u.hyperwrap = function (fn) { -- cgit v1.2.3 From 8a7b661c6e85a7abf568132effac4b68ffe51a6a Mon Sep 17 00:00:00 2001 From: cel Date: Sun, 7 May 2017 19:47:47 -1000 Subject: Redirect to published message from /compose --- lib/serve.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/serve.js b/lib/serve.js index 8e63ff5..defac87 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -132,10 +132,17 @@ Serve.prototype.go = function () { else next() } - function next(err) { + function next(err, publishedMsg) { if (err) { self.res.writeHead(400, {'Content-Type': 'text/plain'}) self.res.end(err.stack) + } else if (publishedMsg) { + if (self.data.redirect_to_published_msg) { + self.redirect(self.app.render.toUrl(publishedMsg.key)) + } else { + self.publishedMsg = publishedMsg + self.handle() + } } else { self.handle() } @@ -188,8 +195,7 @@ Serve.prototype.publish = function (content, cb) { if (err) return cb(err) delete self.data.text delete self.data.recps - self.publishedMsg = msg - return cb() + return cb(null, msg) }) }) } @@ -477,6 +483,7 @@ Serve.prototype.compose = function (ext) { var self = this self.composer({ channel: '', + redirectToPublishedMsg: true, }, function (err, composer) { if (err) return cb(err) pull( @@ -1472,6 +1479,8 @@ Serve.prototype.composer = function (opts, cb) { return [ h('input', {type: 'hidden', name: 'content', value: JSON.stringify(content)}), + opts.redirectToPublishedMsg ? h('input', {type: 'hidden', + name: 'redirect_to_published_msg', value: '1'}) : '', h('div', h('em', 'draft:')), msgContainer, h('div.composer-actions', -- cgit v1.2.3 From 11e894c5c9efbc6ee141f614eea3a328517aee5b Mon Sep 17 00:00:00 2001 From: cel Date: Mon, 8 May 2017 20:54:59 -1000 Subject: Exclude channel from list if all its subscribers unsubscribed --- lib/app.js | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/app.js b/lib/app.js index a76687c..8a74dbb 100644 --- a/lib/app.js +++ b/lib/app.js @@ -270,6 +270,9 @@ App.prototype.streamChannels = function (opts) { return pull( this.sbot.messagesByType({type: 'channel', reverse: true}), this.unboxMessages(), + pull.filter(function (msg) { + return msg.value.content.subscribed + }), pull.map(function (msg) { return msg.value.content.channel }), -- cgit v1.2.3 From 29f56c3e49c767f75164f57ccd1ac52b389794c0 Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 May 2017 14:49:03 -1000 Subject: Optimize looking up follow --- lib/app.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/app.js b/lib/app.js index 8a74dbb..b57e5a6 100644 --- a/lib/app.js +++ b/lib/app.js @@ -250,15 +250,17 @@ App.prototype.streamPeers = function (opts) { App.prototype.getFollow = function (source, dest, cb) { var self = this pull( - self.sbot.links({source: source, dest: dest, rel: 'contact', values: true}), - pull.filter(function (msg) { - var c = msg && msg.value && msg.value.content + self.sbot.links({source: source, dest: dest, rel: 'contact', reverse: true, + values: true, meta: false, keys: false}), + pull.filter(function (value) { + var c = value && value.content return c && c.type === 'contact' }), - pull.reduce(function (doesFollow, msg) { - var c = msg.value.content - return !!c.following - }, false, cb) + pull.take(1), + pull.collect(function (err, msgs) { + if (err) return cb(err) + cb(null, msgs[0] && !!msgs[0].content.following) + }) ) } -- cgit v1.2.3 From 1e1d32ef170391da41c2c064fc9a557a1ba667d1 Mon Sep 17 00:00:00 2001 From: _ssb <_ssb@trouble.kagu-tsuchi.com> Date: Wed, 10 May 2017 22:06:25 -0500 Subject: First pass at listing subscribed channels --- lib/app.js | 16 +++++++++++++++ lib/serve.js | 66 +++++++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 70 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/app.js b/lib/app.js index b57e5a6..f475bd2 100644 --- a/lib/app.js +++ b/lib/app.js @@ -282,6 +282,22 @@ App.prototype.streamChannels = function (opts) { ) } +App.prototype.streamMyChannels = function (id, opts) { + return pull( + this.sbot.createUserStream({id: id, reverse: true}), + this.unboxMessages(), + pull.filter(function (msg) { + if (msg.value.content.type == 'channel') { + return msg.value.content.subscribed + } + }), + pull.map(function (msg) { + return msg.value.content.channel + }), + pull.unique() + ) +} + App.prototype.createContactStreams = function (id) { return new Contacts(this.sbot).createContactStreams(id) } diff --git a/lib/serve.js b/lib/serve.js index defac87..54516e1 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -543,20 +543,48 @@ Serve.prototype.peers = function (ext) { Serve.prototype.channels = function (ext) { var self = this + var id = self.app.sbot.id + + function renderMyChannels() { + return pull( + self.app.streamMyChannels(id), + paramap(function (channel, cb) { + // var subscribed = false + cb(null, [ + h('a', {href: self.app.render.toUrl('/channel/' + channel)}, '#' + channel), + ' ' + ]) + }, 8), + pull.map(u.toHTML), + self.wrapMyChannels() + ) + } + + function renderNetworkChannels() { + return pull( + self.app.streamChannels(), + paramap(function (channel, cb) { + // var subscribed = false + cb(null, [ + h('a', {href: self.app.render.toUrl('/channel/' + channel)}, '#' + channel), + ' ' + ]) + }, 8), + pull.map(u.toHTML), + self.wrapChannels() + ) + } pull( - self.app.streamChannels(), - paramap(function (channel, cb) { - var subscribed = false - cb(null, [ - h('a', {href: self.app.render.toUrl('/channel/' + channel)}, '#' + channel), - ' ' + cat([ + ph('section', {}, [ + ph('h3', {}, 'Channels:'), + renderMyChannels(), + renderNetworkChannels() ]) - }, 8), - pull.map(u.toHTML), - self.wrapChannels(), - self.wrapPage('channels'), - self.respondSink(200, { + ]), + this.wrapPage('channels'), + this.respondSink(200, { 'Content-Type': ctype(ext) }) ) @@ -1294,7 +1322,21 @@ Serve.prototype.wrapChannels = function (opts) { return u.hyperwrap(function (channels, cb) { cb(null, [ h('section', - h('h3', 'Channels') + h('h4', 'Network') + ), + h('section', + channels + ) + ]) + }) +} + +Serve.prototype.wrapMyChannels = function (opts) { + var self = this + return u.hyperwrap(function (channels, cb) { + cb(null, [ + h('section', + h('h4', 'Subscribed') ), h('section', channels -- cgit v1.2.3 From 9ebb886a032f3a3767c24dce9a343311ea8c4481 Mon Sep 17 00:00:00 2001 From: cel Date: Thu, 11 May 2017 20:20:18 -1000 Subject: Handle bad refs better --- lib/render-msg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/render-msg.js b/lib/render-msg.js index 2a0e7df..4ba960c 100644 --- a/lib/render-msg.js +++ b/lib/render-msg.js @@ -342,7 +342,7 @@ RenderMsg.prototype.link = function (link, cb) { var ref = u.linkDest(link) if (!ref) return cb(null, '') self.getName(ref, function (err, name) { - if (err) return cb(err) + if (err) name = truncate(ref, 10) cb(null, h('a', {href: self.toUrl(ref)}, name)) }) } -- cgit v1.2.3 From 24417ffb7f896971664053a13101154e40d6adce Mon Sep 17 00:00:00 2001 From: cel Date: Thu, 11 May 2017 20:25:59 -1000 Subject: Optimize streaming my channels --- lib/app.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'lib') diff --git a/lib/app.js b/lib/app.js index f475bd2..6990aa5 100644 --- a/lib/app.js +++ b/lib/app.js @@ -283,6 +283,24 @@ App.prototype.streamChannels = function (opts) { } App.prototype.streamMyChannels = function (id, opts) { + // use ssb-query plugin if it is available, since it has an index for + // author + type + if (this.sbot.query) return pull( + this.sbot.query.read({ + reverse: true, + query: [ + {$filter: { + value: { + author: id, + content: {type: 'channel', subscribed: true} + } + }}, + {$map: ['value', 'content', 'channel']} + ] + }), + pull.unique() + ) + return pull( this.sbot.createUserStream({id: id, reverse: true}), this.unboxMessages(), -- cgit v1.2.3 From f826bdf13d9bbdfd31a38bfdff382106b8edfd52 Mon Sep 17 00:00:00 2001 From: cel Date: Mon, 15 May 2017 09:24:25 -1000 Subject: Don't crash on missing message --- lib/serve.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/serve.js b/lib/serve.js index 54516e1..c70fcac 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -752,7 +752,8 @@ Serve.prototype.id = function (id, ext) { if (self.query.raw != null) return self.rawId(id) this.app.getMsgDecrypted(id, function (err, rootMsg) { - if (err && err.name === 'NotFoundError') err = null, rootMsg = {key: id} + if (err && err.name === 'NotFoundError') err = null, rootMsg = { + key: id, value: {content: false}} if (err) return self.respond(500, err.stack || err) var rootContent = rootMsg && rootMsg.value && rootMsg.value.content var recps = rootContent && rootContent.recps -- cgit v1.2.3