Files: a893a560b31905d216fdb21487f862a094bb7d6f / app / page / statsShow.js
5821 bytesRaw
1 | const nest = require('depnest') |
2 | const { h, Value, Struct, Array: MutantArray, Dict, onceTrue, map, computed, dictToCollection, throttle } = require('mutant') |
3 | const pull = require('pull-stream') |
4 | const marksum = require('markdown-summary') |
5 | |
6 | exports.gives = nest('app.page.statsShow') |
7 | |
8 | exports.needs = nest({ |
9 | 'sbot.obs.connection': 'first', |
10 | 'history.sync.push': 'first' |
11 | }) |
12 | |
13 | exports.create = (api) => { |
14 | return nest('app.page.statsShow', statsShow) |
15 | |
16 | function statsShow (location) { |
17 | var store = Struct({ |
18 | blogs: MutantArray([]), |
19 | comments: Dict(), |
20 | likes: Dict() |
21 | }) |
22 | |
23 | var howFarBack = Value(0) |
24 | // stats show a moving window of 30 days |
25 | const thirtyDays = 30 * 24 * 60 * 60 * 1000 |
26 | |
27 | // TODO |
28 | var range = computed([howFarBack], howFarBack => { |
29 | const now = Date.now() |
30 | return { |
31 | upper: now - howFarBack * thirtyDays, |
32 | lower: now - (howFarBack + 1) * thirtyDays |
33 | } |
34 | }) |
35 | |
36 | var rangeComments = computed([throttle(dictToCollection(store.comments), 1000), range], (comments, range) => { |
37 | return comments |
38 | .map(c => c.value) |
39 | .reduce((n, sofar) => [...n, ...sofar], []) |
40 | .filter(msg => { |
41 | const ts = msg.value.timestamp |
42 | return ts >= range.lower && ts <= range.upper |
43 | }) |
44 | }) |
45 | |
46 | var rangeLikes = computed([throttle(dictToCollection(store.likes), 1000), range], (likes, range) => { |
47 | return likes |
48 | .map(c => c.value) |
49 | .reduce((n, sofar) => [...n, ...sofar], []) |
50 | .filter(msg => { |
51 | const ts = msg.value.timestamp |
52 | return ts >= range.lower && ts <= range.upper |
53 | }) |
54 | }) |
55 | |
56 | onceTrue(api.sbot.obs.connection, server => { |
57 | fetchBlogs({ server, store }) |
58 | // fetches blogs and all associated data |
59 | |
60 | // ///// test code ///// |
61 | // var blogKey = '%3JeEg7voZF4aplk9xCEAfhFOx+zocbKhgstzvfD3G8w=.sha256' |
62 | // console.log('fetching comments', blogKey) // has 2 comments, 1 like |
63 | |
64 | // pull( |
65 | // server.blogStats.read({ |
66 | // gt: ['L', blogKey, null], |
67 | // lt: ['L', blogKey+'~', undefined], |
68 | // // gt: ['L', blogKey, null], |
69 | // // lte: ['L', blogKey+'~', undefined], |
70 | // // limit: 100, |
71 | // keys: true, |
72 | // values: true, |
73 | // seqs: false, |
74 | // reverse: true |
75 | // }), |
76 | // // pull.filter(o => o.key[1] === blogKey), |
77 | // pull.log(() => console.log('DONE')) |
78 | // ) |
79 | /// ///// test code ///// |
80 | }) |
81 | |
82 | return h('Page -statsShow', [ |
83 | h('div.content', [ |
84 | h('h1', 'Stats'), |
85 | h('section.totals', [ |
86 | h('div.comments', [ |
87 | h('div.count', computed(rangeComments, msgs => msgs.length)), |
88 | h('strong', 'Comments'), |
89 | '(30 days)' |
90 | ]), |
91 | h('div.likes', [ |
92 | h('div.count', computed(rangeLikes, msgs => msgs.length)), |
93 | h('strong', 'Likes'), |
94 | '(30 days)' |
95 | ]), |
96 | h('div.shares', [ |
97 | ]) |
98 | ]), |
99 | h('section.graph', [ |
100 | // TODO insert actual graph |
101 | h('div', [ |
102 | // h('div', [ 'Comments ', map(rangeComments, msg => [new Date(msg.value.timestamp).toDateString(), ' ']) ]), |
103 | // h('div', [ 'Likes ', map(rangeLikes, msg => [new Date(msg.value.timestamp).toDateString(), ' ']) ]) |
104 | ]), |
105 | h('div', [ |
106 | h('a', { href: '#', 'ev-click': () => howFarBack.set(howFarBack() + 1) }, '< Prev 30 days'), |
107 | ' | ', |
108 | h('a', { href: '#', 'ev-click': () => howFarBack.set(howFarBack() - 1) }, 'Next 30 days >'), |
109 | h('div', ['howFarBack:', howFarBack]) // TODO change - this is temporary |
110 | ]) |
111 | ]), |
112 | h('table.blogs', [ |
113 | h('thead', [ |
114 | h('tr', [ |
115 | h('th.details'), |
116 | h('th.comment', 'Comments'), |
117 | h('th.likes', 'Likes') |
118 | ]) |
119 | ]), |
120 | h('tbody', map(store.blogs, blog => h('tr.blog', { id: blog.key }, [ |
121 | h('td.details', [ |
122 | h('div.title', {}, getTitle(blog)), |
123 | h('a', |
124 | { |
125 | href: '#', |
126 | 'ev-click': viewBlog(blog) |
127 | }, |
128 | 'View blog' |
129 | ) |
130 | ]), |
131 | h('td.comments', computed(store.comments.get(blog.key), msgs => msgs ? msgs.length : 0)), |
132 | h('td.likes', computed(store.likes.get(blog.key), msgs => msgs ? msgs.length : 0)) |
133 | // ]), { comparer: (a, b) => a === b })) |
134 | ]))) |
135 | ]) |
136 | ]) |
137 | ]) |
138 | } |
139 | |
140 | function viewBlog (blog) { |
141 | return () => api.history.sync.push(blog) |
142 | } |
143 | } |
144 | |
145 | function getTitle (blog) { |
146 | if (blog.value.content.title) return blog.value.content.title |
147 | else if (blog.value.content.text) return marksum.title(blog.value.content.text) |
148 | else return blog.key |
149 | } |
150 | |
151 | function fetchBlogs ({ server, store }) { |
152 | pull( |
153 | server.blogStats.readBlogs({ reverse: false }), |
154 | pull.drain(blog => { |
155 | store.blogs.push(blog) |
156 | |
157 | fetchComments({ server, store, blog }) |
158 | fetchLikes({ server, store, blog }) |
159 | }) |
160 | ) |
161 | } |
162 | |
163 | function fetchComments ({ server, store, blog }) { |
164 | if (!store.comments.has(blog.key)) store.comments.put(blog.key, MutantArray()) |
165 | |
166 | pull( |
167 | server.blogStats.readComments(blog), |
168 | pull.drain(msg => { |
169 | store.comments.get(blog.key).push(msg) |
170 | // TODO remove my comments from count? |
171 | }) |
172 | ) |
173 | } |
174 | |
175 | function fetchLikes ({ server, store, blog }) { |
176 | if (!store.likes.has(blog.key)) store.likes.put(blog.key, MutantArray()) |
177 | |
178 | pull( |
179 | server.blogStats.readLikes(blog), |
180 | pull.drain(msg => { |
181 | store.likes.get(blog.key).push(msg) |
182 | // TODO this needs reducing... like + unlike are muddled in here |
183 | // find any thing by same author |
184 | // if exists - over-write or delete |
185 | }) |
186 | ) |
187 | } |
188 |
Built with git-ssb-web