diff --git a/components/comment.js b/components/comment.js index 1c4748080..cc921aded 100644 --- a/components/comment.js +++ b/components/comment.js @@ -114,6 +114,8 @@ export default function Comment ({ ? 'yep' : 'nope') const ref = useRef(null) + // if comment is registered with the new comments navigator + const didTrackRef = useRef(false) const { ref: readerRef, onRef: onReaderRef } = useCallbackRef() const router = useRouter() const root = useRoot() @@ -165,6 +167,8 @@ export default function Comment ({ useEffect(() => { // checking navigator because outlining should happen only on item pages if (!navigator || me?.id === item.user?.id) return + // bail if we already registered this comment + if (didTrackRef.current) return const itemCreatedAt = new Date(item.createdAt).getTime() @@ -192,12 +196,18 @@ export default function Comment ({ ref.current.classList.add('outline-new-comment') } - navigator.trackNewComment(ref, itemCreatedAt) + // track the new comment instantly so that a subsequent unsetOutline can remove it immediately + didTrackRef.current = navigator.trackNewComment(ref, itemCreatedAt) + }, [item.id, root.lastCommentAt, root.meCommentsViewedAt]) + // untrack comment from the new comments navigator on unmount + useEffect(() => { return () => { - navigator.untrackNewComment(ref, { includeDescendants: true }) + if (!didTrackRef.current) return + navigator?.untrackNewComment(ref, { includeDescendants: true }) + didTrackRef.current = false } - }, [item.id, root.lastCommentAt, root.meCommentsViewedAt]) + }, []) const bottomedOut = depth === COMMENT_DEPTH_LIMIT || (item.comments?.comments.length === 0 && item.nDirectComments > 0) // Don't show OP badge when anon user comments on anon user posts diff --git a/components/use-comments-navigator.js b/components/use-comments-navigator.js index 3e93ddc7d..e5ef2231d 100644 --- a/components/use-comments-navigator.js +++ b/components/use-comments-navigator.js @@ -59,35 +59,31 @@ export function useCommentsNavigator () { setHasNewComments(false) }, []) - // track a new comment + // track a new comment, returns true if the ref was actually inserted const trackNewComment = useCallback((commentRef, createdAt) => { - setHasNewComments(true) - try { - window.requestAnimationFrame(() => { - if (!commentRef?.current || !commentRef.current.isConnected) return - - // dedupe - const existing = commentRefs.current.some(item => item.ref.current === commentRef.current) - if (existing) return - - // find the correct insertion position to maintain sort order - const insertIndex = commentRefs.current.findIndex(item => item.createdAt > createdAt) - const newItem = { ref: commentRef, createdAt } - - if (insertIndex === -1) { - // append if no newer comments found - commentRefs.current.push(newItem) - } else { - // insert at the correct position to maintain sort order - commentRefs.current.splice(insertIndex, 0, newItem) - } - - throttleCountUpdate() - }) - } catch { - // in the rare case of a ref being disconnected during RAF, ignore to avoid blocking UI + const node = commentRef?.current + if (!node || !node.isConnected) return false + + // dedupe + const existing = commentRefs.current.some(item => item.ref.current === node) + if (existing) return false + + // find the correct insertion position to maintain sort order + const insertIndex = commentRefs.current.findIndex(item => item.createdAt > createdAt) + const newItem = { ref: commentRef, createdAt } + + if (insertIndex === -1) { + // append if no newer comments found + commentRefs.current.push(newItem) + } else { + // insert at the correct position to maintain sort order + commentRefs.current.splice(insertIndex, 0, newItem) } - }, [throttleCountUpdate]) + + setHasNewComments(true) + throttleCountUpdate() + return true + }, [throttleCountUpdate, setHasNewComments]) // remove a comment ref from the list const untrackNewComment = useCallback((commentRef, options = {}) => {