fix(thread): count user id tooltips from thread data

Replace the DOM-based user ID tooltip count with a deduped per-thread author map in `getThreadPostCountsByAuthor()`. This keeps mounted preview copies from doubling ID totals on desktop and mobile thread views.
This commit is contained in:
Tommaso Casaburi
2026-03-16 15:13:04 +08:00
parent 47e8c5a37b
commit 6ecf3c40d3
4 changed files with 87 additions and 12 deletions
+13 -5
View File
@@ -53,6 +53,7 @@ import { computeOmittedCount, filterRepliesForDisplay, getPreviewDisplayReplies,
import { isCommentArchived } from '../../lib/utils/comment-moderation-utils';
import { getThreadTopNavigationState, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
import useDeleteFailedPost from '../../hooks/use-delete-failed-post';
import { getThreadPostCountsByAuthor } from '../../lib/utils/author-post-counts';
import { withResolvedCommentCommunityAddress } from '../../lib/utils/comment-utils';
const { addChallenge } = useChallengesStore.getState();
@@ -97,7 +98,8 @@ const PostInfo = ({
onReject,
quotedByMap,
directRepliesByParentCid,
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]> }) => {
postsByAuthorInThread,
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]>; postsByAuthorInThread?: Map<string, number> }) => {
const { t } = useTranslation();
const { author, cid, deleted, locked, pinned, parentCid, postCid, reason, removed, state, communityAddress, timestamp } = post || {};
const archived = isCommentArchived(post);
@@ -228,12 +230,11 @@ const PostInfo = ({
const handleUserAddressClick = useAuthorAddressClick();
const numberOfPostsByAuthor = (() => {
if (!showUserID || deleted || removed || purged || !shortAddress || !postCid || typeof document === 'undefined') {
if (!showUserID || deleted || removed || purged || !shortAddress || !postCid) {
return 0;
}
const domCount = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length;
return Math.max(domCount, 1);
return Math.max(postsByAuthorInThread?.get(shortAddress) ?? 0, 1);
})();
const { hidden } = useHide(post);
@@ -704,7 +705,8 @@ const Reply = ({
threadNumber,
quotedByMap,
directRepliesByParentCid,
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]> }) => {
postsByAuthorInThread,
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]>; postsByAuthorInThread?: Map<string, number> }) => {
const accountReply = useAccountComment({
commentIndex: typeof reply?.index === 'number' ? reply.index : undefined,
});
@@ -744,6 +746,7 @@ const Reply = ({
<PostInfo
post={post}
postReplyCount={postReplyCount}
postsByAuthorInThread={postsByAuthorInThread}
roles={roles}
isHidden={hidden}
threadNumber={threadNumber}
@@ -890,6 +893,7 @@ const PostDesktop = ({
// Author-deleted replies are hidden from thread replies; moderator removals still render their placeholder.
const filteredReplies = filterRepliesForDisplay(freshRepliesForRender);
const postsByAuthorInThread = getThreadPostCountsByAuthor(resolvedPost, filteredReplies);
const directRepliesByParentCid = (() => {
const map = new Map<string, Comment[]>();
for (const reply of filteredReplies) {
@@ -1011,6 +1015,7 @@ const PostDesktop = ({
isHidden={hidden}
post={resolvedPost}
postReplyCount={replyCount}
postsByAuthorInThread={postsByAuthorInThread}
roles={roles}
threadNumber={resolvedPost?.number}
isModQueue={isModQueue}
@@ -1070,6 +1075,7 @@ const PostDesktop = ({
reply={reply}
roles={roles}
postReplyCount={replyCount}
postsByAuthorInThread={postsByAuthorInThread}
threadNumber={resolvedPost?.number}
quotedByMap={quotedByMap}
directRepliesByParentCid={directRepliesByParentCid}
@@ -1096,6 +1102,7 @@ const PostDesktop = ({
reply={reply}
roles={roles}
postReplyCount={replyCount}
postsByAuthorInThread={postsByAuthorInThread}
threadNumber={resolvedPost?.number}
quotedByMap={quotedByMap}
directRepliesByParentCid={directRepliesByParentCid}
@@ -1114,6 +1121,7 @@ const PostDesktop = ({
reply={reply}
roles={roles}
postReplyCount={replyCount}
postsByAuthorInThread={postsByAuthorInThread}
threadNumber={resolvedPost?.number}
quotedByMap={quotedByMap}
directRepliesByParentCid={directRepliesByParentCid}
+18 -7
View File
@@ -48,6 +48,7 @@ import { filterRepliesForDisplay, getPreviewDisplayReplies } from '../../lib/uti
import { getRenderableMobileBacklinks } from '../../lib/utils/reply-backlink-utils';
import { getThreadTopNavigationState, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
import useDeleteFailedPost from '../../hooks/use-delete-failed-post';
import { getThreadPostCountsByAuthor } from '../../lib/utils/author-post-counts';
import { withResolvedCommentCommunityAddress } from '../../lib/utils/comment-utils';
const { addChallenge } = useChallengesStore.getState();
@@ -62,7 +63,7 @@ const RepliesFooter = ({ hasMore, loadingString }: { hasMore: boolean; loadingSt
// Store scroll position for replies virtuoso across navigations
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
const PostInfoAndMedia = ({ post, postReplyCount = 0, roles, threadNumber }: PostProps) => {
const PostInfoAndMedia = ({ post, postReplyCount = 0, roles, threadNumber, postsByAuthorInThread }: PostProps & { postsByAuthorInThread?: Map<string, number> }) => {
const { t } = useTranslation();
const directories = useDirectories();
const resolvedPost = withResolvedCommentCommunityAddress(post);
@@ -206,12 +207,11 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles, threadNumber }: Pos
const handleUserAddressClick = useAuthorAddressClick();
const numberOfPostsByAuthor = (() => {
if (!showUserID || deleted || removed || purged || !shortAddress || !postCid || typeof document === 'undefined') {
if (!showUserID || deleted || removed || purged || !shortAddress || !postCid) {
return 0;
}
const domCount = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length;
return Math.max(domCount, 1);
return Math.max(postsByAuthorInThread?.get(shortAddress) ?? 0, 1);
})();
const userID = address ? getShortAddress(address) : shortAddress;
@@ -493,7 +493,8 @@ const Reply = ({
threadNumber,
quotedByMap,
directRepliesByParentCid,
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]> }) => {
postsByAuthorInThread,
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]>; postsByAuthorInThread?: Map<string, number> }) => {
const accountReply = useAccountComment({
commentIndex: typeof reply?.index === 'number' ? reply.index : undefined,
});
@@ -525,7 +526,7 @@ const Reply = ({
data-author-address={author?.shortAddress}
data-post-cid={postCid}
>
<PostInfoAndMedia post={post} postReplyCount={postReplyCount} roles={roles} threadNumber={threadNumber} />
<PostInfoAndMedia post={post} postReplyCount={postReplyCount} postsByAuthorInThread={postsByAuthorInThread} roles={roles} threadNumber={threadNumber} />
{!hidden && (!(removed || deleted || purged) || ((removed || deleted) && reason) || purged) && (
<CommentContent comment={post} prependContent={failedPublishNotice} />
)}
@@ -604,6 +605,7 @@ const PostMobile = ({
// Author-deleted replies are hidden from thread replies; moderator removals still render their placeholder.
const filteredReplies = filterRepliesForDisplay(freshRepliesForRender);
const postsByAuthorInThread = getThreadPostCountsByAuthor(resolvedPost, filteredReplies);
const previewDisplayReplies = getPreviewDisplayReplies(filteredReplies, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT);
const directRepliesByParentCid = (() => {
@@ -702,7 +704,13 @@ const PostMobile = ({
data-post-cid={postCid}
>
{shouldShowSnow() && <img src='assets/xmashat.gif' className={styles.xmasHat} alt='' />}
<PostInfoAndMedia post={resolvedPost} postReplyCount={replyCount} roles={roles} threadNumber={resolvedPost?.number} />
<PostInfoAndMedia
post={resolvedPost}
postReplyCount={replyCount}
postsByAuthorInThread={postsByAuthorInThread}
roles={roles}
threadNumber={resolvedPost?.number}
/>
<CommentContent comment={resolvedPost} prependContent={failedPublishNotice} />
<ReplyBacklinks post={resolvedPost} quotedByMap={quotedByMap} directRepliesByParentCid={directRepliesByParentCid} />
</div>
@@ -765,6 +773,7 @@ const PostMobile = ({
<Reply
postReplyCount={replyCount}
reply={reply}
postsByAuthorInThread={postsByAuthorInThread}
roles={roles}
threadNumber={resolvedPost?.number}
quotedByMap={quotedByMap}
@@ -790,6 +799,7 @@ const PostMobile = ({
<Reply
postReplyCount={replyCount}
reply={reply}
postsByAuthorInThread={postsByAuthorInThread}
roles={roles}
threadNumber={resolvedPost?.number}
quotedByMap={quotedByMap}
@@ -807,6 +817,7 @@ const PostMobile = ({
<Reply
postReplyCount={replyCount}
reply={reply}
postsByAuthorInThread={postsByAuthorInThread}
roles={roles}
threadNumber={resolvedPost?.number}
quotedByMap={quotedByMap}