mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
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:
@@ -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}>
|
||||||
|
|||||||
@@ -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}>
|
||||||
|
|||||||
Reference in New Issue
Block a user