feat: highlight reply if visible, render it as floating preview if not visible

This commit is contained in:
Tom (plebeius.eth)
2024-06-07 15:26:46 +02:00
parent 81972c9c85
commit fc21d876ff
4 changed files with 87 additions and 8 deletions
@@ -287,7 +287,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
<div key={index} className={styles.replyContainer} ref={(el) => (replyRefs.current[index] = el)}>
<div className={styles.replyDesktop}>
<div className={styles.sideArrows}>{'>>'}</div>
<div className={`${styles.reply} ${isRouteLinkToReply && styles.highlight}`}>
<div className={`${styles.reply} ${isRouteLinkToReply && styles.highlight}`} id={reply?.cid}>
<PostInfo openReplyModal={openReplyModal} post={reply} roles={roles} />
{reply.link && isValidURL(reply.link) && <PostMedia post={reply} />}
{reply.content && <PostMessage post={reply} />}
@@ -232,7 +232,7 @@ const PostMobile = ({ openReplyModal, post, roles, showAllReplies, showReplies =
<div key={index} className={styles.replyContainer} ref={(el) => (replyRefs.current[index] = el)}>
<div className={styles.replyMobile}>
<div className={styles.reply}>
<div className={`${styles.replyContainer} ${isRouteLinkToReply && styles.highlight}`}>
<div className={`${styles.replyContainer} ${isRouteLinkToReply && styles.highlight}`} id={reply?.cid}>
<PostInfoAndMedia openReplyModal={openReplyModal} post={reply} roles={roles} />
{reply.content && <PostMessageMobile post={reply} />}
<ReplyBacklinks post={reply} />
@@ -15,8 +15,32 @@ interface ReplyQuotePreviewProps {
quotelinkReply?: Comment;
}
const handleQuoteHover = (cid: string, onElementOutOfView: () => void) => {
const targetElement = document.getElementById(cid);
if (!targetElement) 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)
);
};
if (isInViewport(targetElement)) {
targetElement.classList.add('highlight');
} else {
targetElement.classList.remove('highlight');
onElementOutOfView();
}
};
const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, isQuotelinkReply }: ReplyQuotePreviewProps) => {
const [hoveredCid, setHoveredCid] = useState<string | null>(null);
const [outOfViewCid, setOutOfViewCid] = useState<string | null>(null);
const placementRef = useRef<Placement>('right');
const availableWidthRef = useRef<number>(0);
@@ -56,18 +80,37 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
};
}, [update]);
const handleMouseOver = (cid: string | undefined) => {
if (!cid) return;
handleQuoteHover(cid, () => setOutOfViewCid(cid));
setHoveredCid(cid);
};
const handleMouseLeave = (cid: string | null) => {
if (cid) {
const targetElement = document.getElementById(cid);
if (targetElement) {
targetElement.classList.remove('highlight');
}
}
setHoveredCid(null);
setOutOfViewCid(null);
};
const replyBacklink = (
<>
<Link
className={styles.backlink}
to={`/p/${backlinkReply?.subplebbitAddress}/c/${backlinkReply?.cid}`}
ref={refs.setReference}
onMouseOver={() => setHoveredCid(backlinkReply?.cid)}
onMouseLeave={() => setHoveredCid(null)}
onMouseOver={() => handleMouseOver(backlinkReply?.cid)}
onMouseLeave={() => handleMouseLeave(backlinkReply?.cid)}
>
c/{backlinkReply?.shortCid}
</Link>
{hoveredCid === backlinkReply?.cid &&
outOfViewCid === backlinkReply?.cid &&
createPortal(
<div className={styles.replyQuotePreview} ref={refs.setFloating} style={floatingStyles}>
<Post post={backlinkReply} showReplies={false} />
@@ -83,13 +126,14 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
to={`/p/${quotelinkReply?.subplebbitAddress}/c/${quotelinkReply?.cid}`}
ref={refs.setReference}
className={styles.quoteLink}
onMouseOver={() => setHoveredCid(quotelinkReply?.cid)}
onMouseLeave={() => setHoveredCid(null)}
onMouseOver={() => handleMouseOver(quotelinkReply?.cid)}
onMouseLeave={() => handleMouseLeave(quotelinkReply?.cid)}
>
{`c/${quotelinkReply?.cid && Plebbit.getShortCid(quotelinkReply.cid)}`}
</Link>
<br />
{hoveredCid === quotelinkReply?.cid &&
outOfViewCid === quotelinkReply?.cid &&
createPortal(
<div className={styles.replyQuotePreview} ref={refs.setFloating} style={floatingStyles}>
<Post post={quotelinkReply} showReplies={false} />
@@ -104,6 +148,7 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, isQuotelinkReply }: ReplyQuotePreviewProps) => {
const [hoveredCid, setHoveredCid] = useState<string | null>(null);
const [outOfViewCid, setOutOfViewCid] = useState<string | null>(null);
const { refs, floatingStyles, update } = useFloating({
placement: 'bottom',
@@ -118,15 +163,39 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is
};
}, [update]);
const handleMouseOver = (cid: string | undefined) => {
if (!cid) return;
handleQuoteHover(cid, () => setOutOfViewCid(cid));
setHoveredCid(cid);
};
const handleMouseLeave = (cid: string | null) => {
if (cid) {
const targetElement = document.getElementById(cid);
if (targetElement) {
targetElement.classList.remove('highlight');
}
}
setHoveredCid(null);
setOutOfViewCid(null);
};
const replyBacklink = (
<>
<span className={styles.backlink} ref={refs.setReference} onMouseOver={() => setHoveredCid(backlinkReply?.cid)} onMouseLeave={() => setHoveredCid(null)}>
<span
className={styles.backlink}
ref={refs.setReference}
onMouseOver={() => handleMouseOver(backlinkReply?.cid)}
onMouseLeave={() => handleMouseLeave(backlinkReply?.cid)}
>
c/{backlinkReply?.shortCid}{' '}
</span>
<Link to={`/p/${backlinkReply?.subplebbitAddress}/c/${backlinkReply?.cid}`} className={styles.backlinkHash}>
#
</Link>
{hoveredCid === backlinkReply?.cid &&
outOfViewCid === backlinkReply?.cid &&
createPortal(
<div className={styles.replyQuotePreview} ref={refs.setFloating} style={floatingStyles}>
<Post post={backlinkReply} showReplies={false} />
@@ -138,7 +207,12 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is
const replyQuotelink = (
<>
<span ref={refs.setReference} className={styles.quoteLink} onMouseOver={() => setHoveredCid(quotelinkReply?.cid)} onMouseLeave={() => setHoveredCid(null)}>
<span
ref={refs.setReference}
className={styles.quoteLink}
onMouseOver={() => handleMouseOver(quotelinkReply?.cid)}
onMouseLeave={() => handleMouseLeave(quotelinkReply?.cid)}
>
c/{quotelinkReply?.shortCid}{' '}
</span>
<Link className={styles.quoteLink} to={`/p/${quotelinkReply?.subplebbitAddress}/c/${quotelinkReply?.cid}`}>
@@ -146,6 +220,7 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is
</Link>
<br />
{hoveredCid === quotelinkReply?.cid &&
outOfViewCid === quotelinkReply?.cid &&
createPortal(
<div className={styles.replyQuotePreview} ref={refs.setFloating} style={floatingStyles}>
<Post post={quotelinkReply} showReplies={false} />
+4
View File
@@ -26,6 +26,10 @@ hr {
color: var(--post-greentext-color);
}
.highlight {
background: var(--reply-highlight-background-color) !important;
}
@media (max-width: 640px) {
.button {
font-size: var(--button-font-size-mobile);