Files: 81408e3008723f4b30952d1e379525f80a8ddf6d / app / html / filter.js
6374 bytesRaw
1 | const nest = require('depnest') |
2 | const { h, Value, when, computed } = require('mutant') |
3 | const Abort = require('pull-abortable') |
4 | const pull = require('pull-stream') |
5 | const addSuggest = require('suggest-box') |
6 | const { isFeed } = require('ssb-ref') |
7 | const get = require('lodash/get') |
8 | const isEqual = require('lodash/isEqual') |
9 | |
10 | exports.gives = nest('app.html.filter') |
11 | |
12 | exports.needs = nest({ |
13 | 'about.async.suggest': 'first', |
14 | 'channel.async.suggest': 'first', |
15 | 'contact.obs.following': 'first', |
16 | 'keys.sync.id': 'first', |
17 | 'settings.obs.get': 'first', |
18 | 'settings.sync.set': 'first' |
19 | }) |
20 | |
21 | exports.create = function (api) { |
22 | return nest({ |
23 | 'app.html.filter': Filter |
24 | }) |
25 | |
26 | function Filter (draw) { |
27 | const showFilters = Value(false) |
28 | |
29 | const myId = api.keys.sync.id() |
30 | const peopleIFollow = api.contact.obs.following(myId) |
31 | |
32 | const filterSettings = api.settings.obs.get('filter', {exclude: {}}) |
33 | |
34 | const channelInput = h('input', { |
35 | value: filterSettings().exclude.channels, |
36 | 'ev-keyup': (ev) => { |
37 | var text = ev.target.value |
38 | if (text.length === 0 || ev.which === 13) { |
39 | api.settings.sync.set({ |
40 | filter: { |
41 | exclude: { |
42 | channels: text |
43 | } |
44 | } |
45 | }) |
46 | draw() |
47 | } |
48 | } |
49 | }) |
50 | |
51 | const userInput = h('input') |
52 | |
53 | const isFiltered = computed(filterSettings, (filterSettings) => { |
54 | const _settings = Object.assign({}, filterSettings) |
55 | delete _settings.defaults |
56 | |
57 | return !isEqual(_settings, filterSettings.defaults) |
58 | }) |
59 | |
60 | const filterMenu = h('Filter', [ |
61 | when(isFiltered, h('i.custom')), |
62 | h('i.fa.fa-filter', { |
63 | classList: when(showFilters, '-active'), |
64 | 'ev-click': () => showFilters.set(!showFilters()) |
65 | }), |
66 | h('i.fa.fa-angle-up', { 'ev-click': draw }), |
67 | h('div.options', { className: when(showFilters, '', '-hidden') }, [ |
68 | h('header', [ |
69 | 'Filter', |
70 | h('i.fa.fa-filter') |
71 | ]), |
72 | h('section', [ |
73 | h('div.users', [ |
74 | toggle({ type: 'peopleIFollow', filterGroup: 'only', label: 'Only people I follow' }), |
75 | h('div.user-filter', [ |
76 | h('label', 'Only this user (temporary filter):'), |
77 | userInput |
78 | ]) |
79 | ]), |
80 | h('div.channels', [ |
81 | h('label', 'Exclude channels'), |
82 | channelInput |
83 | ]), |
84 | h('div.message-types', [ |
85 | h('header', 'Show messages'), |
86 | toggle({ type: 'post' }), |
87 | toggle({ type: 'like' }), |
88 | toggle({ type: 'about' }), |
89 | toggle({ type: 'contact' }), |
90 | toggle({ type: 'channel' }), |
91 | toggle({ type: 'pub' }), |
92 | toggle({ type: 'chess' }) |
93 | ]), |
94 | h('div.root-messages', [ |
95 | toggle({ type: 'rootMessages', filterGroup: 'only', label: 'Root messages only' }) |
96 | ]) |
97 | ]) |
98 | ]) |
99 | ]) |
100 | |
101 | function toggle ({ type, filterGroup, label }) { |
102 | label = label || type |
103 | filterGroup = filterGroup || 'show' |
104 | |
105 | const state = computed(filterSettings, settings => get(settings, [filterGroup, type])) |
106 | const handleClick = () => { |
107 | const currentState = state() |
108 | |
109 | // TODO use some lodash tool ? |
110 | api.settings.sync.set({ |
111 | filter: { |
112 | [filterGroup]: { |
113 | [type]: !currentState |
114 | } |
115 | } |
116 | }) |
117 | |
118 | draw() |
119 | } |
120 | |
121 | return h('FilterToggle', { 'ev-click': handleClick }, [ |
122 | h('label', label), |
123 | h('i', { classList: when(state, 'fa fa-check-square-o', 'fa fa-square-o') }) |
124 | ]) |
125 | } |
126 | |
127 | const getChannelSuggestions = api.channel.async.suggest() |
128 | addSuggest(channelInput, (inputText, cb) => { |
129 | if (inputText[0] === '#') { |
130 | cb(null, getChannelSuggestions(inputText.slice(1))) |
131 | } |
132 | }, {cls: 'PatchSuggest'}) |
133 | channelInput.addEventListener('suggestselect', ev => { |
134 | const channels = channelInput.value.trim() |
135 | |
136 | api.settings.sync.set({ filter: { exclude: { channels: channels } } }) |
137 | |
138 | draw() |
139 | }) |
140 | |
141 | var userId |
142 | const getAboutSuggestions = api.about.async.suggest() |
143 | addSuggest(userInput, (inputText, cb) => { |
144 | inputText = inputText.replace(/^@/, '') |
145 | cb(null, getAboutSuggestions(inputText.slice(1))) |
146 | }, {cls: 'PatchSuggest'}) |
147 | userInput.addEventListener('suggestselect', ev => { |
148 | userId = ev.detail.id |
149 | userInput.value = userId |
150 | |
151 | draw() |
152 | }) |
153 | |
154 | function followFilter (msg) { |
155 | if (!filterSettings().only.peopleIFollow) return true |
156 | |
157 | return Array.from(peopleIFollow()).concat(myId).includes(msg.value.author) |
158 | } |
159 | |
160 | function userFilter (msg) { |
161 | if (!userId) return true |
162 | |
163 | return msg.value.author === userId |
164 | } |
165 | |
166 | function rootFilter (msg) { |
167 | if (!filterSettings().only.rootMessages) return true |
168 | |
169 | return !msg.value.content.root |
170 | } |
171 | |
172 | function channelFilter (msg) { |
173 | var filters = filterSettings().exclude.channels |
174 | if (!filters) return true |
175 | filters = filters.split(' ').map(c => c.slice(1)) |
176 | |
177 | return msg.value.content && !filters.includes(msg.value.content.channel) |
178 | } |
179 | |
180 | function messageFilter (msg) { |
181 | var { type } = msg.value.content |
182 | if (/^chess/.test(type)) { |
183 | type = 'chess' |
184 | } |
185 | |
186 | return get(filterSettings(), ['show', type], true) |
187 | } |
188 | |
189 | var downScrollAborter |
190 | |
191 | function filterDownThrough () { |
192 | return pull( |
193 | downScrollAborter, |
194 | pull.filter(followFilter), |
195 | pull.filter(userFilter), |
196 | pull.filter(rootFilter), |
197 | pull.filter(channelFilter), |
198 | pull.filter(messageFilter) |
199 | ) |
200 | } |
201 | |
202 | var upScrollAborter |
203 | |
204 | function filterUpThrough () { |
205 | return pull( |
206 | upScrollAborter, |
207 | pull.filter(followFilter), |
208 | pull.filter(userFilter), |
209 | pull.filter(rootFilter), |
210 | pull.filter(channelFilter), |
211 | pull.filter(messageFilter) |
212 | ) |
213 | } |
214 | |
215 | function resetFeed ({ container, content }) { |
216 | if (typeof upScrollAborter === 'function') { |
217 | upScrollAborter.abort() |
218 | downScrollAborter.abort() |
219 | } |
220 | upScrollAborter = Abort() |
221 | downScrollAborter = Abort() |
222 | |
223 | container.scroll(0) |
224 | content.innerHTML = '' |
225 | } |
226 | |
227 | return { |
228 | filterMenu, |
229 | filterDownThrough, |
230 | filterUpThrough, |
231 | resetFeed |
232 | } |
233 | } |
234 | } |
235 |
Built with git-ssb-web