git ssb

16+

Dominic / patchbay



Tree: 3068af308439c6051b8c9e06bc9316b15827aa23

Files: 3068af308439c6051b8c9e06bc9316b15827aa23 / app / page / network.js

6736 bytesRaw
1const nest = require('depnest')
2const { h, Value, Dict, onceTrue, computed, watch, watchAll, throttle } = require('mutant')
3const Chart = require('chart.js')
4const pull = require('pull-stream')
5
6const MINUTE = 60 * 1000
7const DAY = 24 * 60 * MINUTE
8
9const GRAPH_Y_STEP = 50
10const GRAPH_Y_MIN = 100
11
12exports.gives = nest({
13 'app.html.menuItem': true,
14 'app.page.network': true
15})
16
17exports.needs = nest({
18 'about.html.avatar': 'first',
19 'app.sync.goTo': 'first',
20 'sbot.obs.connection': 'first',
21 'sbot.obs.localPeers': 'first',
22 'sbot.obs.connectedPeers': 'first'
23})
24
25exports.create = function (api) {
26 return nest({
27 'app.html.menuItem': menuItem,
28 'app.page.network': networkPage
29 })
30
31 function menuItem () {
32 return h('a', {
33 'ev-click': () => api.app.sync.goTo({ page: 'network' })
34 }, '/network')
35 }
36
37 function networkPage (location) {
38 const minsPerStep = 10
39 const scale = 1 * DAY
40
41 const state = buildState({ api, minsPerStep, scale })
42 const canvas = h('canvas', { height: 500, width: 1200, style: { height: '500px', width: '1200px' } })
43
44 const page = h('NetworkPage', { title: '/network' }, [
45 h('div.container', [
46 h('h1', 'Network'),
47 h('section', [
48 h('h2', [
49 'Local Peers',
50 h('i.fa.fa-question-circle-o', { title: 'these are people on the same WiFi/ LAN as you right now. You might not know some of them yet, but you can click through to find out more about them and follow them if you like.' })
51 ]),
52 computed(state.localPeers, peers => {
53 if (!peers.length) return h('p', 'No local peers (on same wifi/ LAN)')
54
55 return peers.map(peer => api.about.html.avatar(peer))
56 })
57 ]),
58 h('section', [
59 h('h2', [
60 'Remote Peers',
61 h('i.fa.fa-question-circle-o', { title: 'these are peers which have fixed addresses, and are likely friends of friends (a.k.a. pubs)' })
62 ]),
63 computed(state.remotePeers, peers => {
64 if (!peers.length) return h('p', 'No remote peers connected')
65
66 return peers.map(peer => api.about.html.avatar(peer))
67 })
68 ]),
69 h('section', [
70 h('h2', [
71 'Received Messages',
72 h('i.fa.fa-question-circle-o', {
73 title: `Messages received per ${minsPerStep}-minute block over the last ${scale / DAY} days`
74 })
75 ]),
76 canvas
77 ])
78 ])
79 ])
80
81 initialiseChart({ canvas, state })
82
83 return page
84 }
85}
86
87function initialiseChart ({ canvas, state: { data, range } }) {
88 var chart = new Chart(canvas.getContext('2d'), chartConfig(range))
89
90 watch(range, ({ lower, upper }) => {
91 // set horizontal scale
92 chart.options.scales.xAxes[0].time.min = lower
93 chart.options.scales.xAxes[0].time.max = upper
94 chart.update()
95 })
96
97 watchAll([throttle(data, 300), range], (data, { lower, upper }) => {
98 const _data = Object.keys(data)
99 .sort((a, b) => a < b ? -1 : +1)
100 .map(ts => {
101 return {
102 t: Number(ts), // NOTE - might need to offset by a half-step ?
103 y: data[ts]
104 }
105 })
106
107 // update chard data
108 chart.data.datasets[0].data = _data
109
110 // scales the height of the graph (to the visible data)!
111 const slice = _data
112 .filter(d => d.t >= lower && d.t < upper)
113 .map(d => d.y)
114 .sort((a, b) => a > b ? -1 : +1)
115
116 var h = slice[0]
117 if (!h || h < GRAPH_Y_MIN) h = GRAPH_Y_MIN // min-height
118 else h = h + (GRAPH_Y_STEP - h % GRAPH_Y_STEP) // round height to multiples of GRAPH_Y_STEP
119 chart.options.scales.yAxes[0].ticks.max = h
120
121 chart.update()
122 })
123}
124
125// ///// HELPERS /////
126
127function buildState ({ api, minsPerStep, scale }) {
128 const data = Dict({
129 [toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE]: 0,
130 [toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE - scale]: 0
131 })
132 onceTrue(api.sbot.obs.connection, server => {
133 getData({ data, server, minsPerStep, scale })
134 })
135
136 const latest = Value(toTimeBlock(Date.now(), minsPerStep))
137 // start of the most recent bar
138 setInterval(() => {
139 latest.set(toTimeBlock(Date.now(), minsPerStep))
140 }, minsPerStep / 4 * MINUTE)
141
142 const range = computed([latest], (latest) => {
143 return {
144 upper: latest + minsPerStep * MINUTE,
145 lower: latest + minsPerStep * MINUTE - scale
146 }
147 })
148
149 const localPeers = throttle(api.sbot.obs.localPeers(), 1000)
150 const remotePeers = computed([localPeers, throttle(api.sbot.obs.connectedPeers(), 1000)], (local, connected) => {
151 return connected.filter(peer => !local.includes(peer))
152 })
153
154 return {
155 data,
156 range,
157 localPeers,
158 remotePeers
159 }
160}
161
162function getData ({ data, server, minsPerStep, scale }) {
163 const upperEnd = toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE
164 const lowerBound = upperEnd - scale
165
166 const query = [
167 {
168 $filter: {
169 timestamp: { $gte: lowerBound }
170 }
171 }, {
172 $filter: {
173 value: {
174 author: { $ne: server.id }
175 }
176 }
177 }, {
178 $map: {
179 ts: ['timestamp']
180 }
181 }
182 ]
183
184 pull(
185 server.query.read({ query, live: true }),
186 pull.filter(m => !m.sync),
187 pull.map(m => toTimeBlock(m.ts, minsPerStep)),
188 pull.drain(ts => {
189 if (data.has(ts)) data.put(ts, data.get(ts) + 1)
190 else data.put(ts, 1)
191 })
192 )
193}
194
195
196function toTimeBlock (ts, minsPerStep) {
197 return Math.floor(ts / (minsPerStep * MINUTE)) * (minsPerStep * MINUTE)
198}
199
200function chartConfig ({ lower, upper }) {
201 const barColor = 'hsla(215, 57%, 60%, 1)'
202
203 return {
204 type: 'bar',
205 data: {
206 datasets: [{
207 backgroundColor: barColor,
208 borderColor: barColor,
209 data: []
210 }]
211 },
212 options: {
213 legend: {
214 display: false
215 },
216 scales: {
217 xAxes: [{
218 type: 'time',
219 distribution: 'linear',
220 time: {
221 // unit: 'day',
222 // min: lower,
223 // max: upper,
224 // tooltipFormat: 'MMMM D',
225 // stepSize: 7
226 unit: 'minute',
227 min: lower,
228 max: upper,
229 tooltipFormat: 'HH:mm',
230 stepSize: 4 * 60
231 // stepSize: 240
232 },
233 bounds: 'ticks',
234 ticks: {
235 // maxTicksLimit: 4 // already disabled
236 },
237 gridLines: {
238 display: false
239 }
240 // maxBarThickness: 2
241 }],
242
243 yAxes: [{
244 ticks: {
245 min: 0,
246 stepSize: GRAPH_Y_STEP,
247 suggestedMax: GRAPH_Y_MIN
248 }
249 }]
250 },
251 animation: {
252 // duration: 300
253 }
254 }
255 }
256}
257

Built with git-ssb-web