fix(post): stop thread navigation from forcing OP alignment (#1052)

* Update README.md

* fix(post): stop thread navigation from forcing OP alignment

Route-driven thread opens now use the normal top-of-page behavior, while explicit OP permalink intents carry a scrollThreadContainerCid state and resolve against the visible thread container only. The old spacer injection path is removed.

* fix(post): address PR review follow-ups
This commit is contained in:
Tommaso Casaburi
2026-03-11 18:43:36 +08:00
committed by GitHub
parent 3401ec147f
commit 6c03253cb4
9 changed files with 297 additions and 199 deletions
+20 -10
View File
@@ -1,4 +1,4 @@
import { memo, useEffect, useMemo } from 'react';
import { memo, useEffect, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { Comment, Role, useComment, useEditedComment, useSubplebbit } from '@bitsocialnet/bitsocial-react-hooks';
import useSubplebbitsPagesStore from '@bitsocialnet/bitsocial-react-hooks/dist/stores/subplebbits-pages';
@@ -13,7 +13,7 @@ import ErrorDisplay from '../../components/error-display/error-display';
import { PageFooterDesktop, ThreadFooterFirstRow, ThreadFooterStyleRow, ThreadFooterMobile } from '../../components/footer';
import PostDesktop from '../../components/post-desktop';
import PostMobile from '../../components/post-mobile';
import { clearThreadScrollSpacer, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
import { getRequestedThreadTopCid, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
import styles from './post.module.css';
// useComment may not return cached feed data immediately due to its updatedAt comparison logic.
@@ -134,6 +134,7 @@ const PostPage = () => {
const isInAllView = isAllView(location.pathname);
const comment = useCommentWithFeedCache({ commentCid });
const consumedThreadTopScrollRef = useRef<string | null>(null);
const navigate = useNavigate();
useEffect(() => {
@@ -154,23 +155,32 @@ const PostPage = () => {
} else {
post = comment;
}
const requestedThreadTopCid = getRequestedThreadTopCid(location.state);
const { error } = post || {};
useEffect(() => () => clearThreadScrollSpacer(), []);
// These two effects split normal opens from explicit OP-top intents:
// the first keeps ordinary thread visits on `window.scrollTo(0, 0)`, while the
// second consumes `requestedThreadTopCid` once per `location.key` via
// `consumedThreadTopScrollRef` so `scrollThreadContainerToTop(commentCid)` only
// replays for deliberate OP-link clicks and never for route-driven thread opens.
useEffect(() => {
if (!commentCid || post?.cid === commentCid) return;
clearThreadScrollSpacer();
}, [commentCid, post?.cid]);
if (!comment?.cid || comment.parentCid) return;
if (requestedThreadTopCid === comment.cid) return;
window.scrollTo(0, 0);
}, [comment?.cid, comment?.parentCid, requestedThreadTopCid]);
useEffect(() => {
if (!commentCid || post?.cid !== commentCid) return;
if (requestedThreadTopCid !== commentCid) return;
const consumedKey = `${location.key}:${commentCid}`;
if (consumedThreadTopScrollRef.current === consumedKey) return;
if (scrollThreadContainerToTop(commentCid)) {
return;
consumedThreadTopScrollRef.current = consumedKey;
}
window.scrollTo(0, 0);
}, [commentCid, post?.cid]);
}, [commentCid, location.key, post?.cid, requestedThreadTopCid]);
useEffect(() => {
const boardIdentifier = params.boardIdentifier;