* 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
92 lines
2.9 KiB
JavaScript
92 lines
2.9 KiB
JavaScript
import { COMMENT_WITH_NEW_RECURSIVE, COMMENT_WITH_NEW_LIMITED, COMMENT_WITH_NEW_MINIMAL } from '../fragments/comments'
|
|
import { ITEM_FULL } from '../fragments/items'
|
|
|
|
// updates the ncomments field of all ancestors of an item/comment in the cache
|
|
export function updateAncestorsCommentCount (cache, ancestors, increment) {
|
|
// update all ancestors
|
|
ancestors.forEach(id => {
|
|
cache.modify({
|
|
id: `Item:${id}`,
|
|
fields: {
|
|
ncomments (existingNComments = 0) {
|
|
return existingNComments + increment
|
|
}
|
|
},
|
|
optimistic: true
|
|
})
|
|
})
|
|
}
|
|
|
|
// updates the item query in the cache
|
|
// this is used by live comments to update a top level item's comments field
|
|
export function updateItemQuery (cache, id, sort, fn) {
|
|
cache.updateQuery({
|
|
query: ITEM_FULL,
|
|
// updateQuery needs the correct variables to update the correct item
|
|
// the Item query might have the router.query.sort in the variables, so we need to pass it in if it exists
|
|
variables: sort ? { id, sort } : { id }
|
|
}, (data) => {
|
|
if (!data) return data
|
|
return { item: fn(data.item) }
|
|
})
|
|
}
|
|
|
|
// updates a comment fragment in the cache, with fallbacks for comments lacking CommentsRecursive or Comments altogether
|
|
export function updateCommentFragment (cache, id, fn) {
|
|
let result = cache.updateFragment({
|
|
id: `Item:${id}`,
|
|
fragment: COMMENT_WITH_NEW_RECURSIVE,
|
|
fragmentName: 'CommentWithNewRecursive'
|
|
}, (data) => {
|
|
if (!data) return data
|
|
return fn(data)
|
|
})
|
|
|
|
// sometimes comments can start to reach their depth limit, and lack adherence to the CommentsRecursive fragment
|
|
// for this reason, we update the fragment with a limited version that only includes the CommentFields fragment
|
|
if (!result) {
|
|
result = cache.updateFragment({
|
|
id: `Item:${id}`,
|
|
fragment: COMMENT_WITH_NEW_LIMITED,
|
|
fragmentName: 'CommentWithNewLimited'
|
|
}, (data) => {
|
|
if (!data) return data
|
|
return fn(data)
|
|
})
|
|
}
|
|
|
|
// at the deepest level, the comment can't have any children, here we update only the newComments field.
|
|
if (!result) {
|
|
result = cache.updateFragment({
|
|
id: `Item:${id}`,
|
|
fragment: COMMENT_WITH_NEW_MINIMAL,
|
|
fragmentName: 'CommentWithNewMinimal'
|
|
}, (data) => {
|
|
if (!data) return data
|
|
return fn(data)
|
|
})
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
export function calculateDepth (path, rootId, parentId) {
|
|
// calculate depth by counting path segments from root to parent
|
|
const pathSegments = path.split('.')
|
|
const rootIndex = pathSegments.indexOf(rootId.toString())
|
|
const parentIndex = pathSegments.indexOf(parentId.toString())
|
|
|
|
// depth is the distance from root to parent in the path
|
|
const depth = parentIndex - rootIndex
|
|
|
|
return depth
|
|
}
|
|
|
|
// finds the most recent createdAt timestamp from an array of comments
|
|
export function getLatestCommentCreatedAt (comments, latest) {
|
|
return comments.reduce(
|
|
(max, { createdAt }) => (createdAt > max ? createdAt : max),
|
|
latest
|
|
)
|
|
}
|