Created
March 25, 2021 11:34
-
-
Save myfonj/932c50067ed4d112bdf4366ff6cc2309 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name HN: style unread content | |
// @description Save hashes of displayed comments locally and mark new ones when displayed for the first time | |
// @namespace myfonj | |
// @match https://news.ycombinator.com/* | |
// @grant none | |
// @version 1.0 | |
// @author myfonj | |
// ==/UserScript== | |
// Styles | |
document.head.appendChild(document.createElement('style')).textContent = ` | |
.new.new.new { | |
border-right: 2px solid #0F06; display: block; padding-right: 1em; | |
} | |
.justSeen.justSeen.justSeen { | |
border-right: 2px solid #0F03; display: block; padding-right: 1em; | |
} | |
.seen.seen.seen.seen { | |
/* leave it as it was */ | |
} | |
`; | |
const VISIBILITY_DURATION_MS = 900; | |
const CLASSES = { new: 'new', seen: 'seen', justSeen: 'justSeen' }; | |
const HASH_DIGEST_ALGO = 'SHA-1'; // 'SHA-256'; | |
const MAP_EL_HASH = new WeakMap(); | |
const MAP_EL_TIMEOUT = new WeakMap(); | |
const LS_KEY = 'displayed_hashes_' + HASH_DIGEST_ALGO; | |
const SEEN_HASH_LIST = (localStorage.getItem(LS_KEY) || '').split(','); | |
const ELS_TO_WATCH = document.querySelectorAll('.commtext,.storylink'); | |
const VIEWPORT_OBSERVER = new IntersectionObserver( | |
(entries, observer) => { | |
entries.forEach(entry => { | |
const TGT = entry.target; | |
if (entry.isIntersecting) { | |
if (MAP_EL_TIMEOUT.get(TGT)) { | |
// don't set twice on re-entry | |
return | |
} | |
MAP_EL_TIMEOUT.set( | |
TGT, | |
window.setTimeout(function () { | |
if (MAP_EL_TIMEOUT.get(TGT)) { | |
TGT.classList.remove(CLASSES.new); | |
TGT.classList.add(CLASSES.justSeen); | |
SEEN_HASH_LIST.push(MAP_EL_HASH.get(TGT)) | |
MAP_EL_TIMEOUT.delete(TGT); | |
VIEWPORT_OBSERVER.unobserve(TGT); | |
// TODO move the saving to LS to unload | |
// and blur for less writes | |
localStorage.setItem( | |
LS_KEY, | |
SEEN_HASH_LIST.join(',') | |
); | |
} | |
}, VISIBILITY_DURATION_MS) | |
); | |
} else { | |
MAP_EL_TIMEOUT.delete(TGT); | |
} | |
}); | |
}, | |
{ | |
root: null, | |
rootMargin: "-9%", // TODO use "lines" height here? | |
threshold: 0 | |
} | |
); | |
ELS_TO_WATCH.forEach(async el => { | |
const hash = await makeHash(el.textContent); | |
if (SEEN_HASH_LIST.includes(hash)) { | |
el.classList.add(CLASSES.seen); | |
return; | |
} | |
el.classList.add(CLASSES.new); | |
MAP_EL_HASH.set(el, hash); | |
VIEWPORT_OBSERVER.observe(el); | |
}); | |
async function makeHash (input) { | |
return btoa( | |
String.fromCharCode.apply( | |
null, | |
new Uint8Array( | |
await crypto.subtle.digest( | |
HASH_DIGEST_ALGO, | |
(new TextEncoder()).encode(input) | |
) | |
) | |
) | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment