Commit 24269db9f3d8f25ca1b4996019e7c798f5ee96b8
Add option to only show posts from channels you actually subscribe to in main feed
Allows you can hide posts from your friends about topics you don’t care about (by not subscribing to that channel). It will still appear if that post has a tag you follow or mentions you or someone replies with a tag you follow.Matt McKegg committed on 12/12/2017, 3:56:43 AM
Parent: a224084e5f0cf22563242ad706400c3e4a22d5c5
Files changed
locales/en.json | changed |
modules/feed/html/rollup.js | changed |
modules/page/html/render/public.js | changed |
modules/page/html/render/settings.js | changed |
package.json | changed |
sbot/roots.js | changed |
locales/en.json | ||
---|---|---|
@@ -185,6 +185,9 @@ | ||
185 | 185 … | "See more": "See more", |
186 | 186 … | "(missing message)": "(missing message)", |
187 | 187 … | "assigned a description to ": "assigned a description to ", |
188 | 188 … | "Unread Message": "Unread Message", |
189 | - "Font Size": "Font Size" | |
189 … | + "Font Size": "Font Size", | |
190 … | + "Public Feed Options": "Public Feed Options", | |
191 … | + "Only include posts from subscribed channels": "Only include posts from subscribed channels", | |
192 … | + "Default": "Default" | |
190 | 193 … | } |
modules/feed/html/rollup.js | ||
---|---|---|
@@ -171,11 +171,11 @@ | ||
171 | 171 … | stream, |
172 | 172 … | abortable, |
173 | 173 … | pull.filter(msg => msg && msg.value && msg.value.content), |
174 | 174 … | prefiltered ? pull( |
175 | - api.feed.pull.unique(), | |
176 | 175 … | pull.filter(msg => !api.message.sync.isBlocked(msg)), |
177 | 176 … | pull.filter(rootFilter), |
177 … | + api.feed.pull.unique(), | |
178 | 178 … | api.feed.pull.withReplies() |
179 | 179 … | ) : pull( |
180 | 180 … | pull.filter(bumpFilter), |
181 | 181 … | api.feed.pull.rollup(rootFilter) |
modules/page/html/render/public.js | ||
---|---|---|
@@ -71,9 +71,12 @@ | ||
71 | 71 … | if (opts.lt != null && !opts.lt.marker) { |
72 | 72 … | // if an lt has been specified that is not a marker, assume stream is finished |
73 | 73 … | return pull.empty() |
74 | 74 … | } else { |
75 | - return api.sbot.pull.stream(sbot => sbot.patchwork.roots(extend(opts, { ids: [id] }))) | |
75 … | + return api.sbot.pull.stream(sbot => sbot.patchwork.roots(extend(opts, { | |
76 … | + ids: [id], | |
77 … | + onlySubscribedChannels: filters() && filters().onlySubscribed | |
78 … | + }))) | |
76 | 79 … | } |
77 | 80 … | } |
78 | 81 … | |
79 | 82 … | var filters = api.settings.obs.get('filters') |
@@ -88,21 +91,17 @@ | ||
88 | 91 … | var type = msg.value.content.type |
89 | 92 … | if (type === 'vote') return false |
90 | 93 … | |
91 | 94 … | var author = msg.value.author |
92 | - var channel = api.channel.sync.normalize(msg.value.content.channel) | |
93 | - var tagged = checkTag(msg.value.content.mentions) | |
94 | - var isSubscribed = channel ? subscribedChannels().has(channel) : false | |
95 | - return isSubscribed || id === author || following().includes(author) || tagged | |
95 … | + return matchesSubscribedChannel(msg) || id === author || following().includes(author) | |
96 | 96 … | } |
97 | 97 … | }, |
98 | 98 … | rootFilter: function (msg) { |
99 | - var filtered = filters() && filters().following && getType(msg) === 'contact' | |
100 | 99 … | // skip messages that are directly replaced by the previous message |
101 | 100 … | // e.g. follow / unfollow in quick succession |
102 | 101 … | // THIS IS A TOTAL HACK!!! SHOULD BE REPLACED WITH A PROPER ROLLUP! |
103 | 102 … | var isOutdated = isReplacementMessage(msg, lastMessage) |
104 | - if (!filtered && !isOutdated) { | |
103 … | + if (checkFeedFilter(msg) && !isOutdated) { | |
105 | 104 … | lastMessage = msg |
106 | 105 … | return true |
107 | 106 … | } |
108 | 107 … | }, |
@@ -134,8 +133,22 @@ | ||
134 | 133 … | } |
135 | 134 … | |
136 | 135 … | return result |
137 | 136 … | |
137 … | + function checkFeedFilter (root) { | |
138 … | + if (filters()) { | |
139 … | + if (filters().following && getType(root) === 'contact') return false | |
140 … | + } | |
141 … | + return true | |
142 … | + } | |
143 … | + | |
144 … | + function matchesSubscribedChannel (msg) { | |
145 … | + var channel = api.channel.sync.normalize(msg.value.content.channel) | |
146 … | + var tagged = checkTag(msg.value.content.mentions) | |
147 … | + var isSubscribed = channel ? subscribedChannels().has(channel) : false | |
148 … | + return isSubscribed || tagged | |
149 … | + } | |
150 … | + | |
138 | 151 … | function checkTag (mentions) { |
139 | 152 … | if (Array.isArray(mentions)) { |
140 | 153 … | return mentions.some((mention) => { |
141 | 154 … | if (mention && typeof mention.link === 'string' && mention.link.startsWith('#')) { |
@@ -254,8 +267,12 @@ | ||
254 | 267 … | function getType (msg) { |
255 | 268 … | return msg && msg.value && msg.value.content && msg.value.content.type |
256 | 269 … | } |
257 | 270 … | |
271 … | +function hasChannel (msg) { | |
272 … | + return getType(msg) !== 'channel' && msg && msg.value && msg.value.content && !!msg.value.content.channel | |
273 … | +} | |
274 … | + | |
258 | 275 … | function arrayEq (a, b) { |
259 | 276 … | if (Array.isArray(a) && Array.isArray(b) && a.length === b.length && a !== b) { |
260 | 277 … | return a.every((value, i) => value === b[i]) |
261 | 278 … | } |
modules/page/html/render/settings.js | |||
---|---|---|---|
@@ -79,16 +79,19 @@ | |||
79 | 79 … | ]), | |
80 | 80 … | ||
81 | 81 … | h('section', [ | |
82 | 82 … | h('h2', i18n('Public Feed Options')), | |
83 … | + | ||
83 | 84 … | h('div', [ | |
84 | - h('label', [ | ||
85 | - h('input', { | ||
86 | - type: 'checkbox', | ||
87 | - checked: filterFollowing, | ||
88 | - 'ev-change': (ev) => filterFollowing.set(ev.target.checked) | ||
89 | - }), ' ', i18n('Hide following messages') | ||
90 | - ]) | ||
85 … | + checkbox(filterFollowing, { | ||
86 … | + label: i18n('Hide following messages') | ||
87 … | + }) | ||
88 … | + ]), | ||
89 … | + | ||
90 … | + h('div', [ | ||
91 … | + checkbox(onlySubscribed, { | ||
92 … | + label: i18n('Only include posts from subscribed channels') | ||
93 … | + }) | ||
91 | 94 … | ]) | |
92 | 95 … | ]) | |
93 | 96 … | ]) | |
94 | 97 … | ]) | |
@@ -105,4 +108,14 @@ | |||
105 | 108 … | } | |
106 | 109 … | } | |
107 | 110 … | }) | |
108 | 111 … | } | |
112 … | + | ||
113 … | +function checkbox (param, {label}) { | ||
114 … | + return h('label', [ | ||
115 … | + h('input', { | ||
116 … | + type: 'checkbox', | ||
117 … | + checked: param, | ||
118 … | + 'ev-change': (ev) => param.set(ev.target.checked) | ||
119 … | + }), ' ', label | ||
120 … | + ]) | ||
121 … | +} |
package.json | ||
---|---|---|
@@ -39,9 +39,9 @@ | ||
39 | 39 … | "moment": "^2.18.1", |
40 | 40 … | "mutant": "^3.21.2", |
41 | 41 … | "mutant-pull-reduce": "^1.1.0", |
42 | 42 … | "obv": "0.0.1", |
43 | - "patch-settings": "github:mmckegg/patch-settings#e808303e2d3e6c88519266e0d9e18a750dee97a2", | |
43 … | + "patch-settings": "~1.1.0", | |
44 | 44 … | "patchcore": "~1.21.0", |
45 | 45 … | "pull-abortable": "^4.1.0", |
46 | 46 … | "pull-defer": "^0.2.2", |
47 | 47 … | "pull-file": "~1.0.0", |
sbot/roots.js | ||
---|---|---|
@@ -25,9 +25,9 @@ | ||
25 | 25 … | // not really big enough for multiple refresh cycles |
26 | 26 … | var cache = HLRU(100) |
27 | 27 … | |
28 | 28 … | return { |
29 | - latest: function ({ids = [ssb.id]}) { | |
29 … | + latest: function ({ids = [ssb.id], onlySubscribedChannels = false}) { | |
30 | 30 … | var stream = Defer.source() |
31 | 31 … | getFilter((err, filter) => { |
32 | 32 … | if (err) return stream.abort(err) |
33 | 33 … | stream.resolve(pull( |
@@ -52,17 +52,18 @@ | ||
52 | 52 … | var root = item.root || item |
53 | 53 … | var isPrivate = root.value && typeof root.value.content === 'string' |
54 | 54 … | |
55 | 55 … | if (filter && root && root.value && !isPrivate) { |
56 | - return checkReplyForcesDisplay(item) || filter(ids, root) | |
56 … | + var filterResult = filter(ids, root) | |
57 … | + return checkReplyForcesDisplay(item) || shouldShow(filterResult, {onlySubscribedChannels}) | |
57 | 58 … | } |
58 | 59 … | }) |
59 | 60 … | )) |
60 | 61 … | }) |
61 | 62 … | return stream |
62 | 63 … | }, |
63 | 64 … | |
64 | - read: function ({ids = [ssb.id], reverse, limit, lt, gt}) { | |
65 … | + read: function ({ids = [ssb.id], reverse, limit, lt, gt, onlySubscribedChannels = false}) { | |
65 | 66 … | var opts = {reverse, old: true} |
66 | 67 … | |
67 | 68 … | // handle markers passed in to lt / gt |
68 | 69 … | if (lt && typeof lt.timestamp === 'number') lt = lt.timestamp |
@@ -111,11 +112,10 @@ | ||
111 | 112 … | included.add(root.key) |
112 | 113 … | return true |
113 | 114 … | } else if (!seen.has(root.key)) { |
114 | 115 … | seen.add(root.key) |
115 | - var result = filter(ids, root) | |
116 | - if (result) { | |
117 | - // include this item if we have not yet seen it and the root filter passes | |
116 … | + var filterResult = filter(ids, root) | |
117 … | + if (shouldShow(filterResult, {onlySubscribedChannels})) { | |
118 | 118 … | included.add(root.key) |
119 | 119 … | return true |
120 | 120 … | } |
121 | 121 … | } |
@@ -124,8 +124,9 @@ | ||
124 | 124 … | |
125 | 125 … | // MAP ROOT ITEMS |
126 | 126 … | pull.map(item => { |
127 | 127 … | var root = item.root || item |
128 … | + root.filterResult = item.filterResult | |
128 | 129 … | return root |
129 | 130 … | }) |
130 | 131 … | )) |
131 | 132 … | }) |
@@ -153,8 +154,16 @@ | ||
153 | 154 … | } |
154 | 155 … | } |
155 | 156 … | } |
156 | 157 … | |
158 … | + function shouldShow (filterResult, {onlySubscribedChannels}) { | |
159 … | + if (filterResult && onlySubscribedChannels && filterResult.hasChannel) { | |
160 … | + return filterResult.matchesChannel || filterResult.matchingTags.length || filterResult.mentionsYou || filterResult.isYours | |
161 … | + } else { | |
162 … | + return !!filterResult | |
163 … | + } | |
164 … | + } | |
165 … | + | |
157 | 166 … | function getThruCache (key, cb) { |
158 | 167 … | if (cache.has(key)) { |
159 | 168 … | cb(null, cache.get(key)) |
160 | 169 … | } else { |
@@ -176,16 +185,17 @@ | ||
176 | 185 … | if (err) return cb(err) |
177 | 186 … | cb(null, function (ids, msg) { |
178 | 187 … | var type = msg.value.content.type |
179 | 188 … | if (type === 'vote') return false // filter out likes |
189 … | + var hasChannel = !!msg.value.content.channel | |
180 | 190 … | var matchesChannel = (type !== 'channel' && checkChannel(subscriptions, ids, msg.value.content.channel)) |
181 | 191 … | var matchingTags = getMatchingTags(subscriptions, ids, msg.value.content.mentions) |
182 | 192 … | var isYours = ids.includes(msg.value.author) |
183 | 193 … | var mentionsYou = getMentionsYou(ids, msg.value.content.mentions) |
184 | 194 … | var following = checkFollowing(friends, ids, msg.value.author) |
185 | 195 … | if (isYours || matchesChannel || matchingTags.length || following || mentionsYou) { |
186 | 196 … | return { |
187 | - matchingTags, matchesChannel, isYours, following, mentionsYou | |
197 … | + matchingTags, matchesChannel, isYours, following, mentionsYou, hasChannel | |
188 | 198 … | } |
189 | 199 … | } |
190 | 200 … | }) |
191 | 201 … | }) |
Built with git-ssb-web