mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix: show OP badge for number-based quote links
`NumberQuoteLink` now receives thread context from `Markdown` and forwards `isOP` to `ReplyQuotePreview` when the resolved quote CID matches the thread `postCid`. This makes `>>14` render as `>>14 (OP)` consistently with existing `quotedCids` links.
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
import { Fragment, useState } from 'react';
|
import { Fragment, useMemo, useState } from 'react';
|
||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import { Comment, useComment } from '@plebbit/plebbit-react-hooks';
|
import { Comment, useComment } from '@plebbit/plebbit-react-hooks';
|
||||||
import useSubplebbitsPagesStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits-pages';
|
import useSubplebbitsPagesStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits-pages';
|
||||||
|
import usePostNumberStore from '../../stores/use-post-number-store';
|
||||||
import Plebbit from '@plebbit/plebbit-js';
|
import Plebbit from '@plebbit/plebbit-js';
|
||||||
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||||
import { isPostPageView } from '../../lib/utils/view-utils';
|
import { isPostPageView } from '../../lib/utils/view-utils';
|
||||||
@@ -52,6 +53,23 @@ const CommentContent = ({ comment: post }: { comment: Comment }) => {
|
|||||||
const isReply = !!parentCid;
|
const isReply = !!parentCid;
|
||||||
const isReplyingToReply = isReply && parentCid !== postCid;
|
const isReplyingToReply = isReply && parentCid !== postCid;
|
||||||
|
|
||||||
|
const contentNumbers = useMemo(() => {
|
||||||
|
if (!content) return new Set<number>();
|
||||||
|
const matches = content.matchAll(/(?<![>/\w])>>(\d+)(?![\d/])/g);
|
||||||
|
return new Set([...matches].map((m) => parseInt(m[1], 10)));
|
||||||
|
}, [content]);
|
||||||
|
|
||||||
|
const cidToNumber = usePostNumberStore((state) => state.cidToNumber);
|
||||||
|
const filteredQuotedCids = useMemo(() => {
|
||||||
|
if (!quotedCids?.length) return [];
|
||||||
|
return quotedCids.filter((cid: string) => {
|
||||||
|
const num = cidToNumber[cid];
|
||||||
|
return num === undefined || !contentNumbers.has(num);
|
||||||
|
});
|
||||||
|
}, [quotedCids, cidToNumber, contentNumbers]);
|
||||||
|
|
||||||
|
const shouldShowReplyingToReply = isReplyingToReply && (parentCid ? !contentNumbers.has(cidToNumber[parentCid] ?? -1) : true);
|
||||||
|
|
||||||
const stateString = useStateString(post);
|
const stateString = useStateString(post);
|
||||||
|
|
||||||
const loadingString = <div className={styles.stateString}>{stateString !== 'Failed' ? <LoadingEllipsis string={stateString || t('loading')} /> : stateString}</div>;
|
const loadingString = <div className={styles.stateString}>{stateString !== 'Failed' ? <LoadingEllipsis string={stateString || t('loading')} /> : stateString}</div>;
|
||||||
@@ -61,9 +79,9 @@ const CommentContent = ({ comment: post }: { comment: Comment }) => {
|
|||||||
{isReply &&
|
{isReply &&
|
||||||
state !== 'failed' &&
|
state !== 'failed' &&
|
||||||
!(deleted || removed) &&
|
!(deleted || removed) &&
|
||||||
(quotedCids?.length > 0
|
(filteredQuotedCids.length > 0
|
||||||
? quotedCids.map((cid: string) => <QuotedCidLink key={cid} cid={cid} postCid={postCid} />)
|
? filteredQuotedCids.map((cid: string) => <QuotedCidLink key={cid} cid={cid} postCid={postCid} />)
|
||||||
: isReplyingToReply && <ReplyQuotePreview isQuotelinkReply={true} quotelinkReply={quotelinkReply} />)}
|
: shouldShowReplyingToReply && <ReplyQuotePreview isQuotelinkReply={true} quotelinkReply={quotelinkReply} />)}
|
||||||
{removed ? (
|
{removed ? (
|
||||||
reason ? (
|
reason ? (
|
||||||
<>
|
<>
|
||||||
@@ -86,7 +104,7 @@ const CommentContent = ({ comment: post }: { comment: Comment }) => {
|
|||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{!showOriginal && <Markdown content={displayContent} />}
|
{!showOriginal && <Markdown content={displayContent} postCid={postCid} />}
|
||||||
{pendingApproval && (
|
{pendingApproval && (
|
||||||
<>
|
<>
|
||||||
<br />
|
<br />
|
||||||
@@ -102,7 +120,7 @@ const CommentContent = ({ comment: post }: { comment: Comment }) => {
|
|||||||
)}
|
)}
|
||||||
{edit && original?.content !== content && (
|
{edit && original?.content !== content && (
|
||||||
<span className={styles.editedInfo}>
|
<span className={styles.editedInfo}>
|
||||||
{showOriginal && <Markdown content={original?.content} />}
|
{showOriginal && <Markdown content={original?.content} postCid={postCid} />}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<Trans
|
<Trans
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ import styles from './markdown.module.css';
|
|||||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||||
import { canEmbed } from '../embed';
|
import { canEmbed } from '../embed';
|
||||||
import { is5chanLink, transform5chanLinkToInternal, preprocess5chanPatterns } from '../../lib/utils/url-utils';
|
import { is5chanLink, transform5chanLinkToInternal, preprocess5chanPatterns } from '../../lib/utils/url-utils';
|
||||||
|
import usePostNumberStore from '../../stores/use-post-number-store';
|
||||||
|
import useSubplebbitsPagesStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits-pages';
|
||||||
|
import { useComment } from '@plebbit/plebbit-react-hooks';
|
||||||
|
import ReplyQuotePreview from '../reply-quote-preview';
|
||||||
|
|
||||||
interface ContentLinkEmbedProps {
|
interface ContentLinkEmbedProps {
|
||||||
children: any;
|
children: any;
|
||||||
@@ -159,13 +163,36 @@ const spoilerTransform = () => (tree: any) => {
|
|||||||
interface MarkdownProps {
|
interface MarkdownProps {
|
||||||
content: string;
|
content: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
postCid?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderAnchorLink = (children: React.ReactNode, href: string) => {
|
const NUMBER_QUOTE_HREF_REGEX = /^#q-(\d+)$/;
|
||||||
|
|
||||||
|
const NumberQuoteLink = ({ number, threadPostCid }: { number: number; threadPostCid?: string }) => {
|
||||||
|
const cid = usePostNumberStore((state) => state.numberToCid[number]);
|
||||||
|
const commentFromStore = useSubplebbitsPagesStore((state) => (cid ? state.comments[cid] : undefined));
|
||||||
|
const commentFromHook = useComment({ commentCid: cid, onlyIfCached: true });
|
||||||
|
const comment = commentFromHook?.number !== undefined ? commentFromHook : commentFromStore;
|
||||||
|
const isOP = Boolean(threadPostCid && cid === threadPostCid);
|
||||||
|
|
||||||
|
if (!comment) {
|
||||||
|
return <span>{`>>${number}`}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ReplyQuotePreview isQuotelinkReply={true} quotelinkReply={comment} isOP={isOP} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderAnchorLink = (children: React.ReactNode, href: string, threadPostCid?: string) => {
|
||||||
if (!href) {
|
if (!href) {
|
||||||
return <span>{children}</span>;
|
return <span>{children}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const numberQuoteMatch = href.match(NUMBER_QUOTE_HREF_REGEX);
|
||||||
|
if (numberQuoteMatch) {
|
||||||
|
const number = parseInt(numberQuoteMatch[1], 10);
|
||||||
|
return <NumberQuoteLink number={number} threadPostCid={threadPostCid} />;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if this is a valid 5chan link that should be handled internally
|
// Check if this is a valid 5chan link that should be handled internally
|
||||||
if (is5chanLink(href)) {
|
if (is5chanLink(href)) {
|
||||||
const internalPath = transform5chanLinkToInternal(href);
|
const internalPath = transform5chanLinkToInternal(href);
|
||||||
@@ -215,7 +242,7 @@ const renderAnchorLink = (children: React.ReactNode, href: string) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Markdown = ({ content, title }: MarkdownProps) => {
|
const Markdown = ({ content, title, postCid }: MarkdownProps) => {
|
||||||
const remarkPlugins: any[] = [[supersub]];
|
const remarkPlugins: any[] = [[supersub]];
|
||||||
|
|
||||||
if (content && content.length <= MAX_LENGTH_FOR_GFM) {
|
if (content && content.length <= MAX_LENGTH_FOR_GFM) {
|
||||||
@@ -285,10 +312,10 @@ const Markdown = ({ content, title }: MarkdownProps) => {
|
|||||||
console.debug('Invalid URL:', href);
|
console.debug('Invalid URL:', href);
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderAnchorLink(children, href);
|
return renderAnchorLink(children, href, postCid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderAnchorLink(children, href || '');
|
return renderAnchorLink(children, href || '', postCid);
|
||||||
},
|
},
|
||||||
} as ExtendedComponents
|
} as ExtendedComponents
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user