Commit e4249874689b314078266a74aed30d8d3960e8db
realtime updating of local peers, show connection state
Matt McKegg committed on 11/3/2016, 3:21:32 PMParent: 9fdc650f04f0bea7982659a5d5de3ce799b8ad6a
Files changed
api/index.js | changed |
lib/local-with-list.js | added |
modules/obs-local.js | changed |
modules/public.js | changed |
modules/obs-connected.js | added |
server-process.js | changed |
styles/profile-list.mcss | changed |
api/index.js | ||
---|---|---|
@@ -146,7 +146,10 @@ | ||
146 | 146 | CACHE[e.key] = CACHE[e.key] || e.value |
147 | 147 | infoCache.updateFrom(e) |
148 | 148 | }) |
149 | 149 | ) |
150 | + }, | |
151 | + sbot_list_local: function (cb) { | |
152 | + return sbot.local.list(cb) | |
150 | 153 | } |
151 | 154 | } |
152 | 155 | } |
lib/local-with-list.js | ||
---|---|---|
@@ -1,0 +1,63 @@ | ||
1 | +// FROM: https://github.com/ssbc/scuttlebot/blob/master/plugins/local.js | |
2 | + | |
3 | +var broadcast = require('broadcast-stream') | |
4 | +var ref = require('ssb-ref') | |
5 | +var valid = require('scuttlebot/lib/validators') | |
6 | + | |
7 | +// local plugin | |
8 | +// broadcasts the address:port:pubkey triple of the sbot server | |
9 | +// on the LAN, using multicast UDP | |
10 | + | |
11 | +function isFunction (f) { | |
12 | + return 'function' === typeof f | |
13 | +} | |
14 | + | |
15 | +module.exports = { | |
16 | + name: 'local', | |
17 | + version: '2.0.0', | |
18 | + manifest: { | |
19 | + list: 'sync' | |
20 | + }, | |
21 | + init: function (sbot, config) { | |
22 | + var local = broadcast(config.port) | |
23 | + var lastSeen = {} | |
24 | + var localKeys = new Set() | |
25 | + | |
26 | + setInterval(function () { | |
27 | + Object.keys(lastSeen).forEach((key) => { | |
28 | + if (Date.now() - lastSeen[key] < 10e3) { | |
29 | + localKeys.add(key) | |
30 | + } else { | |
31 | + localKeys.delete(key) | |
32 | + delete lastSeen[key] | |
33 | + } | |
34 | + }) | |
35 | + }, 5e3) | |
36 | + | |
37 | + local.on('data', function (buf) { | |
38 | + if (buf.loopback) return | |
39 | + var data = buf.toString() | |
40 | + var peer = ref.parseAddress(data) | |
41 | + if (peer) { | |
42 | + lastSeen[peer.key] = Date.now() | |
43 | + sbot.gossip.add(data, 'local') | |
44 | + } | |
45 | + }) | |
46 | + | |
47 | + setInterval(function () { | |
48 | + // broadcast self | |
49 | + // TODO: sign beacons, so that receipient can be confidant | |
50 | + // that is really your id. | |
51 | + // (which means they can update their peer table) | |
52 | + // Oh if this includes your local address, | |
53 | + // then it becomes unforgeable. | |
54 | + local.write(sbot.getAddress()) | |
55 | + }, 1000) | |
56 | + | |
57 | + return { | |
58 | + list: valid.sync(function () { | |
59 | + return Array.from(localKeys) | |
60 | + }) | |
61 | + } | |
62 | + } | |
63 | +} |
modules/obs-local.js | ||
---|---|---|
@@ -1,8 +1,7 @@ | ||
1 | 1 | var MutantSet = require('@mmckegg/mutant/set') |
2 | 2 | var plugs = require('patchbay/plugs') |
3 | -var sbot_gossip_peers = plugs.first(exports.sbot_gossip_peers = []) | |
4 | -var ip = require('ip') | |
3 | +var sbot_list_local = plugs.first(exports.sbot_list_local = []) | |
5 | 4 | |
6 | 5 | var cache = null |
7 | 6 | |
8 | 7 | exports.obs_local = function () { |
@@ -11,19 +10,20 @@ | ||
11 | 10 | } else { |
12 | 11 | var result = MutantSet([], {nextTick: true}) |
13 | 12 | // todo: make this clean up on unlisten |
14 | 13 | |
15 | - setTimeout(() => { | |
16 | - sbot_gossip_peers((err, peers) => { | |
17 | - if (err) throw console.log(err) | |
18 | - peers.filter((peer) => { | |
19 | - if (ip.isPrivate(peer.host) && (peer.source === 'local')) { | |
20 | - result.add(peer.key) | |
21 | - } | |
22 | - }) | |
23 | - }) | |
24 | - }, 5000) | |
14 | + refresh() | |
15 | + setInterval(refresh, 10e3) | |
25 | 16 | |
26 | 17 | cache = result |
27 | 18 | return result |
28 | 19 | } |
20 | + | |
21 | + // scope | |
22 | + | |
23 | + function refresh () { | |
24 | + sbot_list_local((err, keys) => { | |
25 | + if (err) throw console.log(err) | |
26 | + result.set(keys) | |
27 | + }) | |
28 | + } | |
29 | 29 | } |
modules/public.js | ||
---|---|---|
@@ -21,20 +21,22 @@ | ||
21 | 21 | var obs_recently_updated_feeds = plugs.first(exports.obs_recently_updated_feeds = []) |
22 | 22 | var avatar_image = plugs.first(exports.avatar_image = []) |
23 | 23 | var avatar_name = plugs.first(exports.avatar_name = []) |
24 | 24 | var obs_local = plugs.first(exports.obs_local = []) |
25 | +var obs_connected = plugs.first(exports.obs_connected = []) | |
25 | 26 | |
26 | 27 | exports.screen_view = function (path, sbot) { |
27 | 28 | if (path === '/public') { |
28 | 29 | var id = get_id() |
29 | - var channels = computed(obs_channels(), items => items.slice(0, 6), {comparer: arrayEq}) | |
30 | + var channels = computed(obs_channels(), items => items.slice(0, 8), {comparer: arrayEq}) | |
30 | 31 | var subscribedChannels = obs_subscribed_channels(id) |
31 | 32 | var loading = computed(subscribedChannels.sync, x => !x) |
33 | + var connectedPeers = obs_connected() | |
32 | 34 | var localPeers = obs_local() |
33 | 35 | var following = obs_following(id) |
34 | 36 | |
35 | 37 | var oldest = Date.now() - (2 * 24 * 60 * 60e3) |
36 | - getFirstMessage(id, (err, msg) => { | |
38 | + getFirstMessage(id, (_, msg) => { | |
37 | 39 | if (msg) { |
38 | 40 | // fall back to timestamp stream before this, give 48 hrs for feeds to stabilize |
39 | 41 | if (msg.value.timestamp > oldest) { |
40 | 42 | oldest = Date.now() |
@@ -42,9 +44,9 @@ | ||
42 | 44 | } |
43 | 45 | }) |
44 | 46 | |
45 | 47 | var whoToFollow = computed([obs_following(id), obs_recently_updated_feeds(200)], (following, recent) => { |
46 | - return Array.from(recent).filter(x => x !== id && !following.has(x)).slice(0, 20) | |
48 | + return Array.from(recent).filter(x => x !== id && !following.has(x)).slice(0, 10) | |
47 | 49 | }) |
48 | 50 | |
49 | 51 | return h('SplitView', [ |
50 | 52 | h('div.side', [ |
@@ -77,8 +79,11 @@ | ||
77 | 79 | when(computed(localPeers, x => x.length), h('h2', 'Local')), |
78 | 80 | h('ProfileList', [ |
79 | 81 | MutantMap(localPeers, (id) => { |
80 | 82 | return h('a.profile', { |
83 | + classList: [ | |
84 | + when(computed([connectedPeers, id], (p, id) => p.includes(id)), '-connected') | |
85 | + ], | |
81 | 86 | href: `#${id}` |
82 | 87 | }, [ |
83 | 88 | h('div.avatar', [avatar_image(id)]), |
84 | 89 | h('div.main', [ |
modules/obs-connected.js | ||
---|---|---|
@@ -1,0 +1,29 @@ | ||
1 | +var MutantSet = require('@mmckegg/mutant/set') | |
2 | +var plugs = require('patchbay/plugs') | |
3 | +var sbot_gossip_peers = plugs.first(exports.sbot_gossip_peers = []) | |
4 | + | |
5 | +var cache = null | |
6 | + | |
7 | +exports.obs_connected = function () { | |
8 | + if (cache) { | |
9 | + return cache | |
10 | + } else { | |
11 | + var result = MutantSet([], {nextTick: true}) | |
12 | + // todo: make this clean up on unlisten | |
13 | + | |
14 | + refresh() | |
15 | + setInterval(refresh, 10e3) | |
16 | + | |
17 | + cache = result | |
18 | + return result | |
19 | + } | |
20 | + | |
21 | + // scope | |
22 | + | |
23 | + function refresh () { | |
24 | + sbot_gossip_peers((err, peers) => { | |
25 | + if (err) throw console.log(err) | |
26 | + result.set(peers.filter(x => x.state === 'connected').map(x => x.key)) | |
27 | + }) | |
28 | + } | |
29 | +} |
server-process.js | ||
---|---|---|
@@ -10,9 +10,9 @@ | ||
10 | 10 | .use(require('scuttlebot/plugins/replicate')) |
11 | 11 | .use(require('ssb-blobs')) |
12 | 12 | .use(require('scuttlebot/plugins/invite')) |
13 | 13 | .use(require('scuttlebot/plugins/block')) |
14 | - .use(require('scuttlebot/plugins/local')) | |
14 | + .use(require('./lib/local-with-list')) | |
15 | 15 | .use(require('scuttlebot/plugins/logging')) |
16 | 16 | .use(require('scuttlebot/plugins/private')) |
17 | 17 | .use(require('ssb-links')) |
18 | 18 | .use(require('ssb-query')) |
styles/profile-list.mcss | ||
---|---|---|
@@ -12,10 +12,20 @@ | ||
12 | 12 | |
13 | 13 | background-repeat: no-repeat |
14 | 14 | background-position: right |
15 | 15 | |
16 | + -connected { | |
17 | + background-image: svg(connected) | |
18 | + } | |
19 | + | |
20 | + @svg connected { | |
21 | + width: 20px | |
22 | + height: 12px | |
23 | + content: "<circle cx='6' stroke='none' fill='green' cy='6' r='5' />" | |
24 | + } | |
25 | + | |
16 | 26 | :hover { |
17 | - background: rgba(255, 255, 255, 0.4); | |
27 | + background-color: rgba(255, 255, 255, 0.4); | |
18 | 28 | } |
19 | 29 | |
20 | 30 | div.avatar { |
21 | 31 | img { |
Built with git-ssb-web