From 6954b5bcc91a22e5d400b675a28513aaa7c737a2 Mon Sep 17 00:00:00 2001 From: plebeius Date: Mon, 9 Mar 2026 21:10:56 +0800 Subject: [PATCH] fix(replies): hide author-deleted replies from thread views --- src/components/post-desktop/post-desktop.tsx | 6 +++--- src/components/post-mobile/post-mobile.tsx | 6 +++--- src/lib/utils/__tests__/misc-utils.test.ts | 7 ++++++- src/lib/utils/replies-preview-utils.ts | 5 +++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx index f1beff3e..c33e1c31 100644 --- a/src/components/post-desktop/post-desktop.tsx +++ b/src/components/post-desktop/post-desktop.tsx @@ -47,7 +47,7 @@ import { usePublishCommentModeration } from '@bitsocialnet/bitsocial-react-hooks import useQuotedByMap from '../../hooks/use-quoted-by-map'; import useProgressiveRender from '../../hooks/use-progressive-render'; import { BOARD_REPLIES_PREVIEW_FETCH_SIZE, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT, REPLIES_PER_PAGE } from '../../lib/constants'; -import { computeOmittedCount, getPreviewDisplayReplies, getTotalReplyCount } from '../../lib/utils/replies-preview-utils'; +import { computeOmittedCount, filterRepliesForDisplay, getPreviewDisplayReplies, getTotalReplyCount } from '../../lib/utils/replies-preview-utils'; const { addChallenge } = useChallengesStore.getState(); @@ -892,8 +892,8 @@ const PostDesktop = ({ const commentMediaInfo = useCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight); const hasThumbnail = getHasThumbnail(commentMediaInfo, link); - // Filter out deleted replies with no children for both virtuoso and non-virtuoso rendering - const filteredReplies = repliesForRender.filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))); + // Author-deleted replies are hidden from thread replies; moderator removals still render their placeholder. + const filteredReplies = filterRepliesForDisplay(repliesForRender); const directRepliesByParentCid = (() => { const map = new Map(); for (const reply of filteredReplies) { diff --git a/src/components/post-mobile/post-mobile.tsx b/src/components/post-mobile/post-mobile.tsx index e428ee66..fcfadcbb 100644 --- a/src/components/post-mobile/post-mobile.tsx +++ b/src/components/post-mobile/post-mobile.tsx @@ -41,7 +41,7 @@ import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-util import useQuotedByMap from '../../hooks/use-quoted-by-map'; import useProgressiveRender from '../../hooks/use-progressive-render'; import { BOARD_REPLIES_PREVIEW_FETCH_SIZE, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT, REPLIES_PER_PAGE } from '../../lib/constants'; -import { getPreviewDisplayReplies } from '../../lib/utils/replies-preview-utils'; +import { filterRepliesForDisplay, getPreviewDisplayReplies } from '../../lib/utils/replies-preview-utils'; const { addChallenge } = useChallengesStore.getState(); @@ -628,8 +628,8 @@ const PostMobile = ({ const hasFailedState = state === 'failed'; const isReply = !!parentCid; - // Filter out deleted replies with no children for both virtuoso and non-virtuoso rendering - const filteredReplies = repliesForRender.filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))); + // Author-deleted replies are hidden from thread replies; moderator removals still render their placeholder. + const filteredReplies = filterRepliesForDisplay(repliesForRender); const previewDisplayReplies = getPreviewDisplayReplies(filteredReplies, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT); const directRepliesByParentCid = (() => { diff --git a/src/lib/utils/__tests__/misc-utils.test.ts b/src/lib/utils/__tests__/misc-utils.test.ts index efd0022e..58e8beae 100644 --- a/src/lib/utils/__tests__/misc-utils.test.ts +++ b/src/lib/utils/__tests__/misc-utils.test.ts @@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { copyToClipboard } from '../clipboard-utils'; import { hashStringToColor, getTextColorForBackground, removeMarkdown } from '../post-utils'; import { preloadThemeAssets } from '../preload-utils'; -import { computeOmittedCount, getPreviewDisplayReplies, getTotalReplyCount } from '../replies-preview-utils'; +import { computeOmittedCount, filterRepliesForDisplay, getPreviewDisplayReplies, getTotalReplyCount } from '../replies-preview-utils'; import { getQuotedCidsFromContent, mergeQuotedCids } from '../reply-quote-utils'; import { formatUserIDForDisplay, truncateWithEllipsisInMiddle } from '../string-utils'; import { getFormattedDate, getFormattedTimeAgo, isChristmas } from '../time-utils'; @@ -120,6 +120,11 @@ describe('misc utils', () => { }); it('builds reply previews, omitted counts, and fallback reply totals', () => { + expect(filterRepliesForDisplay([{ cid: 'visible' }, { cid: 'deleted', deleted: true }, { cid: 'removed', deleted: false }])).toEqual([ + { cid: 'visible' }, + { cid: 'removed', deleted: false }, + ]); + expect( getPreviewDisplayReplies( [ diff --git a/src/lib/utils/replies-preview-utils.ts b/src/lib/utils/replies-preview-utils.ts index 8c217919..20d0cd12 100644 --- a/src/lib/utils/replies-preview-utils.ts +++ b/src/lib/utils/replies-preview-utils.ts @@ -2,12 +2,17 @@ import { BOARD_REPLIES_PREVIEW_VISIBLE_COUNT } from '../constants'; interface CommentLike { cid?: string | null; + deleted?: boolean; index?: number; pendingApproval?: boolean; state?: string; timestamp?: number; } +export function filterRepliesForDisplay(replies: T[]): T[] { + return replies.filter((reply) => !reply.deleted); +} + /** * Returns the latest N replies in chronological display order (oldest first). *