From 505aed4c2467bc90e44a6f300c45af8af1f51525 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Thu, 27 Jun 2024 11:03:58 +0200 Subject: [PATCH] feat(post): add tooltips for title and display names that are too long --- src/components/post-desktop/post-desktop.tsx | 42 ++++++++++++----- src/components/post-mobile/post-mobile.tsx | 47 ++++++++++++------- src/components/timestamp/index.ts | 1 - src/components/tooltip/index.ts | 1 + src/components/tooltip/tooltip.module.css | 27 +++++++++++ .../timestamp.tsx => tooltip/tooltip.tsx} | 19 +++++--- src/index.css | 28 ----------- 7 files changed, 100 insertions(+), 65 deletions(-) delete mode 100644 src/components/timestamp/index.ts create mode 100644 src/components/tooltip/index.ts create mode 100644 src/components/tooltip/tooltip.module.css rename src/components/{timestamp/timestamp.tsx => tooltip/tooltip.tsx} (72%) diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx index 43f14bc4..66173dff 100644 --- a/src/components/post-desktop/post-desktop.tsx +++ b/src/components/post-desktop/post-desktop.tsx @@ -5,6 +5,7 @@ import { Comment, useAccount, useComment, useEditedComment } from '@plebbit/pleb import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js'; import styles from '../../views/post/post.module.css'; import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../lib/utils/media-utils'; +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 useEditCommentPrivileges from '../../hooks/use-author-privileges'; @@ -13,22 +14,24 @@ import useHide from '../../hooks/use-hide'; import useReplies from '../../hooks/use-replies'; import useStateString from '../../hooks/use-state-string'; import CommentMedia from '../comment-media'; +import EditMenu from '../edit-menu/edit-menu'; import { canEmbed } from '../embed'; import LoadingEllipsis from '../loading-ellipsis'; import Markdown from '../markdown'; import PostMenuDesktop from './post-menu-desktop'; -import EditMenu from '../edit-menu/edit-menu'; import ReplyQuotePreview from '../reply-quote-preview'; +import Tooltip from '../tooltip'; import { PostProps } from '../../views/post/post'; -import Timestamp from '../timestamp'; import _ from 'lodash'; -import { getFormattedDate } from '../../lib/utils/time-utils'; const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => { const { t } = useTranslation(); - const { author, cid, locked, pinned, parentCid, postCid, replyCount, shortCid, state, subplebbitAddress, timestamp, title } = post || {}; + const { author, cid, locked, pinned, parentCid, postCid, replyCount, shortCid, state, subplebbitAddress, timestamp } = post || {}; + const title = post?.title?.trim(); const replies = useReplies(post); - const { address, displayName, shortAddress } = author || {}; + const { address, shortAddress } = author || {}; + const displayName = author?.displayName?.trim(); + const authorRole = roles?.[address]?.role; const { isDescription, isRules } = post || {}; // custom properties, not from api const stateString = useStateString(post); const isReply = parentCid; @@ -39,10 +42,6 @@ const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => { const isInPostView = isPostPageView(location.pathname, params); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); - const shortDisplayName = displayName?.trim().length > 20 ? displayName?.trim().slice(0, 20).trim() + '...' : displayName?.trim(); - const authorRole = roles?.[address]?.role; - const displayTitle = title && title.length > 75 ? title?.slice(0, 75) + '...' : title; - const account = useAccount(); const accountShortAddress = account?.author?.shortAddress; // if reply by account is pending, it doesn't have an author yet @@ -51,16 +50,35 @@ const PostInfo = ({ openReplyModal, post, roles, isHidden }: PostProps) => { return (
{!isHidden && } - {title && {displayTitle} } + {title && + (title.length <= 75 ? ( + {title} + ) : ( + {title.slice(0, 75) + '(...)'} } + content={title.length < 1000 ? title : title.slice(0, 1000) + '... title too long'} + /> + ))} - {shortDisplayName || _.capitalize(t('anonymous'))} + {displayName ? ( + displayName.length <= 20 ? ( + {displayName} + ) : ( + {displayName.slice(0, 20) + '(...)'}} + content={displayName.length < 1000 ? displayName : displayName.slice(0, 1000) + '... display name too long'} + /> + ) + ) : ( + _.capitalize(t('anonymous')) + )} {authorRole && ` ## Board ${authorRole}`}{' '} {!(isDescription || isRules) && (u/{shortAddress || accountShortAddress}) } - + {getFormattedDate(timestamp)}} content={getFormattedTimeAgo(timestamp)} /> {isDescription || isRules ? '' : ' '} diff --git a/src/components/post-mobile/post-mobile.tsx b/src/components/post-mobile/post-mobile.tsx index 215b8929..5fa9f885 100644 --- a/src/components/post-mobile/post-mobile.tsx +++ b/src/components/post-mobile/post-mobile.tsx @@ -5,7 +5,7 @@ import { Comment, useAccount, useComment, useEditedComment } from '@plebbit/pleb import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js'; import styles from '../../views/post/post.module.css'; import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils'; -import { getFormattedDate } from '../../lib/utils/time-utils'; +import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils'; import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils'; import useCountLinksInReplies from '../../hooks/use-count-links-in-replies'; import useHide from '../../hooks/use-hide'; @@ -14,26 +14,25 @@ import useStateString from '../../hooks/use-state-string'; import CommentMedia from '../comment-media'; import LoadingEllipsis from '../loading-ellipsis'; import Markdown from '../markdown'; -import ReplyQuotePreview from '../reply-quote-preview'; import PostMenuMobile from './post-menu-mobile'; +import ReplyQuotePreview from '../reply-quote-preview'; +import Tooltip from '../tooltip'; import { PostProps } from '../../views/post/post'; -import Timestamp from '../timestamp'; import _ from 'lodash'; const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => { const { t } = useTranslation(); - const { author, cid, link, locked, parentCid, pinned, shortCid, state, subplebbitAddress, timestamp, title } = post || {}; + const { author, cid, link, locked, parentCid, pinned, shortCid, state, subplebbitAddress, timestamp } = post || {}; + const title = post?.title?.trim(); const { isDescription, isRules } = post || {}; // custom properties, not from api - const { address, displayName, shortAddress } = author || {}; + const { address, shortAddress } = author || {}; + const displayName = author?.displayName?.trim(); + const authorRole = roles?.[address]?.role; const location = useLocation(); const isInAllView = isAllView(location.pathname, useParams()); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); - const authorRole = roles?.[address]?.role; - const shortDisplayName = displayName?.trim().length > 20 ? displayName?.trim().slice(0, 20).trim() + '...' : displayName?.trim(); - const displayTitle = title && title.length > 30 ? title?.slice(0, 30) + '(...)' : title; - const commentMediaInfo = getCommentMediaInfo(post); const hasThumbnail = getHasThumbnail(commentMediaInfo, link); const [showThumbnail, setShowThumbnail] = useState(true); @@ -52,7 +51,18 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => { - {shortDisplayName || _.capitalize(t('anonymous'))} + {displayName ? ( + displayName.length <= 20 ? ( + {displayName} + ) : ( + {displayName.slice(0, 20) + '(...)'}} + content={displayName.length < 1000 ? displayName : displayName.slice(0, 1000) + '... display name too long'} + /> + ) + ) : ( + _.capitalize(t('anonymous')) + )} {authorRole && ` ## Board ${authorRole}`}{' '} {!(isDescription || isRules) && (u/{shortAddress || accountShortAddress})} @@ -66,12 +76,15 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => { )} - {title && ( - <> -
- {displayTitle} - - )} + {title && + (title.length <= 30 ? ( + {title} + ) : ( + {title.slice(0, 30) + '(...)'}} + content={title.length < 1000 ? title : title.slice(0, 1000) + '... title too long'} + /> + ))} {subplebbitAddress && (isInAllView || isInSubscriptionsView) && !isReply && ( @@ -80,7 +93,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => { p/{subplebbitAddress && Plebbit.getShortAddress(subplebbitAddress)}
)} - {' '} + {getFormattedDate(timestamp)}} content={getFormattedTimeAgo(timestamp)} />{' '} {!(isDescription || isRules) && ( !cid && e.preventDefault()}> diff --git a/src/components/timestamp/index.ts b/src/components/timestamp/index.ts deleted file mode 100644 index 8fe28740..00000000 --- a/src/components/timestamp/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './timestamp'; diff --git a/src/components/tooltip/index.ts b/src/components/tooltip/index.ts new file mode 100644 index 00000000..bb106e20 --- /dev/null +++ b/src/components/tooltip/index.ts @@ -0,0 +1 @@ +export { default } from './tooltip'; diff --git a/src/components/tooltip/tooltip.module.css b/src/components/tooltip/tooltip.module.css new file mode 100644 index 00000000..109b8466 --- /dev/null +++ b/src/components/tooltip/tooltip.module.css @@ -0,0 +1,27 @@ +.tooltip { + position: absolute; + background-color: #181f24; + font-size: 11px; + line-height: 13px; + padding: 3px 6px; + z-index: 100000; + word-wrap: break-word; + white-space: pre-line; + max-width: 400px; + color: #fff; + text-align: center; +} + +.tooltip::before { + content: ""; + display: block; + width: 0; + height: 0; + position: absolute; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid #181f24; + margin-left: -4px; + bottom: -4px; + left: 50%; +} \ No newline at end of file diff --git a/src/components/timestamp/timestamp.tsx b/src/components/tooltip/tooltip.tsx similarity index 72% rename from src/components/timestamp/timestamp.tsx rename to src/components/tooltip/tooltip.tsx index 28a6e841..12572c9a 100644 --- a/src/components/timestamp/timestamp.tsx +++ b/src/components/tooltip/tooltip.tsx @@ -1,8 +1,13 @@ -import { useState } from 'react'; -import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils'; +import { useState, ReactNode } from 'react'; import { useFloating, autoUpdate, offset, flip, shift, useHover, useFocus, useDismiss, useRole, useInteractions, FloatingPortal } from '@floating-ui/react'; +import styles from './tooltip.module.css'; -const Timestamp = ({ timestamp }: { timestamp: number }) => { +interface TooltipProps { + content: string; + children: ReactNode; +} + +const Tooltip = ({ content, children }: TooltipProps) => { const [isOpen, setIsOpen] = useState(false); const { refs, floatingStyles, context } = useFloating({ @@ -29,12 +34,12 @@ const Timestamp = ({ timestamp }: { timestamp: number }) => { return ( <> - {getFormattedDate(timestamp)} + {children} {isOpen && ( -
- {getFormattedTimeAgo(timestamp)} +
+ {content}
)} @@ -42,4 +47,4 @@ const Timestamp = ({ timestamp }: { timestamp: number }) => { ); }; -export default Timestamp; +export default Tooltip; diff --git a/src/index.css b/src/index.css index 7b8c1295..249adaf5 100644 --- a/src/index.css +++ b/src/index.css @@ -34,34 +34,6 @@ hr { text-transform: capitalize; } -.tooltip { - position: absolute; - background-color: #181f24; - font-size: 11px; - line-height: 13px; - padding: 3px 6px; - z-index: 100000; - word-wrap: break-word; - white-space: pre-line; - max-width: 400px; - color: #fff; - text-align: center; -} - -.tooltip::before { - content: ""; - display: block; - width: 0; - height: 0; - position: absolute; - border-left: 4px solid transparent; - border-right: 4px solid transparent; - border-top: 4px solid #181f24; - margin-left: -4px; - bottom: -4px; - left: 50%; -} - @media (max-width: 640px) { .button { font-size: var(--button-font-size-mobile);