import { memo, useState } from 'react'; import { createPortal } from 'react-dom'; import { useLocation, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react'; import { useBlock } from '@plebbit/plebbit-react-hooks'; import styles from './post-menu-desktop.module.css'; import { getCommentMediaInfo } from '../../../lib/utils/media-utils'; import { copyShareLinkToClipboard, isValidURL, type ShareLinkType } from '../../../lib/utils/url-utils'; import { copyToClipboard } from '../../../lib/utils/clipboard-utils'; import { getBoardPath } from '../../../lib/utils/route-utils'; import { useDirectories } from '../../../hooks/use-directories'; import { isAllView, isCatalogView, isPostPageView, isSubscriptionsView } from '../../../lib/utils/view-utils'; import useHide from '../../../hooks/use-hide'; import capitalize from 'lodash/capitalize'; import { PostMenuProps } from '../../../lib/utils/post-menu-props'; type CopyLinkButtonProps = | { cid: string; subplebbitAddress: string; linkType: 'thread'; onClose: () => void } | { subplebbitAddress: string; linkType: Exclude; onClose: () => void; cid?: undefined }; const CopyLinkButton = ({ cid, subplebbitAddress, linkType, onClose }: CopyLinkButtonProps) => { const { t } = useTranslation(); const directories = useDirectories(); const boardIdentifier = getBoardPath(subplebbitAddress, directories); return (
{ try { if (linkType === 'thread') { await copyShareLinkToClipboard(boardIdentifier, linkType, cid); } else { await copyShareLinkToClipboard(boardIdentifier, linkType); } } catch (error) { console.error('Failed to copy share link', error); } finally { onClose(); } }} >
{t('copy_direct_link')}
); }; const CopyContentIdButton = ({ cid, onClose }: { cid: string; onClose: () => void }) => { const { t } = useTranslation(); return (
{ try { await copyToClipboard(cid); } catch (error) { console.error('Failed to copy content id', error); } finally { onClose(); } }} >
{t('copy_content_id')}
); }; const CopyUserIdButton = ({ address, onClose }: { address: string; onClose: () => void }) => { const { t } = useTranslation(); return (
{ try { await copyToClipboard(address); } catch (error) { console.error('Failed to copy user id', error); } finally { onClose(); } }} >
{t('copy_user_id')}
); }; const ImageSearchButton = ({ url, onClose }: { url: string; onClose: () => void }) => { const { t } = useTranslation(); const [isImageSearchMenuOpen, setIsImageSearchMenuOpen] = useState(false); const { refs, floatingStyles } = useFloating({ placement: 'right-start', middleware: [flip(), shift({ padding: 10 })], }); return (
setIsImageSearchMenuOpen(true)} onMouseLeave={() => setIsImageSearchMenuOpen(false)} ref={refs.setReference} onClick={onClose} > {capitalize(t('image_search'))} » {isImageSearchMenuOpen && (
Google
Yandex
SauceNAO
)}
); }; const BlockUserButton = ({ address }: { address: string }) => { const { t } = useTranslation(); const { blocked, unblock, block } = useBlock({ address }); return (
{blocked ? t('unblock_user') : t('block_user')}
); }; const BlockBoardButton = ({ address }: { address: string }) => { const { t } = useTranslation(); const { blocked, unblock, block } = useBlock({ address }); return (
{blocked ? t('unblock_board') : t('block_board')}
); }; type PostMenuDesktopProps = { postMenu: PostMenuProps; }; const PostMenuDesktop = ({ postMenu }: PostMenuDesktopProps) => { const { t } = useTranslation(); const { authorAddress, cid, link, thumbnailUrl, linkWidth, linkHeight, postCid, subplebbitAddress } = postMenu || {}; const commentMediaInfo = getCommentMediaInfo(link || '', thumbnailUrl || '', linkWidth ?? 0, linkHeight ?? 0); const { thumbnail, type, url } = commentMediaInfo || {}; const [menuBtnRotated, setMenuBtnRotated] = useState(false); const { hidden, unhide, hide } = useHide({ cid: cid || '' }); const location = useLocation(); const params = useParams(); const isInAllView = isAllView(location.pathname); const isInCatalogView = isCatalogView(location.pathname, params); const isInPostPageView = isPostPageView(location.pathname, params); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); const { refs, floatingStyles, context } = useFloating({ placement: 'bottom-start', open: menuBtnRotated, onOpenChange: setMenuBtnRotated, middleware: [offset({ mainAxis: isInCatalogView ? -2 : 6, crossAxis: isInCatalogView ? -1 : 5 }), flip({ fallbackAxisSideDirection: 'end' }), shift({ padding: 10 })], whileElementsMounted: autoUpdate, }); const click = useClick(context); const dismiss = useDismiss(context); const role = useRole(context); const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss, role]); const headingId = useId(); const handleMenuClick = () => { if (cid) { setMenuBtnRotated((prev) => !prev); } }; const handleClose = () => setMenuBtnRotated(false); return ( <> {menuBtnRotated && cid && createPortal(
{cid && subplebbitAddress && } {cid && } {authorAddress && } {!(isInPostPageView && postCid === cid) && (
{ hidden ? unhide() : hide(); handleClose(); }} > {hidden ? (postCid === cid ? t('unhide_thread') : t('unhide_post')) : postCid === cid ? t('hide_thread') : t('hide_post')}
)} {link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && } {authorAddress && } {(isInAllView || isInSubscriptionsView) && subplebbitAddress && }
, document.body, )} ); }; export default memo(PostMenuDesktop);