"use strict"; const pageItemBox = document.getElementById('item-box') const headerSub = document.getElementById('header-sub') const params = new URLSearchParams(document.location.search) // for top listing let items = [] // shuld be an array of items id let currentItem = -1 // for newest listing let newMode = params.get('order') == 'new' let endItem // for the later new items. new items will be fetch until this item let maxItem let new_loader_id // for the api requests let loadedItems = 0 let loader_id let timer = 100 // how many request pre minute // for item query let isQuery = params.get('id') != null const itemType = { job: 'job', // list story: 'story', // list comment: 'comment', poll: 'poll', // list pollopt: 'pollopt' } async function fetchStories() { const req = await fetch('https://hacker-news.firebaseio.com/v0/topstories.json') req.json().then(resolve => { items = resolve currentItem = 0 }) } async function fetchMaxItem() { const req = await fetch('https://hacker-news.firebaseio.com/v0/maxitem.json') return req.json() } function loadItems() { const shouldLoad = () => { const maxScroll = document.documentElement.scrollHeight - document.documentElement.clientHeight return (currentItem < items.length) && window.scrollY > maxScroll - 100 || maxScroll == 0 } if (loader_id || !shouldLoad()) { return } loader_id = setInterval(async () => { if (!shouldLoad()) { loader_id = clearInterval(loader_id) if (newMode && !new_loader_id) { new_loader_id = setInterval(loadFresh, 5e3) } return } const itemId = newMode ? maxItem-- : items[currentItem++] try { const item = await getItem(itemId) makeItemListing(item) } catch (e) { console.log(`[loadItems] got error ${e}`) } }, timer) } function getItem(id) { const url = `https://hacker-news.firebaseio.com/v0/item/${id}.json` try { return fetch(url).then(response => response.json().then(obj => obj)) } catch (e) { console.log(`[getItem] failed to fetch item ${id}`) return } } function makeItemListing(itemObj) { if (itemObj === null || itemObj === undefined) { clearInterval(loader_id) throw new Error(`[makeItemListing] null or undefined object`) } if (itemObj.deleted || itemObj.dead || itemObj.title == "[deleted]") { console.debug(`[makeItemListing] Deleted item: ${itemObj.id}`) return } if (itemObj.type == itemType.comment || itemObj.type == itemType.pollopt) { console.debug(`[makeItemListing] Comment or Poll Option item: ${itemObj.id}`) return } const id = itemObj.id const title = itemObj.title const url = itemObj.url const points = itemObj.score const author = itemObj.by const time = itemObj.time const date = timeSince(itemObj.time) const comments = itemObj.descendants const match = url && url.match(/https?:\/\/([^\/:]+)/) const hostname = match && match[1] let subtitle = `${points} points by ${author} ${date} ago` if (itemObj.type != itemType.job) { subtitle += ` | ${comments} comments` } else { console.log(`[makeItemListing] job item ${title}`) } const element_string =`

${title} (${hostname})

${subtitle}

` const item = document.createElement('a') item.href = "index.html?id=" + id // i got lazy item.time = time item.points = points item.comments = comments item.author = author item.innerHTML = element_string if (url === undefined || hostname === undefined) { const header = item.getElementsByClassName('title')[0] header.innerHTML = title } const children = Array.from(pageItemBox.childNodes) for (const c of children) { if (typeof c == 'object' && c.time < item.time) { try { pageItemBox.insertBefore(item, c) return } catch (error) { console.log(error) console.log(c) clearInterval(loader_id) } } } pageItemBox.appendChild(item) } function timeSince(date) { const now = Date.now() / 1000 const seconds = Math.floor(now - date) if (seconds > 31536000) { return Math.floor(seconds / 31536000) + " years" } if (seconds > 2592000) { return Math.floor(seconds / 2592000) + " months" } if (seconds > 86400) { return Math.floor(seconds / 86400) + " days" } if (seconds > 3600) { return Math.floor(seconds / 3600) + " hours" } if (seconds > 60) { return Math.floor(seconds / 60) + " minutes" } return Math.floor(seconds) + " seconds" } async function itemPage() { headerSub.innerHTML = '' const item = await getItem(params.get('id')) if (item == null || item == undefined) { const element_string =`

