From cc98926fa603bb7d95910aa9e1d8e0d1087f759a Mon Sep 17 00:00:00 2001 From: "plebeius.eth" Date: Thu, 14 Sep 2023 13:41:24 +0200 Subject: [PATCH] add mobile post menu --- src/components/styled/views/Board.styled.jsx | 129 ++++++++ src/components/views/All.jsx | 214 +++++++++++++- src/components/views/Board.jsx | 293 ++++++++++++++++++- src/components/views/Subscriptions.jsx | 216 +++++++++++++- src/components/views/Thread.jsx | 215 +++++++++++++- 5 files changed, 1048 insertions(+), 19 deletions(-) diff --git a/src/components/styled/views/Board.styled.jsx b/src/components/styled/views/Board.styled.jsx index 1f586bf1..ae9f426b 100644 --- a/src/components/styled/views/Board.styled.jsx +++ b/src/components/styled/views/Board.styled.jsx @@ -4091,4 +4091,133 @@ export const PostMenu = styled.div` cursor: pointer; transition: transform 0.1s; transform: ${props => props.rotated ? 'rotate(90deg)' : 'none'}; +`; + +export const PostMenuMobile = styled.div` + font-size: 16px; + line-height: 2.5em; + width: 216px; + + + ul { + border-right-width: 2px !important; + list-style: none; + padding: 0; + margin: 0; + white-space: nowrap; + } + + li { + cursor: pointer; + position: relative; + padding: 2px 4px; + vertical-align: middle; + } + + ${({ selectedStyle }) => { + switch (selectedStyle) { + case 'Yotsuba': + return ` + ul { + border: 1px solid #d9bfb7; + } + + li { + background-color: #f0e0d6; + border-bottom: 1px solid #d9bfb7; + + :hover { + background-color: #ffe; + color: #d00; + } + } + `; + + case 'Yotsuba-B': + return ` + ul { + border: 1px solid #b7c5d9; + } + + li { + background-color: #d6daf0; + border-bottom: 1px solid #b7c5d9; + + :hover { + background-color: #eef2ff; + color: #d00; + } + } + `; + + case 'Futaba': + return ` + ul { + border: 1px solid #d9bfb7; + } + + li { + background-color: #f0e0d6; + border-bottom: 1px solid #d9bfb7; + + :hover { + background-color: #ffe; + color: #d00; + } + } + `; + + case 'Burichan': + return ` + ul { + border: 1px solid #b7c5d9; + } + + li { + background-color: #d6daf0; + border-bottom: 1px solid #b7c5d9; + + :hover { + background-color: #eef2ff; + color: #d00; + } + } + `; + + case 'Tomorrow': + return ` + ul { + border: 1px solid #282a2e; + } + + li { + background-color: #1d1f21; + border-bottom: 1px solid #282a2e; + + :hover { + background-color: #1b1c1e; + } + } + `; + + case 'Photon': + return ` + ul { + border: 1px solid #ccc; + } + + li { + background-color: #ddd; + border-bottom: 1px solid #ccc; + + :hover { + background-color: #eee; + } + } + `; + + default: + return ''; + }} + } `; \ No newline at end of file diff --git a/src/components/views/All.jsx b/src/components/views/All.jsx index d1d9ddd0..123cfd03 100755 --- a/src/components/views/All.jsx +++ b/src/components/views/All.jsx @@ -8,7 +8,7 @@ import { Virtuoso } from 'react-virtuoso'; import { useAccount, useAccountComments, useFeed, usePublishCommentEdit, useSubplebbits } from '@plebbit/plebbit-react-hooks'; import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils' import { debounce } from 'lodash'; -import { Container, NavBar, Header, Break, TopBar, BoardForm, PostMenu } from '../styled/views/Board.styled'; +import { Container, NavBar, Header, Break, TopBar, BoardForm, PostMenu, PostMenuMobile } from '../styled/views/Board.styled'; import { Footer } from '../styled/views/Thread.styled'; import { PostMenuCatalog } from '../styled/views/Catalog.styled'; import { AlertModal } from '../styled/modals/AlertModal.styled'; @@ -85,8 +85,11 @@ const All = () => { const [, setNewSuccessMessage] = useSuccess(); const threadMenuRefs = useRef({}); + const threadMenuRefsMobile = useRef({}); const replyMenuRefs = useRef({}); + const replyMenuRefsMobile = useRef({}); const postMenuCatalogRef = useRef(null); + const postMenuMobileRef = useRef(null); const backlinkRefs = useRef({}); const quoteRefs = useRef({}); const postRefs = useRef({}); @@ -105,8 +108,10 @@ const All = () => { const [isClientRedirectMenuOpen, setIsClientRedirectMenuOpen] = useState(false); const [commentCid, setCommentCid] = useState(null); const [menuPosition, setMenuPosition] = useState({top: 0, left: 0}); + const [mobileMenuPosition, setMobileMenuPosition] = useState({top: 0, left: 0}); const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false); const [openMenuCid, setOpenMenuCid] = useState(null); + const [openMobileMenuCid, setOpenMobileMenuCid] = useState(null); const [outOfViewCid, setOutOfViewCid] = useState(null); const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0}); const [postOnHoverHeight, setPostOnHoverHeight] = useState(0); @@ -196,12 +201,22 @@ const All = () => { setOpenMenuCid(null); }; + const handleMobileOptionClick = () => { + setOpenMobileMenuCid(null); + }; + const handleOutsideClick = useCallback((e) => { if (openMenuCid !== null && !postMenuCatalogRef.current.contains(e.target)) { setOpenMenuCid(null); } }, [openMenuCid, postMenuCatalogRef]); + + const handleMobileOutsideClick = useCallback((e) => { + if (openMobileMenuCid !== null && !postMenuMobileRef.current.contains(e.target)) { + setOpenMobileMenuCid(null); + } + }, [openMobileMenuCid, postMenuMobileRef]); useEffect(() => { @@ -217,6 +232,19 @@ const All = () => { }, [openMenuCid, handleOutsideClick]); + useEffect(() => { + if (openMobileMenuCid !== null) { + document.addEventListener('click', handleMobileOutsideClick); + } else { + document.removeEventListener('click', handleMobileOutsideClick); + } + + return () => { + document.removeEventListener('click', handleMobileOutsideClick); + }; + }, [openMobileMenuCid, handleMobileOutsideClick]); + + useEffect(() => { if (postOnHoverRef.current) { const rect = postOnHoverRef.current.getBoundingClientRect(); @@ -1321,7 +1349,7 @@ const All = () => {
  • handleOptionClick(reply.cid)}> Yandex
  • @@ -1748,7 +1776,96 @@ const All = () => {
    - + + {createPortal( + {postMenuMobileRef.current = el}} + onClick={(event) => event.stopPropagation()} + style={{position: "absolute", + display: openMobileMenuCid === thread.cid ? "block" : "none", + top: mobileMenuPosition.top + 20, + left: mobileMenuPosition.left}}> +
      +
    • { + handleMobileOptionClick(thread.cid); + handleShareClick(selectedAddress, thread.cid) + }}>Share thread
    • + {({ authorAddress }) => ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + <> +
    • handleAuthorEditClick(thread)}>Edit post
    • +
    • handleAuthorDeleteClick(thread)}>Delete post
    • + + ) : null} + {isModerator ? ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + null + ) : ( +
    • { + setModeratingCommentCid(thread.cid) + setIsModerationOpen(true); + handleMobileOptionClick(thread.cid); + setDeletePost(true); + }}> + Delete post +
    • + )} +
    • { + setModeratingCommentCid(thread.cid) + setIsModerationOpen(true); + handleMobileOptionClick(thread.cid); + }}> + Mod tools +
    • + + ) : null} + + )}
      + {(commentMediaInfo && ( + commentMediaInfo.type === 'image' || + (commentMediaInfo.type === 'webpage' && + commentMediaInfo.thumbnail))) ? ( + <> +
    • handleMobileOptionClick(thread.cid)}> + Search image on Google +
    • +
    • handleMobileOptionClick(thread.cid)}> + Search image on Yandex +
    • +
    • handleMobileOptionClick(thread.cid)}> + Search image on SauceNAO +
    • + + ) : null + } +
    +
    , document.body + )} {thread.author.displayName ? thread.author.displayName.length > 20 @@ -1985,7 +2102,96 @@ const All = () => {
    - + + {createPortal( + {postMenuMobileRef.current = el}} + onClick={(event) => event.stopPropagation()} + style={{position: "absolute", + display: openMobileMenuCid === reply.cid ? "block" : "none", + top: mobileMenuPosition.top + 20, + left: mobileMenuPosition.left}}> +
      +
    • { + handleMobileOptionClick(reply.cid); + handleShareClick(selectedAddress, reply.cid) + }}>Share thread
    • + {({ authorAddress }) => ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + <> +
    • handleAuthorEditClick(reply)}>Edit post
    • +
    • handleAuthorDeleteClick(reply)}>Delete post
    • + + ) : null} + {isModerator ? ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + null + ) : ( +
    • { + setModeratingCommentCid(reply.cid) + setIsModerationOpen(true); + handleMobileOptionClick(reply.cid); + setDeletePost(true); + }}> + Delete post +
    • + )} +
    • { + setModeratingCommentCid(reply.cid) + setIsModerationOpen(true); + handleMobileOptionClick(reply.cid); + }}> + Mod tools +
    • + + ) : null} + + )}
      + {(replyMediaInfo && ( + replyMediaInfo.type === 'image' || + (replyMediaInfo.type === 'webpage' && + replyMediaInfo.thumbnail))) ? ( + <> +
    • handleMobileOptionClick(reply.cid)}> + Search image on Google +
    • +
    • handleMobileOptionClick(reply.cid)}> + Search image on Yandex +
    • +
    • handleMobileOptionClick(reply.cid)}> + Search image on SauceNAO +
    • + + ) : null + } +
    +
    , document.body + )} {reply.author.displayName ? reply.author.displayName.length > 20 diff --git a/src/components/views/Board.jsx b/src/components/views/Board.jsx index 1e687ce6..f279d2d9 100755 --- a/src/components/views/Board.jsx +++ b/src/components/views/Board.jsx @@ -8,7 +8,7 @@ import { Virtuoso } from 'react-virtuoso'; import { useAccount, useAccountComments, useFeed, usePublishComment, usePublishCommentEdit, useSubplebbit, useSubscribe } from '@plebbit/plebbit-react-hooks'; import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils' import { debounce } from 'lodash'; -import { Container, NavBar, Header, Break, PostFormLink, PostFormTable, PostForm, TopBar, BoardForm, PostMenu } from '../styled/views/Board.styled'; +import { Container, NavBar, Header, Break, PostFormLink, PostFormTable, PostForm, TopBar, BoardForm, PostMenu, PostMenuMobile } from '../styled/views/Board.styled'; import { Footer } from '../styled/views/Thread.styled'; import { AlertModal } from '../styled/modals/AlertModal.styled'; import { PostMenuCatalog } from '../styled/views/Catalog.styled'; @@ -105,8 +105,11 @@ const Board = () => { const commentRef = useRef(); const linkRef = useRef(); const threadMenuRefs = useRef({}); + const threadMenuRefsMobile = useRef({}); const replyMenuRefs = useRef({}); + const replyMenuRefsMobile = useRef({}); const postMenuCatalogRef = useRef(null); + const postMenuMobileRef = useRef(null); const backlinkRefs = useRef({}); const quoteRefs = useRef({}); const postRefs = useRef({}); @@ -136,7 +139,9 @@ const Board = () => { const [isModerator, setIsModerator] = useState(false); const [commentCid, setCommentCid] = useState(null); const [menuPosition, setMenuPosition] = useState({top: 0, left: 0}); + const [mobileMenuPosition, setMobileMenuPosition] = useState({top: 0, left: 0}); const [openMenuCid, setOpenMenuCid] = useState(null); + const [openMobileMenuCid, setOpenMobileMenuCid] = useState(null); const [outOfViewCid, setOutOfViewCid] = useState(null); const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0}); const [postOnHoverHeight, setPostOnHoverHeight] = useState(0); @@ -210,12 +215,22 @@ const Board = () => { setOpenMenuCid(null); }; + const handleMobileOptionClick = () => { + setOpenMobileMenuCid(null); + }; + const handleOutsideClick = useCallback((e) => { if (openMenuCid !== null && !postMenuCatalogRef.current.contains(e.target)) { setOpenMenuCid(null); } }, [openMenuCid, postMenuCatalogRef]); + + const handleMobileOutsideClick = useCallback((e) => { + if (openMobileMenuCid !== null && !postMenuMobileRef.current.contains(e.target)) { + setOpenMobileMenuCid(null); + } + }, [openMobileMenuCid, postMenuMobileRef]); useEffect(() => { @@ -231,6 +246,19 @@ const Board = () => { }, [openMenuCid, handleOutsideClick]); + useEffect(() => { + if (openMobileMenuCid !== null) { + document.addEventListener('click', handleMobileOutsideClick); + } else { + document.removeEventListener('click', handleMobileOutsideClick); + } + + return () => { + document.removeEventListener('click', handleMobileOutsideClick); + }; + }, [openMobileMenuCid, handleMobileOutsideClick]); + + useEffect(() => { if (postOnHoverRef.current) { const rect = postOnHoverRef.current.getBoundingClientRect(); @@ -1038,7 +1066,32 @@ const Board = () => {
    + {createPortal( + {postMenuMobileRef.current = el}} + onClick={(event) => event.stopPropagation()} + style={{position: "absolute", + display: openMobileMenuCid === "rules" ? "block" : "none", + top: mobileMenuPosition.top + 20, + left: mobileMenuPosition.left}}> +
      +
    • { + handleMobileOptionClick("rules"); + handleShareClick(selectedAddress, "rules") + }}>Share board
    • +
    +
    , document.body + )} {
    - + {createPortal( + {postMenuMobileRef.current = el}} + onClick={(event) => event.stopPropagation()} + style={{position: "absolute", + display: openMobileMenuCid === "rules" ? "block" : "none", + top: mobileMenuPosition.top + 20, + left: mobileMenuPosition.left}}> +
      +
    • { + handleMobileOptionClick("rules"); + handleShareClick(selectedAddress, "rules") + }}>Share board
    • +
    +
    , document.body + )} {
    - + {createPortal( + {postMenuMobileRef.current = el}} + onClick={(event) => event.stopPropagation()} + style={{position: "absolute", + display: openMobileMenuCid === "description" ? "block" : "none", + top: mobileMenuPosition.top + 20, + left: mobileMenuPosition.left}}> +
      +
    • { + handleMobileOptionClick("description"); + handleShareClick(selectedAddress, "description") + }}>Share board
    • +
    +
    , document.body + )} {
  • handleOptionClick(reply.cid)}> Yandex
  • @@ -2439,7 +2542,96 @@ const Board = () => {
    - + + {createPortal( + {postMenuMobileRef.current = el}} + onClick={(event) => event.stopPropagation()} + style={{position: "absolute", + display: openMobileMenuCid === thread.cid ? "block" : "none", + top: mobileMenuPosition.top + 20, + left: mobileMenuPosition.left}}> +
      +
    • { + handleMobileOptionClick(thread.cid); + handleShareClick(selectedAddress, thread.cid) + }}>Share thread
    • + {({ authorAddress }) => ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + <> +
    • handleAuthorEditClick(thread)}>Edit post
    • +
    • handleAuthorDeleteClick(thread)}>Delete post
    • + + ) : null} + {isModerator ? ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + null + ) : ( +
    • { + setModeratingCommentCid(thread.cid) + setIsModerationOpen(true); + handleMobileOptionClick(thread.cid); + setDeletePost(true); + }}> + Delete post +
    • + )} +
    • { + setModeratingCommentCid(thread.cid) + setIsModerationOpen(true); + handleMobileOptionClick(thread.cid); + }}> + Mod tools +
    • + + ) : null} + + )}
      + {(commentMediaInfo && ( + commentMediaInfo.type === 'image' || + (commentMediaInfo.type === 'webpage' && + commentMediaInfo.thumbnail))) ? ( + <> +
    • handleMobileOptionClick(thread.cid)}> + Search image on Google +
    • +
    • handleMobileOptionClick(thread.cid)}> + Search image on Yandex +
    • +
    • handleMobileOptionClick(thread.cid)}> + Search image on SauceNAO +
    • + + ) : null + } +
    +
    , document.body + )} {thread.author.displayName ? thread.author.displayName.length > 20 @@ -2677,7 +2869,96 @@ const Board = () => {
    - + + {createPortal( + {postMenuMobileRef.current = el}} + onClick={(event) => event.stopPropagation()} + style={{position: "absolute", + display: openMobileMenuCid === reply.cid ? "block" : "none", + top: mobileMenuPosition.top + 20, + left: mobileMenuPosition.left}}> +
      +
    • { + handleMobileOptionClick(reply.cid); + handleShareClick(selectedAddress, reply.cid) + }}>Share thread
    • + {({ authorAddress }) => ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + <> +
    • handleAuthorEditClick(reply)}>Edit post
    • +
    • handleAuthorDeleteClick(reply)}>Delete post
    • + + ) : null} + {isModerator ? ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + null + ) : ( +
    • { + setModeratingCommentCid(reply.cid) + setIsModerationOpen(true); + handleMobileOptionClick(reply.cid); + setDeletePost(true); + }}> + Delete post +
    • + )} +
    • { + setModeratingCommentCid(reply.cid) + setIsModerationOpen(true); + handleMobileOptionClick(reply.cid); + }}> + Mod tools +
    • + + ) : null} + + )}
      + {(replyMediaInfo && ( + replyMediaInfo.type === 'image' || + (replyMediaInfo.type === 'webpage' && + replyMediaInfo.thumbnail))) ? ( + <> +
    • handleMobileOptionClick(reply.cid)}> + Search image on Google +
    • +
    • handleMobileOptionClick(reply.cid)}> + Search image on Yandex +
    • +
    • handleMobileOptionClick(reply.cid)}> + Search image on SauceNAO +
    • + + ) : null + } +
    +
    , document.body + )} {reply.author.displayName ? reply.author.displayName.length > 20 diff --git a/src/components/views/Subscriptions.jsx b/src/components/views/Subscriptions.jsx index b7bb60d1..85302afa 100755 --- a/src/components/views/Subscriptions.jsx +++ b/src/components/views/Subscriptions.jsx @@ -9,7 +9,7 @@ import { useAccount, useAccountComments, useFeed, usePublishCommentEdit, useSubp import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils' import { debounce } from 'lodash'; import useGeneralStore from '../../hooks/stores/useGeneralStore'; -import { Container, NavBar, Header, Break, TopBar, BoardForm, PostMenu } from '../styled/views/Board.styled'; +import { Container, NavBar, Header, Break, TopBar, BoardForm, PostMenu, PostMenuMobile } from '../styled/views/Board.styled'; import { Footer } from '../styled/views/Thread.styled'; import { AlertModal } from '../styled/modals/AlertModal.styled'; import { PostMenuCatalog } from '../styled/views/Catalog.styled'; @@ -85,8 +85,11 @@ const Subscriptions = () => { const [, setNewSuccessMessage] = useSuccess(); const threadMenuRefs = useRef({}); + const threadMenuRefsMobile = useRef({}); const replyMenuRefs = useRef({}); + const replyMenuRefsMobile = useRef({}); const postMenuCatalogRef = useRef(null); + const postMenuMobileRef = useRef(null); const backlinkRefs = useRef({}); const quoteRefs = useRef({}); const postRefs = useRef({}); @@ -105,8 +108,10 @@ const Subscriptions = () => { const [isClientRedirectMenuOpen, setIsClientRedirectMenuOpen] = useState(false); const [commentCid, setCommentCid] = useState(null); const [menuPosition, setMenuPosition] = useState({top: 0, left: 0}); + const [mobileMenuPosition, setMobileMenuPosition] = useState({top: 0, left: 0}); const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false); const [openMenuCid, setOpenMenuCid] = useState(null); + const [openMobileMenuCid, setOpenMobileMenuCid] = useState(null); const [outOfViewCid, setOutOfViewCid] = useState(null); const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0}); const [postOnHoverHeight, setPostOnHoverHeight] = useState(0); @@ -196,12 +201,22 @@ const Subscriptions = () => { setOpenMenuCid(null); }; - + const handleMobileOptionClick = () => { + setOpenMobileMenuCid(null); + }; + + const handleOutsideClick = useCallback((e) => { if (openMenuCid !== null && !postMenuCatalogRef.current.contains(e.target)) { setOpenMenuCid(null); } }, [openMenuCid, postMenuCatalogRef]); + + const handleMobileOutsideClick = useCallback((e) => { + if (openMobileMenuCid !== null && !postMenuMobileRef.current.contains(e.target)) { + setOpenMobileMenuCid(null); + } + }, [openMobileMenuCid, postMenuMobileRef]); useEffect(() => { @@ -217,6 +232,19 @@ const Subscriptions = () => { }, [openMenuCid, handleOutsideClick]); + useEffect(() => { + if (openMobileMenuCid !== null) { + document.addEventListener('click', handleMobileOutsideClick); + } else { + document.removeEventListener('click', handleMobileOutsideClick); + } + + return () => { + document.removeEventListener('click', handleMobileOutsideClick); + }; + }, [openMobileMenuCid, handleMobileOutsideClick]); + + useEffect(() => { if (postOnHoverRef.current) { const rect = postOnHoverRef.current.getBoundingClientRect(); @@ -1329,7 +1357,7 @@ const Subscriptions = () => {
  • handleOptionClick(reply.cid)}> Yandex
  • @@ -1756,7 +1784,96 @@ const Subscriptions = () => {
    - + + {createPortal( + {postMenuMobileRef.current = el}} + onClick={(event) => event.stopPropagation()} + style={{position: "absolute", + display: openMobileMenuCid === thread.cid ? "block" : "none", + top: mobileMenuPosition.top + 20, + left: mobileMenuPosition.left}}> +
      +
    • { + handleMobileOptionClick(thread.cid); + handleShareClick(selectedAddress, thread.cid) + }}>Share thread
    • + {({ authorAddress }) => ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + <> +
    • handleAuthorEditClick(thread)}>Edit post
    • +
    • handleAuthorDeleteClick(thread)}>Delete post
    • + + ) : null} + {isModerator ? ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + null + ) : ( +
    • { + setModeratingCommentCid(thread.cid) + setIsModerationOpen(true); + handleMobileOptionClick(thread.cid); + setDeletePost(true); + }}> + Delete post +
    • + )} +
    • { + setModeratingCommentCid(thread.cid) + setIsModerationOpen(true); + handleMobileOptionClick(thread.cid); + }}> + Mod tools +
    • + + ) : null} + + )}
      + {(commentMediaInfo && ( + commentMediaInfo.type === 'image' || + (commentMediaInfo.type === 'webpage' && + commentMediaInfo.thumbnail))) ? ( + <> +
    • handleMobileOptionClick(thread.cid)}> + Search image on Google +
    • +
    • handleMobileOptionClick(thread.cid)}> + Search image on Yandex +
    • +
    • handleMobileOptionClick(thread.cid)}> + Search image on SauceNAO +
    • + + ) : null + } +
    +
    , document.body + )} {thread.author.displayName ? thread.author.displayName.length > 20 @@ -1993,7 +2110,96 @@ const Subscriptions = () => {
    - + + {createPortal( + {postMenuMobileRef.current = el}} + onClick={(event) => event.stopPropagation()} + style={{position: "absolute", + display: openMobileMenuCid === reply.cid ? "block" : "none", + top: mobileMenuPosition.top + 20, + left: mobileMenuPosition.left}}> +
      +
    • { + handleMobileOptionClick(reply.cid); + handleShareClick(selectedAddress, reply.cid) + }}>Share thread
    • + {({ authorAddress }) => ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + <> +
    • handleAuthorEditClick(reply)}>Edit post
    • +
    • handleAuthorDeleteClick(reply)}>Delete post
    • + + ) : null} + {isModerator ? ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + null + ) : ( +
    • { + setModeratingCommentCid(reply.cid) + setIsModerationOpen(true); + handleMobileOptionClick(reply.cid); + setDeletePost(true); + }}> + Delete post +
    • + )} +
    • { + setModeratingCommentCid(reply.cid) + setIsModerationOpen(true); + handleMobileOptionClick(reply.cid); + }}> + Mod tools +
    • + + ) : null} + + )}
      + {(replyMediaInfo && ( + replyMediaInfo.type === 'image' || + (replyMediaInfo.type === 'webpage' && + replyMediaInfo.thumbnail))) ? ( + <> +
    • handleMobileOptionClick(reply.cid)}> + Search image on Google +
    • +
    • handleMobileOptionClick(reply.cid)}> + Search image on Yandex +
    • +
    • handleMobileOptionClick(reply.cid)}> + Search image on SauceNAO +
    • + + ) : null + } +
    +
    , document.body + )} {reply.author.displayName ? reply.author.displayName.length > 20 diff --git a/src/components/views/Thread.jsx b/src/components/views/Thread.jsx index f79be8b0..1f489c4f 100755 --- a/src/components/views/Thread.jsx +++ b/src/components/views/Thread.jsx @@ -7,7 +7,7 @@ import { Tooltip } from 'react-tooltip'; import { useAccount, useAccountComments, useComment, usePublishComment, usePublishCommentEdit, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils' import { debounce } from 'lodash'; -import { Container, NavBar, Header, Break, PostForm, PostFormTable, PostMenu } from '../styled/views/Board.styled'; +import { Container, NavBar, Header, Break, PostForm, PostFormTable, PostMenu, PostMenuMobile } from '../styled/views/Board.styled'; import { ReplyFormLink, TopBar, BottomBar, BoardForm, Footer } from '../styled/views/Thread.styled'; import { AlertModal } from '../styled/modals/AlertModal.styled'; import { PostMenuCatalog } from '../styled/views/Catalog.styled'; @@ -91,8 +91,11 @@ const Thread = () => { const commentRef = useRef(); const linkRef = useRef(); const threadMenuRefs = useRef({}); + const threadMenuRefsMobile = useRef({}); const replyMenuRefs = useRef({}); + const replyMenuRefsMobile = useRef({}); const postMenuRef = useRef(null); + const postMenuMobileRef = useRef(null); const postMenuCatalogRef = useRef(null); const backlinkRefs = useRef({}); const quoteRefs = useRef({}); @@ -114,7 +117,9 @@ const Thread = () => { const [isModerator, setIsModerator] = useState(false); const [commentCid, setCommentCid] = useState(null); const [menuPosition, setMenuPosition] = useState({top: 0, left: 0}); + const [mobileMenuPosition, setMobileMenuPosition] = useState({top: 0, left: 0}); const [openMenuCid, setOpenMenuCid] = useState(null); + const [openMobileMenuCid, setOpenMobileMenuCid] = useState(null); const [outOfViewCid, setOutOfViewCid] = useState(null); const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0}); const [postOnHoverHeight, setPostOnHoverHeight] = useState(0); @@ -189,12 +194,22 @@ const Thread = () => { setOpenMenuCid(null); }; + const handleMobileOptionClick = () => { + setOpenMobileMenuCid(null); + }; + const handleOutsideClick = useCallback((e) => { if (openMenuCid !== null && !postMenuRef.current.contains(e.target) && !postMenuCatalogRef.current.contains(e.target)) { setOpenMenuCid(null); } }, [openMenuCid, postMenuRef, postMenuCatalogRef]); + const handleMobileOutsideClick = useCallback((e) => { + if (openMobileMenuCid !== null && !postMenuMobileRef.current.contains(e.target)) { + setOpenMobileMenuCid(null); + } + }, [openMobileMenuCid, postMenuMobileRef]); + useEffect(() => { if (openMenuCid !== null) { document.addEventListener('click', handleOutsideClick); @@ -208,6 +223,20 @@ const Thread = () => { }, [openMenuCid, handleOutsideClick]); + useEffect(() => { + if (openMobileMenuCid !== null) { + document.addEventListener('click', handleMobileOutsideClick); + } else { + document.removeEventListener('click', handleMobileOutsideClick); + } + + return () => { + document.removeEventListener('click', handleMobileOutsideClick); + }; + }, [openMobileMenuCid, handleMobileOutsideClick]); + + + useEffect(() => { if (postOnHoverRef.current) { const rect = postOnHoverRef.current.getBoundingClientRect(); @@ -1423,7 +1452,7 @@ const Thread = () => {
  • handleOptionClick(reply.cid)}> Yandex
  • @@ -1737,7 +1766,96 @@ const Thread = () => {
    - + + {createPortal( + {postMenuMobileRef.current = el}} + onClick={(event) => event.stopPropagation()} + style={{position: "absolute", + display: openMobileMenuCid === comment.cid ? "block" : "none", + top: mobileMenuPosition.top + 20, + left: mobileMenuPosition.left}}> +
      +
    • { + handleMobileOptionClick(comment.cid); + handleShareClick(selectedAddress, comment.cid) + }}>Share thread
    • + {({ authorAddress }) => ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + <> +
    • handleAuthorEditClick(comment)}>Edit post
    • +
    • handleAuthorDeleteClick(comment)}>Delete post
    • + + ) : null} + {isModerator ? ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + null + ) : ( +
    • { + setModeratingCommentCid(comment.cid) + setIsModerationOpen(true); + handleMobileOptionClick(comment.cid); + setDeletePost(true); + }}> + Delete post +
    • + )} +
    • { + setModeratingCommentCid(comment.cid) + setIsModerationOpen(true); + handleMobileOptionClick(comment.cid); + }}> + Mod tools +
    • + + ) : null} + + )}
      + {(commentMediaInfo && ( + commentMediaInfo.type === 'image' || + (commentMediaInfo.type === 'webpage' && + commentMediaInfo.thumbnail))) ? ( + <> +
    • handleMobileOptionClick(comment.cid)}> + Search image on Google +
    • +
    • handleMobileOptionClick(comment.cid)}> + Search image on Yandex +
    • +
    • handleMobileOptionClick(comment.cid)}> + Search image on SauceNAO +
    • + + ) : null + } +
    +
    , document.body + )} {comment?.author?.displayName ? comment?.author?.displayName.length > 15 @@ -1940,7 +2058,96 @@ const Thread = () => {
    - + + {createPortal( + {postMenuMobileRef.current = el}} + onClick={(event) => event.stopPropagation()} + style={{position: "absolute", + display: openMobileMenuCid === reply.cid ? "block" : "none", + top: mobileMenuPosition.top + 20, + left: mobileMenuPosition.left}}> +
      +
    • { + handleMobileOptionClick(reply.cid); + handleShareClick(selectedAddress, reply.cid) + }}>Share thread
    • + {({ authorAddress }) => ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + <> +
    • handleAuthorEditClick(reply)}>Edit post
    • +
    • handleAuthorDeleteClick(reply)}>Delete post
    • + + ) : null} + {isModerator ? ( + <> + {authorAddress === account?.author.address || + authorAddress === account?.signer.address ? ( + null + ) : ( +
    • { + setModeratingCommentCid(reply.cid) + setIsModerationOpen(true); + handleMobileOptionClick(reply.cid); + setDeletePost(true); + }}> + Delete post +
    • + )} +
    • { + setModeratingCommentCid(reply.cid) + setIsModerationOpen(true); + handleMobileOptionClick(reply.cid); + }}> + Mod tools +
    • + + ) : null} + + )}
      + {(replyMediaInfo && ( + replyMediaInfo.type === 'image' || + (replyMediaInfo.type === 'webpage' && + replyMediaInfo.thumbnail))) ? ( + <> +
    • handleMobileOptionClick(reply.cid)}> + Search image on Google +
    • +
    • handleMobileOptionClick(reply.cid)}> + Search image on Yandex +
    • +
    • handleMobileOptionClick(reply.cid)}> + Search image on SauceNAO +
    • + + ) : null + } +
    +
    , document.body + )} {reply.author?.displayName ? reply.author?.displayName.length > 12