git ssb

2+

mixmix / ticktack



Commit 0d44fc79a12ebf427d26a10693fcfce4fa2e7451

smooth scrolling stats

mix irving committed on 4/23/2018, 2:33:08 AM
Parent: 00c93e22ffba28ec94a7aca067a20eec8ad3a0bd

Files changed

app/page/statsShow.jschanged
app/page/statsShow.mcsschanged
app/page/statsShow.jsView
@@ -1,10 +1,11 @@
11 const nest = require('depnest')
2-const { h, Value, Struct, Array: MutantArray, Dict, onceTrue, map, computed, dictToCollection, throttle } = require('mutant')
2+const { h, resolve, when, Value, Struct, Array: MutantArray, Dict, onceTrue, map, computed, dictToCollection, throttle } = require('mutant')
33 const pull = require('pull-stream')
44 const marksum = require('markdown-summary')
55 const Chart = require('chart.js')
66 const groupBy = require('lodash/groupBy')
7+const merge = require('lodash/merge')
78
89 exports.gives = nest('app.page.statsShow')
910
1011 exports.needs = nest({
@@ -34,12 +35,17 @@
3435 lower: now - (howFarBack + 1) * THIRTY_DAYS
3536 }
3637 })
3738
38- var rangeComments = computed([throttle(dictToCollection(store.comments), 1000), range], (comments, range) => {
39+ var commentsAll = computed(throttle(dictToCollection(store.comments), 1000), (comments) => {
3940 return comments
4041 .map(c => c.value)
4142 .reduce((n, sofar) => [...n, ...sofar], [])
43+ })
44+
45+ // this should perhaps be reduced to just return commentsContextCount
46+ var visibleComments = computed([commentsAll, range], (comments, range) => {
47+ return comments
4248 .filter(msg => {
4349 const ts = msg.value.timestamp
4450 return ts >= range.lower && ts <= range.upper
4551 })
@@ -48,152 +54,98 @@
4854 var rangeLikes = computed([throttle(dictToCollection(store.likes), 1000), range], (likes, range) => {
4955 return likes
5056 .map(c => c.value)
5157 .reduce((n, sofar) => [...n, ...sofar], [])
52- .filter(msg => {
53- const ts = msg.value.timestamp
54- return ts >= range.lower && ts <= range.upper
55- })
58+ // .filter(msg => {
59+ // const ts = msg.value.timestamp
60+ // return ts >= range.lower && ts <= range.upper
61+ // })
5662 })
5763
5864 onceTrue(api.sbot.obs.connection, server => {
5965 fetchBlogs({ server, store })
60- // fetches blogs and all associated data
6166
62- // ///// test code /////
63- // var blogKey = '%3JeEg7voZF4aplk9xCEAfhFOx+zocbKhgstzvfD3G8w=.sha256'
64- // console.log('fetching comments', blogKey) // has 2 comments, 1 like
65-
66- // pull(
67- // server.blogStats.read({
68- // gt: ['L', blogKey, null],
69- // lt: ['L', blogKey+'~', undefined],
70- // // gt: ['L', blogKey, null],
71- // // lte: ['L', blogKey+'~', undefined],
72- // // limit: 100,
73- // keys: true,
74- // values: true,
75- // seqs: false,
76- // reverse: true
77- // }),
78- // // pull.filter(o => o.key[1] === blogKey),
79- // pull.log(() => console.log('DONE'))
80- // )
81- /// ///// test code /////
67+ // const query = {
68+ // gt: ['C', null, range().lower],
69+ // lt: ['C', undefined, range().upper],
70+ // reverse: true,
71+ // values: true,
72+ // keys: false,
73+ // seqs: false
74+ // }
75+ // console.log('test query', query)
76+ // pull(server.blogStats.read(query), pull.log(() => console.log('DONE')))
8277 })
8378 const canvas = h('canvas', { height: 200, width: 600, style: { height: '200px', width: '600px' } })
8479
8580 const page = h('Page -statsShow', [
86- h('div.content', [
87- h('h1', 'Stats'),
88- h('section.totals', [
89- h('div.comments', [
90- h('div.count', computed(rangeComments, msgs => msgs.length)),
91- h('strong', 'Comments'),
92- '(30 days)'
81+ h('Scroller.content', [
82+ h('div.content', [
83+ h('h1', 'Stats'),
84+ h('section.totals', [
85+ h('div.comments', [
86+ h('div.count', computed(visibleComments, msgs => msgs.length)),
87+ h('strong', 'Comments'),
88+ '(30 days)'
89+ ]),
90+ h('div.likes', [
91+ h('div.count', computed(rangeLikes, msgs => msgs.length)),
92+ h('strong', 'Likes'),
93+ '(30 days)'
94+ ]),
95+ h('div.shares', [
96+ ])
9397 ]),
94- h('div.likes', [
95- h('div.count', computed(rangeLikes, msgs => msgs.length)),
96- h('strong', 'Likes'),
97- '(30 days)'
98- ]),
99- h('div.shares', [
100- ])
101- ]),
102- h('section.graph', [
103- canvas,
104- // TODO insert actual graph
105- h('div', [
106- // h('div', [ 'Comments ', map(rangeComments, msg => [new Date(msg.value.timestamp).toDateString(), ' ']) ]),
107- // h('div', [ 'Likes ', map(rangeLikes, msg => [new Date(msg.value.timestamp).toDateString(), ' ']) ])
108- ]),
109- h('div.changeRange', [
110- h('a', { href: '#', 'ev-click': () => howFarBack.set(howFarBack() + 1) }, '< Prev 30 days'),
111- ' | ',
112- h('a', { href: '#', 'ev-click': () => howFarBack.set(howFarBack() - 1) }, 'Next 30 days >')
113- ])
114- ]),
115- h('table.blogs', [
116- h('thead', [
117- h('tr', [
118- h('th.details'),
119- h('th.comment', 'Comments'),
120- h('th.likes', 'Likes')
98+ h('section.graph', [
99+ canvas,
100+ h('div.changeRange', [
101+ '< ',
102+ h('a', { 'ev-click': () => howFarBack.set(howFarBack() + 1) }, 'Prev 30 days'),
103+ ' | ',
104+ when(howFarBack,
105+ h('a', { 'ev-click': () => howFarBack.set(howFarBack() - 1) }, 'Next 30 days'),
106+ h('span', 'Next 30 days')
107+ ),
108+ ' >'
121109 ])
122110 ]),
123- h('tbody', map(store.blogs, blog => h('tr.blog', { id: blog.key }, [
124- h('td.details', [
125- h('div.title', {}, getTitle(blog)),
126- h('a',
127- {
128- href: '#',
129- 'ev-click': viewBlog(blog)
130- },
131- 'View blog'
132- )
111+ h('table.blogs', [
112+ h('thead', [
113+ h('tr', [
114+ h('th.details'),
115+ h('th.comment', 'Comments'),
116+ h('th.likes', 'Likes')
117+ ])
133118 ]),
134- h('td.comments', computed(store.comments.get(blog.key), msgs => msgs ? msgs.length : 0)),
135- h('td.likes', computed(store.likes.get(blog.key), msgs => msgs ? msgs.length : 0))
136- // ]), { comparer: (a, b) => a === b }))
137- ])))
119+ h('tbody', map(store.blogs, blog => h('tr.blog', { id: blog.key }, [
120+ h('td.details', [
121+ h('div.title', {}, getTitle(blog)),
122+ h('a',
123+ {
124+ href: '#',
125+ 'ev-click': viewBlog(blog)
126+ },
127+ 'View blog'
128+ )
129+ ]),
130+ h('td.comments', computed(store.comments.get(blog.key), msgs => msgs ? msgs.length : 0)),
131+ h('td.likes', computed(store.likes.get(blog.key), msgs => msgs ? msgs.length : 0))
132+ // ]), { comparer: (a, b) => a === b }))
133+ ])))
134+ ])
138135 ])
139136 ])
140137 ])
141138
142- Chart.scaleService.updateScaleDefaults('linear', {
143- ticks: { min: 0 }
144- })
145- var chart = new Chart(canvas.getContext('2d'), {
146- type: 'bar',
147- data: {
148- datasets: [{
149- // label: 'My First dataset',
150- backgroundColor: 'hsla(215, 57%, 60%, 1)', // 'hsla(215, 57%, 43%, 1)',
151- borderColor: 'hsla(215, 57%, 60%, 1)',
152- // TODO set initial data as empty to make a good range
153- data: [
154- ]
155- }]
156- },
157- options: {
158- legend: {
159- display: false
160- },
161- scales: {
162- xAxes: [{
163- type: 'time',
164- distribution: 'linear',
165- time: {
166- unit: 'day',
167- min: new Date(range().lower),
168- max: new Date(range().upper)
169- },
170- bounds: 'ticks',
171- ticks: {
172- maxTicksLimit: 4
173- },
174- gridLines: {
175- display: false
176- },
177- maxBarThickness: 20
178- }],
139+ // Chart.scaleService.updateScaleDefaults('linear', {
140+ // ticks: { min: 0 }
141+ // })
142+ var chart = new Chart(canvas.getContext('2d'), chartConfig({ range, chartData: [] }))
179143
180- yAxes: [{
181- ticks: {
182- suggestedMin: 0,
183- suggestedMax: 10,
184- maxTicksLimit: 5
185- }
186- }]
187- },
188- animation: {
189- duration: 300
190- }
191- }
192- })
144+ const toDay = ts => Math.floor(ts / (24 * 60 * 60 * 1000))
193145
194- const toDay = ts => Math.floor(ts / (24 * 60 * 60 * 1000))
195- const rangeCommentData = computed(rangeComments, msgs => {
146+ // TODO take in context (comments/ likes / shares)
147+ const chartData = computed(commentsAll, msgs => {
196148 const grouped = groupBy(msgs, m => toDay(m.value.timestamp))
197149
198150 var data = Object.keys(grouped)
199151 .map(day => {
@@ -203,14 +155,16 @@
203155 }
204156 })
205157 return data
206158 })
207- rangeCommentData((newData) => {
208- chart.data.datasets[0].data = newData
209159
210- chart.options.scales.xAxes[0].time.min = new Date(range().lower)
211- chart.options.scales.xAxes[0].time.max = new Date(range().upper)
160+ chartData(() => {
161+ chart = merge(chart, chartConfig({ range, chartData }))
162+ chart.update()
163+ })
212164
165+ range(() => {
166+ chart = merge(chart, chartConfig({ range, chartData }))
213167 chart.update()
214168 })
215169
216170 return page
@@ -263,4 +217,65 @@
263217 // if exists - over-write or delete
264218 })
265219 )
266220 }
221+
222+function chartConfig ({ range, chartData }) {
223+ const { lower, upper } = resolve(range)
224+
225+ const data = resolve(chartData) || []
226+ const slice = data
227+ .filter(d => d.t >= lower && d.t <= upper)
228+ .map(d => d.y)
229+ .sort((a, b) => a < b)
230+ const localMax = slice[0] ? Math.max(slice[0], 10) : 10
231+
232+ return {
233+ type: 'bar',
234+ data: {
235+ datasets: [{
236+ // label: 'My First dataset',
237+ backgroundColor: 'hsla(215, 57%, 60%, 1)', // 'hsla(215, 57%, 43%, 1)',
238+ borderColor: 'hsla(215, 57%, 60%, 1)',
239+ // TODO set initial data as empty to make a good range
240+ data
241+ }]
242+ },
243+ options: {
244+ legend: {
245+ display: false
246+ },
247+ scales: {
248+ xAxes: [{
249+ type: 'time',
250+ distribution: 'linear',
251+ time: {
252+ unit: 'day',
253+ min: new Date(lower),
254+ max: new Date(upper),
255+ tooltipFormat: 'MMMM D',
256+ stepSize: 7
257+ },
258+ bounds: 'ticks',
259+ ticks: {
260+ // maxTicksLimit: 4
261+ },
262+ gridLines: {
263+ display: false
264+ },
265+ maxBarThickness: 20
266+ }],
267+
268+ yAxes: [{
269+ ticks: {
270+ min: 0,
271+ max: Math.max(localMax, 10),
272+ stepSize: 5
273+ }
274+ }]
275+ },
276+ animation: {
277+ // duration: 300
278+ }
279+ }
280+ }
281+}
app/page/statsShow.mcssView
@@ -1,91 +1,101 @@
11 Page -statsShow {
2- div.content {
3- flex-grow: 0
4- $backgroundPrimaryText
5- margin-top: 1rem
6- width: 1000px
2+ div.Scroller {
3+ display: flex
4+ justify-content: center
75
8- h1 {
9- font-size: .8rem
10- letter-spacing: 4px
11- }
6+ div.content {
7+ flex-grow: 0
8+ $backgroundPrimaryText
9+ padding: 1rem
10+ width: 1000px
1211
13- section.totals {
14- display: flex
15- justify-content: space-between
12+ h1 {
13+ font-size: .8rem
14+ letter-spacing: 4px
15+ }
1616
17- div {
18- div.count {
19- font-size: 3rem
20- font-weight: 600
21- margin-right: .5rem
17+ section.totals {
18+ display: flex
19+ justify-content: space-between
20+
21+ div {
22+ div.count {
23+ font-size: 3rem
24+ font-weight: 600
25+ margin-right: .5rem
26+ }
27+ strong {
28+ margin-right: .5rem
29+ }
2230 }
23- strong {
24- margin-right: .5rem
25- }
2631 }
27- }
2832
29- section.graph {
30- display: flex
31- flex-wrap: wrap
32- justify-content: center
33+ section.graph {
34+ display: flex
35+ flex-wrap: wrap
36+ justify-content: center
3337
34- margin: 2rem 0
38+ margin: 2rem 0
3539
36- canvas {
37- margin-bottom: 1rem
40+ canvas {
41+ margin-bottom: 1rem
42+ }
43+ div.changeRange {
44+ a {
45+ cursor: pointer
46+ :hover { text-decoration: underline }
47+ }
48+ span {
49+ $colorFontSubtle
50+ }
51+ }
3852 }
39- div.changeRange {
40-
41- }
42- }
4353
44- table.blogs {
45- margin: 1rem 0
54+ table.blogs {
55+ margin: 1rem 0
4656
47- thead {
48- tr {
49- margin-bottom: 1rem
50- color: hsl(0, 0%, 25%)
51- td {
57+ thead {
58+ tr {
59+ margin-bottom: 1rem
60+ color: hsl(0, 0%, 25%)
61+ td {
5262
63+ }
5364 }
5465 }
55- }
56- tbody {
57- tr.blog {
58- margin-bottom: 1rem
59- td.details {
60- width: 100%
61- padding: .8rem 2rem .8rem 0
62- border-bottom: 1px solid rgba(0, 0, 0, .05)
63-
64- div.title {
65- font-size: 1.3rem
66- font-weight: 600
67- margin-bottom: .5rem
68- }
66+ tbody {
67+ tr.blog {
68+ margin-bottom: 1rem
69+ td.details {
70+ width: 100%
71+ padding: .8rem 2rem .8rem 0
72+ border-bottom: 1px solid rgba(0, 0, 0, .05)
73+
74+ div.title {
75+ font-size: 1.3rem
76+ font-weight: 600
77+ margin-bottom: .5rem
78+ }
6979
70- a {
71- color: hsl(0, 0%, 15%)
72- font-size: .8rem
73- text-decoration: none
80+ a {
81+ color: hsl(0, 0%, 15%)
82+ font-size: .8rem
83+ text-decoration: none
7484
75- :hover {
76- text-decoration: underline
85+ :hover {
86+ text-decoration: underline
87+ }
7788 }
7889 }
90+ td.comments, td.likes {
91+ padding: 0 2.5rem
92+ font-size: 1.3rem
93+ font-weight: 600
94+ text-align: center
95+ }
7996 }
80- td.comments, td.likes {
81- padding: 0 2.5rem
82- font-size: 1.3rem
83- font-weight: 600
84- text-align: center
85- }
8697 }
8798 }
8899 }
89100 }
90-
91101 }

Built with git-ssb-web