fix(replies): make thread replies update instantly for pending and confirmed states

Aligned 5chan’s flat thread reply behavior with plebones by using `useReplies` in flat-old mode with appending account comments and rendering from `updatedReplies` when available. Reply rows now subscribe to `useAccountComment` by `index`, so pending replies transition to confirmed automatically after challenge verification without requiring manual update or refresh.
This commit is contained in:
plebeius
2026-02-13 19:42:38 +08:00
parent 98f7a52e9b
commit 14c6bf8f3a
2 changed files with 50 additions and 20 deletions
+20 -9
View File
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState, useCallback } from 'react';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
import { Link, useLocation, useNavigationType, useParams } from 'react-router-dom'; import { Link, useLocation, useNavigationType, useParams } from 'react-router-dom';
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso'; import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
import { Comment, useAuthorAvatar, useEditedComment, useReplies, useAccount } from '@plebbit/plebbit-react-hooks'; import { Comment, useAuthorAvatar, useEditedComment, useReplies, useAccount, useAccountComment } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js'; import Plebbit from '@plebbit/plebbit-js';
import styles from '../../views/post/post.module.css'; import styles from '../../views/post/post.module.css';
import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils'; import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
@@ -581,9 +581,13 @@ const Reply = ({
quotedByMap, quotedByMap,
directRepliesByParentCid, directRepliesByParentCid,
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]> }) => { }: PostProps & { directRepliesByParentCid?: Map<string, Comment[]> }) => {
let post = reply; const accountReply = useAccountComment({
commentIndex: typeof reply?.index === 'number' ? reply.index : undefined,
});
const hasReplyIndex = typeof reply?.index === 'number';
let post = hasReplyIndex && accountReply?.index === reply.index ? accountReply : reply;
// handle pending mod or author edit // handle pending mod or author edit
const { editedComment } = useEditedComment({ comment: reply }); const { editedComment } = useEditedComment({ comment: post });
if (editedComment) { if (editedComment) {
post = editedComment; post = editedComment;
} }
@@ -666,8 +670,15 @@ const PostDesktop = ({
const { hidden, unhide, hide } = useHide({ cid }); const { hidden, unhide, hide } = useHide({ cid });
const isHidden = hidden && !isInPostPageView; const isHidden = hidden && !isInPostPageView;
const repliesResult = useReplies({ comment: post, flat: true, accountComments: { newerThan: Infinity } }); const repliesResult = useReplies({
comment: post,
sortType: 'old',
flat: true,
accountComments: { newerThan: Infinity, append: true },
});
const { replies, hasMore, loadMore } = repliesResult; const { replies, hasMore, loadMore } = repliesResult;
const updatedReplies = (repliesResult as { updatedReplies?: Comment[] }).updatedReplies;
const repliesForRender = updatedReplies?.length ? updatedReplies : replies || [];
const reset = (repliesResult as { reset?: () => Promise<void> }).reset; const reset = (repliesResult as { reset?: () => Promise<void> }).reset;
const setResetFunction = useFeedResetStore((s) => s.setResetFunction); const setResetFunction = useFeedResetStore((s) => s.setResetFunction);
useEffect(() => { useEffect(() => {
@@ -680,7 +691,7 @@ const PostDesktop = ({
const registerComments = usePostNumberStore((s) => s.registerComments); const registerComments = usePostNumberStore((s) => s.registerComments);
const prevCidsRef = useRef<string>(''); const prevCidsRef = useRef<string>('');
useEffect(() => { useEffect(() => {
const all = post ? [post, ...(replies || [])] : replies || []; const all = post ? [post, ...repliesForRender] : repliesForRender;
if (!all.length) return; if (!all.length) return;
const cidsKey = all const cidsKey = all
.map((c) => c?.cid) .map((c) => c?.cid)
@@ -690,10 +701,10 @@ const PostDesktop = ({
if (cidsKey === prevCidsRef.current) return; if (cidsKey === prevCidsRef.current) return;
prevCidsRef.current = cidsKey; prevCidsRef.current = cidsKey;
registerComments(all); registerComments(all);
}, [post, replies, registerComments]); }, [post, repliesForRender, registerComments]);
const visiblelinksCount = useCountLinksInReplies(post, 5); const visiblelinksCount = useCountLinksInReplies(post, 5);
const totalLinksCount = useCountLinksInReplies(post); const totalLinksCount = useCountLinksInReplies(post);
const replyCount = replies?.length; const replyCount = repliesForRender.length;
const repliesCount = pinned ? replyCount : replyCount - 5; const repliesCount = pinned ? replyCount : replyCount - 5;
const linksCount = pinned ? totalLinksCount : totalLinksCount - visiblelinksCount; const linksCount = pinned ? totalLinksCount : totalLinksCount - visiblelinksCount;
@@ -706,7 +717,7 @@ const PostDesktop = ({
const hasThumbnail = getHasThumbnail(commentMediaInfo, link); const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
// Filter out deleted replies with no children for both virtuoso and non-virtuoso rendering // Filter out deleted replies with no children for both virtuoso and non-virtuoso rendering
const filteredReplies = useMemo(() => (replies || []).filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))), [replies]); const filteredReplies = useMemo(() => repliesForRender.filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))), [repliesForRender]);
const directRepliesByParentCid = useMemo(() => { const directRepliesByParentCid = useMemo(() => {
const map = new Map<string, Comment[]>(); const map = new Map<string, Comment[]>();
for (const reply of filteredReplies) { for (const reply of filteredReplies) {
@@ -894,7 +905,7 @@ const PostDesktop = ({
!showAllReplies && !showAllReplies &&
!(pinned && !isInPostPageView && !showOmittedReplies[cid]) && !(pinned && !isInPostPageView && !showOmittedReplies[cid]) &&
!isInPendingPostView && !isInPendingPostView &&
replies && repliesForRender &&
showReplies && showReplies &&
(showOmittedReplies[cid] ? filteredReplies : filteredReplies.slice(-5)).map((reply, index) => ( (showOmittedReplies[cid] ? filteredReplies : filteredReplies.slice(-5)).map((reply, index) => (
<div key={index} className={styles.replyContainer}> <div key={index} className={styles.replyContainer}>
+30 -11
View File
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Link, useLocation, useNavigationType, useParams } from 'react-router-dom'; import { Link, useLocation, useNavigationType, useParams } from 'react-router-dom';
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso'; import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
import { Comment, useAuthorAvatar, useEditedComment, useReplies, useAccount, usePublishCommentModeration } from '@plebbit/plebbit-react-hooks'; import { Comment, useAuthorAvatar, useEditedComment, useReplies, useAccount, usePublishCommentModeration, useAccountComment } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js'; import Plebbit from '@plebbit/plebbit-js';
import styles from '../../views/post/post.module.css'; import styles from '../../views/post/post.module.css';
import { shouldShowSnow } from '../../lib/snow'; import { shouldShowSnow } from '../../lib/snow';
@@ -387,7 +387,15 @@ const PostMediaContent = ({ post, link }: { post: any; link: string }) => {
const ReplyBacklinks = ({ post, quotedByMap }: PostProps) => { const ReplyBacklinks = ({ post, quotedByMap }: PostProps) => {
const { cid, parentCid } = post || {}; const { cid, parentCid } = post || {};
const { replies } = useReplies({ comment: post, flat: true, accountComments: { newerThan: Infinity } }); const repliesResult = useReplies({
comment: post,
sortType: 'old',
flat: true,
accountComments: { newerThan: Infinity, append: true },
});
const { replies } = repliesResult;
const updatedReplies = (repliesResult as { updatedReplies?: Comment[] }).updatedReplies;
const repliesForRender = updatedReplies?.length ? updatedReplies : replies || [];
const opBacklinks = const opBacklinks =
cid && cid &&
@@ -400,9 +408,9 @@ const ReplyBacklinks = ({ post, quotedByMap }: PostProps) => {
) )
.filter(Boolean); .filter(Boolean);
const replyBacklinks = cid && parentCid && ((replies?.length || 0) > 0 || quotedByMap?.get(cid)?.length) && ( const replyBacklinks = cid && parentCid && ((repliesForRender?.length || 0) > 0 || quotedByMap?.get(cid)?.length) && (
<> <>
{replies?.map( {repliesForRender?.map(
(reply: Comment, index: number) => (reply: Comment, index: number) =>
reply?.parentCid === cid && reply?.cid && !(reply?.deleted || reply?.removed) && <ReplyQuotePreview key={index} isBacklinkReply={true} backlinkReply={reply} />, reply?.parentCid === cid && reply?.cid && !(reply?.deleted || reply?.removed) && <ReplyQuotePreview key={index} isBacklinkReply={true} backlinkReply={reply} />,
)} )}
@@ -426,9 +434,13 @@ const ReplyBacklinks = ({ post, quotedByMap }: PostProps) => {
}; };
const Reply = ({ postReplyCount, reply, roles, threadNumber, quotedByMap }: PostProps) => { const Reply = ({ postReplyCount, reply, roles, threadNumber, quotedByMap }: PostProps) => {
let post = reply; const accountReply = useAccountComment({
commentIndex: typeof reply?.index === 'number' ? reply.index : undefined,
});
const hasReplyIndex = typeof reply?.index === 'number';
let post = hasReplyIndex && accountReply?.index === reply.index ? accountReply : reply;
// handle pending mod or author edit // handle pending mod or author edit
const { editedComment } = useEditedComment({ comment: reply }); const { editedComment } = useEditedComment({ comment: post });
if (editedComment) { if (editedComment) {
post = editedComment; post = editedComment;
} }
@@ -481,8 +493,15 @@ const PostMobile = ({
const directories = useDirectories(); const directories = useDirectories();
const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, directories) : undefined; const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, directories) : undefined;
const linksCount = useCountLinksInReplies(post); const linksCount = useCountLinksInReplies(post);
const repliesResult = useReplies({ comment: post, accountComments: { newerThan: Infinity } }); const repliesResult = useReplies({
comment: post,
sortType: 'old',
flat: true,
accountComments: { newerThan: Infinity, append: true },
});
const { replies, hasMore, loadMore } = repliesResult; const { replies, hasMore, loadMore } = repliesResult;
const updatedReplies = (repliesResult as { updatedReplies?: Comment[] }).updatedReplies;
const repliesForRender = updatedReplies?.length ? updatedReplies : replies || [];
const reset = (repliesResult as { reset?: () => Promise<void> }).reset; const reset = (repliesResult as { reset?: () => Promise<void> }).reset;
const setResetFunction = useFeedResetStore((s) => s.setResetFunction); const setResetFunction = useFeedResetStore((s) => s.setResetFunction);
useEffect(() => { useEffect(() => {
@@ -495,7 +514,7 @@ const PostMobile = ({
const registerComments = usePostNumberStore((s) => s.registerComments); const registerComments = usePostNumberStore((s) => s.registerComments);
const prevCidsRef = useRef<string>(''); const prevCidsRef = useRef<string>('');
useEffect(() => { useEffect(() => {
const all = post ? [post, ...(replies || [])] : replies || []; const all = post ? [post, ...repliesForRender] : repliesForRender;
if (!all.length) return; if (!all.length) return;
const cidsKey = all const cidsKey = all
.map((c) => c?.cid) .map((c) => c?.cid)
@@ -505,7 +524,7 @@ const PostMobile = ({
if (cidsKey === prevCidsRef.current) return; if (cidsKey === prevCidsRef.current) return;
prevCidsRef.current = cidsKey; prevCidsRef.current = cidsKey;
registerComments(all); registerComments(all);
}, [post, replies, registerComments]); }, [post, repliesForRender, registerComments]);
const isInPostPageView = isPostPageView(location.pathname, params); const isInPostPageView = isPostPageView(location.pathname, params);
const { hidden, unhide } = useHide({ cid }); const { hidden, unhide } = useHide({ cid });
@@ -514,7 +533,7 @@ const PostMobile = ({
const hasFailedState = state === 'failed'; const hasFailedState = state === 'failed';
// Filter out deleted replies with no children for both virtuoso and non-virtuoso rendering // Filter out deleted replies with no children for both virtuoso and non-virtuoso rendering
const filteredReplies = useMemo(() => (replies || []).filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))), [replies]); const filteredReplies = useMemo(() => repliesForRender.filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))), [repliesForRender]);
const quotedByMap = useQuotedByMap(filteredReplies); const quotedByMap = useQuotedByMap(filteredReplies);
@@ -660,7 +679,7 @@ const PostMobile = ({
{!(pinned && !isInPostView) && {!(pinned && !isInPostView) &&
!showAllReplies && !showAllReplies &&
!isInPendingPostView && !isInPendingPostView &&
replies && repliesForRender &&
showReplies && showReplies &&
filteredReplies.slice(-5).map((reply, index) => ( filteredReplies.slice(-5).map((reply, index) => (
<div key={index} className={styles.replyContainer}> <div key={index} className={styles.replyContainer}>