refactor: migrate 5chan to the community hooks API (#1073)

* refactor(community-api): migrate 5chan to community hooks

* fix(review): address PR feedback

* fix(review): preserve legacy board context fallbacks

* fix(review): address latest bot feedback

* fix(review): use communityAddress in edit menu privileges
This commit is contained in:
Tommaso Casaburi
2026-03-13 13:25:10 +08:00
committed by GitHub
parent 9dc4d96d27
commit d7703953fb
105 changed files with 2659 additions and 1491 deletions
@@ -118,7 +118,7 @@ describe('ExternalNumberQuoteLink', () => {
comment: { cid: 'cid-77' },
isUnavailable: false,
route: '/fit/thread/cid-77',
subplebbitAddress: 'fit',
communityAddress: 'fit',
});
await act(async () => {
@@ -57,7 +57,7 @@ vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
useComment: ({ commentCid }: { commentCid?: string }) => (commentCid ? testState.comments[commentCid] : undefined),
}));
vi.mock('@bitsocialnet/bitsocial-react-hooks/dist/stores/subplebbits-pages', () => ({
vi.mock('@bitsocialnet/bitsocial-react-hooks/dist/stores/communities-pages', () => ({
default: (selector: (state: { comments: typeof testState.comments }) => unknown) =>
selector({
comments: testState.comments,
@@ -135,7 +135,7 @@ vi.mock('../external-number-quote-link', () => ({
let container: HTMLDivElement;
let root: Root;
const renderMarkdown = async (props: { content: string; postCid?: string; subplebbitAddress?: string; title?: string }, initialEntry = '/mu/thread/post-1') => {
const renderMarkdown = async (props: { content: string; postCid?: string; communityAddress?: string; title?: string }, initialEntry = '/mu/thread/post-1') => {
await act(async () => {
root.render(createElement(MemoryRouter, { initialEntries: [initialEntry] }, createElement(Markdown, props)));
});
@@ -198,7 +198,7 @@ describe('Markdown', () => {
await renderMarkdown({
content: '>>42',
postCid: 'comment-42',
subplebbitAddress: 'music-posting.eth',
communityAddress: 'music-posting.eth',
});
const quotePreview = container.querySelector('[data-testid="reply-quote-preview"]');
@@ -211,7 +211,7 @@ describe('Markdown', () => {
it('renders lazy same-board and cross-board number quotes when the cid is not cached', async () => {
await renderMarkdown({
content: '>>42 >>>/fit/77',
subplebbitAddress: 'music-posting.eth',
communityAddress: 'music-posting.eth',
});
const lazyLinks = Array.from(container.querySelectorAll('[data-testid="external-number-quote-link"]'));
+15 -15
View File
@@ -13,7 +13,7 @@ import { is5chanLink, transform5chanLinkToInternal, isValidCrossboardPattern } f
import { CROSSBOARD_NUMBER_QUOTE_TOKEN_REGEX, type ExternalQuoteReference } from '../../lib/utils/external-quote-utils';
import { isUnavailableQuoteTarget } from '../../lib/utils/quote-link-utils';
import usePostNumberStore from '../../stores/use-post-number-store';
import useSubplebbitsPagesStore from '@bitsocialnet/bitsocial-react-hooks/dist/stores/subplebbits-pages';
import useCommunitiesPagesStore from '@bitsocialnet/bitsocial-react-hooks/dist/stores/communities-pages';
import { useComment } from '@bitsocialnet/bitsocial-react-hooks';
import ReplyQuotePreview from '../reply-quote-preview';
import ExternalNumberQuoteLink from './external-number-quote-link';
@@ -240,11 +240,11 @@ function tokenize(text: string): Token[] {
interface RenderContext {
isInCatalogView: boolean;
postCid?: string;
subplebbitAddress?: string;
communityAddress?: string;
}
function renderTokens(tokens: Token[], context: RenderContext): React.ReactNode[] {
const { isInCatalogView, postCid, subplebbitAddress } = context;
const { isInCatalogView, postCid, communityAddress } = context;
return tokens.map((token, i) => {
switch (token.type) {
@@ -261,12 +261,12 @@ function renderTokens(tokens: Token[], context: RenderContext): React.ReactNode[
</ContentLinkEmbed>
);
}
return <React.Fragment key={i}>{renderAnchorLink(href, href, postCid, subplebbitAddress)}</React.Fragment>;
return <React.Fragment key={i}>{renderAnchorLink(href, href, postCid, communityAddress)}</React.Fragment>;
}
case 'quoteLink':
return (
<span key={i} className={styles.inlineQuoteLink}>
<NumberQuoteLink number={token.number} threadPostCid={postCid} subplebbitAddress={subplebbitAddress} />
<NumberQuoteLink number={token.number} threadPostCid={postCid} communityAddress={communityAddress} />
</span>
);
case 'crossBoardNumberQuoteLink':
@@ -295,12 +295,12 @@ interface MarkdownProps {
content: string;
title?: string;
postCid?: string;
subplebbitAddress?: string;
communityAddress?: string;
}
const NumberQuoteLink = ({ number, threadPostCid, subplebbitAddress }: { number: number; threadPostCid?: string; subplebbitAddress?: string }) => {
const cid = usePostNumberStore((state) => (subplebbitAddress ? state.numberToCid[subplebbitAddress]?.[number] : undefined));
const commentFromStore = useSubplebbitsPagesStore((state) => (cid ? state.comments[cid] : undefined));
const NumberQuoteLink = ({ number, threadPostCid, communityAddress }: { number: number; threadPostCid?: string; communityAddress?: string }) => {
const cid = usePostNumberStore((state) => (communityAddress ? state.numberToCid[communityAddress]?.[number] : undefined));
const commentFromStore = useCommunitiesPagesStore((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);
@@ -311,14 +311,14 @@ const NumberQuoteLink = ({ number, threadPostCid, subplebbitAddress }: { number:
);
}
if (!cid && subplebbitAddress) {
if (!cid && communityAddress) {
return (
<ExternalNumberQuoteLink
reference={{
kind: 'same-board',
number,
raw: `>>${number}`,
subplebbitAddress,
subplebbitAddress: communityAddress,
}}
/>
);
@@ -327,7 +327,7 @@ const NumberQuoteLink = ({ number, threadPostCid, subplebbitAddress }: { number:
return <ReplyQuotePreview isQuotelinkReply={true} quotelinkReply={comment} quotelinkNumber={number} isOP={isOP} showTrailingBreak={false} />;
};
const renderAnchorLink = (children: React.ReactNode, href: string, threadPostCid?: string, subplebbitAddress?: string) => {
const renderAnchorLink = (children: React.ReactNode, href: string, threadPostCid?: string, communityAddress?: string) => {
if (!href) {
return <span>{children}</span>;
}
@@ -380,7 +380,7 @@ const renderAnchorLink = (children: React.ReactNode, href: string, threadPostCid
);
};
const Markdown = ({ content, title, postCid, subplebbitAddress }: MarkdownProps) => {
const Markdown = ({ content, title, postCid, communityAddress }: MarkdownProps) => {
const location = useLocation();
const params = useParams();
const isInCatalogView = isCatalogView(location.pathname, params);
@@ -400,7 +400,7 @@ const Markdown = ({ content, title, postCid, subplebbitAddress }: MarkdownProps)
const isGreentext = /^>[^>]/.test(line) || line === '>';
const tokens = tokenize(line);
const lineElements = renderTokens(tokens, { isInCatalogView, postCid, subplebbitAddress });
const lineElements = renderTokens(tokens, { isInCatalogView, postCid, communityAddress });
if (isGreentext) {
elements.push(
@@ -414,7 +414,7 @@ const Markdown = ({ content, title, postCid, subplebbitAddress }: MarkdownProps)
});
return elements;
}, [content, isInCatalogView, postCid, subplebbitAddress]);
}, [content, isInCatalogView, postCid, communityAddress]);
return (
<span className={styles.markdown}>