From a6f6e42bd7daeb2caf05bb79d468d98f25837f10 Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Fri, 15 May 2026 18:35:41 +0700 Subject: [PATCH] fix(mod queue): stop resurfacing stale pending history (#1130) --- .../utils/__tests__/mod-queue-utils.test.ts | 32 +++++++++++++++++- src/lib/utils/mod-queue-utils.ts | 23 +++++++++++++ src/views/mod-queue/mod-queue.tsx | 33 ++++++++----------- 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/lib/utils/__tests__/mod-queue-utils.test.ts b/src/lib/utils/__tests__/mod-queue-utils.test.ts index 14639b03..08a4b426 100644 --- a/src/lib/utils/__tests__/mod-queue-utils.test.ts +++ b/src/lib/utils/__tests__/mod-queue-utils.test.ts @@ -1,5 +1,11 @@ import { describe, expect, it } from 'vitest'; -import { filterVisibleModQueueFeed, getModQueueCommentRoute, getQueuedCommentRouteState } from '../mod-queue-utils'; +import { + filterVisibleModQueueFeed, + getModQueueCommentRoute, + getQueuedCommentRouteState, + getVisibleQueuedCommentHistory, + shouldKeepQueuedCommentHistory, +} from '../mod-queue-utils'; describe('mod queue utils', () => { it('keeps all queue comments unless they were locally dismissed', () => { @@ -31,6 +37,30 @@ describe('mod queue utils', () => { ]); }); + it('keeps only terminal local moderation states in queue history', () => { + expect(shouldKeepQueuedCommentHistory({ cid: 'pending', pendingApproval: true })).toBe(false); + expect(shouldKeepQueuedCommentHistory({ cid: 'published', pendingApproval: false })).toBe(false); + expect(shouldKeepQueuedCommentHistory({ cid: 'approved', approved: true, pendingApproval: false })).toBe(true); + expect(shouldKeepQueuedCommentHistory({ cid: 'rejected', approved: false, pendingApproval: false })).toBe(true); + expect(shouldKeepQueuedCommentHistory({ cid: 'removed', pendingApproval: true, removed: true })).toBe(true); + }); + + it('does not resurface stale pending history after the live feed drops it', () => { + const feed = [{ cid: 'live-pending', communityAddress: 'tech.eth', pendingApproval: true }]; + const history = [ + { cid: 'stale-pending', communityAddress: 'tech.eth', pendingApproval: true }, + { cid: 'live-pending', communityAddress: 'tech.eth', pendingApproval: true }, + { cid: 'approved', approved: true, communityAddress: 'tech.eth', pendingApproval: false }, + { cid: 'rejected', approved: false, communityAddress: 'tech.eth', pendingApproval: false }, + { cid: 'other-board-approved', approved: true, communityAddress: 'g.eth', pendingApproval: false }, + ]; + + expect(getVisibleQueuedCommentHistory(feed, history, ['tech.eth'])).toEqual([ + { cid: 'approved', approved: true, communityAddress: 'tech.eth', pendingApproval: false }, + { cid: 'rejected', approved: false, communityAddress: 'tech.eth', pendingApproval: false }, + ]); + }); + it('builds excerpt routes from the comment permalink cid', () => { expect(getModQueueCommentRoute('g', 'reply-cid')).toBe('/g/thread/reply-cid'); expect(getModQueueCommentRoute('g', undefined)).toBeUndefined(); diff --git a/src/lib/utils/mod-queue-utils.ts b/src/lib/utils/mod-queue-utils.ts index 2cf7e4cc..b8857d5e 100644 --- a/src/lib/utils/mod-queue-utils.ts +++ b/src/lib/utils/mod-queue-utils.ts @@ -1,5 +1,7 @@ import type { Comment } from '@bitsocial/bitsocial-react-hooks'; import { getCommentCommunityAddress } from './comment-utils'; +import { isPendingApprovalRejected } from './pending-approval-moderation'; +import { areSameBoardAddress } from './route-utils'; import { getThreadTopNavigationState } from './thread-scroll-utils'; type ModQueueCommentLike = { @@ -102,6 +104,27 @@ export const getQueuedCommentRouteState = (comment: ModQueueCommentLike | undefi }; }; +export const shouldKeepQueuedCommentHistory = (comment: ModQueueCommentLike | undefined): boolean => comment?.approved === true || isPendingApprovalRejected(comment); + +export const getVisibleQueuedCommentHistory = ( + feed: readonly ModQueueCommentLike[], + queuedCommentHistory: readonly T[], + communityAddresses: readonly string[], +): T[] => { + const liveCids = feed.reduce>((cids, comment) => { + if (comment.cid) cids.add(comment.cid); + return cids; + }, new Set()); + + return queuedCommentHistory.filter((comment) => { + const commentCommunityAddress = getCommentCommunityAddress(comment); + if (!comment.cid || !shouldKeepQueuedCommentHistory(comment) || liveCids.has(comment.cid) || !commentCommunityAddress) { + return false; + } + return communityAddresses.some((communityAddress) => areSameBoardAddress(communityAddress, commentCommunityAddress)); + }); +}; + export const filterVisibleModQueueFeed = ( feed: T[], selectedBoardFilter: string | null, diff --git a/src/views/mod-queue/mod-queue.tsx b/src/views/mod-queue/mod-queue.tsx index f71b5e18..87066eb8 100644 --- a/src/views/mod-queue/mod-queue.tsx +++ b/src/views/mod-queue/mod-queue.tsx @@ -26,7 +26,14 @@ import useChallengesStore from '../../stores/use-challenges-store'; import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils'; import { getCommentCommunityAddress } from '../../lib/utils/comment-utils'; import { formatErrorForDisplay } from '../../lib/utils/error-utils'; -import { filterVisibleModQueueFeed, getModQueueCommentRoute, getQueuedCommentRouteState, getQueuedCommentSnapshot } from '../../lib/utils/mod-queue-utils'; +import { + filterVisibleModQueueFeed, + getModQueueCommentRoute, + getQueuedCommentRouteState, + getQueuedCommentSnapshot, + getVisibleQueuedCommentHistory, + shouldKeepQueuedCommentHistory, +} from '../../lib/utils/mod-queue-utils'; import Tooltip from '../../components/tooltip'; import { useAccountCommunityAddresses } from '../../hooks/use-account-community-addresses'; import { useCommunityIdentifier, useCommunityIdentifiers } from '../../hooks/use-community-identifiers'; @@ -908,7 +915,7 @@ const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProp () => feed.flatMap((comment) => { const snapshot = getQueuedCommentSnapshot(comment); - return snapshot ? [snapshot] : []; + return snapshot && shouldKeepQueuedCommentHistory(snapshot) ? [snapshot] : []; }), [feed], ); @@ -918,24 +925,10 @@ const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProp } }, [queuedCommentSnapshots, rememberCommentsInQueue]); - const feedWithHistory = useMemo(() => { - const liveCids = feed.reduce>((cids, comment) => { - if (comment.cid) cids.add(comment.cid); - return cids; - }, new Set()); - return [ - ...feed, - ...(queuedCommentHistory.filter((comment) => { - const commentCommunityAddress = getCommentCommunityAddress(comment); - return ( - comment.cid && - !liveCids.has(comment.cid) && - commentCommunityAddress && - communityAddresses.some((communityAddress) => areSameBoardAddress(communityAddress, commentCommunityAddress)) - ); - }) as Comment[]), - ]; - }, [communityAddresses, feed, queuedCommentHistory]); + const feedWithHistory = useMemo( + () => [...feed, ...(getVisibleQueuedCommentHistory(feed, queuedCommentHistory, communityAddresses) as Comment[])], + [communityAddresses, feed, queuedCommentHistory], + ); const dismissedCommentCidSet = useMemo(() => new Set(dismissedCommentCids), [dismissedCommentCids]); const filteredFeed = useMemo(