mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat(post): add backlinks for quotedCids
This commit is contained in:
@@ -73,6 +73,7 @@ const PostInfo = ({
|
|||||||
isPublishing,
|
isPublishing,
|
||||||
onApprove,
|
onApprove,
|
||||||
onReject,
|
onReject,
|
||||||
|
quotedByMap,
|
||||||
}: PostProps) => {
|
}: PostProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { author, cid, deleted, locked, pinned, parentCid, postCid, reason, removed, state, subplebbitAddress, timestamp } = post || {};
|
const { author, cid, deleted, locked, pinned, parentCid, postCid, reason, removed, state, subplebbitAddress, timestamp } = post || {};
|
||||||
@@ -425,6 +426,16 @@ const PostInfo = ({
|
|||||||
reply?.cid &&
|
reply?.cid &&
|
||||||
!(reply?.deleted || reply?.removed) && <ReplyQuotePreview key={index} isBacklinkReply={true} backlinkReply={reply} />,
|
!(reply?.deleted || reply?.removed) && <ReplyQuotePreview key={index} isBacklinkReply={true} backlinkReply={reply} />,
|
||||||
)}
|
)}
|
||||||
|
{cid &&
|
||||||
|
parentCid &&
|
||||||
|
quotedByMap
|
||||||
|
?.get(cid)
|
||||||
|
?.map(
|
||||||
|
(reply: Comment, index: number) =>
|
||||||
|
reply?.parentCid !== cid &&
|
||||||
|
reply?.cid &&
|
||||||
|
!(reply?.deleted || reply?.removed) && <ReplyQuotePreview key={`qb-${index}`} isBacklinkReply={true} backlinkReply={reply} />,
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -527,7 +538,7 @@ const PostMedia = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Reply = ({ postReplyCount, reply, roles, threadNumber }: PostProps) => {
|
const Reply = ({ postReplyCount, reply, roles, threadNumber, quotedByMap }: PostProps) => {
|
||||||
let post = reply;
|
let post = reply;
|
||||||
// handle pending mod or author edit
|
// handle pending mod or author edit
|
||||||
const { editedComment } = useEditedComment({ comment: reply });
|
const { editedComment } = useEditedComment({ comment: reply });
|
||||||
@@ -555,7 +566,7 @@ const Reply = ({ postReplyCount, reply, roles, threadNumber }: PostProps) => {
|
|||||||
<div className={styles.replyDesktop}>
|
<div className={styles.replyDesktop}>
|
||||||
<div className={styles.sideArrows}>{'>>'}</div>
|
<div className={styles.sideArrows}>{'>>'}</div>
|
||||||
<div className={`${styles.reply} ${isRouteLinkToReply && styles.highlight}`} data-cid={cid} data-author-address={author?.shortAddress} data-post-cid={postCid}>
|
<div className={`${styles.reply} ${isRouteLinkToReply && styles.highlight}`} data-cid={cid} data-author-address={author?.shortAddress} data-post-cid={postCid}>
|
||||||
<PostInfo post={post} postReplyCount={postReplyCount} roles={roles} isHidden={hidden} threadNumber={threadNumber} />
|
<PostInfo post={post} postReplyCount={postReplyCount} roles={roles} isHidden={hidden} threadNumber={threadNumber} quotedByMap={quotedByMap} />
|
||||||
{link && !hidden && !(deleted || removed) && isValidURL(link) && (
|
{link && !hidden && !(deleted || removed) && isValidURL(link) && (
|
||||||
<PostMedia
|
<PostMedia
|
||||||
commentMediaInfo={commentMediaInfo}
|
commentMediaInfo={commentMediaInfo}
|
||||||
@@ -622,6 +633,22 @@ const PostDesktop = ({
|
|||||||
// 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(() => (replies || []).filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))), [replies]);
|
||||||
|
|
||||||
|
// Compute quotedByMap: map each quoted CID to array of replies that quote it
|
||||||
|
const quotedByMap = useMemo(() => {
|
||||||
|
const map = new Map<string, Comment[]>();
|
||||||
|
for (const reply of filteredReplies) {
|
||||||
|
const quotedCids = reply.quotedCids;
|
||||||
|
if (quotedCids?.length) {
|
||||||
|
for (const quotedCid of quotedCids) {
|
||||||
|
const arr = map.get(quotedCid);
|
||||||
|
if (arr) arr.push(reply);
|
||||||
|
else map.set(quotedCid, [reply]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [filteredReplies]);
|
||||||
|
|
||||||
// Virtuoso scroll position management for infinite replies
|
// Virtuoso scroll position management for infinite replies
|
||||||
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
|
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
|
||||||
const virtuosoStateKey = `replies-desktop-${cid}`;
|
const virtuosoStateKey = `replies-desktop-${cid}`;
|
||||||
@@ -747,7 +774,7 @@ const PostDesktop = ({
|
|||||||
data={filteredReplies}
|
data={filteredReplies}
|
||||||
itemContent={(index, reply) => (
|
itemContent={(index, reply) => (
|
||||||
<div className={styles.replyContainer}>
|
<div className={styles.replyContainer}>
|
||||||
<Reply reply={reply} roles={roles} postReplyCount={replyCount} threadNumber={post?.number} />
|
<Reply reply={reply} roles={roles} postReplyCount={replyCount} threadNumber={post?.number} quotedByMap={quotedByMap} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
useWindowScroll={true}
|
useWindowScroll={true}
|
||||||
@@ -766,7 +793,7 @@ const PostDesktop = ({
|
|||||||
!hasMore &&
|
!hasMore &&
|
||||||
filteredReplies.map((reply, index) => (
|
filteredReplies.map((reply, index) => (
|
||||||
<div key={index} className={styles.replyContainer}>
|
<div key={index} className={styles.replyContainer}>
|
||||||
<Reply reply={reply} roles={roles} postReplyCount={replyCount} threadNumber={post?.number} />
|
<Reply reply={reply} roles={roles} postReplyCount={replyCount} threadNumber={post?.number} quotedByMap={quotedByMap} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{/* Non-virtualized rendering for board view (last 5 replies or show omitted) */}
|
{/* Non-virtualized rendering for board view (last 5 replies or show omitted) */}
|
||||||
@@ -778,7 +805,7 @@ const PostDesktop = ({
|
|||||||
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}>
|
||||||
<Reply reply={reply} roles={roles} postReplyCount={replyCount} threadNumber={post?.number} />
|
<Reply reply={reply} roles={roles} postReplyCount={replyCount} threadNumber={post?.number} quotedByMap={quotedByMap} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -377,14 +377,14 @@ const PostMediaContent = ({ post, link }: { post: any; link: string }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ReplyBacklinks = ({ post }: 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 { replies } = useReplies({ comment: post, flat: true, accountComments: { newerThan: Infinity } });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
cid &&
|
cid &&
|
||||||
parentCid &&
|
parentCid &&
|
||||||
replies.length > 0 && (
|
(replies.length > 0 || quotedByMap?.get(cid)?.length) && (
|
||||||
<div className={styles.mobileReplyBacklinks}>
|
<div className={styles.mobileReplyBacklinks}>
|
||||||
{replies.map(
|
{replies.map(
|
||||||
(reply: Comment, index: number) =>
|
(reply: Comment, index: number) =>
|
||||||
@@ -392,12 +392,20 @@ const ReplyBacklinks = ({ post }: PostProps) => {
|
|||||||
reply?.cid &&
|
reply?.cid &&
|
||||||
!(reply?.deleted || reply?.removed) && <ReplyQuotePreview key={index} isBacklinkReply={true} backlinkReply={reply} />,
|
!(reply?.deleted || reply?.removed) && <ReplyQuotePreview key={index} isBacklinkReply={true} backlinkReply={reply} />,
|
||||||
)}
|
)}
|
||||||
|
{quotedByMap
|
||||||
|
?.get(cid)
|
||||||
|
?.map(
|
||||||
|
(reply: Comment, index: number) =>
|
||||||
|
reply?.parentCid !== cid &&
|
||||||
|
reply?.cid &&
|
||||||
|
!(reply?.deleted || reply?.removed) && <ReplyQuotePreview key={`qb-${index}`} isBacklinkReply={true} backlinkReply={reply} />,
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Reply = ({ postReplyCount, reply, roles, threadNumber }: PostProps) => {
|
const Reply = ({ postReplyCount, reply, roles, threadNumber, quotedByMap }: PostProps) => {
|
||||||
let post = reply;
|
let post = reply;
|
||||||
// handle pending mod or author edit
|
// handle pending mod or author edit
|
||||||
const { editedComment } = useEditedComment({ comment: reply });
|
const { editedComment } = useEditedComment({ comment: reply });
|
||||||
@@ -423,7 +431,7 @@ const Reply = ({ postReplyCount, reply, roles, threadNumber }: PostProps) => {
|
|||||||
>
|
>
|
||||||
<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={reply} />
|
<ReplyBacklinks post={reply} quotedByMap={quotedByMap} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -463,6 +471,22 @@ 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(() => (replies || []).filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))), [replies]);
|
const filteredReplies = useMemo(() => (replies || []).filter((reply) => !(reply.deleted && (reply.replyCount === 0 || !reply.replyCount))), [replies]);
|
||||||
|
|
||||||
|
// Compute quotedByMap: map each quoted CID to array of replies that quote it
|
||||||
|
const quotedByMap = useMemo(() => {
|
||||||
|
const map = new Map<string, Comment[]>();
|
||||||
|
for (const reply of filteredReplies) {
|
||||||
|
const quotedCids = reply.quotedCids;
|
||||||
|
if (quotedCids?.length) {
|
||||||
|
for (const quotedCid of quotedCids) {
|
||||||
|
const arr = map.get(quotedCid);
|
||||||
|
if (arr) arr.push(reply);
|
||||||
|
else map.set(quotedCid, [reply]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [filteredReplies]);
|
||||||
|
|
||||||
// Virtuoso scroll position management for infinite replies
|
// Virtuoso scroll position management for infinite replies
|
||||||
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
|
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
|
||||||
const virtuosoStateKey = `replies-mobile-${cid}`;
|
const virtuosoStateKey = `replies-mobile-${cid}`;
|
||||||
@@ -578,7 +602,7 @@ 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} />
|
<Reply postReplyCount={replyCount} reply={reply} roles={roles} threadNumber={post?.number} quotedByMap={quotedByMap} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
useWindowScroll={true}
|
useWindowScroll={true}
|
||||||
@@ -597,7 +621,7 @@ const PostMobile = ({
|
|||||||
!hasMore &&
|
!hasMore &&
|
||||||
filteredReplies.map((reply, index) => (
|
filteredReplies.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} />
|
<Reply postReplyCount={replyCount} reply={reply} roles={roles} threadNumber={post?.number} quotedByMap={quotedByMap} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{/* Non-virtualized rendering for board view (last 5 replies) */}
|
{/* Non-virtualized rendering for board view (last 5 replies) */}
|
||||||
@@ -608,7 +632,7 @@ const PostMobile = ({
|
|||||||
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}>
|
||||||
<Reply postReplyCount={replyCount} reply={reply} roles={roles} threadNumber={post?.number} />
|
<Reply postReplyCount={replyCount} reply={reply} roles={roles} threadNumber={post?.number} quotedByMap={quotedByMap} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export interface PostProps {
|
|||||||
isPublishing?: boolean;
|
isPublishing?: boolean;
|
||||||
onApprove?: () => void;
|
onApprove?: () => void;
|
||||||
onReject?: () => void;
|
onReject?: () => void;
|
||||||
|
quotedByMap?: Map<string, Comment[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Post = ({
|
export const Post = ({
|
||||||
|
|||||||
Reference in New Issue
Block a user