app/html/filter.jsView |
---|
1 | 1 … | const nest = require('depnest') |
2 | | -const { h, Value, when } = require('mutant') |
| 2 … | +const { h, Value, when, computed } = require('mutant') |
3 | 3 … | const Abort = require('pull-abortable') |
4 | 4 … | const pull = require('pull-stream') |
5 | 5 … | const addSuggest = require('suggest-box') |
6 | 6 … | const { isFeed } = require('ssb-ref') |
| 7 … | +const some = require('lodash/some') |
| 8 … | +const get = require('lodash/get') |
| 9 … | +const isEqual = require('lodash/isEqual') |
7 | 10 … | |
8 | 11 … | exports.gives = nest('app.html.filter') |
9 | 12 … | |
10 | 13 … | exports.needs = nest({ |
11 | | - 'about.async.suggest': 'first', |
| 14 … | + 'channel.async.suggest': 'first', |
12 | 15 … | 'contact.obs.following': 'first', |
13 | | - 'keys.sync.id': 'first' |
| 16 … | + 'keys.sync.id': 'first', |
| 17 … | + 'settings.obs.get': 'first', |
| 18 … | + 'settings.sync.set': 'first' |
14 | 19 … | }) |
15 | 20 … | |
16 | 21 … | exports.create = function (api) { |
17 | 22 … | return nest({ |
22 | 27 … | const showFilters = Value(false) |
23 | 28 … | |
24 | 29 … | const myId = api.keys.sync.id() |
25 | 30 … | const peopleIFollow = api.contact.obs.following(myId) |
26 | | - const onlyPeopleIFollow = Value(false) |
27 | | - const onlyAuthor = Value() |
28 | 31 … | |
29 | | - const showPost = Value(true) |
30 | | - const showAbout = Value(true) |
31 | | - const showVote = Value(false) |
32 | | - const showContact = Value(false) |
33 | | - const showChannel = Value(false) |
34 | | - const showPub = Value(false) |
| 32 … | + const { set } = api.settings.sync |
35 | 33 … | |
36 | | - const authorInput = h('input', { |
37 | | - 'ev-keyup': (ev) => { |
38 | | - const author = ev.target.value |
39 | | - if (author && !isFeed(author)) return |
| 34 … | + const filterSettings = api.settings.obs.get('filter') |
40 | 35 … | |
41 | | - onlyAuthor.set(author) |
42 | | - draw() |
43 | | - } |
| 36 … | + const channelInput = h('input', |
| 37 … | + { value: filterSettings().exclude.channels, |
| 38 … | + 'ev-keyup': (ev) => { |
| 39 … | + var text = ev.target.value |
| 40 … | + if (text.length == 0 || ev.which == 13) { |
| 41 … | + api.settings.sync.set({ |
| 42 … | + filter: { |
| 43 … | + exclude: { |
| 44 … | + channels: text |
| 45 … | + } |
| 46 … | + } |
| 47 … | + }) |
| 48 … | + draw() |
| 49 … | + } |
| 50 … | + } |
| 51 … | + } |
| 52 … | + ) |
| 53 … | + |
| 54 … | + const isFiltered = computed(filterSettings, (filterSettings) => { |
| 55 … | + const _settings = Object.assign({}, filterSettings) |
| 56 … | + delete _settings.defaults |
| 57 … | + |
| 58 … | + return !isEqual(_settings, filterSettings.defaults) |
44 | 59 … | }) |
45 | 60 … | |
46 | 61 … | const filterMenu = h('Filter', [ |
47 | | - h('i', { |
48 | | - classList: when(showFilters, 'fa fa-filter -active', 'fa fa-filter'), |
| 62 … | + when(isFiltered, h('i.custom')), |
| 63 … | + h('i.fa.fa-filter', { |
| 64 … | + classList: when(showFilters, '-active'), |
49 | 65 … | 'ev-click': () => showFilters.set(!showFilters()) |
50 | 66 … | }), |
51 | 67 … | h('i.fa.fa-angle-up', { 'ev-click': draw }), |
52 | | - h('div', { className: when(showFilters, '', '-hidden') }, [ |
| 68 … | + h('div.options', { className: when(showFilters, '', '-hidden') }, [ |
53 | 69 … | h('header', [ |
54 | 70 … | 'Filter', |
55 | 71 … | h('i.fa.fa-filter') |
56 | 72 … | ]), |
57 | 73 … | h('section', [ |
58 | | - h('div.author', [ |
59 | | - h('label', 'Show author'), |
60 | | - authorInput |
| 74 … | + h('div.channels', [ |
| 75 … | + h('label', 'Exclude channels'), |
| 76 … | + channelInput |
61 | 77 … | ]), |
62 | | - toggle({ obs: onlyPeopleIFollow, label: 'Only people I follow' }), |
| 78 … | + toggle({ type: 'peopleIFollow', filterGroup: 'only', label: 'Only people I follow' }), |
63 | 79 … | h('div.message-types', [ |
64 | 80 … | h('header', 'Show messages'), |
65 | | - toggle({ obs: showPost, label: 'post' }), |
66 | | - toggle({ obs: showVote, label: 'like' }), |
67 | | - toggle({ obs: showAbout, label: 'about' }), |
68 | | - toggle({ obs: showContact, label: 'contact' }), |
69 | | - toggle({ obs: showChannel, label: 'channel' }), |
70 | | - toggle({ obs: showPub, label: 'pub' }) |
| 81 … | + toggle({ type: 'post' }), |
| 82 … | + toggle({ type: 'like' }), |
| 83 … | + toggle({ type: 'about' }), |
| 84 … | + toggle({ type: 'contact' }), |
| 85 … | + toggle({ type: 'channel' }), |
| 86 … | + toggle({ type: 'pub' }), |
| 87 … | + toggle({ type: 'chess' }) |
71 | 88 … | ]) |
72 | 89 … | ]) |
73 | 90 … | ]) |
74 | 91 … | ]) |
75 | 92 … | |
76 | | - function toggle ({ obs, label }) { |
77 | | - return h('FilterToggle', { |
78 | | - 'ev-click': () => { |
79 | | - obs.set(!obs()) |
80 | | - draw() |
81 | | - }}, [ |
82 | | - h('label', label), |
83 | | - h('i', { classList: when(obs, 'fa fa-check-square-o', 'fa fa-square-o') }) |
84 | | - ] |
85 | | - ) |
| 93 … | + function toggle ({ type, filterGroup, label }) { |
| 94 … | + label = label || type |
| 95 … | + filterGroup = filterGroup || 'show' |
| 96 … | + |
| 97 … | + const state = computed(filterSettings, settings => get(settings, [filterGroup, type])) |
| 98 … | + const handleClick = () => { |
| 99 … | + const currentState = state() |
| 100 … | + |
| 101 … | + |
| 102 … | + api.settings.sync.set({ |
| 103 … | + filter: { |
| 104 … | + [filterGroup]: { |
| 105 … | + [type]: !currentState |
| 106 … | + } |
| 107 … | + } |
| 108 … | + }) |
| 109 … | + |
| 110 … | + draw() |
| 111 … | + } |
| 112 … | + |
| 113 … | + return h('FilterToggle', { 'ev-click': handleClick }, [ |
|
| 114 … | + h('label', label), |
| 115 … | + h('i', { classList: when(state, 'fa fa-check-square-o', 'fa fa-square-o') }) |
| 116 … | + ]) |
86 | 117 … | } |
87 | 118 … | |
88 | | - |
89 | | - const getProfileSuggestions = api.about.async.suggest() |
90 | | - addSuggest(authorInput, (inputText, cb) => { |
91 | | - if (inputText[0] === '@') inputText = inputText.slice(1) |
92 | | - cb(null, getProfileSuggestions(inputText)) |
| 119 … | + const getChannelSuggestions = api.channel.async.suggest() |
| 120 … | + addSuggest(channelInput, (inputText, cb) => { |
| 121 … | + if (inputText[0] === '#') { |
| 122 … | + cb(null, getChannelSuggestions(inputText.slice(1))) |
| 123 … | + } |
93 | 124 … | }, {cls: 'PatchSuggest'}) |
94 | | - authorInput.addEventListener('suggestselect', ev => { |
95 | | - authorInput.value = ev.detail.id |
| 125 … | + channelInput.addEventListener('suggestselect', ev => { |
| 126 … | + const channels = channelInput.value.trim() |
| 127 … | + |
| 128 … | + api.settings.sync.set({ filter: { exclude: { channels: channels } } }) |
| 129 … | + |
| 130 … | + draw() |
96 | 131 … | }) |
97 | 132 … | |
98 | 133 … | function followFilter (msg) { |
99 | | - if (!onlyPeopleIFollow()) return true |
| 134 … | + if (!filterSettings().only.peopleIFollow) return true |
100 | 135 … | |
101 | 136 … | return Array.from(peopleIFollow()).includes(msg.value.author) |
102 | 137 … | } |
103 | 138 … | |
104 | | - function authorFilter (msg) { |
105 | | - if (!onlyAuthor()) return true |
| 139 … | + function channelFilter (msg) { |
| 140 … | + var filters = filterSettings().exclude.channels |
| 141 … | + if (!filters) return true |
| 142 … | + filters = filters.split(' ').map(c => c.slice(1)) |
106 | 143 … | |
107 | | - return msg.value.author === onlyAuthor() |
| 144 … | + return msg.value.content && !filters.includes(msg.value.content.channel) |
108 | 145 … | } |
109 | 146 … | |
110 | 147 … | function messageFilter (msg) { |
111 | | - switch (msg.value.content.type) { |
112 | | - case 'post': |
113 | | - return showPost() |
114 | | - case 'vote': |
115 | | - return showVote() |
116 | | - case 'about': |
117 | | - return showAbout() |
118 | | - case 'contact': |
119 | | - return showContact() |
120 | | - case 'channel': |
121 | | - return showChannel() |
122 | | - case 'pub': |
123 | | - return showPub() |
124 | | - default: |
125 | | - return true |
| 148 … | + var { type } = msg.value.content |
| 149 … | + if (/^chess/.test(type)) { |
| 150 … | + type = 'chess' |
126 | 151 … | } |
| 152 … | + |
| 153 … | + return get(filterSettings(), ['show', type], true) |
127 | 154 … | } |
128 | 155 … | |
129 | 156 … | var downScrollAborter |
130 | 157 … | |
131 | 158 … | function filterDownThrough () { |
132 | 159 … | return pull( |
133 | 160 … | downScrollAborter, |
134 | 161 … | pull.filter(followFilter), |
135 | | - pull.filter(authorFilter), |
| 162 … | + pull.filter(channelFilter), |
136 | 163 … | pull.filter(messageFilter) |
137 | 164 … | ) |
138 | 165 … | } |
139 | 166 … | |
142 | 169 … | function filterUpThrough () { |
143 | 170 … | return pull( |
144 | 171 … | upScrollAborter, |
145 | 172 … | pull.filter(followFilter), |
146 | | - pull.filter(authorFilter), |
| 173 … | + pull.filter(channelFilter), |
147 | 174 … | pull.filter(messageFilter) |
148 | 175 … | ) |
149 | 176 … | } |
150 | 177 … | |