sbot/roots.jsView |
---|
4 | 4 | var pullCat = require('pull-cat') |
5 | 5 | var HLRU = require('hashlru') |
6 | 6 | var extend = require('xtend') |
7 | 7 | var normalizeChannel = require('../lib/normalize-channel') |
| 8 | +var Defer = require('pull-defer') |
8 | 9 | |
9 | 10 | |
10 | 11 | var getRoot = require('patchcore/message/sync/root').create().message.sync.root |
11 | 12 | |
24 | 25 | var cache = HLRU(100) |
25 | 26 | |
26 | 27 | return { |
27 | 28 | latest: function ({ids = [ssb.id]}) { |
28 | | - var filter = null |
29 | | - return pull( |
30 | | - |
31 | | - index.read({old: false}), |
| 29 | + var stream = Defer.source() |
| 30 | + getFilter((err, filter) => { |
| 31 | + if (err) return stream.abort(err) |
| 32 | + stream.resolve(pull( |
| 33 | + index.read({old: false}), |
32 | 34 | |
33 | | - |
34 | | - pull.asyncMap((item, cb) => { |
35 | | - if (!filter) { |
36 | | - |
37 | | - getFilter((err, result) => { |
38 | | - if (err) return cb(err) |
39 | | - filter = result |
40 | | - cb(null, item) |
41 | | - }) |
42 | | - } else { |
43 | | - cb(null, item) |
44 | | - } |
45 | | - }), |
| 35 | + |
| 36 | + pull.filter(item => { |
| 37 | + if (filter && item.value && item.value) { |
| 38 | + var filterResult = filter(ids, item.value) |
| 39 | + if (filterResult) { |
| 40 | + item.value.filterResult = filterResult |
| 41 | + return true |
| 42 | + } |
| 43 | + } |
| 44 | + }), |
46 | 45 | |
47 | | - |
48 | | - pull.filter(item => { |
49 | | - if (filter && item.value && item.value) { |
50 | | - return filter(ids, item.value) |
51 | | - } |
52 | | - }), |
| 46 | + |
| 47 | + LookupRoots(), |
53 | 48 | |
54 | | - |
55 | | - pull.asyncMap((item, cb) => { |
56 | | - var msg = item.value |
57 | | - var key = item.key[1] |
58 | | - if (key === msg.key) { |
59 | | - |
60 | | - cb(null, msg) |
61 | | - } |
62 | | - getThruCache(key, (_, value) => { |
63 | | - cb(null, extend(msg, { |
64 | | - root: value |
65 | | - })) |
| 49 | + |
| 50 | + pull.filter(item => { |
| 51 | + var root = item.root || item |
| 52 | + if (filter && root && root.value) { |
| 53 | + return checkReplyForcesDisplay(item) || filter(ids, root) |
| 54 | + } |
66 | 55 | }) |
67 | | - }), |
68 | | - |
69 | | - |
70 | | - pull.filter(item => { |
71 | | - var root = item.root || item |
72 | | - if (filter && root && root.value) { |
73 | | - return filter(ids, root) |
74 | | - } |
75 | | - }) |
76 | | - ) |
| 56 | + )) |
| 57 | + }) |
| 58 | + return stream |
77 | 59 | }, |
78 | | - read: function ({ids = [ssb.id], reverse, live, old, limit, lt, gt}) { |
79 | | - var opts = {reverse, live, old} |
80 | 60 | |
| 61 | + read: function ({ids = [ssb.id], reverse, limit, lt, gt}) { |
| 62 | + var opts = {reverse, old: true} |
| 63 | + |
81 | 64 | |
82 | 65 | if (lt && typeof lt.timestamp === 'number') lt = lt.timestamp |
83 | 66 | if (gt && typeof gt.timestamp === 'number') gt = gt.timestamp |
84 | 67 | if (typeof lt === 'number') opts.lt = [lt] |
85 | 68 | if (typeof gt === 'number') opts.gt = [gt] |
86 | 69 | |
87 | 70 | var seen = new Set() |
| 71 | + var included = new Set() |
88 | 72 | var marker = {marker: true, timestamp: null} |
89 | | - var filter = null |
90 | 73 | |
91 | | - var stream = pull( |
| 74 | + var stream = Defer.source() |
92 | 75 | |
93 | | - |
94 | | - index.read(opts), |
| 76 | + getFilter((err, filter) => { |
| 77 | + if (err) return stream.abort(err) |
| 78 | + stream.resolve(pull( |
| 79 | + |
| 80 | + index.read(opts), |
95 | 81 | |
96 | | - |
97 | | - pull.asyncMap((item, cb) => { |
98 | | - if (!filter) { |
99 | | - |
100 | | - getFilter((err, result) => { |
101 | | - if (err) return cb(err) |
102 | | - filter = result |
103 | | - cb(null, item) |
104 | | - }) |
105 | | - } else { |
106 | | - cb(null, item) |
107 | | - } |
108 | | - }), |
| 82 | + |
| 83 | + pull.filter(item => { |
| 84 | + |
| 85 | + marker.timestamp = item.key[0] |
109 | 86 | |
110 | | - |
111 | | - pull.filter(item => { |
112 | | - if (filter && item.value && item.value.value) { |
113 | | - return filter(ids, item.value) |
114 | | - } |
115 | | - }), |
| 87 | + if (filter && item.value && item.value) { |
| 88 | + var filterResult = filter(ids, item.value) |
| 89 | + if (filterResult) { |
| 90 | + item.value.filterResult = filterResult |
| 91 | + return true |
| 92 | + } |
| 93 | + } |
| 94 | + }), |
116 | 95 | |
117 | | - |
118 | | - pull.map(item => { |
119 | | - if (item.sync) return item |
120 | | - marker.timestamp = item.key[0] |
121 | | - return item.key[1] |
122 | | - }), |
| 96 | + |
| 97 | + LookupRoots(), |
123 | 98 | |
124 | | - |
125 | | - pull.filter(item => { |
126 | | - if (old === false) return true |
127 | | - if (item && item.sync) { |
128 | | - return true |
129 | | - } else if (typeof item === 'string') { |
130 | | - if (!seen.has(item)) { |
131 | | - seen.add(item) |
132 | | - return true |
| 99 | + |
| 100 | + pull.filter(item => { |
| 101 | + var root = item.root || item |
| 102 | + |
| 103 | + |
| 104 | + if (!included.has(root.key) && filter && root && root.value) { |
| 105 | + if (checkReplyForcesDisplay(item)) { |
| 106 | + |
| 107 | + included.add(root.key) |
| 108 | + return true |
| 109 | + } else if (!seen.has(root.key)) { |
| 110 | + seen.add(root.key) |
| 111 | + var result = filter(ids, root) |
| 112 | + if (result) { |
| 113 | + |
| 114 | + included.add(root.key) |
| 115 | + return true |
| 116 | + } |
| 117 | + } |
133 | 118 | } |
134 | | - } |
135 | | - }), |
| 119 | + }), |
136 | 120 | |
137 | | - |
138 | | - pull.asyncMap((item, cb) => { |
139 | | - if (item.sync) return cb(null, item) |
140 | | - var key = item |
141 | | - getThruCache(key, cb) |
142 | | - }), |
| 121 | + |
| 122 | + pull.map(item => { |
| 123 | + var root = item.root || item |
| 124 | + return root |
| 125 | + }) |
| 126 | + )) |
| 127 | + }) |
143 | 128 | |
144 | | - |
145 | | - pull.filter(msg => { |
146 | | - if (filter && msg.value && !getRoot(msg)) { |
147 | | - return filter(ids, msg) |
148 | | - } |
149 | | - }) |
150 | | - ) |
151 | | - |
152 | 129 | |
153 | 130 | if (typeof limit === 'number') { |
154 | 131 | var count = 0 |
155 | 132 | return pullCat([ |
196 | 173 | cb(null, function (ids, msg) { |
197 | 174 | var type = msg.value.content.type |
198 | 175 | if (type === 'vote') return false |
199 | 176 | var matchesChannel = (type !== 'channel' && checkChannel(subscriptions, ids, msg.value.content.channel)) |
200 | | - var matchesTag = checkTag(subscriptions, ids, msg.value.content.mentions) |
201 | | - return ids.includes(msg.value.author) || matchesChannel || matchesTag || checkFollowing(friends, ids, msg.value.author) |
| 177 | + var matchingTags = getMatchingTags(subscriptions, ids, msg.value.content.mentions) |
| 178 | + var isYours = ids.includes(msg.value.author) |
| 179 | + var mentionsYou = getMentionsYou(ids, msg.value.content.mentions) |
| 180 | + var following = checkFollowing(friends, ids, msg.value.author) |
| 181 | + if (isYours || matchesChannel || matchingTags.length || following || mentionsYou) { |
| 182 | + return { |
| 183 | + matchingTags, matchesChannel, isYours, following, mentionsYou |
| 184 | + } |
| 185 | + } |
202 | 186 | }) |
203 | 187 | }) |
204 | 188 | }) |
205 | 189 | } |
| 190 | + |
| 191 | + function LookupRoots () { |
| 192 | + return pull.asyncMap((item, cb) => { |
| 193 | + var msg = item.value |
| 194 | + var key = item.key[1] |
| 195 | + if (key === msg.key) { |
| 196 | + |
| 197 | + return cb(null, msg) |
| 198 | + } |
| 199 | + getThruCache(key, (_, value) => { |
| 200 | + cb(null, extend(msg, { |
| 201 | + root: value |
| 202 | + })) |
| 203 | + }) |
| 204 | + }) |
| 205 | + } |
206 | 206 | } |
207 | 207 | |
208 | | -function checkTag (lookup, ids, mentions) { |
| 208 | +function getMatchingTags (lookup, ids, mentions) { |
209 | 209 | if (Array.isArray(mentions)) { |
210 | | - return mentions.some((mention) => { |
| 210 | + return mentions.reduce((result, mention) => { |
211 | 211 | if (mention && typeof mention.link === 'string' && mention.link.startsWith('#')) { |
212 | | - return checkChannel(lookup, ids, mention.link.slice(1)) |
| 212 | + if (checkChannel(lookup, ids, mention.link.slice(1))) { |
| 213 | + result.push(normalizeChannel(mention.link.slice(1))) |
| 214 | + } |
213 | 215 | } |
| 216 | + return result |
| 217 | + }, []) |
| 218 | + } |
| 219 | + return [] |
| 220 | +} |
| 221 | + |
| 222 | +function getMentionsYou (ids, mentions) { |
| 223 | + if (Array.isArray(mentions)) { |
| 224 | + return mentions.some((mention) => { |
| 225 | + if (mention && typeof mention.link === 'string') { |
| 226 | + return ids.includes(mention.link) |
| 227 | + } |
214 | 228 | }) |
215 | 229 | } |
216 | 230 | } |
217 | 231 | |
| 232 | +function checkReplyForcesDisplay (item) { |
| 233 | + var filterResult = item.filterResult || {} |
| 234 | + var matchesTags = filterResult.matchingTags && !!filterResult.matchingTags.length |
| 235 | + return matchesTags || filterResult.isYours || filterResult.mentionsYou |
| 236 | +} |
| 237 | + |
218 | 238 | function checkFollowing (lookup, ids, target) { |
219 | 239 | |
220 | 240 | if (!lookup) return false |
221 | 241 | |