// ES2018 Hacker News client under 100 lines of code
const baseUrl = 'https://hacker-news.firebaseio.com/v0/'
const fetchAll = async (...endpoints) => {
const responses = await Promise.all(endpoints.map(endpoint => fetch(`${ baseUrl }${ endpoint }.json`)))
responses.forEach(response => { if (!response.ok) throw Error(`${ response.status } ${ response.statusText }`) })
return await Promise.all(responses.map(response => response.json()))
}
const formatter = new Intl.DateTimeFormat(undefined, { dateStyle: 'short', timeStyle: 'short' })
const list = async ids => {
document.querySelector('.hn').innerHTML = ``
const $list = document.querySelector('.hn ol')
const items = await fetchAll(...ids.map(id => `item/${ id }`)) // FIXME indeterminate order?
for (const item of items) { // TODO generator, infinite scrolling
const $item = document.createElement('li')
$list.appendChild($item)
const domain = item.url ? new URL(item.url).host : '' // ask, jobs and others
$item.innerHTML = `${ item.title } ${ domain }
${ item.score } points by ${ item.by } ${ formatter.format(new Date(item.time * 1000)) }
${ item.descendants || 0 } comments`
}
}
const renderComments = async ($article, kids) => {
if (!kids) return
const items = await fetchAll(...kids.map(kid => `item/${ kid }`))
for (const item of items) {
const $comment = document.createElement('blockquote')
$article.appendChild($comment)
$comment.innerHTML = `
${ item.by || 'deleted'} ${ formatter.format(new Date(item.time * 1000)) } ${item.dead ? 'flagged' : ''}
${ item.text || '' } `
renderComments($comment.querySelector('details'), item.kids)
}
}
const item = item => {
if (!item) throw Error('no such item')
document.querySelector('.hn').innerHTML = `
${item.text || /* item.url || */ ''}`
const $article = document.querySelector('.hn article')
renderComments($article, item.kids)
}
const profile = user => {
if (!user) throw Error(`no such user`)
document.querySelector('.hn').innerHTML = `${ user.id }
- About
- ${ user.about || 'N/A' }
- Submitted
- ${ user.submitted.length }
- Karma
- ${ user.karma}
- Created
- ${ formatter.format(new Date(user.created * 1000)) }
`
}
const params = new URLSearchParams(document.location.search)
const [id, user, type, query] = [params.get('id'), params.get('user'), params.get('page') || 'topstories', params.get('query')]
const $nav = document.createElement('nav')
document.querySelector('header').appendChild($nav)
$nav.innerHTML = `
`
;(async () => {
try {
if (!query) { // TODO ignore query, try to render what you can... let plugins handle the rest
if (id) {
item((await fetchAll(`item/${ id }`))[0])
} else if (user) {
profile((await fetchAll(`user/${ user }`))[0])
} else if (type) {
await list(((await fetchAll(type))[0]).slice(0, 150))
}
}
} catch (e) {
console.error(e); document.querySelector('.hn').innerHTML = e
}
})()