From 59fad7db31f5b874c08c44310a772433444c0b67 Mon Sep 17 00:00:00 2001 From: Les Orchard Date: Thu, 12 Oct 2017 00:56:48 -0400 Subject: [PATCH] New Feed component --- README.md | 2 +- src/components/ActivityPub/index.js | 2 +- src/components/Feed/fetch.js | 28 +++++++++++++++ src/components/Feed/index.js | 54 +++++++++++++++++++++++++++++ src/components/Feed/index.scss | 32 +++++++++++++++++ src/containers/App.js | 2 ++ 6 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 src/components/Feed/fetch.js create mode 100644 src/components/Feed/index.js create mode 100644 src/components/Feed/index.scss diff --git a/README.md b/README.md index 2cb5fdf..e681b98 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/components/ActivityPub/index.js b/src/components/ActivityPub/index.js index 819e9f9..70096d2 100644 --- a/src/components/ActivityPub/index.js +++ b/src/components/ActivityPub/index.js @@ -14,7 +14,7 @@ export class ActivityPub extends React.Component { return (

- {name} (@{username}) + {name} (@{username})

    diff --git a/src/components/Feed/fetch.js b/src/components/Feed/fetch.js new file mode 100644 index 0000000..0e6af43 --- /dev/null +++ b/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 })); +} diff --git a/src/components/Feed/index.js b/src/components/Feed/index.js new file mode 100644 index 0000000..fe7edc5 --- /dev/null +++ b/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 ( + +

    {title}

    +
    +
      + {items + .slice(0, maxItems) + .map((item, idx) => this.renderItem(item, idx))} +
    +
    +
    + ); + } + + renderItem(item, idx) { + const { title, summary, link, date } = item; + const createMarkup = () => ({ __html: summary }); + return ( +
  • + + {timeago().format(date)} + + {title} + {summary &&
    } +
  • + ); + } +} + +export default Feed; diff --git a/src/components/Feed/index.scss b/src/components/Feed/index.scss new file mode 100644 index 0000000..5390c55 --- /dev/null +++ b/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; + } + } + } +} diff --git a/src/containers/App.js b/src/containers/App.js index d6212b3..511b86a 100644 --- a/src/containers/App.js +++ b/src/containers/App.js @@ -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', @@ -80,6 +81,7 @@ export default class App extends Component { /> +