Something went Bad :(

Couldn't get your result from the api

` const item = document.createElement('div') item.innerHTML = element_string pageItemBox.appendChild(item) } const type = item.type let target = pageItemBox if (type != itemType.comment && type != itemType.pollopt) { if (item.url) { headerSub.innerHTML = `${item.title} | ${item.score} points` } else { headerSub.innerHTML = `${item.title} | ${item.score} points` } } if (item.type == itemType.poll) { await loadPollOpt(item.parts) } else if (type == itemType.pollopt){ return loadPollOpt([item.id]) } if (!item.kids) { console.log(type) return } if (type == itemType.comment) { loadCommnets([item.id], target) } else { loadCommnets(item.kids, target) } } async function loadPollOpt(opts) { for (const id of opts) { const item = await getItem(id) if (!item || !item.text || !item.score) { continue } const element_string =`
 ${item.score}

${item.text}

` const element = document.createElement('div') element.className = 'block' element.innerHTML = element_string pageItemBox.appendChild(element) } pageItemBox.innerHTML += '
' } const sleep = (ms) => new Promise(resolve => { const reachedEnd = () => { const maxScroll = document.documentElement.scrollHeight - document.documentElement.clientHeight return window.scrollY >= maxScroll - 100 } const waitForScroll = () => { if (!reachedEnd()) { return } resolve() document.removeEventListener('scroll', waitForScroll) } if (reachedEnd()) { setTimeout(resolve, ms) } else { document.addEventListener('scroll', waitForScroll) } }) async function loadCommnets(ids, target) { const message = (msg) => `
${msg}
` for (const id of ids) { await sleep(timer) try { const itemObj = await getItem(id) if (itemObj == undefined || itemObj.type != itemType.comment || !itemObj.text) { continue } const item = document.createElement('div') item.className = 'card' item.innerHTML = message(itemObj.text) target.appendChild(item) if (itemObj.kids && itemObj.kids.length > 0) { const mainBox = item.getElementsByClassName('content')[0] const subBox = document.createElement('div') subBox.className = 'card' mainBox.innerHTML += '
' mainBox.appendChild(subBox) await loadCommnets(itemObj.kids, subBox) } } catch (e) { console.log(`[loadCommnets] failed to load comment ${id}, error ${e}`) } } } async function loadPage() { if (isQuery) { return itemPage() } if (newMode) { maxItem = await fetchMaxItem() endItem = maxItem } else { await fetchStories() } loadItems() document.addEventListener('scroll', loadItems) } loadPage() let switchBtn = document.getElementById('switch-btn') if (newMode) { switchBtn.innerText = 'Top' switchBtn.className = 'button is-primary' } switchBtn.addEventListener("click", () => { if (newMode) { window.location.assign('index.html') } else { window.location.assign('index.html?order=new') } }) // ================ new items section stuff =================== let permission = 'denied' let notificationBtn = document.getElementById('notification-btn') notificationBtn.addEventListener("click", updatePermission) function updatePermission() { if (!newMode) { notificationBtn.style.visibility = 'collapse' return } Notification.requestPermission().then(result => { permission = result notificationBtn.disabled = permission == 'denied' }) } updatePermission() async function notify(count) { if (count == 0) { return } new Notification("Clone Wars", { body: `${count} new Posts`}) } async function loadFresh() { let newMax = endItem try { newMax = await fetchMaxItem() } catch (e) { console.log(`[loadFresh] got error ${e}`) } const newEnd = newMax if (newMax <= endItem) { return } console.log(`[loadFresh] New Items ${newMax - endItem}`) const count = pageItemBox.childElementCount let intervalId = setInterval(() => { if (newMax <= endItem) { endItem = newEnd notify(pageItemBox.childElementCount - count) clearInterval(intervalId) return } try { getItem(newMax--).then(item => { if (item) { makeItemListing(item) } }) } catch (e) { console.log(`[loadFresh] got error ${e}`) } }, timer) } if (!isQuery) { setInterval(() => Array.from(pageItemBox.getElementsByTagName('a')).map(c => { if (!c.time) { return } const sub = c.getElementsByClassName('subtitle')[0] const date = timeSince(c.time) const points = c.points const author = c.author const comments = c.comments let subtitle = `${points} points by ${author} ${date} ago` if (comments !== undefined) { subtitle += ` | ${comments} comments` } sub.innerHTML = subtitle }), 1e3) }