Skip to content

Commit

Permalink
New Feed component
Browse files Browse the repository at this point in the history
  • Loading branch information
lmorchard committed Oct 12, 2017
1 parent ae430c1 commit 59fad7d
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -17,7 +17,7 @@ old-school Web 2.0 mashup using my own personal data exhaust.

## TODO

- [ ] RSS / Atom component
- [ ] DRY out the SCSS in common between components

- [ ] Flickr photos component

Expand Down
2 changes: 1 addition & 1 deletion src/components/ActivityPub/index.js
Expand Up @@ -14,7 +14,7 @@ export class ActivityPub extends React.Component {
return (
<Card {...this.props} className={classnames('activitypub', name)}>
<h3>
{name} (<a href={`${profileUrl}`} title={username}>@{username}</a>)
{name} (<a href={profileUrl} title={username}>@{username}</a>)
</h3>
<section>
<ul>
Expand Down
28 changes: 28 additions & 0 deletions src/components/Feed/fetch.js
@@ -0,0 +1,28 @@
import fetch from 'node-fetch';
import FeedParser from 'feedparser';

export default async function fetchData(config, name) {
const { title, feedUrls } = config;

return Promise.all(
feedUrls.map(async feedUrl => {
const res = await fetch(feedUrl);
return new Promise((resolve, reject) => {
const feed = { name, title, meta: {}, items: [] };
res.body
.on('error', error => reject(error))
.pipe(new FeedParser())
.on('error', error => reject(error))
.on('meta', meta => (feed.meta = meta))
.on('readable', function() {
const stream = this;
let item;
while ((item = stream.read())) {
feed.items.push(item);
}
})
.on('end', () => resolve(feed));
});
})
).then(feeds => ({ name, title, feeds }));
}
54 changes: 54 additions & 0 deletions src/components/Feed/index.js
@@ -0,0 +1,54 @@
import React from 'react';
import classnames from 'classnames';
import timeago from 'timeago.js';

import Card from '../Card';
import './index.scss';

export class Feed extends React.Component {
render() {
const { name, title, feeds } = this.props;
const maxItems = this.props.maxItems || 12;
const seenUrls = new Set();

// Merge, de-dupe, and sort items from all feeds.
const items = feeds
.reduce((acc, curr) => acc.concat(curr.items), [])
.filter(item => {
const { link } = item;
const seen = seenUrls.has(link);
seenUrls.add(link);
return !seen;
})
.sort((a, b) => b.date.localeCompare(a.date));

return (
<Card {...this.props} className={classnames('feed', name)}>
<h3>{title}</h3>
<section>
<ul>
{items
.slice(0, maxItems)
.map((item, idx) => this.renderItem(item, idx))}
</ul>
</section>
</Card>
);
}

renderItem(item, idx) {
const { title, summary, link, date } = item;
const createMarkup = () => ({ __html: summary });
return (
<li key={idx} className="item">
<a className="createdAt" href={link} title={date} dateTime={date}>
{timeago().format(date)}
</a>
<a className="link" href={link}><span className="title">{title}</span></a>
{summary && <div className="content" dangerouslySetInnerHTML={createMarkup()} />}
</li>
);
}
}

export default Feed;
32 changes: 32 additions & 0 deletions src/components/Feed/index.scss
@@ -0,0 +1,32 @@
.card.feed {
ul {
list-style: none;
margin: 0.5em 1em;
padding: 0;
li {
clear: both;
margin-bottom: 1.5em;
padding-left: 1.75em;
&::before {
float: left;
width: 1.75em;
margin-left: -1.75em;
font-style: normal;
font-weight: normal;
font-family: "fxemoji-symbols";
content: "\1F501";
}
a {
font-weight: bold;
}
.createdAt {
float: right;
font-weight: normal;
white-space: nowrap;
padding: 0 0 1em 1em;
font-size: 0.75em;
text-decoration: underline dotted;
}
}
}
}
2 changes: 2 additions & 0 deletions src/containers/App.js
Expand Up @@ -16,6 +16,7 @@ import Goodreads from '../components/Goodreads';
import Pocket from '../components/Pocket';
import Project from '../components/Project';
import ActivityPub from '../components/ActivityPub';
import Feed from '../components/Feed';

const themes = [
'default',
Expand Down Expand Up @@ -80,6 +81,7 @@ export default class App extends Component {
/>
<Blog {...this.props.Blog} theme={theme()} />
<ActivityPub {...this.props.Toots} name="Toots" maxItems={7} theme={theme()} />
<Feed {...this.props.TTRSS} maxItems={12} theme={theme()} />
<Twitter {...this.props.Twitter} maxItems={7} theme={theme()} />
<Github {...this.props.Github} maxItems={7} theme={theme()} />
<Pocket {...this.props.Pocket} maxItems={7} theme={theme()} />
Expand Down

0 comments on commit 59fad7d

Please sign in to comment.