From 60fb79a78874df958d80d1980d4aa11191b5add3 Mon Sep 17 00:00:00 2001 From: Soxasora Date: Tue, 21 Apr 2026 20:10:44 +0200 Subject: [PATCH 1/3] fix: untrack new comments only on actual unmount or interaction --- components/comment.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/components/comment.js b/components/comment.js index 1c4748080..9cf2df917 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() @@ -135,6 +137,7 @@ export default function Comment ({ classes.add('outline-new-comment-unset') // untrack new comment and its descendants if it's not a live comment navigator?.untrackNewComment(ref, { includeDescendants: hasOutline }) + didTrackRef.current = false } useEffect(() => { @@ -165,6 +168,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 +197,18 @@ export default function Comment ({ ref.current.classList.add('outline-new-comment') } + didTrackRef.current = true 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 From 450fb864d1694eaa0403ca202bb21e5845ab2e93 Mon Sep 17 00:00:00 2001 From: Soxasora Date: Mon, 27 Apr 2026 12:46:47 +0200 Subject: [PATCH 2/3] synchronous trackNewComment to avoid outline<->navigator sync drift --- components/comment.js | 4 +-- components/use-comments-navigator.js | 50 +++++++++++++--------------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/components/comment.js b/components/comment.js index 9cf2df917..145eef499 100644 --- a/components/comment.js +++ b/components/comment.js @@ -197,8 +197,8 @@ export default function Comment ({ ref.current.classList.add('outline-new-comment') } - didTrackRef.current = true - 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 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 = {}) => { From 429d08b20f590001229d5050f6a80a95a2a719da Mon Sep 17 00:00:00 2001 From: Soxasora Date: Mon, 27 Apr 2026 13:14:10 +0200 Subject: [PATCH 3/3] don't reset didTrackRef on unsetOutline --- components/comment.js | 1 - 1 file changed, 1 deletion(-) diff --git a/components/comment.js b/components/comment.js index 145eef499..cc921aded 100644 --- a/components/comment.js +++ b/components/comment.js @@ -137,7 +137,6 @@ export default function Comment ({ classes.add('outline-new-comment-unset') // untrack new comment and its descendants if it's not a live comment navigator?.untrackNewComment(ref, { includeDescendants: hasOutline }) - didTrackRef.current = false } useEffect(() => {