git ssb

2+

mixmix / ticktack



Tree: 2266bb60f9e3162b169687e66a63363af534ece5

Files: 2266bb60f9e3162b169687e66a63363af534ece5 / ssb-server-ticktack.js

6099 bytesRaw
1const FlumeView = require('flumeview-level')
2const get = require('lodash/get')
3const pull = require('pull-stream')
4const defer = require('pull-defer')
5const isBlog = require('scuttle-blog/isBlog')
6const { isMsg: isMsgRef } = require('ssb-ref')
7
8const getType = (msg) => get(msg, 'value.content.type')
9const getAuthor = (msg) => get(msg, 'value.author')
10const getCommentRoot = (msg) => get(msg, 'value.content.root')
11const getLikeRoot = (msg) => get(msg, 'value.content.vote.link')
12const getShareRoot = (msg) => get(msg, 'value.content.share.link')
13const getTimestamp = (msg) => get(msg, 'value.timestamp')
14
15const FLUME_VIEW_VERSION = 1
16
17module.exports = {
18 name: 'ticktack',
19 version: 1,
20 manifest: {
21 get: 'async',
22 read: 'source',
23 readBlogs: 'source',
24 getBlogs: 'async',
25 readComments: 'source',
26 readAllComments: 'source',
27 readAllLikes: 'source',
28 readAllShares: 'source',
29 readLikes: 'source'
30 },
31 init: (server, config) => {
32 console.log('> initialising ticktack plugin')
33 const myKey = server.keys.id
34
35 const view = server._flumeUse(
36 'ticktack',
37 FlumeView(FLUME_VIEW_VERSION, map)
38 )
39
40 return {
41 get: view.get,
42 read: view.read,
43 readBlogs,
44 getBlogs,
45 readComments,
46 readAllComments,
47 readAllLikes,
48 readAllShares,
49 readLikes
50 // readShares
51 }
52
53 function map (msg, seq) {
54 var root
55
56 switch (getType(msg)) {
57 case 'blog':
58 if (isBlog(msg) && isMyMsg(msg)) return [['B', msg.key, getTimestamp(msg)]]
59 else return []
60
61 case 'vote':
62 root = getLikeRoot(msg)
63 // TODO figure out how to only store likes I care about
64 if (root) return [['L', root, getTimestamp(msg)]]
65 else return []
66
67 // Note this catches:
68 // - all likes, on all things D:
69 // - likes AND unlikes
70
71 case 'post':
72 root = getCommentRoot(msg)
73 // TODO figure out how to only store comments I care about
74 if (!root && isMyMsg(msg) && isPlog(msg)) return [['B', msg.key, getTimestamp(msg)]]
75 else if (root) return [['C', root, getTimestamp(msg)]]
76 else return []
77
78 // Note this catches:
79 // - all comments, on all things D:
80
81 default:
82 return []
83 }
84 }
85
86 function readBlogs (options = {}) {
87 const query = Object.assign({}, {
88 gte: ['B', null, null],
89 // null is the 'minimum' structure in bytewise ordering
90 lte: ['B', undefined, undefined],
91 reverse: true,
92 values: true,
93 keys: false,
94 seqs: false
95 }, options)
96
97 return view.read(query)
98 }
99
100 function getBlogs (options, cb) {
101 if (typeof options === 'function') {
102 cb = options
103 options = {}
104 }
105
106 pull(
107 readBlogs(options),
108 pull.collect(cb)
109 )
110 }
111
112 function readComments (blog, options = {}) {
113 var key = getBlogKey(blog)
114
115 const query = Object.assign({}, {
116 gt: ['C', key, null],
117 lt: ['C', key, undefined],
118 // undefined is the 'maximum' structure in bytewise ordering https://www.npmjs.com/package/bytewise#order-of-supported-structures
119 reverse: true,
120 values: true,
121 keys: false,
122 seqs: false
123 }, options)
124
125 return view.read(query)
126 }
127
128 function readLikes (blog, options = {}) {
129 var key = getBlogKey(blog)
130
131 const query = Object.assign({}, {
132 gt: ['L', key, null],
133 lt: ['L', key, undefined],
134 reverse: true,
135 values: true,
136 keys: false,
137 seqs: false
138 }, options)
139
140 return view.read(query)
141 }
142
143 function readAllComments (opts = {}) {
144 return readAllSource({
145 type: 'post',
146 makeFilter: blogIds => msg => {
147 if (getAuthor(msg) === myKey) return false // exclude my posts
148 if (getCommentRoot(msg) === undefined) return false // want only 'comments' (reply posts)
149 // NOTE - this one will get nested replies too
150
151 return blogIds.includes(getCommentRoot(msg)) // is about one of my blogs
152 },
153 opts
154 })
155 }
156
157 function readAllLikes (opts = {}) {
158 return readAllSource({
159 type: 'vote',
160 makeFilter: blogIds => msg => {
161 if (getAuthor(msg) === myKey) return false // exclude my likes
162
163 return blogIds.includes(getLikeRoot(msg)) // is about one of my blogs
164 },
165 opts
166 })
167 }
168
169 function readAllShares (opts = {}) {
170 return readAllSource({
171 type: 'share',
172 makeFilter: (blogIds) => msg => {
173 if (getAuthor(msg) === myKey) return false // exclude my shares
174
175 return blogIds.includes(getShareRoot(msg)) // is about one of my blogs
176 },
177 opts
178 })
179 }
180
181 function readAllSource ({ type, makeFilter, opts = {} }) {
182 var source = defer.source()
183
184 getBlogs({ keys: true, values: false }, (err, data) => {
185 if (err) throw err
186
187 const blogIds = data.map(d => d[1])
188
189 opts.type = type
190 var limit = opts.limit
191 delete opts.limit
192 // have to remove limit from the query otherwise Next stalls out if it doesn't get a new result
193
194 const _source = pull(
195 server.messagesByType(opts), // TODO - check/ note why I didn't use e.g. readComments
196 pull.filter(makeFilter(blogIds)),
197 limit ? pull.take(limit) : true
198 )
199
200 source.resolve(_source)
201 })
202
203 return source
204 }
205
206 function isMyMsg (msg) {
207 return getAuthor(msg) === myKey
208 }
209 }
210}
211
212function getBlogKey (blog) {
213 if (isMsgRef(blog)) return blog
214 // else if (isMsgRef(blog.key) && isBlog(blog)) return blog.key
215 else if (isMsgRef(blog.key) && (isBlog(blog) || isPlog(blog))) return blog.key
216}
217
218// a Plog is a Blog shaped Post!
219function isPlog (msg) {
220 // if (get(msg, 'value.content.text', '').length >= 2500) console.log(get(msg, 'value.content.text', '').length)
221 return get(msg, 'value.content.text', '').length >= 2500
222}
223

Built with git-ssb-web