Skip to content

Commit d456c2f

Browse files
committed
add filter option to Feed (closes #19)
1 parent 2bf3433 commit d456c2f

File tree

6 files changed

+68
-6
lines changed

6 files changed

+68
-6
lines changed

packages/client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@fortawesome/free-solid-svg-icons": "^5.10.2",
3737
"@fortawesome/react-fontawesome": "^0.1.4",
3838
"classnames": "^2.2.6",
39+
"escape-string-regexp": "^2.0.0",
3940
"lazysizes": "^5.1.1",
4041
"prop-types": "^15.7.2",
4142
"rc-notification": "^3.3.1",

packages/client/src/components/FeedBox/Body/Edit/List/SortableFeed/index.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
66
import {
77
faBars,
88
faEye,
9+
faFilter,
910
faLink,
1011
faPen,
1112
faTrash,
@@ -40,6 +41,7 @@ const FEED_ITEM_DISPLAY = {
4041
EDIT_TITLE: 1,
4142
EDIT_URL: 2,
4243
EDIT_DISPLAY: 3,
44+
EDIT_FILTER: 4,
4345
}
4446

4547
const Feed = ({ feed }) => {
@@ -49,6 +51,7 @@ const Feed = ({ feed }) => {
4951
const [newCustomTitle, setNewCustomTitle] = useState(feed.customTitle)
5052
const [newUrl, setNewUrl] = useState(feed.url)
5153
const [newDisplay, setNewDisplay] = useState(feed.display)
54+
const [newFilter, setNewFilter] = useState(feed.filter)
5255

5356
const dispatchTitle = () => {
5457
dispatch(editFeed(feed.id, { customTitle: newCustomTitle }))
@@ -62,6 +65,10 @@ const Feed = ({ feed }) => {
6265
dispatch(editFeed(feed.id, { display: val || newDisplay }))
6366
}
6467

68+
const dispatchFilter = () => {
69+
dispatch(editFeed(feed.id, { filter: newFilter }))
70+
}
71+
6572
const makeInputOnKeyUp = (dispatchValue) => (
6673
(ev) => {
6774
if (ev.keyCode === 13) {
@@ -147,6 +154,21 @@ const Feed = ({ feed }) => {
147154
</form>
148155
</div>
149156
)
157+
} else if (feedItemDisplay === FEED_ITEM_DISPLAY.EDIT_FILTER) {
158+
feedDisplay = (
159+
<div className={css.form}>
160+
<form onSubmit={(ev) => ev.preventDefault()}>
161+
<input
162+
className="nondraggable"
163+
onChange={(ev) => setNewFilter(ev.target.value)}
164+
onKeyUp={makeInputOnKeyUp(dispatchFilter)}
165+
placeholder={feed.filter}
166+
ref={inputRef}
167+
value={newFilter}
168+
/>
169+
</form>
170+
</div>
171+
)
150172
} else {
151173
feedDisplay = (
152174
<span className={css.title} title={feed.url}>
@@ -186,6 +208,13 @@ const Feed = ({ feed }) => {
186208
>
187209
<FontAwesomeIcon icon={faEye} />
188210
</button>
211+
<button
212+
onClick={makeEditOnClick(dispatchUrl, FEED_ITEM_DISPLAY.EDIT_FILTER)}
213+
title="Edit filter"
214+
type="button"
215+
>
216+
<FontAwesomeIcon icon={faFilter} />
217+
</button>
189218
<button
190219
onClick={() => dispatch(deleteFeed(feed.id))}
191220
title="Remove feed"

packages/client/src/store/models/Feed.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default class Feed extends Model {
1818
title: attr({ getDefault: () => 'New feed' }),
1919
status: attr({ getDefault: () => FEED_STATUS.NEW }),
2020
display: attr({ getDefault: () => FEED_DISPLAY.DETAILED }),
21+
filter: attr(),
2122
error: attr(),
2223
index: attr(),
2324
lastFetched: attr({ getDefault: () => 0 }),

packages/client/src/store/selectors/feedItem.js

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,31 @@
11
import { createSelector } from 'redux-orm'
2+
import escapeStringRegexp from 'escape-string-regexp'
23

34
import orm from 'newsdash/store/orm'
45
import selectId from './id'
56

7+
const filterItems = (items, pattern) => {
8+
const testFields = ['link', 'title', 'content']
9+
const regexps = pattern.split(',').reduce((acc, word) => {
10+
try {
11+
acc.push(new RegExp(escapeStringRegexp(word.trim()), 'i'))
12+
} catch {
13+
//
14+
}
15+
return acc
16+
}, [])
17+
return items.filter((item) => {
18+
for (let i = 0; i < testFields.length; i += 1) {
19+
for (let j = 0; j < regexps.length; j += 1) {
20+
if (regexps[j].test(item[testFields[i]])) {
21+
return false
22+
}
23+
}
24+
}
25+
return true
26+
})
27+
}
28+
629
const getAllFeedItems = createSelector(
730
orm,
831
(session) => session
@@ -16,12 +39,14 @@ const makeGetFeedItems = () => createSelector(
1639
selectId,
1740
(session, id) => {
1841
const feed = session.Feed.withId(id)
19-
return feed
20-
? feed
21-
.items
22-
.toRefArray()
23-
.sort((a, b) => b.date - a.date) // sort by date
24-
: []
42+
if (!feed) {
43+
return []
44+
}
45+
const items = feed
46+
.items
47+
.toRefArray()
48+
.sort((a, b) => b.date - a.date) // sort by date
49+
return feed.filter ? filterItems(items, feed.filter) : items
2550
}
2651
)
2752

packages/server/src/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const FIELDS_FEED = [
3535
'link',
3636
'title',
3737
'display',
38+
'filter',
3839
['index', 'int'],
3940
['feedBox', 'int'],
4041
]

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3623,6 +3623,11 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
36233623
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
36243624
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
36253625

3626+
escape-string-regexp@^2.0.0:
3627+
version "2.0.0"
3628+
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
3629+
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
3630+
36263631
eslint-config-airbnb-base@^14.0.0:
36273632
version "14.0.0"
36283633
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.0.0.tgz#8a7bcb9643d13c55df4dd7444f138bf4efa61e17"

0 commit comments

Comments
 (0)