git ssb

16+

Dominic / patchbay



Tree: a9195c696583b3d711feccb0b9ec40bc2ea10f1e

Files: a9195c696583b3d711feccb0b9ec40bc2ea10f1e / app / page / network.js

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

Built with git-ssb-web