mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(replies): refresh thread backlinks from live account replies (#1058)
* fix(replies): refresh thread backlinks from live account replies * refactor(replies): extract useRegisterFreshReplies hook per CodeRabbit
This commit is contained in:
@@ -41,11 +41,12 @@ import useReplyModalStore from '../../stores/use-reply-modal-store';
|
|||||||
import { selectPostMenuProps } from '../../lib/utils/post-menu-props';
|
import { selectPostMenuProps } from '../../lib/utils/post-menu-props';
|
||||||
import useChallengesStore from '../../stores/use-challenges-store';
|
import useChallengesStore from '../../stores/use-challenges-store';
|
||||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||||
import usePostNumberStore from '../../stores/use-post-number-store';
|
import useRegisterFreshReplies from '../../hooks/use-register-fresh-replies';
|
||||||
import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils';
|
import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils';
|
||||||
import { usePublishCommentModeration } from '@bitsocialnet/bitsocial-react-hooks';
|
import { usePublishCommentModeration } from '@bitsocialnet/bitsocial-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 useFreshReplies from '../../hooks/use-fresh-replies';
|
||||||
import { BOARD_REPLIES_PREVIEW_FETCH_SIZE, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT, 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, filterRepliesForDisplay, getPreviewDisplayReplies, getTotalReplyCount } from '../../lib/utils/replies-preview-utils';
|
import { computeOmittedCount, filterRepliesForDisplay, getPreviewDisplayReplies, getTotalReplyCount } from '../../lib/utils/replies-preview-utils';
|
||||||
import { getThreadTopNavigationState, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
|
import { getThreadTopNavigationState, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
|
||||||
@@ -860,6 +861,8 @@ const PostDesktop = ({
|
|||||||
? fullReplies
|
? fullReplies
|
||||||
: previewReplies
|
: previewReplies
|
||||||
: getPreviewDisplayReplies(previewReplies, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT);
|
: getPreviewDisplayReplies(previewReplies, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT);
|
||||||
|
const freshRepliesForRender = useFreshReplies(repliesForRender);
|
||||||
|
useRegisterFreshReplies(post, freshRepliesForRender);
|
||||||
const setResetFunction = useFeedResetStore((s) => s.setResetFunction);
|
const setResetFunction = useFeedResetStore((s) => s.setResetFunction);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if ((isInPostPageView || isInPendingPostView) && reset) {
|
if ((isInPostPageView || isInPendingPostView) && reset) {
|
||||||
@@ -868,23 +871,9 @@ const PostDesktop = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [isInPostPageView, isInPendingPostView, reset, setResetFunction]);
|
}, [isInPostPageView, isInPendingPostView, reset, setResetFunction]);
|
||||||
const registerComments = usePostNumberStore((s) => s.registerComments);
|
|
||||||
const prevCidsRef = useRef<string>('');
|
|
||||||
useEffect(() => {
|
|
||||||
const all = post ? [post, ...repliesForRender] : repliesForRender;
|
|
||||||
if (!all.length) return;
|
|
||||||
const cidsKey = all
|
|
||||||
.map((c) => c?.cid)
|
|
||||||
.filter(Boolean)
|
|
||||||
.sort()
|
|
||||||
.join(',');
|
|
||||||
if (cidsKey === prevCidsRef.current) return;
|
|
||||||
prevCidsRef.current = cidsKey;
|
|
||||||
registerComments(all);
|
|
||||||
}, [post, repliesForRender, registerComments]);
|
|
||||||
const visiblelinksCount = useCountLinksInReplies(post, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT);
|
const visiblelinksCount = useCountLinksInReplies(post, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT);
|
||||||
const totalLinksCount = useCountLinksInReplies(post);
|
const totalLinksCount = useCountLinksInReplies(post);
|
||||||
const replyCount = repliesForRender.length;
|
const replyCount = freshRepliesForRender.length;
|
||||||
|
|
||||||
const totalReplyCount = getTotalReplyCount({
|
const totalReplyCount = getTotalReplyCount({
|
||||||
replyCount: post?.replyCount,
|
replyCount: post?.replyCount,
|
||||||
@@ -904,7 +893,7 @@ const PostDesktop = ({
|
|||||||
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
||||||
|
|
||||||
// Author-deleted replies are hidden from thread replies; moderator removals still render their placeholder.
|
// Author-deleted replies are hidden from thread replies; moderator removals still render their placeholder.
|
||||||
const filteredReplies = filterRepliesForDisplay(repliesForRender);
|
const filteredReplies = filterRepliesForDisplay(freshRepliesForRender);
|
||||||
const directRepliesByParentCid = (() => {
|
const directRepliesByParentCid = (() => {
|
||||||
const map = new Map<string, Comment[]>();
|
const map = new Map<string, Comment[]>();
|
||||||
for (const reply of filteredReplies) {
|
for (const reply of filteredReplies) {
|
||||||
@@ -1121,7 +1110,7 @@ const PostDesktop = ({
|
|||||||
{!isHidden &&
|
{!isHidden &&
|
||||||
!showAllReplies &&
|
!showAllReplies &&
|
||||||
!isInPendingPostView &&
|
!isInPendingPostView &&
|
||||||
repliesForRender &&
|
freshRepliesForRender &&
|
||||||
showReplies &&
|
showReplies &&
|
||||||
filteredReplies.map((reply) => (
|
filteredReplies.map((reply) => (
|
||||||
<div key={reply.cid} className={styles.replyContainer}>
|
<div key={reply.cid} className={styles.replyContainer}>
|
||||||
|
|||||||
@@ -36,10 +36,11 @@ import useReplyModalStore from '../../stores/use-reply-modal-store';
|
|||||||
import { selectPostMenuProps } from '../../lib/utils/post-menu-props';
|
import { selectPostMenuProps } from '../../lib/utils/post-menu-props';
|
||||||
import useChallengesStore from '../../stores/use-challenges-store';
|
import useChallengesStore from '../../stores/use-challenges-store';
|
||||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||||
import usePostNumberStore from '../../stores/use-post-number-store';
|
import useRegisterFreshReplies from '../../hooks/use-register-fresh-replies';
|
||||||
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 useFreshReplies from '../../hooks/use-fresh-replies';
|
||||||
import { BOARD_REPLIES_PREVIEW_FETCH_SIZE, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT, REPLIES_PER_PAGE } from '../../lib/constants';
|
import { BOARD_REPLIES_PREVIEW_FETCH_SIZE, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT, REPLIES_PER_PAGE } from '../../lib/constants';
|
||||||
import { filterRepliesForDisplay, getPreviewDisplayReplies } from '../../lib/utils/replies-preview-utils';
|
import { filterRepliesForDisplay, getPreviewDisplayReplies } from '../../lib/utils/replies-preview-utils';
|
||||||
import { getThreadTopNavigationState, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
|
import { getThreadTopNavigationState, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
|
||||||
@@ -608,6 +609,8 @@ const PostMobile = ({
|
|||||||
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 || [];
|
||||||
|
const freshRepliesForRender = useFreshReplies(repliesForRender);
|
||||||
|
useRegisterFreshReplies(post, freshRepliesForRender);
|
||||||
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(() => {
|
||||||
@@ -617,20 +620,6 @@ const PostMobile = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [isInPostView, isInPendingPostView, reset, setResetFunction]);
|
}, [isInPostView, isInPendingPostView, reset, setResetFunction]);
|
||||||
const registerComments = usePostNumberStore((s) => s.registerComments);
|
|
||||||
const prevCidsRef = useRef<string>('');
|
|
||||||
useEffect(() => {
|
|
||||||
const all = post ? [post, ...repliesForRender] : repliesForRender;
|
|
||||||
if (!all.length) return;
|
|
||||||
const cidsKey = all
|
|
||||||
.map((c) => c?.cid)
|
|
||||||
.filter(Boolean)
|
|
||||||
.sort()
|
|
||||||
.join(',');
|
|
||||||
if (cidsKey === prevCidsRef.current) return;
|
|
||||||
prevCidsRef.current = cidsKey;
|
|
||||||
registerComments(all);
|
|
||||||
}, [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 });
|
||||||
@@ -640,7 +629,7 @@ const PostMobile = ({
|
|||||||
const isReply = !!parentCid;
|
const isReply = !!parentCid;
|
||||||
|
|
||||||
// Author-deleted replies are hidden from thread replies; moderator removals still render their placeholder.
|
// Author-deleted replies are hidden from thread replies; moderator removals still render their placeholder.
|
||||||
const filteredReplies = filterRepliesForDisplay(repliesForRender);
|
const filteredReplies = filterRepliesForDisplay(freshRepliesForRender);
|
||||||
const previewDisplayReplies = getPreviewDisplayReplies(filteredReplies, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT);
|
const previewDisplayReplies = getPreviewDisplayReplies(filteredReplies, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT);
|
||||||
|
|
||||||
const directRepliesByParentCid = (() => {
|
const directRepliesByParentCid = (() => {
|
||||||
@@ -837,7 +826,7 @@ const PostMobile = ({
|
|||||||
{/* Non-virtualized rendering for board view (last 5 replies) */}
|
{/* Non-virtualized rendering for board view (last 5 replies) */}
|
||||||
{!showAllReplies &&
|
{!showAllReplies &&
|
||||||
!isInPendingPostView &&
|
!isInPendingPostView &&
|
||||||
repliesForRender &&
|
freshRepliesForRender &&
|
||||||
showReplies &&
|
showReplies &&
|
||||||
previewDisplayReplies.map((reply) => (
|
previewDisplayReplies.map((reply) => (
|
||||||
<div key={reply.cid} className={styles.replyContainer}>
|
<div key={reply.cid} className={styles.replyContainer}>
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { createElement } from 'react';
|
||||||
|
import { createRoot, type Root } from 'react-dom/client';
|
||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
import useFreshReplies from '../use-fresh-replies';
|
||||||
|
|
||||||
|
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||||
|
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
|
||||||
|
|
||||||
|
const testState = vi.hoisted(() => ({
|
||||||
|
accountComments: [] as Array<Record<string, unknown>>,
|
||||||
|
replies: [] as Array<Record<string, unknown>>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
|
||||||
|
useAccountComments: () => ({
|
||||||
|
accountComments: testState.accountComments,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let container: HTMLDivElement;
|
||||||
|
let latestValue: ReturnType<typeof useFreshReplies>;
|
||||||
|
let root: Root;
|
||||||
|
|
||||||
|
const HookHarness = () => {
|
||||||
|
latestValue = useFreshReplies(testState.replies as never);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderHook = () => {
|
||||||
|
act(() => {
|
||||||
|
root.render(createElement(HookHarness));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('useFreshReplies', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
testState.accountComments = [];
|
||||||
|
testState.replies = [];
|
||||||
|
|
||||||
|
container = document.createElement('div');
|
||||||
|
document.body.appendChild(container);
|
||||||
|
root = createRoot(container);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
act(() => root.unmount());
|
||||||
|
container.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaces stale indexed replies with the latest account comment objects', () => {
|
||||||
|
testState.replies = [
|
||||||
|
{
|
||||||
|
cid: 'reply-cid',
|
||||||
|
content: 'stale reply',
|
||||||
|
index: 3,
|
||||||
|
number: undefined,
|
||||||
|
subplebbitAddress: 'music.eth',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cid: 'network-reply-cid',
|
||||||
|
content: 'network reply',
|
||||||
|
subplebbitAddress: 'music.eth',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
testState.accountComments = [
|
||||||
|
{
|
||||||
|
cid: 'reply-cid',
|
||||||
|
content: 'fresh reply',
|
||||||
|
index: 3,
|
||||||
|
number: 27,
|
||||||
|
subplebbitAddress: 'music.eth',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
renderHook();
|
||||||
|
|
||||||
|
expect(latestValue[0]).toBe(testState.accountComments[0] as never);
|
||||||
|
expect(latestValue[0]?.number).toBe(27);
|
||||||
|
expect(latestValue[1]).toBe(testState.replies[1] as never);
|
||||||
|
|
||||||
|
testState.accountComments = [
|
||||||
|
{
|
||||||
|
...testState.accountComments[0],
|
||||||
|
number: 28,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
renderHook();
|
||||||
|
|
||||||
|
expect(latestValue[0]?.number).toBe(28);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { createElement } from 'react';
|
||||||
|
import { createRoot, type Root } from 'react-dom/client';
|
||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
import useQuotedByMap from '../use-quoted-by-map';
|
||||||
|
import usePostNumberStore from '../../stores/use-post-number-store';
|
||||||
|
|
||||||
|
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||||
|
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
|
||||||
|
|
||||||
|
const testState = vi.hoisted(() => ({
|
||||||
|
replies: [] as Array<Record<string, unknown>>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({}));
|
||||||
|
|
||||||
|
let container: HTMLDivElement;
|
||||||
|
let latestValue: ReturnType<typeof useQuotedByMap>;
|
||||||
|
let root: Root;
|
||||||
|
|
||||||
|
const HookHarness = () => {
|
||||||
|
latestValue = useQuotedByMap(testState.replies as never, 'music.eth');
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderHook = () => {
|
||||||
|
act(() => {
|
||||||
|
root.render(createElement(HookHarness));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('useQuotedByMap', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
testState.replies = [];
|
||||||
|
usePostNumberStore.setState({
|
||||||
|
cidToNumber: { 'op-cid': 1 },
|
||||||
|
numberToCid: { 'music.eth': { 1: 'op-cid' } },
|
||||||
|
});
|
||||||
|
|
||||||
|
container = document.createElement('div');
|
||||||
|
document.body.appendChild(container);
|
||||||
|
root = createRoot(container);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
act(() => root.unmount());
|
||||||
|
container.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('refreshes mapped replies when a published account reply later gains its post number', () => {
|
||||||
|
testState.replies = [
|
||||||
|
{
|
||||||
|
cid: 'reply-cid',
|
||||||
|
content: 'replying to >>1',
|
||||||
|
state: 'succeeded',
|
||||||
|
subplebbitAddress: 'music.eth',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
renderHook();
|
||||||
|
|
||||||
|
expect(latestValue.get('op-cid')?.[0]?.number).toBeUndefined();
|
||||||
|
|
||||||
|
testState.replies = [
|
||||||
|
{
|
||||||
|
...testState.replies[0],
|
||||||
|
number: 42,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
renderHook();
|
||||||
|
|
||||||
|
expect(latestValue.get('op-cid')?.[0]?.number).toBe(42);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { Comment, useAccountComments } from '@bitsocialnet/bitsocial-react-hooks';
|
||||||
|
|
||||||
|
const useFreshReplies = (replies: Comment[] = []) => {
|
||||||
|
const { accountComments } = useAccountComments();
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
if (!replies.length || !accountComments?.length) {
|
||||||
|
return replies;
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountCommentsByIndex = new Map<number, Comment>();
|
||||||
|
for (const accountComment of accountComments) {
|
||||||
|
if (typeof accountComment?.index === 'number') {
|
||||||
|
accountCommentsByIndex.set(accountComment.index, accountComment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasFreshReplies = false;
|
||||||
|
const nextReplies = replies.map((reply) => {
|
||||||
|
if (typeof reply?.index !== 'number') {
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
const freshReply = accountCommentsByIndex.get(reply.index);
|
||||||
|
if (!freshReply) {
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFreshReplies = true;
|
||||||
|
return freshReply;
|
||||||
|
});
|
||||||
|
|
||||||
|
return hasFreshReplies ? nextReplies : replies;
|
||||||
|
}, [accountComments, replies]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useFreshReplies;
|
||||||
@@ -9,7 +9,7 @@ interface ReplyQuoteTargets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getReplyFingerprint = (reply: Comment) =>
|
const getReplyFingerprint = (reply: Comment) =>
|
||||||
`${reply?.cid ?? ''}|${reply?.deleted ? '1' : '0'}|${reply?.removed ? '1' : '0'}|${reply?.edit?.timestamp ?? ''}|${reply?.state ?? ''}`;
|
`${reply?.cid ?? ''}|${reply?.number ?? ''}|${reply?.deleted ? '1' : '0'}|${reply?.removed ? '1' : '0'}|${reply?.edit?.timestamp ?? ''}|${reply?.state ?? ''}`;
|
||||||
|
|
||||||
const areQuotedByMapsEquivalent = (previousMap: Map<string, Comment[]>, nextMap: Map<string, Comment[]>) => {
|
const areQuotedByMapsEquivalent = (previousMap: Map<string, Comment[]>, nextMap: Map<string, Comment[]>) => {
|
||||||
if (previousMap.size !== nextMap.size) {
|
if (previousMap.size !== nextMap.size) {
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import type { Comment } from '@bitsocialnet/bitsocial-react-hooks';
|
||||||
|
import usePostNumberStore from '../stores/use-post-number-store';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers post and fresh replies with the post-number store so backlinks
|
||||||
|
* and post numbers stay in sync when replies gain their number from account comments.
|
||||||
|
* Uses a fingerprint (cidsKey) to avoid redundant registerComments calls.
|
||||||
|
*/
|
||||||
|
const useRegisterFreshReplies = (post: Comment | undefined, freshRepliesForRender: Comment[]) => {
|
||||||
|
const registerComments = usePostNumberStore((s) => s.registerComments);
|
||||||
|
const prevCidsRef = useRef<string>('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const all = post ? [post, ...freshRepliesForRender] : freshRepliesForRender;
|
||||||
|
if (!all.length) return;
|
||||||
|
|
||||||
|
const cidsKey = all
|
||||||
|
.map((comment) => {
|
||||||
|
const commentKey = comment?.cid ?? (typeof comment?.index === 'number' ? `index:${comment.index}` : `timestamp:${comment?.timestamp ?? ''}`);
|
||||||
|
return `${comment?.subplebbitAddress ?? ''}:${commentKey}:${typeof comment?.number === 'number' ? comment.number : ''}`;
|
||||||
|
})
|
||||||
|
.sort()
|
||||||
|
.join(',');
|
||||||
|
|
||||||
|
if (cidsKey === prevCidsRef.current) return;
|
||||||
|
prevCidsRef.current = cidsKey;
|
||||||
|
registerComments(all);
|
||||||
|
}, [post, freshRepliesForRender, registerComments]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useRegisterFreshReplies;
|
||||||
Reference in New Issue
Block a user