import { useState } from 'react'; import { Link, useLocation, useParams } from 'react-router-dom'; import { Trans, useTranslation } from 'react-i18next'; import { Role, useAccount, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js'; import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils'; import { getFormattedDate } from '../../lib/utils/time-utils'; import { isPostPageView } from '../../lib/utils/view-utils'; import useCountLinksInReplies from '../../hooks/use-count-links-in-replies'; import useReplies from '../../hooks/use-replies'; import useStateString from '../../hooks/use-state-string'; import useWindowWidth from '../../hooks/use-window-width'; import styles from './post.module.css'; import CommentMedia from '../comment-media'; import { canEmbed } from '../embed'; import LoadingEllipsis from '../loading-ellipsis'; import Markdown from '../markdown'; import _ from 'lodash'; import { getDisplayMediaInfoType } from '../../lib/utils/media-utils'; interface PostProps { index?: number; post?: any; reply?: any; roles?: Role[]; showAllReplies?: boolean; openReplyModal?: (cid: string) => void; } const PostDesktop = ({ openReplyModal, post, roles, showAllReplies }: PostProps) => { const { t } = useTranslation(); const { author, cid, content, link, linkHeight, linkWidth, locked, pinned, postCid, replyCount, shortCid, state, subplebbitAddress, timestamp, title } = post || {}; const { address, displayName, shortAddress } = author || {}; const shortDisplayName = displayName?.trim().length > 20 ? displayName?.trim().slice(0, 20).trim() + '...' : displayName?.trim(); const authorRole = roles?.[address]?.role; const { isDescription, isRules } = post || {}; // custom properties, not from api const params = useParams(); const location = useLocation(); const isInPostPage = isPostPageView(location.pathname, params); const displayTitle = title && title.length > 75 ? title?.slice(0, 75) + '...' : title; const displayContent = content && !isInPostPage && content.length > 1000 ? content?.slice(0, 1000) + '(...)' : content; const commentMediaInfo = getCommentMediaInfo(post); const { type, url } = commentMediaInfo || {}; const embedUrl = url && new URL(url); const hasThumbnail = getHasThumbnail(commentMediaInfo, link); const [showThumbnail, setShowThumbnail] = useState(true); const replies = useReplies(post); const visiblelinksCount = useCountLinksInReplies(post, 5); const totallinksCount = useCountLinksInReplies(post); const repliesCount = pinned ? replyCount : replyCount - 5; const linksCount = pinned ? totallinksCount : totallinksCount - visiblelinksCount; const [menuBtnRotated, setMenuBtnRotated] = useState(false); // pending reply by account is not yet published const account = useAccount(); const accountShortAddress = account?.author?.shortAddress; return (

{!isInPostPage && ( )} {url && (
{t('link')}:{' '} {url.length > 30 ? url.slice(0, 30) + '...' : url} {' '} ({type && _.lowerCase(getDisplayMediaInfoType(type, t))}) {!showThumbnail && (type === 'iframe' || type === 'video' || type === 'audio') && ( {' '} [ setShowThumbnail(true)}> {t('close')} ] )} {showThumbnail && !hasThumbnail && embedUrl && canEmbed(embedUrl) && ( {' '} [ setShowThumbnail(false)}> {t('open')} ] )}
{(hasThumbnail || (!hasThumbnail && !showThumbnail)) && ( )}
)}
{title && {displayTitle} } {shortDisplayName || _.capitalize(t('anonymous'))} {authorRole && ` ## Board ${authorRole}`}{' '} {!(isDescription || isRules) && (u/{shortAddress || accountShortAddress}) } {getFormattedDate(timestamp)} {isDescription || isRules ? '' : ' '} {!(isDescription || isRules) && ( !cid && e.preventDefault()}> c/ {!cid ? ( {state === 'failed' ? 'Failed' : 'Pending'} ) : ( openReplyModal && openReplyModal(cid)}> {shortCid} )} )} {pinned && ( )} {locked && ( )} [{t('reply')}] setMenuBtnRotated(!menuBtnRotated)} style={{ transform: menuBtnRotated ? 'rotate(90deg)' : 'rotate(0deg)' }} > ▶
{!content &&
} {content && (
{content.length > 1000 && !isInPostPage && (
}} />
)}
)} {(replies.length > 5 || (pinned && replies.length > 0)) && !isInPostPage && ( {linksCount > 0 ? ( }} values={{ repliesCount, linksCount }} /> ) : ( }} values={{ repliesCount }} /> )} )} {!(pinned && !isInPostPage) && replies && (showAllReplies ? replies : replies.slice(-5)).map((reply, index) => (
))}
); }; const ReplyDesktop = ({ reply, roles, openReplyModal }: PostProps) => { const { t } = useTranslation(); const { author, cid, content, link, linkHeight, linkWidth, parentCid, pinned, postCid, shortCid, state, subplebbitAddress, timestamp } = reply || {}; const { address, displayName, shortAddress } = author || {}; const shortDisplayName = displayName?.trim().length > 20 ? displayName?.trim().slice(0, 20).trim() + '...' : displayName?.trim(); const authorRole = roles?.[address]?.role; const commentMediaInfo = getCommentMediaInfo(reply); const { type, url } = commentMediaInfo || {}; const embedUrl = url && new URL(url); const hasThumbnail = getHasThumbnail(commentMediaInfo, link); const [showThumbnail, setShowThumbnail] = useState(true); const isReplyingToReply = postCid !== parentCid; const [menuBtnRotated, setMenuBtnRotated] = useState(false); const stateString = useStateString(reply); const loadingString = stateString && (
{stateString !== 'Failed' ? : stateString}
); // pending reply by account is not yet published const account = useAccount(); const accountShortAddress = account?.author?.shortAddress; return (
{'>>'}
{shortDisplayName || _.capitalize(t('anonymous'))} {authorRole && ` ## Board ${authorRole}`}{' '} (u/{shortAddress || accountShortAddress}) {getFormattedDate(timestamp)} !cid && e.preventDefault()}> c/ {!cid ? ( {state === 'failed' ? 'Failed' : 'Pending'} ) : ( openReplyModal && openReplyModal(cid)}> {shortCid} )} {pinned && ( )} setMenuBtnRotated(!menuBtnRotated)} style={{ transform: menuBtnRotated ? 'rotate(90deg)' : 'rotate(0deg)' }} > ▶
{url && (
{t('link')}:{' '} {link.length > 30 ? link?.slice(0, 30) + '...' : link} {' '} ({type && _.lowerCase(getDisplayMediaInfoType(type, t))}) {!showThumbnail && (type === 'iframe' || type === 'video' || type === 'audio') && ( {' '} [ setShowThumbnail(true)}> {t('close')} ] )} {showThumbnail && !hasThumbnail && embedUrl && canEmbed(embedUrl) && ( {' '} [ setShowThumbnail(false)}> {t('open')} ] )}
{(hasThumbnail || (!hasThumbnail && !showThumbnail)) && ( )}
)} {content && (
{isReplyingToReply && ( <> {`c/${Plebbit.getShortCid(parentCid)}`}
)} {!cid && state === 'pending' && ( <>
{loadingString} )}
)}
); }; const PostMobile = ({ openReplyModal, post, roles, showAllReplies }: PostProps) => { const { t } = useTranslation(); const { author, cid, content, link, linkHeight, linkWidth, locked, pinned, replyCount, shortCid, state, subplebbitAddress, timestamp, title } = post || {}; const { address, displayName, shortAddress } = author || {}; const shortDisplayName = displayName?.trim().length > 20 ? displayName?.trim().slice(0, 20).trim() + '...' : displayName?.trim(); const authorRole = roles?.[address]?.role; const { isDescription, isRules } = post || {}; // custom properties, not from api const params = useParams(); const location = useLocation(); const isInPostPage = isPostPageView(location.pathname, params); const linksCount = useCountLinksInReplies(post); const displayTitle = title && title.length > 30 ? title?.slice(0, 30) + '(...)' : title; const displayContent = content && !isInPostPage && content.length > 1000 ? content?.slice(0, 1000) : content; const commentMediaInfo = getCommentMediaInfo(post); const hasThumbnail = getHasThumbnail(commentMediaInfo, link); const [showThumbnail, setShowThumbnail] = useState(true); const replies = useReplies(post); // pending reply by account is not yet published const account = useAccount(); const accountShortAddress = account?.author?.shortAddress; return (

... {shortDisplayName || _.capitalize(t('anonymous'))} {authorRole && ` ## Board ${authorRole}`}{' '} {!(isDescription || isRules) && (u/{shortAddress || accountShortAddress})} {pinned && ( )} {locked && ( )} {title && ( <>
{displayTitle} )}
{getFormattedDate(timestamp)}{' '} {!(isDescription || isRules) && ( !cid && e.preventDefault()}> c/ {!cid ? ( {state === 'failed' ? 'Failed' : 'Pending'} ) : ( openReplyModal && openReplyModal(cid)}> {shortCid} )} )}
{(hasThumbnail || link) && ( )} {content && (
{content.length > 1000 && !isInPostPage && (
}} />
)}
)}
{!isInPostPage && (
{replyCount > 0 && `${replyCount} Replies`} {linksCount > 0 && ` / ${linksCount} Links`} {t('view_thread')}
)}
{!(pinned && !isInPostPage) && replies && (showAllReplies ? replies : replies.slice(-5)).map((reply, index) => (
))}
); }; const ReplyMobile = ({ reply, roles, openReplyModal }: PostProps) => { const { t } = useTranslation(); const { author, content, cid, link, linkHeight, linkWidth, parentCid, pinned, postCid, shortCid, state, subplebbitAddress, timestamp } = reply || {}; const { address, displayName, shortAddress } = author || {}; const shortDisplayName = displayName?.trim().length > 20 ? displayName?.trim().slice(0, 20).trim() + '...' : displayName?.trim(); const authorRole = roles?.[address]?.role; const commentMediaInfo = getCommentMediaInfo(reply); const hasThumbnail = getHasThumbnail(commentMediaInfo, link); const [showThumbnail, setShowThumbnail] = useState(true); const isReplyingToReply = postCid !== parentCid; // pending reply by account is not yet published const account = useAccount(); const accountShortAddress = account?.author?.shortAddress; const stateString = useStateString(reply); const loadingString = stateString && (
{stateString !== 'Failed' ? : stateString}
); return (
... {shortDisplayName || _.capitalize(t('anonymous'))} {authorRole && ` ## Board ${authorRole}`}{' '} (u/{shortAddress || accountShortAddress}) {pinned && ( )} {getFormattedDate(timestamp)}{' '} !cid && e.preventDefault()}> c/ {!cid ? ( {state === 'failed' ? 'Failed' : 'Pending'} ) : ( openReplyModal && openReplyModal(cid)}> {shortCid} )}
{(hasThumbnail || link) && ( )} {content && (
{isReplyingToReply && ( <> {`c/${Plebbit.getShortCid(parentCid)}`}
)} {!cid && state === 'pending' && ( <>
{loadingString} )}
)}
); }; const Post = ({ post, showAllReplies = false, openReplyModal }: PostProps) => { const subplebbit = useSubplebbit({ subplebbitAddress: post?.subplebbitAddress }); const isMobile = useWindowWidth() < 640; return (
{isMobile ? ( ) : ( )}
); }; export default Post;