Skip to content

Instantly share code, notes, and snippets.

@zackbloom
Created February 4, 2020 18:13
Show Gist options
  • Save zackbloom/948c01c982b5cac71dc73a7925b14187 to your computer and use it in GitHub Desktop.
Save zackbloom/948c01c982b5cac71dc73a7925b14187 to your computer and use it in GitHub Desktop.
const Parser = require('rss-parser')
const parser = new Parser()
const FETCH_TIMEOUT = 3000
const FEED_ITEM_LIMIT = 5
function fetchFeed(url) {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
resolve(null)
}, FETCH_TIMEOUT)
fetch(url)
.then(response => {
clearTimeout(timeoutId)
return response.text()
})
.then(xml => parser.parseString(xml))
.then(resolve)
.catch(reject)
})
}
function fetchFeeds(urls) {
return Promise.all(urls.map(fetchFeed))
}
function renderItem(item, index) {
const date = item.isoDate ? new Date(item.isoDate) : null
return `
<div class="item-container${index % 2 === 0 ? '' : ' item-dark'}">
<div class="item">
<div class="item-title-container">
<a class="item-title" href="${item.link}">${item.title}</a>
</div>
<div class="item-date">
${date ? date.toDateString() : ''}
</div>
<div class="item-content">
${item.content}
</div>
</div>
</div>
`
}
function renderFeed(feed) {
if (!feed) {
return ''
}
return `
<div class="feed-title-container">
<div class="feed-title">${feed.title}</div>
</div>
${
feed.items
.slice(0, FEED_ITEM_LIMIT)
.map(renderItem)
.reduce((acc, item) => `${acc}${item}`)
}
`
}
function renderPage(feeds) {
return `
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Roboto&display=swap" rel="stylesheet">
<style>
html, body {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
}
*, *:before, *:after {
box-sizing: border-box;
}
.feed-title-container {
width: 100%;
color: #ffffff;
background: #262626;
}
.feed-title {
max-width: 750px;
margin: 0 auto;
padding: 12px;
}
.item-container {
width: 100%;
}
.item-dark {
background: #f9f9f9;
}
.item {
max-width: 750px;
margin: 0 auto;
padding: 24px 12px;
}
.item-title-container {
padding: 0 0 6px;
}
.item-title {
font-size: 1.5em;
text-decoration: none;
color: #262626;
}
@media only screen and (min-device-width: 768px) {
.item-title {
font-size: 2em;
}
}
.item-title:hover {
color: #2f7a91;
}
.item-date {
color: #58585d;
font-weight: 300;
font-size: .8em;
padding: 0 0 12px;
}
.item-content {
color: #58585d;
overflow-y: scroll;
}
.item-content * img {
border-radius: 6px;
}
.item-content > a {
text-decoration: none;
color: #2f7a91;
}
.item-content * a {
text-decoration: none;
color: #2f7a91;
}
</style>
</head>
<body>
${feeds.map(renderFeed).reduce((acc, feed) => `${acc}${feed}`)}
</body>
</html>
`
}
async function rawHtmlResponse(html) {
const init = {
headers: {
'content-type': 'text/html;charset=UTF-8',
},
}
return new Response(html, init)
}
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const urls = [
'https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml',
'https://xkcd.com/rss.xml',
'https://www.reddit.com/.rss',
]
const feeds = await fetchFeeds(urls)
const html = renderPage(feeds)
return rawHtmlResponse(html)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment