git ssb

2+

mixmix / ticktack



Commit 1c2359030ac279f1fdfef805d7938f3003372c24

minimal stats page

mix irving committed on 4/17/2018, 12:16:28 AM
Parent: 856796486d48c7b494769019a74b9b843471ee41

Files changed

app/html/header.jschanged
app/index.jschanged
app/page/statsShow.jsadded
background-process.jschanged
blog/sync/isBlog.jschanged
package-lock.jsonchanged
package.jsonchanged
router/sync/routes.jschanged
ssb-server-blog-stats.jsadded
app/html/header.jsView
@@ -38,8 +38,11 @@
3838 }),
3939 h('img.settings', {
4040 src: when(isSettings, assetPath('settings_on.png'), assetPath('settings.png')),
4141 'ev-click': () => push({page: 'settings'})
42+ }),
43+ h('i.fa.fa-bell', {
44+ 'ev-click': () => push({page: 'statsShow'})
4245 })
4346 ])
4447 ])
4548 })
app/index.jsView
@@ -46,8 +46,9 @@
4646 userEdit: require('./page/userEdit'),
4747 // userFind: require('./page/userFind'),
4848 userShow: require('./page/userShow'),
4949 splash: require('./page/splash'),
50+ statsShow: require('./page/statsShow'),
5051 threadNew: require('./page/threadNew'),
5152 threadShow: require('./page/threadShow')
5253 },
5354 sync: {
app/page/statsShow.jsView
@@ -1,0 +1,90 @@
1+const nest = require('depnest')
2+const { h, Array: MutantArray, Dict, onceTrue, map } = require('mutant')
3+const pull = require('pull-stream')
4+
5+exports.gives = nest('app.page.statsShow')
6+
7+exports.needs = nest({
8+ 'sbot.obs.connection': 'first'
9+})
10+
11+exports.create = (api) => {
12+ return nest('app.page.statsShow', statsShow)
13+
14+ function statsShow (location) {
15+ var blogs = MutantArray([])
16+ var comments = Dict()
17+ var likes = Dict()
18+ // comments(console.log)
19+
20+ onceTrue(api.sbot.obs.connection, server => {
21+ // console.log(Object.keys(server.blogStats))
22+
23+ fetchBlogs()
24+
25+ function fetchBlogs () {
26+ pull(
27+ server.blogStats.readBlogs({ reverse: false }),
28+ pull.drain(blog => {
29+ blogs.push(blog)
30+ fetchComments(blog)
31+ fetchLikes(blog)
32+ })
33+ )
34+ }
35+
36+ function fetchComments (blog) {
37+ if (!comments.has(blog.key)) comments.put(blog.key, MutantArray())
38+
39+ pull(
40+ server.blogStats.readComments({ blog }),
41+ pull.drain(comment => {
42+ comments.get(blog.key).push(comment)
43+ })
44+ )
45+ }
46+
47+ function fetchLikes (blog) {
48+ if (!likes.has(blog.key)) likes.put(blog.key, MutantArray())
49+
50+ pull(
51+ server.blogStats.readLikes({ blog }),
52+ pull.drain(comment => {
53+ likes.get(blog.key).push(comment)
54+ })
55+ )
56+ }
57+
58+ // ///// test code /////
59+ var blogKey = '%3JeEg7voZF4aplk9xCEAfhFOx+zocbKhgstzvfD3G8w=.sha256'
60+ // console.log('fetching comments', blogKey) // has 2 comments, 1 like
61+
62+ pull(
63+ server.blogStats.read({
64+ gt: ['L', blogKey, null],
65+ lt: ['L', blogKey+'~', undefined],
66+ // gt: ['L', blogKey, null],
67+ // lte: ['L', blogKey+'~', undefined],
68+ // limit: 100,
69+ keys: true,
70+ values: true,
71+ seqs: false,
72+ reverse: true
73+ }),
74+ // pull.filter(o => o.key[1] === blogKey),
75+ pull.log(() => console.log('DONE'))
76+ )
77+ /// ///// test code /////
78+ })
79+
80+ return h('Page -statsShow', [
81+ h('pre', map(blogs, blog => {
82+ return h('div', [
83+ h('b', blog.value.content.title),
84+ h('div', map(comments.get(blog.key), msg => 'C')),
85+ h('div', map(likes.get(blog.key), msg => 'L'))
86+ ])
87+ }))
88+ ])
89+ }
90+}
background-process.jsView
@@ -21,23 +21,21 @@
2121 .use(require('ssb-about'))
2222 // .use(require('ssb-ebt'))
2323 .use(require('ssb-ws'))
2424 .use(require('ssb-server-channel'))
25+ .use(require('./ssb-server-blog-stats'))
2526
2627 Client(config.keys, config, (err, ssbServer) => {
27- if (ssbServer === undefined) {
28+ if (err) {
2829 console.log('> starting sbot')
2930 var sbot = createSbot(config)
3031
3132 console.log(' > updating updating manifest.json')
3233 var manifest = sbot.getManifest()
3334 fs.writeFileSync(Path.join(config.path, 'manifest.json'), JSON.stringify(manifest))
3435 electron.ipcRenderer.send('server-started')
35- }
36- else {
36+ } else {
3737 console.log('> sbot running elsewhere')
3838 electron.ipcRenderer.send('server-started')
3939 // TODO send some warning to the client side
4040 }
4141 })
42-
43-
blog/sync/isBlog.jsView
@@ -1,6 +1,7 @@
11 const nest = require('depnest')
22 const get = require('lodash/get')
3+const isBlog = require('scuttle-blog/isBlog')
34
45 exports.gives = nest({
56 'blog.sync.isBlog': true,
67 })
@@ -8,14 +9,15 @@
89 const MIN_LENGTH_FOR_BLOG_POST = 800
910
1011 exports.create = function (api) {
1112 return nest({
12- 'blog.sync.isBlog': isBlog
13+ 'blog.sync.isBlog': isBloggy
1314 })
1415
15- function isBlog (msg) {
16+ function isBloggy (msg) {
17+ if (isBlog(msg)) return true
18+
1619 const type = msg.value.content.type
17- if (type === 'blog') return true
1820 if (type === 'post' && get(msg, 'value.content.text', '').length > MIN_LENGTH_FOR_BLOG_POST) return true
1921 return false
2022 }
2123 }
package-lock.jsonView
The diff is too large to show. Use a local git client to view these changes.
Old file size: 238763 bytes
New file size: 248886 bytes
package.jsonView
@@ -25,8 +25,9 @@
2525 "depject": "^4.1.1",
2626 "depnest": "^1.3.0",
2727 "electron-default-menu": "^1.0.1",
2828 "electron-window-state": "^4.1.1",
29+ "flumeview-level": "^3.0.2",
2930 "font-awesome": "^4.7.0",
3031 "html-escape": "^2.0.0",
3132 "human-time": "0.0.1",
3233 "hyper-nav": "^2.0.0",
@@ -52,8 +53,9 @@
5253 "pull-obv": "^1.3.0",
5354 "pull-stream": "^3.6.0",
5455 "read-directory": "^2.1.0",
5556 "require-style": "^1.0.1",
57+ "scuttle-blog": "^1.0.0",
5658 "scuttlebot": "10.4.10",
5759 "secret-stack": "4.0.1",
5860 "setimmediate": "^1.0.5",
5961 "ssb-about": "^0.1.0",
router/sync/routes.jsView
@@ -23,8 +23,9 @@
2323 'app.page.userEdit': 'first',
2424 // 'app.page.userFind': 'first',
2525 'app.page.userShow': 'first',
2626 'app.page.splash': 'first',
27+ 'app.page.statsShow': 'first',
2728 'app.page.threadNew': 'first',
2829 'app.page.threadShow': 'first',
2930 // 'app.page.image': 'first',
3031 'blob.sync.url': 'first'
@@ -50,12 +51,15 @@
5051 !get(location, 'value.private') // treats public posts as 'blogs'
5152 }, pages.blogShow ],
5253
5354 // Channel related pages
54- [ location => location.page === 'channelSubscriptions', pages.channelSubscriptions],
55+ [ location => location.page === 'channelSubscriptions', pages.channelSubscriptions ],
5556 [ location => location.page === 'channelShow', pages.channelShow ],
5657 [ location => location.channel, pages.channelShow ],
5758
59+ // Stats pages
60+ [ location => location.page === 'statsShow', pages.statsShow ],
61+
5862 // AddressBook pages
5963 [ location => location.page === 'addressBook', pages.addressBook ],
6064
6165 // Private Thread pages
ssb-server-blog-stats.jsView
@@ -1,0 +1,148 @@
1+const FlumeView = require('flumeview-level')
2+const get = require('lodash/get')
3+const pull = require('pull-stream')
4+const isBlog = require('scuttle-blog/isBlog')
5+const { isMsg: isMsgRef } = require('ssb-ref')
6+
7+const getType = (msg) => get(msg, 'value.content.type')
8+const getAuthor = (msg) => get(msg, 'value.author')
9+const getCommentRoot = (msg) => get(msg, 'value.content.root')
10+const getLikeRoot = (msg) => get(msg, 'value.content.vote.link')
11+const getTimestamp = (msg) => get(msg, 'value.timestamp')
12+
13+const FLUME_VIEW_VERSION = 5
14+
15+module.exports = {
16+ name: 'blogStats',
17+ version: 1,
18+ manifest: {
19+ get: 'async',
20+ read: 'source',
21+ readBlogs: 'source',
22+ getBlogs: 'async',
23+ readComments: 'source',
24+ readLikes: 'source'
25+ },
26+ init: (server, config) => {
27+ console.log('initialising blog-stats plugin')
28+ const myKey = server.keys.id
29+
30+ const view = server._flumeUse(
31+ 'internalblogStats',
32+ FlumeView(FLUME_VIEW_VERSION, map)
33+ )
34+
35+ return {
36+ get: view.get,
37+ read: view.read,
38+ readBlogs,
39+ getBlogs,
40+ readComments,
41+ readLikes,
42+ // getLikes
43+ // getComments
44+ }
45+
46+ function map (msg, seq) {
47+ var root
48+
49+ switch (getType(msg)) {
50+ case 'blog':
51+ if (isBlog(msg) && myBlog(msg)) return [['B', msg.key, getTimestamp(msg)]]
52+ else return []
53+
54+ case 'vote':
55+ // process.stdout.write('L')
56+ root = getLikeRoot(msg)
57+ // TODO figure out how to only store likes I care about
58+ if (root) return [['L', root, getTimestamp(msg)]]
59+ else return []
60+
61+ // Note this catches:
62+ // - all likes, on all things D:
63+ // - likes AND unlikes
64+
65+ case 'post':
66+ // process.stdout.write('C')
67+ root = getCommentRoot(msg)
68+ // TODO figure out how to only store likes I care about
69+ if (root) return [['C', root, getTimestamp(msg)]]
70+ else return []
71+
72+ // Note this catches:
73+ // - all comments, on all things D:
74+
75+ default:
76+ return []
77+ }
78+ }
79+
80+ function readBlogs (options = {}) {
81+ const query = Object.assign({}, {
82+ gte: ['B', null, null],
83+ // null is the 'minimum' structure in bytewise ordering
84+ lte: ['B~', undefined, undefined],
85+ reverse: true,
86+ values: true,
87+ keys: false,
88+ seqs: false
89+ }, options)
90+
91+ return view.read(query)
92+ }
93+
94+ function getBlogs (options, cb) {
95+ pull(
96+ readBlogs(options),
97+ pull.collect(cb)
98+ )
99+ }
100+
101+ function readComments (options = {}) {
102+ var key
103+ if (!options.blog) key = null
104+ else if (isMsgRef(options.blog)) key = options.blog
105+ else if (isMsgRef(options.blog.key) && isBlog(options.blog)) key = options.blog.key
106+
107+ const query = Object.assign({}, {
108+ gt: ['C', key, null],
109+ lt: ['C', key, undefined],
110+ // undefined is the 'maximum' structure in bytewise ordering https://www.npmjs.com/package/bytewise#order-of-supported-structures
111+ reverse: true,
112+ values: true,
113+ keys: false,
114+ seqs: false
115+ }, options)
116+
117+ delete query.blog
118+
119+ return view.read(query)
120+ }
121+
122+ function readLikes (options = {}) {
123+ var key
124+ if (!options.blog) key = null
125+ else if (isMsgRef(options.blog)) key = options.blog
126+ else if (isMsgRef(options.blog.key) && isBlog(options.blog)) key = options.blog.key
127+
128+ const query = Object.assign({}, {
129+ // gt: ['L', key, null],
130+ // lt: ['L', key, undefined], // why doesn't this work?
131+ gt: ['L', key, null], // null is minimum in bytewise ordering
132+ lt: ['L', key + '~', undefined], // undefinted in maximum in bytewise ordering
133+ reverse: true,
134+ values: true,
135+ keys: false,
136+ seqs: false
137+ }, options)
138+
139+ delete query.blog
140+
141+ return view.read(query)
142+ }
143+
144+ function myBlog (msg) {
145+ return getAuthor(msg) === myKey
146+ }
147+ }
148+}

Built with git-ssb-web