git ssb

16+

Dominic / patchbay



Tree: 545298571be66be4f361b65df10a9ddbd073c492

Files: 545298571be66be4f361b65df10a9ddbd073c492 / app / page / network.js

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

Built with git-ssb-web