mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(replies): reply permalink didn't auto-scroll to deep replies
This commit is contained in:
@@ -19,6 +19,7 @@ import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
|||||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||||
import useHide from '../../hooks/use-hide';
|
import useHide from '../../hooks/use-hide';
|
||||||
import useStateString from '../../hooks/use-state-string';
|
import useStateString from '../../hooks/use-state-string';
|
||||||
|
import useScrollToReply from '../../hooks/use-scroll-to-reply';
|
||||||
import CommentContent from '../comment-content';
|
import CommentContent from '../comment-content';
|
||||||
import CommentMedia from '../comment-media';
|
import CommentMedia from '../comment-media';
|
||||||
import EditMenu from '../edit-menu/edit-menu';
|
import EditMenu from '../edit-menu/edit-menu';
|
||||||
@@ -375,7 +376,7 @@ const Reply = ({ postReplyCount, reply, roles, threadNumber }: PostProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PostDesktop = ({ post, roles, showAllReplies, showReplies = true }: PostProps) => {
|
const PostDesktop = ({ post, roles, showAllReplies, showReplies = true, targetReplyCid }: PostProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { author, cid, content, deleted, link, linkHeight, linkWidth, pinned, postCid, removed, spoiler, state, subplebbitAddress, thumbnailUrl, parentCid } = post || {};
|
const { author, cid, content, deleted, link, linkHeight, linkWidth, pinned, postCid, removed, spoiler, state, subplebbitAddress, thumbnailUrl, parentCid } = post || {};
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -429,6 +430,16 @@ const PostDesktop = ({ post, roles, showAllReplies, showReplies = true }: PostPr
|
|||||||
|
|
||||||
const lastVirtuosoState = navigationType === 'POP' ? lastVirtuosoStates?.[virtuosoStateKey] : undefined;
|
const lastVirtuosoState = navigationType === 'POP' ? lastVirtuosoStates?.[virtuosoStateKey] : undefined;
|
||||||
|
|
||||||
|
const shouldScrollToReply = showAllReplies && showReplies && !isInPendingPostView && !!targetReplyCid;
|
||||||
|
useScrollToReply({
|
||||||
|
targetReplyCid,
|
||||||
|
replies: filteredReplies,
|
||||||
|
hasMore,
|
||||||
|
loadMore,
|
||||||
|
virtuosoRef,
|
||||||
|
enabled: shouldScrollToReply,
|
||||||
|
});
|
||||||
|
|
||||||
// Footer component for Virtuoso showing loading state
|
// Footer component for Virtuoso showing loading state
|
||||||
const RepliesFooter = () =>
|
const RepliesFooter = () =>
|
||||||
hasMore ? (
|
hasMore ? (
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { useCommentMediaInfo } from '../../hooks/use-comment-media-info';
|
|||||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||||
import useHide from '../../hooks/use-hide';
|
import useHide from '../../hooks/use-hide';
|
||||||
import useStateString from '../../hooks/use-state-string';
|
import useStateString from '../../hooks/use-state-string';
|
||||||
|
import useScrollToReply from '../../hooks/use-scroll-to-reply';
|
||||||
import CommentContent from '../comment-content';
|
import CommentContent from '../comment-content';
|
||||||
import CommentMedia from '../comment-media';
|
import CommentMedia from '../comment-media';
|
||||||
import LoadingEllipsis from '../loading-ellipsis';
|
import LoadingEllipsis from '../loading-ellipsis';
|
||||||
@@ -282,7 +283,7 @@ const Reply = ({ postReplyCount, reply, roles, threadNumber }: PostProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PostMobile = ({ post, roles, showAllReplies, showReplies = true }: PostProps) => {
|
const PostMobile = ({ post, roles, showAllReplies, showReplies = true, targetReplyCid }: PostProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { author, cid, pinned, postCid, replyCount, state, subplebbitAddress } = post || {};
|
const { author, cid, pinned, postCid, replyCount, state, subplebbitAddress } = post || {};
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -324,6 +325,16 @@ const PostMobile = ({ post, roles, showAllReplies, showReplies = true }: PostPro
|
|||||||
|
|
||||||
const lastVirtuosoState = navigationType === 'POP' ? lastVirtuosoStates?.[virtuosoStateKey] : undefined;
|
const lastVirtuosoState = navigationType === 'POP' ? lastVirtuosoStates?.[virtuosoStateKey] : undefined;
|
||||||
|
|
||||||
|
const shouldScrollToReply = showAllReplies && showReplies && !isInPendingPostView && !!targetReplyCid;
|
||||||
|
useScrollToReply({
|
||||||
|
targetReplyCid,
|
||||||
|
replies: filteredReplies,
|
||||||
|
hasMore,
|
||||||
|
loadMore,
|
||||||
|
virtuosoRef,
|
||||||
|
enabled: shouldScrollToReply,
|
||||||
|
});
|
||||||
|
|
||||||
// Footer component for Virtuoso showing loading state
|
// Footer component for Virtuoso showing loading state
|
||||||
const RepliesFooter = () =>
|
const RepliesFooter = () =>
|
||||||
hasMore ? (
|
hasMore ? (
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
/* @vitest-environment jsdom */
|
||||||
|
import { act, useMemo } from 'react';
|
||||||
|
import { describe, it, vi, beforeEach, afterEach, expect } from 'vitest';
|
||||||
|
import { createRoot, Root } from 'react-dom/client';
|
||||||
|
import { VirtuosoHandle } from 'react-virtuoso';
|
||||||
|
import useScrollToReply from './use-scroll-to-reply';
|
||||||
|
|
||||||
|
const TestHarness = ({
|
||||||
|
targetReplyCid,
|
||||||
|
replies,
|
||||||
|
hasMore,
|
||||||
|
loadMore,
|
||||||
|
virtuosoRef,
|
||||||
|
renderTargetElement = false,
|
||||||
|
}: {
|
||||||
|
targetReplyCid?: string;
|
||||||
|
replies: Array<{ cid?: string | null }>;
|
||||||
|
hasMore: boolean;
|
||||||
|
loadMore: () => void;
|
||||||
|
virtuosoRef: React.RefObject<VirtuosoHandle | null>;
|
||||||
|
renderTargetElement?: boolean;
|
||||||
|
}) => {
|
||||||
|
const memoizedReplies = useMemo(() => replies, [replies]);
|
||||||
|
useScrollToReply({
|
||||||
|
targetReplyCid,
|
||||||
|
replies: memoizedReplies,
|
||||||
|
hasMore,
|
||||||
|
loadMore,
|
||||||
|
virtuosoRef,
|
||||||
|
enabled: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return renderTargetElement ? <div data-cid={targetReplyCid} /> : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('useScrollToReply', () => {
|
||||||
|
const originalScrollIntoView = Element.prototype.scrollIntoView;
|
||||||
|
let root: Root;
|
||||||
|
let container: HTMLDivElement;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
Element.prototype.scrollIntoView = vi.fn();
|
||||||
|
container = document.createElement('div');
|
||||||
|
document.body.appendChild(container);
|
||||||
|
root = createRoot(container);
|
||||||
|
vi.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
act(() => {
|
||||||
|
root.unmount();
|
||||||
|
});
|
||||||
|
container.remove();
|
||||||
|
Element.prototype.scrollIntoView = originalScrollIntoView;
|
||||||
|
vi.useRealTimers();
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls to target reply index when present', async () => {
|
||||||
|
const scrollToIndex = vi.fn();
|
||||||
|
const virtuosoRef = { current: { scrollToIndex } as unknown as VirtuosoHandle };
|
||||||
|
const replies = [{ cid: 'a' }, { cid: 'b' }, { cid: 'c' }];
|
||||||
|
|
||||||
|
await act(() => {
|
||||||
|
root.render(<TestHarness targetReplyCid='b' replies={replies} hasMore={true} loadMore={vi.fn()} virtuosoRef={virtuosoRef} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(() => Promise.resolve());
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
vi.advanceTimersByTime(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(scrollToIndex).toHaveBeenCalledWith({
|
||||||
|
index: 1,
|
||||||
|
align: 'center',
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads more when target reply is not yet present', async () => {
|
||||||
|
const loadMore = vi.fn();
|
||||||
|
const virtuosoRef = { current: { scrollToIndex: vi.fn() } as unknown as VirtuosoHandle };
|
||||||
|
let intervalCallback: (() => void) | null = null;
|
||||||
|
let timeoutCallback: (() => void) | null = null;
|
||||||
|
|
||||||
|
vi.spyOn(window, 'setInterval').mockImplementation((callback) => {
|
||||||
|
intervalCallback = callback as () => void;
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.spyOn(window, 'setTimeout').mockImplementation((callback) => {
|
||||||
|
timeoutCallback = callback as () => void;
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(() => {
|
||||||
|
root.render(<TestHarness targetReplyCid='missing' replies={[{ cid: 'a' }]} hasMore={true} loadMore={loadMore} virtuosoRef={virtuosoRef} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(() => Promise.resolve());
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
intervalCallback?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
timeoutCallback?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(loadMore).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls into view when virtualization is not active', async () => {
|
||||||
|
const loadMore = vi.fn();
|
||||||
|
const virtuosoRef = { current: { scrollToIndex: vi.fn() } as unknown as VirtuosoHandle };
|
||||||
|
|
||||||
|
await act(() => {
|
||||||
|
root.render(
|
||||||
|
<TestHarness targetReplyCid='target' replies={[{ cid: 'target' }]} hasMore={false} loadMore={loadMore} virtuosoRef={virtuosoRef} renderTargetElement={true} />,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(() => Promise.resolve());
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
vi.advanceTimersByTime(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Element.prototype.scrollIntoView).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { VirtuosoHandle } from 'react-virtuoso';
|
||||||
|
|
||||||
|
type ReplyItem = {
|
||||||
|
cid?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface UseScrollToReplyParams {
|
||||||
|
targetReplyCid?: string;
|
||||||
|
replies: ReplyItem[];
|
||||||
|
hasMore: boolean;
|
||||||
|
loadMore: () => void;
|
||||||
|
virtuosoRef: React.RefObject<VirtuosoHandle | null>;
|
||||||
|
enabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_MAX_LOAD_ATTEMPTS = 500;
|
||||||
|
const MAX_LOAD_DURATION_MS = 120000;
|
||||||
|
const LOAD_MORE_THROTTLE_MS = 300;
|
||||||
|
const AUTO_SCROLL_INTERVAL_MS = 350;
|
||||||
|
|
||||||
|
const useScrollToReply = ({ targetReplyCid, replies, hasMore, loadMore, virtuosoRef, enabled = true }: UseScrollToReplyParams) => {
|
||||||
|
const hasScrolledRef = useRef(false);
|
||||||
|
const loadAttemptsRef = useRef(0);
|
||||||
|
const lastRepliesLengthRef = useRef(replies.length);
|
||||||
|
const loadMoreTimeoutRef = useRef<number | null>(null);
|
||||||
|
const lastLoadAtRef = useRef(0);
|
||||||
|
const loadStartAtRef = useRef(0);
|
||||||
|
const lastScrollIndexRef = useRef(-1);
|
||||||
|
const intervalRef = useRef<number | null>(null);
|
||||||
|
const latestTargetRef = useRef(targetReplyCid);
|
||||||
|
const latestRepliesRef = useRef(replies);
|
||||||
|
const latestHasMoreRef = useRef(hasMore);
|
||||||
|
const latestLoadMoreRef = useRef(loadMore);
|
||||||
|
|
||||||
|
// Only reset when the target changes, NOT when replies.length changes
|
||||||
|
// (replies.length changing is expected as we load more pages)
|
||||||
|
useEffect(() => {
|
||||||
|
hasScrolledRef.current = false;
|
||||||
|
loadAttemptsRef.current = 0;
|
||||||
|
lastRepliesLengthRef.current = replies.length;
|
||||||
|
lastLoadAtRef.current = 0;
|
||||||
|
loadStartAtRef.current = Date.now();
|
||||||
|
lastScrollIndexRef.current = -1;
|
||||||
|
if (intervalRef.current) {
|
||||||
|
window.clearInterval(intervalRef.current);
|
||||||
|
intervalRef.current = null;
|
||||||
|
}
|
||||||
|
if (loadMoreTimeoutRef.current) {
|
||||||
|
window.clearTimeout(loadMoreTimeoutRef.current);
|
||||||
|
loadMoreTimeoutRef.current = null;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [targetReplyCid]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
latestTargetRef.current = targetReplyCid;
|
||||||
|
}, [targetReplyCid]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
latestRepliesRef.current = replies;
|
||||||
|
}, [replies]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
latestHasMoreRef.current = hasMore;
|
||||||
|
}, [hasMore]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
latestLoadMoreRef.current = loadMore;
|
||||||
|
}, [loadMore]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (replies.length !== lastRepliesLengthRef.current) {
|
||||||
|
lastRepliesLengthRef.current = replies.length;
|
||||||
|
}
|
||||||
|
}, [replies.length]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!enabled || !targetReplyCid || intervalRef.current) return;
|
||||||
|
|
||||||
|
intervalRef.current = window.setInterval(() => {
|
||||||
|
const latestTarget = latestTargetRef.current;
|
||||||
|
const latestReplies = latestRepliesRef.current;
|
||||||
|
const latestHasMore = latestHasMoreRef.current;
|
||||||
|
const latestLoadMore = latestLoadMoreRef.current;
|
||||||
|
|
||||||
|
if (!enabled || !latestTarget || hasScrolledRef.current) {
|
||||||
|
if (intervalRef.current) {
|
||||||
|
window.clearInterval(intervalRef.current);
|
||||||
|
intervalRef.current = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!latestHasMore) {
|
||||||
|
const element = document.querySelector(`[data-cid="${latestTarget}"]`);
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
hasScrolledRef.current = true;
|
||||||
|
} else {
|
||||||
|
// Reply not found after loading all pages
|
||||||
|
console.warn(`[scroll-to-reply] Could not find reply with CID "${latestTarget}" in the feed.`);
|
||||||
|
hasScrolledRef.current = true; // Stop trying
|
||||||
|
}
|
||||||
|
if (intervalRef.current) {
|
||||||
|
window.clearInterval(intervalRef.current);
|
||||||
|
intervalRef.current = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iteratively load pages until the target reply appears in the list.
|
||||||
|
const targetIndex = latestReplies.findIndex((reply) => reply?.cid === latestTarget);
|
||||||
|
if (targetIndex >= 0) {
|
||||||
|
hasScrolledRef.current = true;
|
||||||
|
if (loadMoreTimeoutRef.current) {
|
||||||
|
window.clearTimeout(loadMoreTimeoutRef.current);
|
||||||
|
loadMoreTimeoutRef.current = null;
|
||||||
|
}
|
||||||
|
if (intervalRef.current) {
|
||||||
|
window.clearInterval(intervalRef.current);
|
||||||
|
intervalRef.current = null;
|
||||||
|
}
|
||||||
|
virtuosoRef.current?.scrollToIndex({ index: targetIndex, align: 'center', behavior: 'smooth' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smoothly scroll to the latest loaded replies while loading more pages
|
||||||
|
const lastIndex = latestReplies.length - 1;
|
||||||
|
if (lastIndex >= 0 && lastIndex !== lastScrollIndexRef.current) {
|
||||||
|
lastScrollIndexRef.current = lastIndex;
|
||||||
|
virtuosoRef.current?.scrollToIndex({ index: lastIndex, align: 'end', behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadDuration = Date.now() - loadStartAtRef.current;
|
||||||
|
if (!latestHasMore || loadAttemptsRef.current >= DEFAULT_MAX_LOAD_ATTEMPTS || loadDuration >= MAX_LOAD_DURATION_MS) return;
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastLoadAtRef.current < LOAD_MORE_THROTTLE_MS) return;
|
||||||
|
|
||||||
|
lastLoadAtRef.current = now;
|
||||||
|
loadAttemptsRef.current += 1;
|
||||||
|
|
||||||
|
if (loadMoreTimeoutRef.current) {
|
||||||
|
window.clearTimeout(loadMoreTimeoutRef.current);
|
||||||
|
}
|
||||||
|
loadMoreTimeoutRef.current = window.setTimeout(() => {
|
||||||
|
latestLoadMore();
|
||||||
|
}, LOAD_MORE_THROTTLE_MS);
|
||||||
|
}, AUTO_SCROLL_INTERVAL_MS);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (intervalRef.current) {
|
||||||
|
window.clearInterval(intervalRef.current);
|
||||||
|
intervalRef.current = null;
|
||||||
|
}
|
||||||
|
if (loadMoreTimeoutRef.current) {
|
||||||
|
window.clearTimeout(loadMoreTimeoutRef.current);
|
||||||
|
loadMoreTimeoutRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [enabled, targetReplyCid, virtuosoRef]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useScrollToReply;
|
||||||
@@ -23,10 +23,11 @@ export interface PostProps {
|
|||||||
roles?: Role[];
|
roles?: Role[];
|
||||||
showAllReplies?: boolean;
|
showAllReplies?: boolean;
|
||||||
showReplies?: boolean;
|
showReplies?: boolean;
|
||||||
|
targetReplyCid?: string;
|
||||||
threadNumber?: number;
|
threadNumber?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Post = ({ post, showAllReplies = false, showReplies = true }: PostProps) => {
|
export const Post = ({ post, showAllReplies = false, showReplies = true, targetReplyCid }: PostProps) => {
|
||||||
// Only subscribe to roles field to avoid rerenders from updatingState changes
|
// Only subscribe to roles field to avoid rerenders from updatingState changes
|
||||||
const roles = useSubplebbitField(post?.subplebbitAddress, (subplebbit) => subplebbit?.roles);
|
const roles = useSubplebbitField(post?.subplebbitAddress, (subplebbit) => subplebbit?.roles);
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
@@ -43,9 +44,9 @@ export const Post = ({ post, showAllReplies = false, showReplies = true }: PostP
|
|||||||
<div className={styles.thread}>
|
<div className={styles.thread}>
|
||||||
<div className={styles.postContainer}>
|
<div className={styles.postContainer}>
|
||||||
{isMobile ? (
|
{isMobile ? (
|
||||||
<PostMobile post={comment} roles={roles} showAllReplies={showAllReplies} showReplies={showReplies} />
|
<PostMobile post={comment} roles={roles} showAllReplies={showAllReplies} showReplies={showReplies} targetReplyCid={targetReplyCid} />
|
||||||
) : (
|
) : (
|
||||||
<PostDesktop post={comment} roles={roles} showAllReplies={showAllReplies} showReplies={showReplies} />
|
<PostDesktop post={comment} roles={roles} showAllReplies={showAllReplies} showReplies={showReplies} targetReplyCid={targetReplyCid} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,8 +86,9 @@ const PostPage = () => {
|
|||||||
const { error } = post || {};
|
const { error } = post || {};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!comment?.cid || comment.parentCid) return;
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}, []);
|
}, [comment?.cid, comment?.parentCid]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const boardIdentifier = params.boardIdentifier;
|
const boardIdentifier = params.boardIdentifier;
|
||||||
@@ -110,6 +112,8 @@ const PostPage = () => {
|
|||||||
const shouldShowPostError = post?.error && post?.replyCount > 0 && post?.replies?.length === 0;
|
const shouldShowPostError = post?.error && post?.replyCount > 0 && post?.replies?.length === 0;
|
||||||
const shouldShowSubplebbitError = subplebbitError?.message && !post?.cid;
|
const shouldShowSubplebbitError = subplebbitError?.message && !post?.cid;
|
||||||
|
|
||||||
|
const targetReplyCid = comment?.parentCid ? comment?.cid : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
{shouldShowPostError && (
|
{shouldShowPostError && (
|
||||||
@@ -117,7 +121,7 @@ const PostPage = () => {
|
|||||||
<ErrorDisplay error={error} />
|
<ErrorDisplay error={error} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Post post={post} showAllReplies={true} />
|
<Post post={post} showAllReplies={true} targetReplyCid={targetReplyCid} />
|
||||||
{shouldShowSubplebbitError && (
|
{shouldShowSubplebbitError && (
|
||||||
<div className={styles.error}>
|
<div className={styles.error}>
|
||||||
<ErrorDisplay error={subplebbitError} />
|
<ErrorDisplay error={subplebbitError} />
|
||||||
|
|||||||
Reference in New Issue
Block a user