* enhance: FaviconProvider, keep track of new comment IDs to change favicon, remove new comment IDs per outline removal * don't track oneself comments * enhance: auto-show new comments, idempotency by ignoring already injected comments, preserveScroll utility * fadeIn animation on comment injection; cleanup: remove unused counts and thread handling; non-critical fix: always give rootLastCommentAt a value * reliably preserve scroll position by tracking a reference found at the center of the viewport; cleanup: add more comments, add cleanup function * mitigate fractional scrolling subtle layout shifts by rounding the new reference element position * enhanced outlining system, favicon context keeps track of new comments presence - de-outlining now happens only for outlined comments - enhanced outlining: add outline only if isNewComment - de-outlining will remove the new comments favicon - on unmount remove the new comments favicon * remove the new comments favicon on new comments injection * track only deduplicated new comments * fix typo * clearer unsetOutline conditions, fix typo in live comments hook * backport: remove the injectedComment class from injected comments after animation ends * set the new comments favicon on any new outlined comment * enhance: directly inject new comments; cleanup: dismantle ShowNewComments, remove newComments field * tweaks: slower injection animation, clear favicon on Comment section unmount * change nDirectComments bug strategy to avoiding updates on comment edit * cleanup: better naming, re-instate injected comments outline * injection: major cache utilities refactor, don't preserve scroll if no comments have been injected - don't preserve scroll if after deduplication we don't inject any comments - use manual read/write cache updates to control the flow -- allows to check if we are really injecting or not - reduce polling to 5 seconds instead of 10 - light cleanup -- removed update cache functions -- added 'injected' to typeDefs (gql consistency) * cleanup: detailed comments, refactor, remove clutter Refactor: + clearer variables + depth calculation utility function + use destructured Apollo cache + extract item object from item query + skip ignored comment instead of ending the loop CSS: + from-to fadeIn animation keyframes - floatingComments unused class Favicon: + provider exported by default * fix wrong merge * split: remove favicon context * split: remove favicon pngs * regression: revert to updateQuery for multiple comment fragments handling * reverse multiple reads for deduplication on comment injection * fix regression on apollo manipulations via fn; cleanup: remove wrong deps from outlining
54 lines
1.7 KiB
JavaScript
54 lines
1.7 KiB
JavaScript
export default function preserveScroll (callback) {
|
|
// preserve the actual scroll position
|
|
const scrollTop = window.scrollY
|
|
|
|
// if the scroll position is at the top, we don't need to preserve it, just call the callback
|
|
if (scrollTop <= 0) {
|
|
callback()
|
|
return
|
|
}
|
|
|
|
// get a reference element at the center of the viewport to track if content is added above it
|
|
const ref = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2)
|
|
const refTop = ref ? ref.getBoundingClientRect().top + scrollTop : scrollTop
|
|
|
|
// observe the document for changes in height
|
|
const observer = new window.MutationObserver(() => {
|
|
// request animation frame to ensure the DOM is updated
|
|
window.requestAnimationFrame(() => {
|
|
// we can't proceed if we couldn't find a traceable reference element
|
|
if (!ref) {
|
|
cleanup()
|
|
return
|
|
}
|
|
|
|
// get the new position of the reference element along with the new scroll position
|
|
const newRefTop = ref ? ref.getBoundingClientRect().top + window.scrollY : window.scrollY
|
|
// has the reference element moved?
|
|
const refMoved = newRefTop - refTop
|
|
|
|
// if the reference element moved, we need to scroll to the new position
|
|
if (refMoved > 0) {
|
|
window.scrollTo({
|
|
// some browsers don't respond well to fractional scroll position, so we round up the new position to the nearest integer
|
|
top: scrollTop + Math.ceil(refMoved),
|
|
behavior: 'instant'
|
|
})
|
|
}
|
|
|
|
cleanup()
|
|
})
|
|
})
|
|
|
|
const timeout = setTimeout(() => cleanup(), 1000) // fallback
|
|
|
|
function cleanup () {
|
|
clearTimeout(timeout)
|
|
observer.disconnect()
|
|
}
|
|
|
|
observer.observe(document.body, { childList: true, subtree: true })
|
|
|
|
callback()
|
|
}
|