(???) / ES2018 Hacker News client under 100 lines of code
(???) onst baseUrl = 'https://hacker-news.firebaseio.com/v0/'
(???) onst fetchAll = async (...endpoints) => {
(???) // TODO use forEach(async)
(???) 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()))
(???)
(???) onst formatter = new Intl.DateTimeFormat(undefined, { dateStyle: 'short', timeStyle: 'short' })
(???) onst list = async ids => {
(???) document.querySelector('.hn').innerHTML = `<section><h2>Hacker News</h2><ol></ol></section>`
(???) 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 = `<a href="${ item.url || '?id=' + item.id}">${ item.title }</a> <small>${ domain }</small><br>
(???) small>${ item.score } points by <a href="?user=${ item.by }">${ item.by }</a> <a href="?id=${ item.id }">${ formatter.format(new Date(item.time * 1000)) }</a>
(???) <a href="?id=${ item.id }">${ item.descendants || 0 } comments</a></small>`
(???) }
(???)
(???) onst 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 = `<details open><summary>
(???) small><a href="?user=${ item.by }">${ item.by || 'deleted'}</a> <a href="?id=${ item.id }">${ formatter.format(new Date(item.time * 1000)) }</a> ${item.dead ? 'flagged' : ''}</small>
(???) /summary>
(???) { item.text || '' }</details>`
(???) renderComments($comment.querySelector('details'), item.kids)
(???) }
(???)
(???) onst item = item => {
(???) if (!item) throw Error('no such item')
(???) document.querySelector('.hn').innerHTML = `<article><header>
(???) <h1><a href="${ item.url || '?id=' + id }">${ item.title || '' }</a></h1>
(???) <small>by <a href="?user=${ item.by }">${ item.by }</a> <a href="?id=${ item.id }">${ formatter.format(new Date(item.time * 1000)) }</a> <a href="?id=${ item.parent }">${ item.parent ? 'parent' : '' }</a> ${item.dead ? 'flagged' : ''}</small>
(???) /header>
(???) {item.text || /* item.url || */ ''}</article>`
(???) const $article = document.querySelector('.hn article')
(???) renderComments($article, item.kids)
(???)
(???) onst profile = user => {
(???) if (!user) throw Error(`no such user`)
(???) document.querySelector('.hn').innerHTML = `<section><h1>${ user.id }</h1>
(???) <dl>
(???) <dt>About</dt><dd>${ user.about || 'N/A' }</dd>
(???) <dt>Submitted</dt><dd>${ user.submitted.length }</dd>
(???) <dt>Karma</dt><dd>${ user.karma}</dd>
(???) <dt>Created</dt><dd>${ formatter.format(new Date(user.created * 1000)) }</dd>
(???) </dl>
(???) /section>`
(???)
(???) onst params = new URLSearchParams(document.location.search)
(???) onst [id, user, type, query] = [params.get('id'), params.get('user'), params.get('page') || 'topstories', params.get('query')]
(???) onst $nav = document.createElement('nav')
(DOC) ocument.querySelector('header').appendChild($nav)
(???) nav.innerHTML = `<ul class="${ type }">
(???) ${['topstories', 'beststories', 'newstories', 'showstories', 'askstories', 'jobstories']
(???) .map(i => `<li><a class="${ i }" href="?page=${ i }">${ i.replace('stories', '') }</a></li>`).join('')}
(???) /ul>`
(???) (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
(???) }
(???) )()