aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/app.js115
-rw-r--r--lib/serve.js56
-rw-r--r--package.json3
3 files changed, 174 insertions, 0 deletions
diff --git a/lib/app.js b/lib/app.js
index d22e2b7..5630f41 100644
--- a/lib/app.js
+++ b/lib/app.js
@@ -8,6 +8,8 @@ var ssbAvatar = require('ssb-avatar')
var hasher = require('pull-hash/ext/ssb')
var multicb = require('multicb')
var paramap = require('pull-paramap')
+var many = require('pull-many')
+var defer = require('pull-defer')
var Serve = require('./serve')
var Render = require('./render')
@@ -239,3 +241,116 @@ App.prototype.streamChannels = function (opts) {
pull.unique()
)
}
+
+App.prototype.streamFollows = function (id) {
+ return pull(
+ this.sbot.links({
+ source: id,
+ rel: 'contact',
+ values: true,
+ reverse: true,
+ }),
+ pull.map(function (msg) {
+ return msg.value.content
+ }),
+ pull.filter(),
+ pull.unique(function (c) {
+ return c.contact
+ }),
+ pull.filter(function (c) {
+ return c.following
+ }),
+ pull.map(function (c) {
+ return c.contact
+ })
+ )
+}
+
+App.prototype.streamFollowers = function (id) {
+ return pull(
+ this.sbot.links({
+ dest: id,
+ rel: 'contact',
+ values: true,
+ reverse: true,
+ }),
+ pull.unique(function (msg) {
+ return msg.value.author
+ }),
+ pull.filter(function (msg) {
+ var c = msg.value.content
+ return c && c.following
+ }),
+ pull.map(function (msg) {
+ return msg.value.author
+ })
+ )
+}
+
+App.prototype.streamFriends = function (id, endCb) {
+ var follows = {}, followers = {}
+ return pull(
+ many([
+ pull(
+ this.streamFollows(id),
+ pull.map(function (id) {
+ return {id: id, follow: true}
+ })
+ ),
+ pull(
+ this.streamFollowers(id),
+ pull.map(function (id) {
+ return {id: id, follower: true}
+ })
+ )
+ ]),
+ pull.filter(function (op) {
+ if (op.follow) {
+ if (followers[op.id]) {
+ delete followers[op.id]
+ return true
+ } else {
+ follows[op.id] = true
+ return false
+ }
+ }
+ if (op.follower) {
+ if (follows[op.id]) {
+ delete follows[op.id]
+ return true
+ } else {
+ followers[op.id] = true
+ return false
+ }
+ }
+ }),
+ pull.map(function (op) {
+ return op.id
+ }),
+ endCb && function (read) {
+ return function (abort, cb) {
+ read(abort, function (end, data) {
+ cb(end, data)
+ if (end) endCb(end === true ? null : end, {
+ followers: Object.keys(followers),
+ follows: Object.keys(follows),
+ })
+ })
+ }
+ }
+ )
+}
+
+App.prototype.createContactStreams = function (id) {
+ var follows = defer.source()
+ var followers = defer.source()
+ var friends = this.streamFriends(id, function (err, more) {
+ follows.resolve(err ? pull.error(err) : pull.values(more.follows))
+ followers.resolve(err ? pull.error(err) : pull.values(more.followers))
+ })
+ return {
+ friends: friends,
+ follows: follows,
+ followers: followers,
+ }
+}
diff --git a/lib/serve.js b/lib/serve.js
index 4b0d58d..68eebda 100644
--- a/lib/serve.js
+++ b/lib/serve.js
@@ -18,6 +18,7 @@ var Busboy = require('busboy')
var mime = require('mime-types')
var ident = require('pull-identify-filetype')
var htime = require('human-time')
+var ph = require('pull-hyperscript')
module.exports = Serve
@@ -230,6 +231,7 @@ Serve.prototype.path = function (url) {
case '/links': return this.links(m[2])
case '/static': return this.static(m[2])
case '/emoji': return this.emoji(m[2])
+ case '/contacts': return this.contacts(m[2])
}
return this.respond(404, 'Not found')
}
@@ -424,6 +426,53 @@ Serve.prototype.channels = function (ext) {
)
}
+Serve.prototype.contacts = function (path) {
+ var self = this
+ var id = String(path).substr(1)
+ var contacts = self.app.createContactStreams(id)
+
+ function renderFriendsList() {
+ return pull(
+ paramap(function (id, cb) {
+ self.app.getAbout(id, function (err, about) {
+ var name = about && about.name || id.substr(0, 8) + '…'
+ cb(null, h('a', {href: self.app.render.toUrl('/contacts/' + id)}, name))
+ })
+ }, 8),
+ pull.map(function (el) {
+ return [el, ' ']
+ }),
+ pull.flatten(),
+ pull.map(u.toHTML)
+ )
+ }
+
+ 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('h4', {}, 'Friends'),
+ renderFriendsList()(contacts.friends),
+ ph('h4', {}, 'Follows'),
+ renderFriendsList()(contacts.follows),
+ ph('h4', {}, 'Followers'),
+ renderFriendsList()(contacts.followers)
+ ])
+ ]),
+ this.wrapPage('contacts: ' + id),
+ this.respondSink(200, {
+ 'Content-Type': ctype('html')
+ })
+ )
+}
Serve.prototype.type = function (path) {
var q = this.query
@@ -892,6 +941,7 @@ var relationshipActions = [
Serve.prototype.wrapUserFeed = function (isScrolled, id) {
var self = this
var myId = self.app.sbot.id
+ var render = self.app.render
return u.hyperwrap(function (thread, cb) {
var done = multicb({pluck: 1, spread: true})
self.app.getAbout(id, done())
@@ -912,6 +962,12 @@ Serve.prototype.wrapUserFeed = function (isScrolled, id) {
about.description ? h('div',
{innerHTML: self.app.render.markdown(about.description)}) : ''
)),
+ h('tr',
+ h('td'),
+ h('td',
+ h('a', {href: render.toUrl('/contacts/' + id)}, 'contacts')
+ )
+ ),
isScrolled ? '' : [
id === myId ? '' : h('tr',
h('td'),
diff --git a/package.json b/package.json
index 5589696..28f1704 100644
--- a/package.json
+++ b/package.json
@@ -13,8 +13,11 @@
"mime-types": "^2.1.12",
"multicb": "^1.2.1",
"pull-cat": "^1.1.11",
+ "pull-defer": "^0.2.2",
"pull-hash": "^1.0.0",
+ "pull-hyperscript": "^0.2.2",
"pull-identify-filetype": "^1.1.0",
+ "pull-many": "^1.0.8",
"pull-paginate": "^1.0.0",
"pull-paramap": "^1.2.1",
"pull-stream": "^3.5.0",