import { memo, useMemo, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Comment, Subplebbit, useAccount } from '@plebbit/plebbit-react-hooks'; import useSubplebbitsStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits'; import styles from '../home.module.css'; import usePopularPosts from '../../../hooks/use-popular-posts'; import usePopularThreadsOptionsStore from '../../../stores/use-popular-threads-options-store'; import { getCommentMediaInfo } from '../../../lib/utils/media-utils'; import { CatalogPostMedia } from '../../../components/catalog-row'; import LoadingEllipsis from '../../../components/loading-ellipsis'; import BoxModal from '../box-modal'; import { MultisubSubplebbit, useDefaultSubplebbits } from '../../../hooks/use-default-subplebbits'; import { getBoardPath } from '../../../lib/utils/route-utils'; import { removeMarkdown } from '../../../lib/utils/post-utils'; import { useStableSubplebbits } from '../../../hooks/use-stable-subplebbit'; interface PopularThreadProps { post: Comment; multisub: MultisubSubplebbit[]; } export const ContentPreview = ({ content, maxLength = 99 }: { content: string; maxLength?: number }) => { const plainText = removeMarkdown(content).trim().replaceAll(' ', '').replace(/\n\n/g, '\n').replaceAll('\n\n', ''); const truncatedText = plainText.length > maxLength ? `${plainText.substring(0, maxLength).trim()}...` : plainText; return truncatedText; }; // Memoize to prevent rerenders when parent rerenders due to updatingState const PopularThreadCard = memo( ({ post, multisub }: PopularThreadProps) => { const { cid, content, link, linkHeight, linkWidth, subplebbitAddress, thumbnailUrl, title } = post || {}; const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight); const defaultSubplebbits = useDefaultSubplebbits(); // Find the matching MultisubSubplebbit entry and get its title const multisubEntry = multisub.find((ms) => ms?.address === subplebbitAddress); const boardTitle = multisubEntry?.title?.replace(/^\/[^/]+\/\s*-\s*/, '') || ''; const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, defaultSubplebbits) : ''; return (
{boardTitle}
{title && ( <> {title.trim()} {content && ': '} )} {content && }
); }, // Custom equality: rerender if post.cid or multisub entry title changes (prevProps, nextProps) => { if (prevProps.post?.cid !== nextProps.post?.cid) return false; // Compare the relevant multisub entry for this post's subplebbitAddress const prevEntry = prevProps.multisub.find((ms) => ms?.address === prevProps.post?.subplebbitAddress); const nextEntry = nextProps.multisub.find((ms) => ms?.address === nextProps.post?.subplebbitAddress); return prevEntry?.title === nextEntry?.title; }, ); // Uses stable subplebbits hook to avoid re-renders from updatingState changes const PopularThreadsBox = ({ multisub, subplebbitAddresses }: { multisub: MultisubSubplebbit[]; subplebbitAddresses: string[] }) => { const { t } = useTranslation(); const { showWorksafeContentOnly, showNsfwContentOnly } = usePopularThreadsOptionsStore(); const account = useAccount(); const addSubplebbitToStore = useSubplebbitsStore((state) => state.addSubplebbitToStore); // Trigger fetching subplebbits (same as useSubplebbits does internally) useEffect(() => { if (!account || !subplebbitAddresses) return; for (const address of subplebbitAddresses) { addSubplebbitToStore(address, account).catch(() => {}); } }, [subplebbitAddresses?.toString(), account?.id]); // Use stable hook that only re-renders when actual content changes const subplebbits = useStableSubplebbits(subplebbitAddresses); const getFilteredSubplebbits = () => { if (showWorksafeContentOnly) { return subplebbits.filter((sub: Subplebbit) => { const multisubEntry = multisub.find((ms) => ms?.address === sub?.address); return multisubEntry ? !multisubEntry.nsfw : true; }); } if (showNsfwContentOnly) { return subplebbits.filter((sub: Subplebbit) => { const multisubEntry = multisub.find((ms) => ms?.address === sub?.address); return multisubEntry ? multisubEntry.nsfw : false; }); } return subplebbits; }; const filteredSubplebbits = useMemo(getFilteredSubplebbits, [subplebbits, showWorksafeContentOnly, showNsfwContentOnly, multisub]); const { popularPosts } = usePopularPosts(filteredSubplebbits); const isLoading = popularPosts.length === 0; return (

{t('popular_threads')}

{isLoading ? : popularPosts.map((post: any) => )}
); }; export default PopularThreadsBox;