import { useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { Link } from 'react-router-dom'; import { Comment, useAccount } from '@plebbit/plebbit-react-hooks'; import { useFloating, offset, shift, size, autoUpdate, Placement } from '@floating-ui/react'; import useIsMobile from '../../hooks/use-is-mobile'; import styles from '../../views/post/post.module.css'; import { Post } from '../../views/post'; interface ReplyQuotePreviewProps { isBacklinkReply?: boolean; backlinkReply?: Comment; isQuotelinkReply?: boolean; quotelinkReply?: Comment; } const handleQuoteHover = (cid: string, onElementOutOfView: () => void) => { const targetElements = document.querySelectorAll(`[data-cid="${cid}"]`); if (targetElements.length === 0) return; const isInViewport = (element: HTMLElement) => { const bounding = element.getBoundingClientRect(); return ( bounding.top >= 0 && bounding.left >= 0 && bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) && bounding.right <= (window.innerWidth || document.documentElement.clientWidth) ); }; let anyInView = false; targetElements.forEach((element) => { const htmlElement = element as HTMLElement; if (isInViewport(htmlElement)) { htmlElement.classList.add('highlight'); anyInView = true; } else { htmlElement.classList.remove('highlight'); } }); if (!anyInView) { onElementOutOfView(); } }; const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, isQuotelinkReply }: ReplyQuotePreviewProps) => { const [hoveredCid, setHoveredCid] = useState(null); const [outOfViewCid, setOutOfViewCid] = useState(null); const placementRef = useRef('right'); const availableWidthRef = useRef(0); const { refs, floatingStyles, update } = useFloating({ placement: placementRef.current, middleware: [ shift({ padding: 10 }), offset({ mainAxis: placementRef.current === 'right' ? 8 : 4 }), size({ apply({ availableWidth, elements }) { availableWidthRef.current = availableWidth; if (availableWidth >= 250) { elements.floating.style.maxWidth = `${availableWidth - 12}px`; } else if (placementRef.current === 'right') { placementRef.current = 'left'; } }, }), ], whileElementsMounted: autoUpdate, }); useEffect(() => { const handleResize = () => { const availableWidth = availableWidthRef.current; if (availableWidth >= 250) { placementRef.current = 'right'; } else { placementRef.current = 'left'; } update(); }; window.addEventListener('resize', handleResize); return () => { window.removeEventListener('resize', handleResize); }; }, [update]); const handleMouseOver = (cid: string | undefined) => { if (!cid) return; handleQuoteHover(cid, () => setOutOfViewCid(cid)); setHoveredCid(cid); }; const handleMouseLeave = (cid: string | null) => { if (cid) { const targetElements = document.querySelectorAll(`[data-cid="${cid}"]`); targetElements.forEach((element) => { element.classList.remove('highlight'); }); } setHoveredCid(null); setOutOfViewCid(null); }; const replyBacklink = ( <> handleMouseOver(backlinkReply?.cid)} onMouseLeave={() => handleMouseLeave(backlinkReply?.cid)} > c/{backlinkReply?.shortCid} {hoveredCid === backlinkReply?.cid && outOfViewCid === backlinkReply?.cid && createPortal(
, document.body, )} ); const account = useAccount(); const replyQuotelink = ( <> handleMouseOver(quotelinkReply?.cid)} onMouseLeave={() => handleMouseLeave(quotelinkReply?.cid)} > {`c/${quotelinkReply?.shortCid}`} {quotelinkReply?.author?.address === account?.author?.address && ' (You)'}
{hoveredCid === quotelinkReply?.cid && outOfViewCid === quotelinkReply?.cid && createPortal(
, document.body, )} ); return isBacklinkReply ? replyBacklink : isQuotelinkReply && replyQuotelink; }; const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, isQuotelinkReply }: ReplyQuotePreviewProps) => { const [hoveredCid, setHoveredCid] = useState(null); const [outOfViewCid, setOutOfViewCid] = useState(null); const { refs, floatingStyles, update } = useFloating({ placement: 'bottom', middleware: [shift({ padding: isBacklinkReply ? 5 : 10 })], whileElementsMounted: autoUpdate, }); useEffect(() => { window.addEventListener('resize', () => update()); return () => { window.removeEventListener('resize', () => update()); }; }, [update]); const handleMouseOver = (cid: string | undefined) => { if (!cid) return; handleQuoteHover(cid, () => setOutOfViewCid(cid)); setHoveredCid(cid); }; const handleMouseLeave = (cid: string | null) => { if (cid) { const targetElements = document.querySelectorAll(`[data-cid="${cid}"]`); targetElements.forEach((element) => { element.classList.remove('highlight'); }); } setHoveredCid(null); setOutOfViewCid(null); }; const replyBacklink = ( <> handleMouseOver(backlinkReply?.cid)} onMouseLeave={() => handleMouseLeave(backlinkReply?.cid)} > c/{backlinkReply?.shortCid}{' '} # {hoveredCid === backlinkReply?.cid && outOfViewCid === backlinkReply?.cid && createPortal(
, document.body, )} ); const account = useAccount(); const replyQuotelink = ( <> handleMouseOver(quotelinkReply?.cid)} onMouseLeave={() => handleMouseLeave(quotelinkReply?.cid)} > c/{quotelinkReply?.shortCid} {quotelinkReply?.author?.address === account?.author?.address && ' (You)'} {' '} #
{hoveredCid === quotelinkReply?.cid && outOfViewCid === quotelinkReply?.cid && createPortal(
, document.body, )} ); return isBacklinkReply ? replyBacklink : isQuotelinkReply && replyQuotelink; }; const ReplyQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, isQuotelinkReply }: ReplyQuotePreviewProps) => { const isMobile = useIsMobile(); return isMobile ? ( ) : ( ); }; export default ReplyQuotePreview;