Commit abd49a5d6c84f499180e6bdbb52e5188d42df75e
Merge pull request #67 from ssbc/network_refactor
Network refactormix irving authored on 1/13/2017, 5:32:32 AM
GitHub committed on 1/13/2017, 5:32:32 AM
Parent: 3e3485dd605cf45a5f56a2864e4e437f7a9006c7
Parent: 2d80ace8ef63530006b28f25f53eb7577cb929c6
Files changed
h.js | changed |
modules_basic/about.js | changed |
modules_basic/avatar.js | changed |
modules_basic/message-author.js | changed |
modules_extra/network.js | changed |
modules_extra/network.mcss | added |
package.json | changed |
h.js | ||
---|---|---|
@@ -1,1 +1,4 @@ | ||
1 | -module.exports = require('micro-css/h')(require('@mmckegg/mutant/html-element')) | |
1 … | +const { h } = require('@mmckegg/mutant') | |
2 … | + | |
3 … | +module.exports = require('micro-css/h')(h) | |
4 … | + |
modules_basic/about.js | ||
---|---|---|
@@ -1,7 +1,7 @@ | ||
1 | 1 … | const fs = require('fs') |
2 | 2 … | const h = require('../h') |
3 | -const when = require('@mmckegg/mutant/when') | |
3 … | +const { when } = require('@mmckegg/mutant') | |
4 | 4 … | |
5 | 5 … | exports.needs = { |
6 | 6 … | blob_url: 'first', |
7 | 7 … | markdown: 'first' |
modules_basic/avatar.js | ||
---|---|---|
@@ -9,29 +9,37 @@ | ||
9 | 9 … | |
10 | 10 … | exports.gives = { |
11 | 11 … | avatar: true, |
12 | 12 … | avatar_image_name_link: true, |
13 | - avatar_image_link: true | |
13 … | + avatar_image_link: true, | |
14 … | + avatar_name_link: true | |
14 | 15 … | } |
15 | 16 … | |
16 | 17 … | exports.create = function (api) { |
18 … | + return { | |
19 … | + avatar, | |
20 … | + avatar_image_name_link, | |
21 … | + avatar_image_link, | |
22 … | + avatar_name_link | |
23 … | + } | |
17 | 24 … | |
18 | - var exports = {} | |
19 | - exports.avatar = function (author, classes) { | |
25 … | + function avatar (author, classes) { | |
20 | 26 … | return exports.avatar_image_name_link(author, classes) |
21 | 27 … | } |
22 | 28 … | |
23 | - exports.avatar_image_name_link = function (author, classes) { | |
29 … | + function avatar_image_name_link (author, classes) { | |
24 | 30 … | return api.avatar_link(author, [ |
25 | 31 … | api.avatar_image(author, classes), |
26 | 32 … | api.avatar_name(author) |
27 | 33 … | ]) |
28 | 34 … | } |
29 | 35 … | |
30 | - exports.avatar_image_link = function (author, classes) { | |
36 … | + function avatar_image_link (author, classes) { | |
31 | 37 … | return api.avatar_link(author, api.avatar_image(author, classes)) |
32 | 38 … | } |
33 | 39 … | |
34 | - return exports | |
40 … | + function avatar_name_link (author, classes) { | |
41 … | + return api.avatar_link(author, api.avatar_name(author)) | |
42 … | + } | |
35 | 43 … | } |
36 | 44 … | |
37 | 45 … |
modules_basic/message-author.js | ||
---|---|---|
@@ -1,7 +1,7 @@ | ||
1 | 1 … | const fs = require('fs') |
2 | 2 … | const h = require('../h') |
3 | -const when = require('@mmckegg/mutant/when') | |
3 … | +const { when }= require('@mmckegg/mutant') | |
4 | 4 … | |
5 | 5 … | exports.needs = { |
6 | 6 … | avatar_link: 'first', |
7 | 7 … | avatar_image: 'first', |
modules_extra/network.js | ||
---|---|---|
@@ -1,92 +1,84 @@ | ||
1 | -var isVisible = require('is-visible').isVisible | |
2 | -var h = require('hyperscript') | |
1 … | +const fs = require('fs') | |
2 … | +// const { isVisible } = require('is-visible') | |
3 … | +const h = require('../h') | |
4 … | +const human = require('human-time') | |
3 | 5 … | |
6 … | +const { | |
7 … | + Struct, Value, Dict, | |
8 … | + dictToCollection, map: mutantMap, when, computed | |
9 … | +} = require('@mmckegg/mutant') | |
10 … | + | |
4 | 11 … | //var avatar = plugs.first(exports.avatar = []) |
5 | 12 … | //var sbot_gossip_peers = plugs.first(exports.sbot_gossip_peers = []) |
6 | 13 … | //var sbot_gossip_connect = plugs.first(exports.sbot_gossip_connect = []) |
7 | 14 … | |
8 | 15 … | exports.needs = { |
9 | - avatar: 'first', | |
16 … | + avatar_image_link: 'first', | |
17 … | + avatar_name_link: 'first', | |
10 | 18 … | sbot_gossip_peers: 'first', |
11 | 19 … | sbot_gossip_connect: 'first' |
12 | 20 … | } |
13 | 21 … | |
14 | 22 … | exports.gives = { |
15 | 23 … | menu_items: true, |
16 | 24 … | builtin_tabs: true, |
17 | - screen_view: true | |
25 … | + screen_view: true, | |
26 … | + mcss: true | |
18 | 27 … | } |
19 | 28 … | |
20 | 29 … | //sbot_gossip_connect |
21 | 30 … | //sbot_gossip_add |
22 | 31 … | |
23 | -var human = require('human-time') | |
24 | 32 … | |
25 | 33 … | function legacyToMultiServer(addr) { |
26 | 34 … | return 'net:'+addr.host + ':'+addr.port + '~shs:'+addr.key.substring(1).replace('.ed25519','') |
27 | 35 … | } |
28 | 36 … | |
29 | -//types of peers | |
30 | - | |
31 | - | |
32 | 37 … | //on the same wifi network |
33 | -function isLocal (e) { | |
38 … | +function isLocal (peer) { | |
34 | 39 … | // don't rely on private ip address, because |
35 | 40 … | // cjdns creates fake private ip addresses. |
36 | - return ip.isPrivate(e.host) && e.type === 'local' | |
41 … | + return ip.isPrivate(peer.host) && peer.type === 'local' | |
37 | 42 … | } |
38 | 43 … | |
39 | 44 … | |
40 | -//pub is running scuttlebot >=8 | |
41 | -//have connected successfully. | |
42 | -function isLongterm (e) { | |
43 | - return e.ping && e.ping.rtt && e.ping.rtt.mean > 0 | |
44 | -} | |
45 … | +function getType (peer) { | |
46 … | + return ( | |
47 … | + isLongterm(peer) ? 'modern' | |
48 … | + : isLegacy(peer) ? 'legacy' | |
49 … | + : isInactive(peer) ? 'inactive' | |
50 … | + : isUnattempted(peer) ? 'unattempted' | |
51 … | + : 'other' //should never happen | |
52 … | + ) | |
45 | 53 … | |
46 | -//pub is running scuttlebot < 8 | |
47 | -//have connected sucessfully | |
48 | -function isLegacy (peer) { | |
49 | - return /connect/.test(peer.state) || (peer.duration && peer.duration.mean) > 0 && !isLongterm(peer) | |
50 | -} | |
54 … | + //pub is running scuttlebot >=8 | |
55 … | + //have connected successfully. | |
56 … | + function isLongterm (peer) { | |
57 … | + return peer.ping && peer.ping.rtt && peer.ping.rtt.mean > 0 | |
58 … | + } | |
51 | 59 … | |
52 | -//tried to connect, but failed. | |
53 | -function isInactive (e) { | |
54 | - return e.stateChange && (e.duration && e.duration.mean == 0) | |
55 | -} | |
60 … | + //pub is running scuttlebot < 8 | |
61 … | + //have connected sucessfully | |
62 … | + function isLegacy (peer) { | |
63 … | + return /connect/.test(peer.state) || (peer.duration && peer.duration.mean) > 0 && !isLongterm(peer) | |
64 … | + } | |
56 | 65 … | |
57 | -//havn't tried to connect peer yet. | |
58 | -function isUnattempted (e) { | |
59 | - return !e.stateChange | |
60 | -} | |
66 … | + //tried to connect, but failed. | |
67 … | + function isInactive (peer) { | |
68 … | + return peer.stateChange && (peer.duration && peer.duration.mean == 0) | |
69 … | + } | |
61 | 70 … | |
62 | -function getType (e) { | |
63 | - return ( | |
64 | - isLongterm(e) ? 'modern' | |
65 | - : isLegacy(e) ? 'legacy' | |
66 | - : isInactive(e) ? 'inactive' | |
67 | - : isUnattempted(e) ? 'unattempted' | |
68 | - : 'other' //should never happen | |
69 | - ) | |
71 … | + //havn't tried to connect peer yet. | |
72 … | + function isUnattempted (peer) { | |
73 … | + return !peer.stateChange | |
74 … | + } | |
70 | 75 … | } |
71 | 76 … | |
72 | -function origin (e) { | |
73 | - return e.source === 'local' ? 0 : 1 | |
77 … | +function origin (peer) { | |
78 … | + return peer.source === 'local' ? 0 : 1 | |
74 | 79 … | } |
75 | 80 … | |
76 | -var states = { | |
77 | - connected: 3, | |
78 | - connecting: 2 | |
79 | -} | |
80 | - | |
81 | -var types = { | |
82 | - modern: 4, | |
83 | - legacy: 3, | |
84 | - inactive: 2, | |
85 | - unattempted: 1, | |
86 | - other: 0 | |
87 | -} | |
88 | - | |
89 | 81 … | function round(n) { |
90 | 82 … | return Math.round(n*100)/100 |
91 | 83 … | } |
92 | 84 … | |
@@ -99,78 +91,163 @@ | ||
99 | 91 … | else |
100 | 92 … | return round(s)+'ms' |
101 | 93 … | } |
102 | 94 … | |
95 … | +function peerListSort (a, b) { | |
96 … | + var states = { | |
97 … | + connected: 3, | |
98 … | + connecting: 2 | |
99 … | + } | |
103 | 100 … | |
101 … | + //types of peers | |
102 … | + var types = { | |
103 … | + modern: 4, | |
104 … | + legacy: 3, | |
105 … | + inactive: 2, | |
106 … | + unattempted: 1, | |
107 … | + other: 0 | |
108 … | + } | |
104 | 109 … | |
105 | -exports.create = function (api) { | |
110 … | + return ( | |
111 … | + (states[b.state] || 0) - (states[a.state] || 0) | |
112 … | + || origin(b) - origin(a) | |
113 … | + || types[getType(b)] - types[getType(a)] | |
114 … | + || b.stateChange - a.stateChange | |
115 … | + ) | |
116 … | +} | |
106 | 117 … | |
107 | - return { | |
108 | - menu_items: function () { | |
109 | - return h('a', {href: '#/network'}, '/network') | |
110 | - }, | |
118 … | +function formatDate (time) { | |
119 … | + return new Date(time).toString() | |
120 … | +} | |
111 | 121 … | |
112 | - builtin_tabs: function () { | |
113 | - return ['/network'] | |
114 | - }, | |
122 … | +function humanDate (time) { | |
123 … | + return human(new Date(time)).replace(/minute/, 'min').replace(/second/, 'sec') | |
124 … | +} | |
115 | 125 … | |
116 | - screen_view: function (path) { | |
126 … | +exports.create = function (api) { | |
117 | 127 … | |
118 | - if(path !== '/network') return | |
128 … | + return { | |
129 … | + menu_items: () => h('a', {href: '#/network'}, '/network'), | |
130 … | + builtin_tabs: () => ['/network'], | |
131 … | + screen_view, | |
132 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
133 … | + } | |
119 | 134 … | |
120 | - var ol = h('ul.network') | |
135 … | + function screen_view (path) { | |
136 … | + if (path !== '/network') return | |
121 | 137 … | |
122 | - ;(function poll () { | |
138 … | + var peers = obs_gossip_peers(api) | |
123 | 139 … | |
124 | - //if this tab isn't open, don't update. | |
125 | - //todo: make a better way to do this... | |
126 | - if(!isVisible(ol)) | |
127 | - return setTimeout(poll, 1000) | |
140 … | + return h('div', { style: {'overflow':'auto'}, className: 'column scroller' }, [ | |
141 … | + h('Network', [ | |
142 … | + mutantMap(peers, peer => { | |
143 … | + var { key, ping, source, state, stateChange } = peer | |
128 | 144 … | |
129 | - api.sbot_gossip_peers(function (err, list) { | |
130 | - ol.innerHTML = '' | |
131 | - list.sort(function (a, b) { | |
132 | - return ( | |
133 | - (states[b.state] || 0) - (states[a.state] || 0) | |
134 | - || origin(b) - origin(a) | |
135 | - || types[getType(b)] - types[getType(a)] | |
136 | - || b.stateChange - a.stateChange | |
137 | - ) | |
138 | - }).forEach(function (peer) { | |
139 | - ol.appendChild(h('div', | |
140 | - api.avatar(peer.key, 'thumbnail'), | |
141 | - h('div', | |
142 | - peer.state || 'not connected', | |
143 | - ' ', | |
144 | - getType(peer), | |
145 | - ' ', | |
146 | - //TODO: show nicer details, with labels. etc. | |
147 | - (peer.ping && peer.ping.rtt) ? duration(peer.ping.rtt.mean) : '', | |
148 | - ' ', | |
149 | - (peer.ping && peer.ping.skew) ? duration(peer.ping.skew.mean) : '', | |
150 | - h('label', | |
151 | - {title: new Date(peer.stateChange).toString()}, | |
152 | - peer.stateChange && ('(' + human(new Date(peer.stateChange))) + ')') | |
153 | - ), | |
154 | - 'source:'+peer.source, | |
155 | - h('pre', legacyToMultiServer(peer)), | |
156 | - h('button', 'connect', {onclick: function () { | |
157 | - api.sbot_gossip_connect(peer, function (err) { | |
145 … | + return h('NetworkConnection', [ | |
146 … | + h('section.avatar', [ | |
147 … | + api.avatar_image_link(key()), | |
148 … | + ]), | |
149 … | + h('section.name', [ | |
150 … | + api.avatar_name_link(key()), | |
151 … | + ]), | |
152 … | + h('section.type', [ | |
153 … | + computed(peer, getType), | |
154 … | + ]), | |
155 … | + h('section.source', [ | |
156 … | + h('label', 'source:'), | |
157 … | + h('code', source) | |
158 … | + ]), | |
159 … | + h('section.state', [ | |
160 … | + h('label', 'state:'), | |
161 … | + h('i', { | |
162 … | + className: computed(state, (state) => '-'+state) | |
163 … | + }), | |
164 … | + h('code', when(state, state, 'not connected')) | |
165 … | + ]), | |
166 … | + h('section.actions', [ | |
167 … | + h('button', { | |
168 … | + 'ev-click': () => { | |
169 … | + api.sbot_gossip_connect(peer(), (err) => { | |
158 | 170 … | if(err) console.error(err) |
159 | - else console.log('connected to', peer) | |
171 … | + else console.log('connected to', peer()) | |
160 | 172 … | }) |
161 | - }}) | |
173 … | + }}, | |
174 … | + 'connect' | |
162 | 175 … | ) |
163 | - ) | |
164 | - }) | |
165 | - | |
166 | - setTimeout(poll, 5000) | |
176 … | + ]), | |
177 … | + h('section.time-ago', [ | |
178 … | + h('div', | |
179 … | + { title: computed(stateChange, formatDate) }, | |
180 … | + [ computed(stateChange, humanDate) ] | |
181 … | + ) | |
182 … | + ]), | |
183 … | + h('section.ping', [ | |
184 … | + h('div.rtt', [ | |
185 … | + h('label', 'rtt:'), | |
186 … | + h('code', computed(ping.rtt.mean, duration)) | |
187 … | + ]), | |
188 … | + h('div.skew', [ | |
189 … | + h('label', 'skew:'), | |
190 … | + h('code', computed(ping.skew.mean, duration)) | |
191 … | + ]), | |
192 … | + ]), | |
193 … | + h('section.address', [ | |
194 … | + h('code', computed(peer, legacyToMultiServer)) | |
195 … | + ]) | |
196 … | + ]) | |
167 | 197 … | }) |
198 … | + ]) | |
199 … | + ]) | |
200 … | + } | |
201 … | +} | |
168 | 202 … | |
169 | - })() | |
203 … | +function obs_gossip_peers (api) { | |
204 … | + var timer = null | |
205 … | + var state = Dict({}, { | |
206 … | + onListen: () => { | |
207 … | + timer = setInterval(refresh, 5e3) | |
208 … | + }, | |
209 … | + onUnlisten: () => { | |
210 … | + clearInterval(timer) | |
211 … | + } | |
212 … | + }) | |
170 | 213 … | |
171 | - return h('div.column.scroll-y', ol) | |
172 | - } | |
214 … | + refresh() | |
215 … | + | |
216 … | + return dictToCollection.values(state) | |
217 … | + | |
218 … | + function refresh () { | |
219 … | + api.sbot_gossip_peers((err, peers) => { | |
220 … | + peers.forEach(data => { | |
221 … | + var id = legacyToMultiServer(data) | |
222 … | + var current = state.get(id) | |
223 … | + if (!current) { | |
224 … | + current = Peer() | |
225 … | + current.set(data) | |
226 … | + state.put(id, current) | |
227 … | + } else { | |
228 … | + current.set(data) | |
229 … | + } | |
230 … | + }) | |
231 … | + }) | |
173 | 232 … | } |
174 | 233 … | } |
175 | 234 … | |
235 … | +function Peer () { | |
236 … | + var peer = Struct({ | |
237 … | + key: Value(), | |
238 … | + ping: Struct({ | |
239 … | + rtt: Struct({ | |
240 … | + mean: Value() | |
241 … | + }), | |
242 … | + skew: Struct({ | |
243 … | + mean: Value() | |
244 … | + }) | |
245 … | + }), | |
246 … | + source: Value(), | |
247 … | + state: Value(), | |
248 … | + stateChange: Value() | |
249 … | + }) | |
176 | 250 … | |
251 … | + return peer | |
252 … | +} | |
253 … | + |
modules_extra/network.mcss | ||
---|---|---|
@@ -1,0 +1,117 @@ | ||
1 … | +Network { | |
2 … | + div { | |
3 … | + margin: .8rem | |
4 … | + border-bottom: 1px solid gainsboro | |
5 … | + } | |
6 … | + | |
7 … | +} | |
8 … | + | |
9 … | +NetworkConnection { | |
10 … | + padding-left: 6rem | |
11 … | + padding-bottom: .8rem | |
12 … | + position: relative | |
13 … | + | |
14 … | + display: flex | |
15 … | + flex-direction: row | |
16 … | + flex-wrap: wrap | |
17 … | + align-items: center | |
18 … | + | |
19 … | + section.avatar { | |
20 … | + position: absolute | |
21 … | + left: 0 | |
22 … | + top: 0 | |
23 … | + | |
24 … | + a img { | |
25 … | + height: 3rem | |
26 … | + width: 3rem | |
27 … | + } | |
28 … | + } | |
29 … | + | |
30 … | + section.time-ago { | |
31 … | + position: absolute | |
32 … | + left: 0 | |
33 … | + top: 3.5rem | |
34 … | + | |
35 … | + font-size: .8rem | |
36 … | + $textSubtle | |
37 … | + } | |
38 … | + | |
39 … | + section.name { | |
40 … | + flex-basis: 100% | |
41 … | + | |
42 … | + margin-right: 1rem | |
43 … | + } | |
44 … | + | |
45 … | + section.type { | |
46 … | + flex-basis: 100% | |
47 … | + | |
48 … | + font-size: .8rem | |
49 … | + $textSubtle | |
50 … | + } | |
51 … | + | |
52 … | + section.source { | |
53 … | + flex-basis: 100% | |
54 … | + label { margin-right: .5rem } | |
55 … | + } | |
56 … | + | |
57 … | + section.state { | |
58 … | + display: flex | |
59 … | + flex-direction: row | |
60 … | + align-items: center | |
61 … | + | |
62 … | + margin-right: 2rem | |
63 … | + | |
64 … | + label { margin-right: .5rem } | |
65 … | + i { | |
66 … | + width: 1rem | |
67 … | + height: 1rem | |
68 … | + border-radius: .5rem | |
69 … | + | |
70 … | + margin: 0 .2rem 0 .5rem | |
71 … | + | |
72 … | + background: #ccc | |
73 … | + | |
74 … | + -connecting, -disconnecting { | |
75 … | + background: #95cc95 | |
76 … | + } | |
77 … | + | |
78 … | + -connected { | |
79 … | + background: #06e206 | |
80 … | + } | |
81 … | + } | |
82 … | + } | |
83 … | + | |
84 … | + section.actions { | |
85 … | + button { | |
86 … | + padding: .2rem | |
87 … | + font-size: .7rem | |
88 … | + } | |
89 … | + } | |
90 … | + | |
91 … | + | |
92 … | + section.ping { | |
93 … | + flex-basis: 100% | |
94 … | + display: flex | |
95 … | + | |
96 … | + font-size: .8rem | |
97 … | + $textSubtle | |
98 … | + | |
99 … | + div.rtt { | |
100 … | + flex-basis: 8rem | |
101 … | + label { margin-right: .5rem } | |
102 … | + } | |
103 … | + | |
104 … | + div.skew { | |
105 … | + flex-basis: 8rem | |
106 … | + label { margin-right: .5rem } | |
107 … | + } | |
108 … | + } | |
109 … | + | |
110 … | + section.address{ | |
111 … | + flex-basis: 100% | |
112 … | + | |
113 … | + font-size: .8rem | |
114 … | + $textSubtle | |
115 … | + } | |
116 … | +} | |
117 … | + |
package.json | ||
---|---|---|
@@ -7,9 +7,9 @@ | ||
7 | 7 … | "type": "git", |
8 | 8 … | "url": "git://github.com/dominictarr/patchbay.git" |
9 | 9 … | }, |
10 | 10 … | "dependencies": { |
11 | - "@mmckegg/mutant": "^3.10.1", | |
11 … | + "@mmckegg/mutant": "^3.12.0", | |
12 | 12 … | "brfs": "^1.4.3", |
13 | 13 … | "cont": "^1.0.3", |
14 | 14 … | "dataurl-": "^0.1.0", |
15 | 15 … | "depject": "^3.0.0", |
Built with git-ssb-web