mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
perf: defer board reply fetching and remove nested mobile backlink fetch
Split useReplies in post-desktop.tsx and post-mobile.tsx into preview/full modes so board cards avoid full REPLIES_PER_PAGE fetches by default. Added replies-preview-utils.ts and switched mobile backlinks to use precomputed directRepliesByParentCid map instead of per-reply useReplies.
This commit is contained in:
@@ -45,7 +45,8 @@ import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-util
|
|||||||
import { usePublishCommentModeration } from '@plebbit/plebbit-react-hooks';
|
import { usePublishCommentModeration } from '@plebbit/plebbit-react-hooks';
|
||||||
import useQuotedByMap from '../../hooks/use-quoted-by-map';
|
import useQuotedByMap from '../../hooks/use-quoted-by-map';
|
||||||
import useProgressiveRender from '../../hooks/use-progressive-render';
|
import useProgressiveRender from '../../hooks/use-progressive-render';
|
||||||
import { REPLIES_PER_PAGE } from '../../lib/constants';
|
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';
|
||||||
|
|
||||||
const { addChallenge } = useChallengesStore.getState();
|
const { addChallenge } = useChallengesStore.getState();
|
||||||
|
|
||||||
@@ -699,17 +700,45 @@ 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({
|
const { showOmittedReplies, setShowOmittedReplies } = useShowOmittedReplies();
|
||||||
comment: post,
|
|
||||||
|
const shouldFetchPreview = showReplies && !isModQueue && !showAllReplies && showOmittedReplies[cid] !== true;
|
||||||
|
const shouldFetchFull = showReplies && !isModQueue && (showAllReplies || showOmittedReplies[cid]);
|
||||||
|
|
||||||
|
const previewRepliesResult = useReplies({
|
||||||
|
comment: shouldFetchPreview ? post : undefined,
|
||||||
|
sortType: 'new',
|
||||||
|
flat: true,
|
||||||
|
repliesPerPage: BOARD_REPLIES_PREVIEW_FETCH_SIZE,
|
||||||
|
accountComments: { newerThan: Infinity, append: true },
|
||||||
|
});
|
||||||
|
const fullRepliesResult = useReplies({
|
||||||
|
comment: shouldFetchFull ? post : undefined,
|
||||||
sortType: 'old',
|
sortType: 'old',
|
||||||
flat: true,
|
flat: true,
|
||||||
repliesPerPage: REPLIES_PER_PAGE,
|
repliesPerPage: REPLIES_PER_PAGE,
|
||||||
accountComments: { newerThan: Infinity, append: true },
|
accountComments: { newerThan: Infinity, append: true },
|
||||||
});
|
});
|
||||||
const { replies, hasMore, loadMore } = repliesResult;
|
|
||||||
const updatedReplies = (repliesResult as { updatedReplies?: Comment[] }).updatedReplies;
|
const previewReplies = (previewRepliesResult as { updatedReplies?: Comment[] }).updatedReplies?.length
|
||||||
const repliesForRender = updatedReplies?.length ? updatedReplies : replies || [];
|
? (previewRepliesResult as { updatedReplies?: Comment[] }).updatedReplies!
|
||||||
const reset = (repliesResult as { reset?: () => Promise<void> }).reset;
|
: previewRepliesResult.replies || [];
|
||||||
|
const fullReplies = (fullRepliesResult as { updatedReplies?: Comment[] }).updatedReplies?.length
|
||||||
|
? (fullRepliesResult as { updatedReplies?: Comment[] }).updatedReplies!
|
||||||
|
: fullRepliesResult.replies || [];
|
||||||
|
|
||||||
|
const { hasMore, loadMore } = fullRepliesResult;
|
||||||
|
const reset = (fullRepliesResult as { reset?: () => Promise<void> }).reset;
|
||||||
|
|
||||||
|
const fullIsFetching = shouldFetchFull && fullReplies.length === 0 && fullRepliesResult.hasMore;
|
||||||
|
|
||||||
|
const repliesForRender = showAllReplies
|
||||||
|
? fullReplies
|
||||||
|
: showOmittedReplies[cid]
|
||||||
|
? fullReplies.length
|
||||||
|
? fullReplies
|
||||||
|
: previewReplies
|
||||||
|
: getPreviewDisplayReplies(previewReplies, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT);
|
||||||
const setResetFunction = useFeedResetStore((s) => s.setResetFunction);
|
const setResetFunction = useFeedResetStore((s) => s.setResetFunction);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if ((isInPostPageView || isInPendingPostView) && reset) {
|
if ((isInPostPageView || isInPendingPostView) && reset) {
|
||||||
@@ -732,13 +761,21 @@ const PostDesktop = ({
|
|||||||
prevCidsRef.current = cidsKey;
|
prevCidsRef.current = cidsKey;
|
||||||
registerComments(all);
|
registerComments(all);
|
||||||
}, [post, repliesForRender, registerComments]);
|
}, [post, repliesForRender, registerComments]);
|
||||||
const visiblelinksCount = useCountLinksInReplies(post, 5);
|
const visiblelinksCount = useCountLinksInReplies(post, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT);
|
||||||
const totalLinksCount = useCountLinksInReplies(post);
|
const totalLinksCount = useCountLinksInReplies(post);
|
||||||
const replyCount = repliesForRender.length;
|
const replyCount = repliesForRender.length;
|
||||||
|
|
||||||
const repliesCount = pinned ? replyCount : replyCount - 5;
|
const totalReplyCount = getTotalReplyCount({
|
||||||
|
replyCount: post?.replyCount,
|
||||||
|
fullLoadedCount: fullReplies.length,
|
||||||
|
previewLoadedCount: previewReplies.length,
|
||||||
|
});
|
||||||
|
const repliesCount = computeOmittedCount({
|
||||||
|
totalReplyCount,
|
||||||
|
visibleCount: BOARD_REPLIES_PREVIEW_VISIBLE_COUNT,
|
||||||
|
pinned,
|
||||||
|
});
|
||||||
const linksCount = pinned ? totalLinksCount : totalLinksCount - visiblelinksCount;
|
const linksCount = pinned ? totalLinksCount : totalLinksCount - visiblelinksCount;
|
||||||
const { showOmittedReplies, setShowOmittedReplies } = useShowOmittedReplies();
|
|
||||||
|
|
||||||
const stateString = useStateString(post) || t('downloading_board');
|
const stateString = useStateString(post) || t('downloading_board');
|
||||||
const hasFailedState = state === 'failed';
|
const hasFailedState = state === 'failed';
|
||||||
@@ -870,7 +907,7 @@ const PostDesktop = ({
|
|||||||
{!isHidden && !content && !(deleted || removed) && <div className={styles.spacer} />}
|
{!isHidden && !content && !(deleted || removed) && <div className={styles.spacer} />}
|
||||||
{!isHidden && <CommentContent comment={post} />}
|
{!isHidden && <CommentContent comment={post} />}
|
||||||
</div>
|
</div>
|
||||||
{!isHidden && !isInPendingPostView && (replyCount > 5 || (pinned && repliesCount > 0)) && !isInPostPageView && (
|
{!isHidden && !isInPendingPostView && repliesCount > 0 && !isInPostPageView && (
|
||||||
<span className={styles.summary}>
|
<span className={styles.summary}>
|
||||||
<span
|
<span
|
||||||
className={`${showOmittedReplies[cid] ? styles.hideOmittedReplies : styles.showOmittedReplies} ${styles.omittedRepliesButtonWrapper}`}
|
className={`${showOmittedReplies[cid] ? styles.hideOmittedReplies : styles.showOmittedReplies} ${styles.omittedRepliesButtonWrapper}`}
|
||||||
@@ -939,14 +976,14 @@ const PostDesktop = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{/* Non-virtualized rendering for board view (last 5 replies or show omitted) */}
|
{/* Non-virtualized rendering for board view (preview replies when collapsed, full when expanded) */}
|
||||||
{!isHidden &&
|
{!isHidden &&
|
||||||
!showAllReplies &&
|
!showAllReplies &&
|
||||||
!(pinned && !isInPostPageView && !showOmittedReplies[cid]) &&
|
!(pinned && !isInPostPageView && !showOmittedReplies[cid]) &&
|
||||||
!isInPendingPostView &&
|
!isInPendingPostView &&
|
||||||
repliesForRender &&
|
repliesForRender &&
|
||||||
showReplies &&
|
showReplies &&
|
||||||
(showOmittedReplies[cid] ? filteredReplies : filteredReplies.slice(-5)).map((reply, index) => (
|
filteredReplies.map((reply, index) => (
|
||||||
<div key={index} className={styles.replyContainer}>
|
<div key={index} className={styles.replyContainer}>
|
||||||
<Reply
|
<Reply
|
||||||
reply={reply}
|
reply={reply}
|
||||||
@@ -958,6 +995,11 @@ const PostDesktop = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
{!isHidden && !showAllReplies && showOmittedReplies[cid] && fullIsFetching && showReplies && filteredReplies.length > 0 && (
|
||||||
|
<div className={styles.stateString}>
|
||||||
|
<LoadingEllipsis string={t('loading')} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!isInPendingPostView && stateString && !hasFailedState && state !== 'succeeded' && isInPostPageView && !(!showReplies && !showAllReplies) ? (
|
{!isInPendingPostView && stateString && !hasFailedState && state !== 'succeeded' && isInPostPageView && !(!showReplies && !showAllReplies) ? (
|
||||||
<div className={styles.stateString}>
|
<div className={styles.stateString}>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import usePostNumberStore from '../../stores/use-post-number-store';
|
|||||||
import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils';
|
import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils';
|
||||||
import useQuotedByMap from '../../hooks/use-quoted-by-map';
|
import useQuotedByMap from '../../hooks/use-quoted-by-map';
|
||||||
import useProgressiveRender from '../../hooks/use-progressive-render';
|
import useProgressiveRender from '../../hooks/use-progressive-render';
|
||||||
import { REPLIES_PER_PAGE } from '../../lib/constants';
|
import { BOARD_REPLIES_PREVIEW_FETCH_SIZE, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT, REPLIES_PER_PAGE } from '../../lib/constants';
|
||||||
|
|
||||||
const { addChallenge } = useChallengesStore.getState();
|
const { addChallenge } = useChallengesStore.getState();
|
||||||
|
|
||||||
@@ -403,17 +403,13 @@ const PostMediaContent = ({ post, link }: { post: any; link: string }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ReplyBacklinks = ({ post, quotedByMap }: PostProps) => {
|
interface ReplyBacklinksProps extends PostProps {
|
||||||
|
directRepliesByParentCid?: Map<string, Comment[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReplyBacklinks = ({ post, quotedByMap, directRepliesByParentCid }: ReplyBacklinksProps) => {
|
||||||
const { cid, parentCid } = post || {};
|
const { cid, parentCid } = post || {};
|
||||||
const repliesResult = useReplies({
|
const directReplies = directRepliesByParentCid?.get(cid) || [];
|
||||||
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 &&
|
||||||
@@ -426,9 +422,9 @@ const ReplyBacklinks = ({ post, quotedByMap }: PostProps) => {
|
|||||||
)
|
)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
const replyBacklinks = cid && parentCid && ((repliesForRender?.length || 0) > 0 || quotedByMap?.get(cid)?.length) && (
|
const replyBacklinks = cid && parentCid && (directReplies.length > 0 || quotedByMap?.get(cid)?.length) && (
|
||||||
<>
|
<>
|
||||||
{repliesForRender?.map(
|
{directReplies.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} />,
|
||||||
)}
|
)}
|
||||||
@@ -451,7 +447,14 @@ const ReplyBacklinks = ({ post, quotedByMap }: PostProps) => {
|
|||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Reply = ({ postReplyCount, reply, roles, threadNumber, quotedByMap }: PostProps) => {
|
const Reply = ({
|
||||||
|
postReplyCount,
|
||||||
|
reply,
|
||||||
|
roles,
|
||||||
|
threadNumber,
|
||||||
|
quotedByMap,
|
||||||
|
directRepliesByParentCid,
|
||||||
|
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]> }) => {
|
||||||
const accountReply = useAccountComment({
|
const accountReply = useAccountComment({
|
||||||
commentIndex: typeof reply?.index === 'number' ? reply.index : undefined,
|
commentIndex: typeof reply?.index === 'number' ? reply.index : undefined,
|
||||||
});
|
});
|
||||||
@@ -481,7 +484,7 @@ const Reply = ({ postReplyCount, reply, roles, threadNumber, quotedByMap }: Post
|
|||||||
>
|
>
|
||||||
<PostInfoAndMedia post={post} postReplyCount={postReplyCount} roles={roles} threadNumber={threadNumber} />
|
<PostInfoAndMedia post={post} postReplyCount={postReplyCount} roles={roles} threadNumber={threadNumber} />
|
||||||
{!hidden && (!(removed || deleted) || ((removed || deleted) && reason)) && <CommentContent comment={post} />}
|
{!hidden && (!(removed || deleted) || ((removed || deleted) && reason)) && <CommentContent comment={post} />}
|
||||||
<ReplyBacklinks post={post} quotedByMap={quotedByMap} />
|
<ReplyBacklinks post={post} quotedByMap={quotedByMap} directRepliesByParentCid={directRepliesByParentCid} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -511,13 +514,22 @@ 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({
|
const shouldFetchReplies = showReplies && !isModQueue;
|
||||||
comment: post,
|
const previewRepliesResult = useReplies({
|
||||||
|
comment: shouldFetchReplies && !showAllReplies ? post : undefined,
|
||||||
|
sortType: 'new',
|
||||||
|
flat: true,
|
||||||
|
repliesPerPage: BOARD_REPLIES_PREVIEW_FETCH_SIZE,
|
||||||
|
accountComments: { newerThan: Infinity, append: true },
|
||||||
|
});
|
||||||
|
const fullRepliesResult = useReplies({
|
||||||
|
comment: shouldFetchReplies && showAllReplies ? post : undefined,
|
||||||
sortType: 'old',
|
sortType: 'old',
|
||||||
flat: true,
|
flat: true,
|
||||||
repliesPerPage: REPLIES_PER_PAGE,
|
repliesPerPage: REPLIES_PER_PAGE,
|
||||||
accountComments: { newerThan: Infinity, append: true },
|
accountComments: { newerThan: Infinity, append: true },
|
||||||
});
|
});
|
||||||
|
const repliesResult = showAllReplies ? fullRepliesResult : previewRepliesResult;
|
||||||
const { replies, hasMore, loadMore } = repliesResult;
|
const { replies, hasMore, loadMore } = repliesResult;
|
||||||
const updatedReplies = (repliesResult as { updatedReplies?: Comment[] }).updatedReplies;
|
const updatedReplies = (repliesResult as { updatedReplies?: Comment[] }).updatedReplies;
|
||||||
const repliesForRender = updatedReplies?.length ? updatedReplies : replies || [];
|
const repliesForRender = updatedReplies?.length ? updatedReplies : replies || [];
|
||||||
@@ -554,6 +566,21 @@ const PostMobile = ({
|
|||||||
// 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(() => repliesForRender.filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))), [repliesForRender]);
|
const filteredReplies = useMemo(() => repliesForRender.filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))), [repliesForRender]);
|
||||||
|
|
||||||
|
const directRepliesByParentCid = useMemo(() => {
|
||||||
|
const map = new Map<string, Comment[]>();
|
||||||
|
for (const reply of filteredReplies) {
|
||||||
|
const directParentCid = reply?.parentCid;
|
||||||
|
if (!directParentCid || !reply?.cid) continue;
|
||||||
|
const existingReplies = map.get(directParentCid);
|
||||||
|
if (existingReplies) {
|
||||||
|
existingReplies.push(reply);
|
||||||
|
} else {
|
||||||
|
map.set(directParentCid, [reply]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [filteredReplies]);
|
||||||
|
|
||||||
const quotedByMap = useQuotedByMap(filteredReplies);
|
const quotedByMap = useQuotedByMap(filteredReplies);
|
||||||
|
|
||||||
const visibleReplies = useProgressiveRender(filteredReplies, {
|
const visibleReplies = useProgressiveRender(filteredReplies, {
|
||||||
@@ -625,7 +652,7 @@ const PostMobile = ({
|
|||||||
{shouldShowSnow() && <img src='assets/xmashat.gif' className={styles.xmasHat} alt='' />}
|
{shouldShowSnow() && <img src='assets/xmashat.gif' className={styles.xmasHat} alt='' />}
|
||||||
<PostInfoAndMedia post={post} postReplyCount={replyCount} roles={roles} threadNumber={post?.number} />
|
<PostInfoAndMedia post={post} postReplyCount={replyCount} roles={roles} threadNumber={post?.number} />
|
||||||
<CommentContent comment={post} />
|
<CommentContent comment={post} />
|
||||||
<ReplyBacklinks post={post} quotedByMap={quotedByMap} />
|
<ReplyBacklinks post={post} quotedByMap={quotedByMap} directRepliesByParentCid={directRepliesByParentCid} />
|
||||||
</div>
|
</div>
|
||||||
{!isInPostView && !isInPendingPostView && (showReplies || isModQueue) && (
|
{!isInPostView && !isInPendingPostView && (showReplies || isModQueue) && (
|
||||||
<div className={styles.postLink}>
|
<div className={styles.postLink}>
|
||||||
@@ -673,7 +700,14 @@ const PostMobile = ({
|
|||||||
data={filteredReplies}
|
data={filteredReplies}
|
||||||
itemContent={(index, reply) => (
|
itemContent={(index, reply) => (
|
||||||
<div className={styles.replyContainer}>
|
<div className={styles.replyContainer}>
|
||||||
<Reply postReplyCount={replyCount} reply={reply} roles={roles} threadNumber={post?.number} quotedByMap={quotedByMap} />
|
<Reply
|
||||||
|
postReplyCount={replyCount}
|
||||||
|
reply={reply}
|
||||||
|
roles={roles}
|
||||||
|
threadNumber={post?.number}
|
||||||
|
quotedByMap={quotedByMap}
|
||||||
|
directRepliesByParentCid={directRepliesByParentCid}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
useWindowScroll={true}
|
useWindowScroll={true}
|
||||||
@@ -692,7 +726,14 @@ const PostMobile = ({
|
|||||||
!hasMore &&
|
!hasMore &&
|
||||||
visibleReplies.map((reply, index) => (
|
visibleReplies.map((reply, index) => (
|
||||||
<div key={index} className={styles.replyContainer}>
|
<div key={index} className={styles.replyContainer}>
|
||||||
<Reply postReplyCount={replyCount} reply={reply} roles={roles} threadNumber={post?.number} quotedByMap={quotedByMap} />
|
<Reply
|
||||||
|
postReplyCount={replyCount}
|
||||||
|
reply={reply}
|
||||||
|
roles={roles}
|
||||||
|
threadNumber={post?.number}
|
||||||
|
quotedByMap={quotedByMap}
|
||||||
|
directRepliesByParentCid={directRepliesByParentCid}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{/* Non-virtualized rendering for board view (last 5 replies) */}
|
{/* Non-virtualized rendering for board view (last 5 replies) */}
|
||||||
@@ -701,9 +742,16 @@ const PostMobile = ({
|
|||||||
!isInPendingPostView &&
|
!isInPendingPostView &&
|
||||||
repliesForRender &&
|
repliesForRender &&
|
||||||
showReplies &&
|
showReplies &&
|
||||||
filteredReplies.slice(-5).map((reply, index) => (
|
filteredReplies.slice(-BOARD_REPLIES_PREVIEW_VISIBLE_COUNT).map((reply, index) => (
|
||||||
<div key={index} className={styles.replyContainer}>
|
<div key={index} className={styles.replyContainer}>
|
||||||
<Reply postReplyCount={replyCount} reply={reply} roles={roles} threadNumber={post?.number} quotedByMap={quotedByMap} />
|
<Reply
|
||||||
|
postReplyCount={replyCount}
|
||||||
|
reply={reply}
|
||||||
|
roles={roles}
|
||||||
|
threadNumber={post?.number}
|
||||||
|
quotedByMap={quotedByMap}
|
||||||
|
directRepliesByParentCid={directRepliesByParentCid}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
/** Max replies loaded per page from useReplies. Threads with > this use Virtuoso. */
|
/** Max replies loaded per page from useReplies. Threads with > this use Virtuoso. */
|
||||||
export const REPLIES_PER_PAGE = 500;
|
export const REPLIES_PER_PAGE = 500;
|
||||||
|
|
||||||
|
/** Number of replies shown in preview mode on board/listing views. */
|
||||||
|
export const BOARD_REPLIES_PREVIEW_VISIBLE_COUNT = 5;
|
||||||
|
|
||||||
|
/** Number of replies fetched for preview mode on board/listing views. */
|
||||||
|
export const BOARD_REPLIES_PREVIEW_FETCH_SIZE = 25;
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { BOARD_REPLIES_PREVIEW_VISIBLE_COUNT } from '../constants';
|
||||||
|
|
||||||
|
export interface CommentLike {
|
||||||
|
cid?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From replies sorted by 'new' (newest first), returns the latest N in chronological
|
||||||
|
* display order (oldest of those first). Handles fewer-than-N replies.
|
||||||
|
*/
|
||||||
|
export function getPreviewDisplayReplies<T extends CommentLike>(replies: T[], visibleCount: number = BOARD_REPLIES_PREVIEW_VISIBLE_COUNT): T[] {
|
||||||
|
const slice = replies.slice(0, visibleCount);
|
||||||
|
return [...slice].reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComputeOmittedParams {
|
||||||
|
totalReplyCount: number;
|
||||||
|
visibleCount: number;
|
||||||
|
pinned?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes omitted reply count for board view. For pinned threads, collapsed view
|
||||||
|
* shows 0 replies, so omitted = total. For non-pinned, omitted = total - visible.
|
||||||
|
* Result is always clamped at zero.
|
||||||
|
*/
|
||||||
|
export function computeOmittedCount({ totalReplyCount, visibleCount, pinned = false }: ComputeOmittedParams): number {
|
||||||
|
if (pinned) {
|
||||||
|
return Math.max(0, totalReplyCount);
|
||||||
|
}
|
||||||
|
return Math.max(0, totalReplyCount - visibleCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetTotalReplyCountParams {
|
||||||
|
replyCount: number | undefined;
|
||||||
|
fullLoadedCount: number;
|
||||||
|
previewLoadedCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns total reply count: post.replyCount when defined, else max of loaded counts.
|
||||||
|
*/
|
||||||
|
export function getTotalReplyCount({ replyCount, fullLoadedCount, previewLoadedCount }: GetTotalReplyCountParams): number {
|
||||||
|
if (typeof replyCount === 'number' && replyCount >= 0) {
|
||||||
|
return replyCount;
|
||||||
|
}
|
||||||
|
return Math.max(fullLoadedCount, previewLoadedCount);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user