Commit b594e41bf900f3f0b270f809fae0596be6cceb54
refactor ticktack-plugin to make generic readAll sources
mix irving committed on 5/7/2018, 6:52:48 AMParent: f3a07a62ed183316f08c7c0cf24dcb767afd29b1
Files changed
app/page/notifications.js | changed |
app/page/statsShow.js | changed |
background-process.js | changed |
ssb-server-blog-stats.js | deleted |
ssb-server-ticktack.js | added |
app/page/notifications.js | ||
---|---|---|
@@ -15,8 +15,10 @@ | ||
15 | 15 | exports.create = (api) => { |
16 | 16 | return nest('app.page.notifications', function (location) { |
17 | 17 | // location here can expected to be: { page: 'notifications', section: * } |
18 | 18 | |
19 | + if (!location.section) return | |
20 | + | |
19 | 21 | var scroller = api.app.html.scroller({ |
20 | 22 | classList: ['content'], |
21 | 23 | stream: createBlogCommentStream, |
22 | 24 | render: Comment |
@@ -28,9 +30,9 @@ | ||
28 | 30 | |
29 | 31 | onceTrue(api.sbot.obs.connection, server => { |
30 | 32 | if (resolved) return |
31 | 33 | |
32 | - source.resolve(server.blogStats.readAllComments(opts)) | |
34 | + source.resolve(server.ticktack.readAllComments(opts)) | |
33 | 35 | resolved = true |
34 | 36 | }) |
35 | 37 | |
36 | 38 | return source |
app/page/statsShow.js | ||
---|---|---|
@@ -186,9 +186,9 @@ | ||
186 | 186 | |
187 | 187 | function fetchBlogData ({ server, store }) { |
188 | 188 | const myKey = server.id |
189 | 189 | |
190 | - server.blogStats.getBlogs({}, (err, blogs) => { | |
190 | + server.ticktack.getBlogs({}, (err, blogs) => { | |
191 | 191 | if (err) console.error(err) |
192 | 192 | |
193 | 193 | // TODO - change this once merge in the new notifications-hanger work |
194 | 194 | // i.e. do one query for ALL comments on my blogs as opposed to N queries |
@@ -205,9 +205,9 @@ | ||
205 | 205 | function fetchComments ({ server, store, blog }) { |
206 | 206 | if (!store.comments.has(blog.key)) store.comments.put(blog.key, MutantArray()) |
207 | 207 | |
208 | 208 | pull( |
209 | - server.blogStats.readComments(blog), | |
209 | + server.ticktack.readComments(blog), | |
210 | 210 | pull.drain(msg => { |
211 | 211 | if (msg.value.author === myKey) return |
212 | 212 | store.comments.get(blog.key).push(msg) |
213 | 213 | }) |
@@ -217,9 +217,9 @@ | ||
217 | 217 | function fetchLikes ({ server, store, blog }) { |
218 | 218 | if (!store.likes.has(blog.key)) store.likes.put(blog.key, MutantArray()) |
219 | 219 | |
220 | 220 | pull( |
221 | - server.blogStats.readLikes(blog), | |
221 | + server.ticktack.readLikes(blog), | |
222 | 222 | pull.drain(msg => { |
223 | 223 | if (msg.value.author === myKey) return |
224 | 224 | |
225 | 225 | const isUnlike = get(msg, 'value.content.vote.value', 1) < 1 |
background-process.js | ||
---|---|---|
@@ -21,9 +21,9 @@ | ||
21 | 21 | .use(require('ssb-about')) |
22 | 22 | // .use(require('ssb-ebt')) |
23 | 23 | .use(require('ssb-ws')) |
24 | 24 | .use(require('ssb-server-channel')) |
25 | - .use(require('./ssb-server-blog-stats')) | |
25 | + .use(require('./ssb-server-ticktack')) | |
26 | 26 | |
27 | 27 | Client(config.keys, config, (err, ssbServer) => { |
28 | 28 | if (err) { |
29 | 29 | console.log('> starting sbot') |
ssb-server-blog-stats.js | ||
---|---|---|
@@ -1,206 +1,0 @@ | ||
1 | -const FlumeView = require('flumeview-level') | |
2 | -const get = require('lodash/get') | |
3 | -const pull = require('pull-stream') | |
4 | -const defer = require('pull-defer') | |
5 | -const isBlog = require('scuttle-blog/isBlog') | |
6 | -const { isMsg: isMsgRef } = require('ssb-ref') | |
7 | - | |
8 | -const getType = (msg) => get(msg, 'value.content.type') | |
9 | -const getAuthor = (msg) => get(msg, 'value.author') | |
10 | -const getCommentRoot = (msg) => get(msg, 'value.content.root') | |
11 | -const getLikeRoot = (msg) => get(msg, 'value.content.vote.link') | |
12 | -const getTimestamp = (msg) => get(msg, 'value.timestamp') | |
13 | - | |
14 | -const FLUME_VIEW_VERSION = 1 | |
15 | - | |
16 | -module.exports = { | |
17 | - name: 'blogStats', | |
18 | - version: 1, | |
19 | - manifest: { | |
20 | - get: 'async', | |
21 | - read: 'source', | |
22 | - readBlogs: 'source', | |
23 | - getBlogs: 'async', | |
24 | - readComments: 'source', | |
25 | - readAllComments: 'source', // TEMP | |
26 | - readLikes: 'source' | |
27 | - }, | |
28 | - init: (server, config) => { | |
29 | - console.log('> initialising blog-stats plugin') | |
30 | - const myKey = server.keys.id | |
31 | - | |
32 | - const view = server._flumeUse( | |
33 | - 'internalblogStats', | |
34 | - FlumeView(FLUME_VIEW_VERSION, map) | |
35 | - ) | |
36 | - | |
37 | - return { | |
38 | - get: view.get, | |
39 | - read: view.read, | |
40 | - readBlogs, | |
41 | - getBlogs, | |
42 | - readComments, | |
43 | - readAllComments, | |
44 | - readLikes | |
45 | - // readShares | |
46 | - } | |
47 | - | |
48 | - function map (msg, seq) { | |
49 | - var root | |
50 | - | |
51 | - switch (getType(msg)) { | |
52 | - case 'blog': | |
53 | - if (isBlog(msg) && isMyMsg(msg)) return [['B', msg.key, getTimestamp(msg)]] | |
54 | - else return [] | |
55 | - | |
56 | - case 'vote': | |
57 | - root = getLikeRoot(msg) | |
58 | - // TODO figure out how to only store likes I care about | |
59 | - if (root) return [['L', root, getTimestamp(msg)]] | |
60 | - else return [] | |
61 | - | |
62 | - // Note this catches: | |
63 | - // - all likes, on all things D: | |
64 | - // - likes AND unlikes | |
65 | - | |
66 | - case 'post': | |
67 | - root = getCommentRoot(msg) | |
68 | - // TODO figure out how to only store comments I care about | |
69 | - if (!root && isMyMsg(msg) && isPlog(msg)) return [['B', msg.key, getTimestamp(msg)]] | |
70 | - else if (root) return [['C', root, getTimestamp(msg)]] | |
71 | - else return [] | |
72 | - | |
73 | - // Note this catches: | |
74 | - // - all comments, on all things D: | |
75 | - | |
76 | - default: | |
77 | - return [] | |
78 | - } | |
79 | - } | |
80 | - | |
81 | - // a Plog is a Blog shaped Post! | |
82 | - function isPlog (msg) { | |
83 | - // if (get(msg, 'value.content.text', '').length >= 2500) console.log(get(msg, 'value.content.text', '').length) | |
84 | - return get(msg, 'value.content.text', '').length >= 2500 | |
85 | - } | |
86 | - | |
87 | - function readBlogs (options = {}) { | |
88 | - const query = Object.assign({}, { | |
89 | - gte: ['B', null, null], | |
90 | - // null is the 'minimum' structure in bytewise ordering | |
91 | - lte: ['B', undefined, undefined], | |
92 | - reverse: true, | |
93 | - values: true, | |
94 | - keys: false, | |
95 | - seqs: false | |
96 | - }, options) | |
97 | - | |
98 | - return view.read(query) | |
99 | - } | |
100 | - | |
101 | - function getBlogs (options, cb) { | |
102 | - if (typeof options === 'function') { | |
103 | - cb = options | |
104 | - options = {} | |
105 | - } | |
106 | - | |
107 | - pull( | |
108 | - readBlogs(options), | |
109 | - pull.collect(cb) | |
110 | - ) | |
111 | - } | |
112 | - | |
113 | - function readComments (blog, options = {}) { | |
114 | - var key = getBlogKey(blog) | |
115 | - | |
116 | - const query = Object.assign({}, { | |
117 | - gt: ['C', key, null], | |
118 | - lt: ['C', key, undefined], | |
119 | - // undefined is the 'maximum' structure in bytewise ordering https://www.npmjs.com/package/bytewise#order-of-supported-structures | |
120 | - reverse: true, | |
121 | - values: true, | |
122 | - keys: false, | |
123 | - seqs: false | |
124 | - }, options) | |
125 | - | |
126 | - return view.read(query) | |
127 | - } | |
128 | - | |
129 | - function readAllComments (opts = {}) { | |
130 | - var source = defer.source() | |
131 | - | |
132 | - getBlogs({ keys: true, values: false }, (err, data) => { | |
133 | - if (err) throw err | |
134 | - | |
135 | - const blogIds = data.map(d => d[1]) | |
136 | - | |
137 | - opts.type = 'post' | |
138 | - var limit = opts.limit | |
139 | - delete opts.limit | |
140 | - // have to remove limit from the query otherwise Next stalls out if it doesn't get a new result | |
141 | - | |
142 | - const _source = pull( | |
143 | - server.messagesByType(opts), | |
144 | - pull.filter(msg => { | |
145 | - if (msg.value.author === server.id) return false // exclude my posts | |
146 | - if (msg.value.content.root === undefined) return false // want only 'comments' (reply posts) | |
147 | - | |
148 | - return blogIds.includes(msg.value.content.root) // is about one of my blogs | |
149 | - }), | |
150 | - limit ? pull.take(limit) : true | |
151 | - ) | |
152 | - | |
153 | - // I don't know what order results some out of flumeview-level read | |
154 | - // which makes this perhaps unideal for Next / mutant-scroll | |
155 | - // const query = { | |
156 | - // gt: [ 'C', null, opts.gt || null ], | |
157 | - // lt: [ 'C', undefined, opts.lt || undefined ], | |
158 | - // reverse: opts.reverse === undefined ? true : opts.reverse, | |
159 | - // live: opts.reverse === undefined ? true : opts.reverse, | |
160 | - // values: true, | |
161 | - // keys: true, | |
162 | - // seqs: false | |
163 | - // } | |
164 | - // const _source = pull( | |
165 | - // view.read(query), | |
166 | - // pull.filter(result => { | |
167 | - // return blogIds.includes(result.key[1]) | |
168 | - // }), | |
169 | - // pull.map(result => result.value), | |
170 | - // pull.take(opts.limit) | |
171 | - // ) | |
172 | - | |
173 | - source.resolve(_source) | |
174 | - }) | |
175 | - | |
176 | - return pull( | |
177 | - source | |
178 | - ) | |
179 | - } | |
180 | - | |
181 | - function readLikes (blog, options = {}) { | |
182 | - var key = getBlogKey(blog) | |
183 | - | |
184 | - const query = Object.assign({}, { | |
185 | - gt: ['L', key, null], | |
186 | - lt: ['L', key, undefined], | |
187 | - reverse: true, | |
188 | - values: true, | |
189 | - keys: false, | |
190 | - seqs: false | |
191 | - }, options) | |
192 | - | |
193 | - return view.read(query) | |
194 | - } | |
195 | - | |
196 | - function getBlogKey (blog) { | |
197 | - if (isMsgRef(blog)) return blog | |
198 | - // else if (isMsgRef(blog.key) && isBlog(blog)) return blog.key | |
199 | - else if (isMsgRef(blog.key) && (isBlog(blog) || isPlog(blog))) return blog.key | |
200 | - } | |
201 | - | |
202 | - function isMyMsg (msg) { | |
203 | - return getAuthor(msg) === myKey | |
204 | - } | |
205 | - } | |
206 | -} |
ssb-server-ticktack.js | ||
---|---|---|
@@ -1,0 +1,221 @@ | ||
1 | +const FlumeView = require('flumeview-level') | |
2 | +const get = require('lodash/get') | |
3 | +const pull = require('pull-stream') | |
4 | +const defer = require('pull-defer') | |
5 | +const isBlog = require('scuttle-blog/isBlog') | |
6 | +const { isMsg: isMsgRef } = require('ssb-ref') | |
7 | + | |
8 | +const getType = (msg) => get(msg, 'value.content.type') | |
9 | +const getAuthor = (msg) => get(msg, 'value.author') | |
10 | +const getCommentRoot = (msg) => get(msg, 'value.content.root') | |
11 | +const getLikeRoot = (msg) => get(msg, 'value.content.vote.link') | |
12 | +const getTimestamp = (msg) => get(msg, 'value.timestamp') | |
13 | + | |
14 | +const FLUME_VIEW_VERSION = 1 | |
15 | + | |
16 | +module.exports = { | |
17 | + name: 'ticktack', | |
18 | + version: 1, | |
19 | + manifest: { | |
20 | + get: 'async', | |
21 | + read: 'source', | |
22 | + readBlogs: 'source', | |
23 | + getBlogs: 'async', | |
24 | + readComments: 'source', | |
25 | + readAllComments: 'source', | |
26 | + readAllLikes: 'source', | |
27 | + readAllShares: 'source', | |
28 | + readLikes: 'source' | |
29 | + }, | |
30 | + init: (server, config) => { | |
31 | + console.log('> initialising ticktack plugin') | |
32 | + const myKey = server.keys.id | |
33 | + | |
34 | + const view = server._flumeUse( | |
35 | + 'ticktack', | |
36 | + FlumeView(FLUME_VIEW_VERSION, map) | |
37 | + ) | |
38 | + | |
39 | + return { | |
40 | + get: view.get, | |
41 | + read: view.read, | |
42 | + readBlogs, | |
43 | + getBlogs, | |
44 | + readComments, | |
45 | + readAllComments, | |
46 | + readAllLikes, | |
47 | + readAllShares, | |
48 | + readLikes | |
49 | + // readShares | |
50 | + } | |
51 | + | |
52 | + function map (msg, seq) { | |
53 | + var root | |
54 | + | |
55 | + switch (getType(msg)) { | |
56 | + case 'blog': | |
57 | + if (isBlog(msg) && isMyMsg(msg)) return [['B', msg.key, getTimestamp(msg)]] | |
58 | + else return [] | |
59 | + | |
60 | + case 'vote': | |
61 | + root = getLikeRoot(msg) | |
62 | + // TODO figure out how to only store likes I care about | |
63 | + if (root) return [['L', root, getTimestamp(msg)]] | |
64 | + else return [] | |
65 | + | |
66 | + // Note this catches: | |
67 | + // - all likes, on all things D: | |
68 | + // - likes AND unlikes | |
69 | + | |
70 | + case 'post': | |
71 | + root = getCommentRoot(msg) | |
72 | + // TODO figure out how to only store comments I care about | |
73 | + if (!root && isMyMsg(msg) && isPlog(msg)) return [['B', msg.key, getTimestamp(msg)]] | |
74 | + else if (root) return [['C', root, getTimestamp(msg)]] | |
75 | + else return [] | |
76 | + | |
77 | + // Note this catches: | |
78 | + // - all comments, on all things D: | |
79 | + | |
80 | + default: | |
81 | + return [] | |
82 | + } | |
83 | + } | |
84 | + | |
85 | + function readBlogs (options = {}) { | |
86 | + const query = Object.assign({}, { | |
87 | + gte: ['B', null, null], | |
88 | + // null is the 'minimum' structure in bytewise ordering | |
89 | + lte: ['B', undefined, undefined], | |
90 | + reverse: true, | |
91 | + values: true, | |
92 | + keys: false, | |
93 | + seqs: false | |
94 | + }, options) | |
95 | + | |
96 | + return view.read(query) | |
97 | + } | |
98 | + | |
99 | + function getBlogs (options, cb) { | |
100 | + if (typeof options === 'function') { | |
101 | + cb = options | |
102 | + options = {} | |
103 | + } | |
104 | + | |
105 | + pull( | |
106 | + readBlogs(options), | |
107 | + pull.collect(cb) | |
108 | + ) | |
109 | + } | |
110 | + | |
111 | + function readComments (blog, options = {}) { | |
112 | + var key = getBlogKey(blog) | |
113 | + | |
114 | + const query = Object.assign({}, { | |
115 | + gt: ['C', key, null], | |
116 | + lt: ['C', key, undefined], | |
117 | + // undefined is the 'maximum' structure in bytewise ordering https://www.npmjs.com/package/bytewise#order-of-supported-structures | |
118 | + reverse: true, | |
119 | + values: true, | |
120 | + keys: false, | |
121 | + seqs: false | |
122 | + }, options) | |
123 | + | |
124 | + return view.read(query) | |
125 | + } | |
126 | + | |
127 | + function readLikes (blog, options = {}) { | |
128 | + var key = getBlogKey(blog) | |
129 | + | |
130 | + const query = Object.assign({}, { | |
131 | + gt: ['L', key, null], | |
132 | + lt: ['L', key, undefined], | |
133 | + reverse: true, | |
134 | + values: true, | |
135 | + keys: false, | |
136 | + seqs: false | |
137 | + }, options) | |
138 | + | |
139 | + return view.read(query) | |
140 | + } | |
141 | + | |
142 | + function readAllComments (opts = {}) { | |
143 | + return readAllSource({ | |
144 | + type: 'post', | |
145 | + makeFilter: blogIds => msg => { | |
146 | + if (getAuthor(msg) === myKey) return false // exclude my posts | |
147 | + if (getCommentRoot(msg) === undefined) return false // want only 'comments' (reply posts) | |
148 | + // NOTE - this one will get nested replies too | |
149 | + | |
150 | + return blogIds.includes(getCommentRoot(msg)) // is about one of my blogs | |
151 | + }, | |
152 | + opts | |
153 | + }) | |
154 | + } | |
155 | + | |
156 | + function readAllLikes (opts = {}) { | |
157 | + return readAllSource({ | |
158 | + type: 'vote', | |
159 | + makeFilter: blogIds => msg => { | |
160 | + if (getAuthor(msg) === myKey) return false // exclude my likes | |
161 | + | |
162 | + return blogIds.includes(getLikeRoot(msg)) // is about one of my blogs | |
163 | + }, | |
164 | + opts | |
165 | + }) | |
166 | + } | |
167 | + | |
168 | + function readAllShares (opts = {}) { | |
169 | + return readAllSource({ | |
170 | + type: 'share', | |
171 | + makeFilter: (blogIds) => msg => { | |
172 | + if (getAuthor(msg) === myKey) return false // exclude my shares | |
173 | + | |
174 | + return blogIds.includes(getLikeRoot(msg)) // is about one of my blogs | |
175 | + }, | |
176 | + opts | |
177 | + }) | |
178 | + } | |
179 | + | |
180 | + function readAllSource ({ type, makeFilter, opts = {} }) { | |
181 | + var source = defer.source() | |
182 | + | |
183 | + getBlogs({ keys: true, values: false }, (err, data) => { | |
184 | + if (err) throw err | |
185 | + | |
186 | + const blogIds = data.map(d => d[1]) | |
187 | + | |
188 | + opts.type = type | |
189 | + var limit = opts.limit | |
190 | + delete opts.limit | |
191 | + // have to remove limit from the query otherwise Next stalls out if it doesn't get a new result | |
192 | + | |
193 | + const _source = pull( | |
194 | + server.messagesByType(opts), | |
195 | + pull.filter(makeFilter(blogIds)), | |
196 | + limit ? pull.take(limit) : true | |
197 | + ) | |
198 | + | |
199 | + source.resolve(_source) | |
200 | + }) | |
201 | + | |
202 | + return source | |
203 | + } | |
204 | + | |
205 | + function isMyMsg (msg) { | |
206 | + return getAuthor(msg) === myKey | |
207 | + } | |
208 | + } | |
209 | +} | |
210 | + | |
211 | +function getBlogKey (blog) { | |
212 | + if (isMsgRef(blog)) return blog | |
213 | + // else if (isMsgRef(blog.key) && isBlog(blog)) return blog.key | |
214 | + else if (isMsgRef(blog.key) && (isBlog(blog) || isPlog(blog))) return blog.key | |
215 | +} | |
216 | + | |
217 | +// a Plog is a Blog shaped Post! | |
218 | +function isPlog (msg) { | |
219 | + // if (get(msg, 'value.content.text', '').length >= 2500) console.log(get(msg, 'value.content.text', '').length) | |
220 | + return get(msg, 'value.content.text', '').length >= 2500 | |
221 | +} |
Built with git-ssb-web