feat(post): add post count and highlight functionality to u/address

This commit is contained in:
Tom (plebeius.eth)
2024-06-28 22:09:11 +02:00
parent 140900d1bc
commit a6a41edd76
8 changed files with 139 additions and 30 deletions
@@ -20,7 +20,6 @@
}
.boardSubtitle {
margin-top: 3px;
font-size: var(--board-address-font-size);
color: var(--board-address-text-color);
}
+31 -2
View File
@@ -8,6 +8,7 @@ import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '.
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isValidURL } from '../../lib/utils/url-utils';
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
import useAuthorAddressClick from '../../hooks/use-author-address-click';
import useEditCommentPrivileges from '../../hooks/use-author-privileges';
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
import useHide from '../../hooks/use-hide';
@@ -47,6 +48,9 @@ const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => {
const { isCommentAuthorMod, isAccountMod, isAccountCommentAuthor } = useEditCommentPrivileges({ commentAuthorAddress: address, subplebbitAddress });
const handleUserAddressClick = useAuthorAddressClick();
const numberOfPostsByAuthor = document.querySelectorAll(`.${shortAddress}`).length;
return (
<div className={styles.postInfo}>
{!isHidden && <EditMenu isAccountCommentAuthor={isAccountCommentAuthor} isAccountMod={isAccountMod} isCommentAuthorMod={isCommentAuthorMod} post={post} />}
@@ -75,7 +79,24 @@ const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => {
)}
{authorRole && ` ## Board ${authorRole}`}{' '}
</span>
{!(isDescription || isRules) && <span className={styles.userAddress}>(u/{shortAddress || accountShortAddress}) </span>}
{!(isDescription || isRules) && (
<>
(u/
<Tooltip
children={
<span
title='Highlight posts by this user address'
className={styles.userAddress}
onClick={() => handleUserAddressClick(shortAddress || accountShortAddress)}
>
{shortAddress || accountShortAddress}
</span>
}
content={`${numberOfPostsByAuthor} ${numberOfPostsByAuthor === 1 ? 'post' : 'posts'} by this user address`}
/>
){' '}
</>
)}
</span>
<span className={styles.dateTime}>
<Tooltip children={<span>{getFormattedDate(timestamp)}</span>} content={getFormattedTimeAgo(timestamp)} />
@@ -272,7 +293,15 @@ const Reply = ({ openReplyModal, reply, roles }: PostProps) => {
return (
<div className={styles.replyDesktop}>
<div className={styles.sideArrows}>{'>>'}</div>
<div className={`${styles.reply} ${isRouteLinkToReply && styles.highlight} ${hidden && styles.postDesktopHidden}`} id={cid}>
<div
className={`
${styles.reply}
${isRouteLinkToReply && styles.highlight}
${hidden && styles.postDesktopHidden}
${cid}
${post?.author?.shortAddress}
`}
>
<PostInfo openReplyModal={openReplyModal} post={post} roles={roles} />
{link && !hidden && isValidURL(link) && <PostMedia post={post} />}
{content && !hidden && <PostMessage post={post} />}
+31 -2
View File
@@ -7,6 +7,7 @@ import styles from '../../views/post/post.module.css';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
import useAuthorAddressClick from '../../hooks/use-author-address-click';
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
import useHide from '../../hooks/use-hide';
import useReplies from '../../hooks/use-replies';
@@ -45,6 +46,9 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
const stateString = useStateString(post);
const handleUserAddressClick = useAuthorAddressClick();
const numberOfPostsByAuthor = document.querySelectorAll(`.${shortAddress}`).length;
return (
<>
<div className={styles.postInfo}>
@@ -65,7 +69,24 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
)}
{authorRole && ` ## Board ${authorRole}`}{' '}
</span>
{!(isDescription || isRules) && <span className={styles.address}>(u/{shortAddress || accountShortAddress})</span>}
{!(isDescription || isRules) && (
<>
(u/
<Tooltip
children={
<span
title='Highlight posts by this user address'
className={styles.userAddress}
onClick={() => handleUserAddressClick(shortAddress || accountShortAddress)}
>
{shortAddress || accountShortAddress}
</span>
}
content={`${numberOfPostsByAuthor} ${numberOfPostsByAuthor === 1 ? 'post' : 'posts'} by this user address`}
/>
){' '}
</>
)}
{pinned && (
<span className={styles.stickyIconWrapper}>
<img src='assets/icons/sticky.gif' alt='' className={styles.stickyIcon} title={t('sticky')} />
@@ -224,7 +245,15 @@ const Reply = ({ openReplyModal, reply, roles }: PostProps) => {
return (
<div className={styles.replyMobile}>
<div className={styles.reply}>
<div className={`${styles.replyContainer} ${isRouteLinkToReply && styles.highlight}`} id={cid}>
<div
className={`
${styles.replyContainer}
${isRouteLinkToReply && styles.highlight}
${hidden && styles.postDesktopHidden}
${cid}
${post?.author?.shortAddress}
`}
>
<PostInfoAndMedia openReplyModal={openReplyModal} post={post} roles={roles} />
{content && !hidden && <PostMessageMobile post={post} />}
<ReplyBacklinks post={reply} />
@@ -15,9 +15,9 @@ interface ReplyQuotePreviewProps {
}
const handleQuoteHover = (cid: string, onElementOutOfView: () => void) => {
const targetElement = document.getElementById(cid);
const targetElements = document.querySelectorAll(`.${cid}`);
if (!targetElement) return;
if (targetElements.length === 0) return;
const isInViewport = (element: HTMLElement) => {
const bounding = element.getBoundingClientRect();
@@ -29,10 +29,19 @@ const handleQuoteHover = (cid: string, onElementOutOfView: () => void) => {
);
};
if (isInViewport(targetElement)) {
targetElement.classList.add('highlight');
} else {
targetElement.classList.remove('highlight');
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();
}
};
@@ -88,10 +97,10 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
const handleMouseLeave = (cid: string | null) => {
if (cid) {
const targetElement = document.getElementById(cid);
if (targetElement) {
targetElement.classList.remove('highlight');
}
const targetElements = document.querySelectorAll(`.${cid}`);
targetElements.forEach((element) => {
element.classList.remove('highlight');
});
}
setHoveredCid(null);
setOutOfViewCid(null);
@@ -174,10 +183,10 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is
const handleMouseLeave = (cid: string | null) => {
if (cid) {
const targetElement = document.getElementById(cid);
if (targetElement) {
targetElement.classList.remove('highlight');
}
const targetElements = document.querySelectorAll(`.${cid}`);
targetElements.forEach((element) => {
element.classList.remove('highlight');
});
}
setHoveredCid(null);
setOutOfViewCid(null);
+1 -1
View File
@@ -24,7 +24,7 @@ const Tooltip = ({ content, children }: TooltipProps) => {
],
});
const hover = useHover(context, { move: false, delay: { open: 250, close: 0 } });
const hover = useHover(context, { move: false, delay: { open: 500, close: 0 } });
const focus = useFocus(context);
const dismiss = useDismiss(context);
const role = useRole(context, { role: 'tooltip' });
+29
View File
@@ -0,0 +1,29 @@
const useAuthorAddressClick = () => {
const handleUserAddressClick = (shortAddress: string | undefined) => {
if (!shortAddress) return;
// Select the elements corresponding to the clicked short address
const elements = document.querySelectorAll(`.${shortAddress}`);
// Check if the clicked address is already highlighted
const isAlreadyHighlighted = Array.from(elements).some((element) => element.classList.contains('highlight'));
// Remove highlight from all elements with the .highlight class
const prevElements = document.querySelectorAll('.highlight');
prevElements.forEach((element) => {
element.classList.remove('highlight');
});
// If the clicked address was already highlighted, don't add the highlight back
if (isAlreadyHighlighted) return;
// Highlight the new elements
elements.forEach((element) => {
element.classList.add('highlight');
});
};
return handleUserAddressClick;
};
export default useAuthorAddressClick;
+16 -10
View File
@@ -143,10 +143,23 @@ const Footer = () => {
{t('about')}
</a>
</li>
{downloadAppLink && (
<li>
<a href={downloadAppLink} target='_blank' rel='noopener noreferrer'>
{t('download_app')}
</a>
</li>
)}
<li>
<a href='https://twitter.com/getplebchan' target='_blank' rel='noopener noreferrer'>
Twitter
</a>
<Link
to='/faq'
onClick={(e) => {
e.preventDefault();
alert('work in progress');
}}
>
FAQ
</Link>
</li>
<li>
<a href='https://t.me/plebbit' target='_blank' rel='noopener noreferrer'>
@@ -163,13 +176,6 @@ const Footer = () => {
GitHub
</a>
</li>
{downloadAppLink && (
<li>
<a href={downloadAppLink} target='_blank' rel='noopener noreferrer'>
{t('download_app')}
</a>
</li>
)}
<li>
<a href='https://etherscan.io/token/0xEA81DaB2e0EcBc6B5c4172DE4c22B6Ef6E55Bd8f' target='_blank' rel='noopener noreferrer'>
{t('token')}
+8
View File
@@ -66,6 +66,14 @@
width: 100%;
}
.userAddress {
cursor: pointer;
}
.postDesktop .userAddress:hover {
color: var(--button-desktop-text-color-hover);
}
.postDesktop .subject {
color: var(--post-subject-text-color);
font-weight: var(--post-subject-font-weight);