mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
add mobile post menu
This commit is contained in:
@@ -4091,4 +4091,133 @@ export const PostMenu = styled.div`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: transform 0.1s;
|
transition: transform 0.1s;
|
||||||
transform: ${props => props.rotated ? 'rotate(90deg)' : 'none'};
|
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 '';
|
||||||
|
}}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
@@ -8,7 +8,7 @@ import { Virtuoso } from 'react-virtuoso';
|
|||||||
import { useAccount, useAccountComments, useFeed, usePublishCommentEdit, useSubplebbits } from '@plebbit/plebbit-react-hooks';
|
import { useAccount, useAccountComments, useFeed, usePublishCommentEdit, useSubplebbits } from '@plebbit/plebbit-react-hooks';
|
||||||
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'
|
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'
|
||||||
import { debounce } from 'lodash';
|
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 { Footer } from '../styled/views/Thread.styled';
|
||||||
import { PostMenuCatalog } from '../styled/views/Catalog.styled';
|
import { PostMenuCatalog } from '../styled/views/Catalog.styled';
|
||||||
import { AlertModal } from '../styled/modals/AlertModal.styled';
|
import { AlertModal } from '../styled/modals/AlertModal.styled';
|
||||||
@@ -85,8 +85,11 @@ const All = () => {
|
|||||||
const [, setNewSuccessMessage] = useSuccess();
|
const [, setNewSuccessMessage] = useSuccess();
|
||||||
|
|
||||||
const threadMenuRefs = useRef({});
|
const threadMenuRefs = useRef({});
|
||||||
|
const threadMenuRefsMobile = useRef({});
|
||||||
const replyMenuRefs = useRef({});
|
const replyMenuRefs = useRef({});
|
||||||
|
const replyMenuRefsMobile = useRef({});
|
||||||
const postMenuCatalogRef = useRef(null);
|
const postMenuCatalogRef = useRef(null);
|
||||||
|
const postMenuMobileRef = useRef(null);
|
||||||
const backlinkRefs = useRef({});
|
const backlinkRefs = useRef({});
|
||||||
const quoteRefs = useRef({});
|
const quoteRefs = useRef({});
|
||||||
const postRefs = useRef({});
|
const postRefs = useRef({});
|
||||||
@@ -105,8 +108,10 @@ const All = () => {
|
|||||||
const [isClientRedirectMenuOpen, setIsClientRedirectMenuOpen] = useState(false);
|
const [isClientRedirectMenuOpen, setIsClientRedirectMenuOpen] = useState(false);
|
||||||
const [commentCid, setCommentCid] = useState(null);
|
const [commentCid, setCommentCid] = useState(null);
|
||||||
const [menuPosition, setMenuPosition] = useState({top: 0, left: 0});
|
const [menuPosition, setMenuPosition] = useState({top: 0, left: 0});
|
||||||
|
const [mobileMenuPosition, setMobileMenuPosition] = useState({top: 0, left: 0});
|
||||||
const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false);
|
const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false);
|
||||||
const [openMenuCid, setOpenMenuCid] = useState(null);
|
const [openMenuCid, setOpenMenuCid] = useState(null);
|
||||||
|
const [openMobileMenuCid, setOpenMobileMenuCid] = useState(null);
|
||||||
const [outOfViewCid, setOutOfViewCid] = useState(null);
|
const [outOfViewCid, setOutOfViewCid] = useState(null);
|
||||||
const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0});
|
const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0});
|
||||||
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
|
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
|
||||||
@@ -196,12 +201,22 @@ const All = () => {
|
|||||||
setOpenMenuCid(null);
|
setOpenMenuCid(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleMobileOptionClick = () => {
|
||||||
|
setOpenMobileMenuCid(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleOutsideClick = useCallback((e) => {
|
const handleOutsideClick = useCallback((e) => {
|
||||||
if (openMenuCid !== null && !postMenuCatalogRef.current.contains(e.target)) {
|
if (openMenuCid !== null && !postMenuCatalogRef.current.contains(e.target)) {
|
||||||
setOpenMenuCid(null);
|
setOpenMenuCid(null);
|
||||||
}
|
}
|
||||||
}, [openMenuCid, postMenuCatalogRef]);
|
}, [openMenuCid, postMenuCatalogRef]);
|
||||||
|
|
||||||
|
const handleMobileOutsideClick = useCallback((e) => {
|
||||||
|
if (openMobileMenuCid !== null && !postMenuMobileRef.current.contains(e.target)) {
|
||||||
|
setOpenMobileMenuCid(null);
|
||||||
|
}
|
||||||
|
}, [openMobileMenuCid, postMenuMobileRef]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -217,6 +232,19 @@ const All = () => {
|
|||||||
}, [openMenuCid, handleOutsideClick]);
|
}, [openMenuCid, handleOutsideClick]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (openMobileMenuCid !== null) {
|
||||||
|
document.addEventListener('click', handleMobileOutsideClick);
|
||||||
|
} else {
|
||||||
|
document.removeEventListener('click', handleMobileOutsideClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('click', handleMobileOutsideClick);
|
||||||
|
};
|
||||||
|
}, [openMobileMenuCid, handleMobileOutsideClick]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (postOnHoverRef.current) {
|
if (postOnHoverRef.current) {
|
||||||
const rect = postOnHoverRef.current.getBoundingClientRect();
|
const rect = postOnHoverRef.current.getBoundingClientRect();
|
||||||
@@ -1321,7 +1349,7 @@ const All = () => {
|
|||||||
</li>
|
</li>
|
||||||
<li onClick={() => handleOptionClick(reply.cid)}>
|
<li onClick={() => handleOptionClick(reply.cid)}>
|
||||||
<a
|
<a
|
||||||
href={`https://yandex.com/images/search?url=${replyMediaInfo.url}`}
|
href={`https://yandex.com/images/search?img_url=${replyMediaInfo.url}&rpt=imageview`}
|
||||||
target="_blank" rel="noreferrer"
|
target="_blank" rel="noreferrer"
|
||||||
>Yandex</a>
|
>Yandex</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -1748,7 +1776,96 @@ const All = () => {
|
|||||||
<div key={`mob-c-${index}`} className="op-container">
|
<div key={`mob-c-${index}`} className="op-container">
|
||||||
<div key={`mob-po-${index}`} className="post op op-mobile">
|
<div key={`mob-po-${index}`} className="post op op-mobile">
|
||||||
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
||||||
<button key={`mob-pb-${index}`} className="post-menu-button-mobile" style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
<button key={`mob-pb-${index}`} className="post-menu-button-mobile"
|
||||||
|
ref={el => {
|
||||||
|
threadMenuRefsMobile.current[thread.cid] = el;
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const rect = threadMenuRefsMobile.current[thread.cid].getBoundingClientRect();
|
||||||
|
setMobileMenuPosition({top: rect.top + window.scrollY, left: rect.left});
|
||||||
|
setOpenMobileMenuCid(prevCid => (prevCid === thread.cid ? null : thread.cid));
|
||||||
|
}}
|
||||||
|
style={{ all: 'unset', cursor: 'pointer' }}
|
||||||
|
>...</button>
|
||||||
|
{createPortal(
|
||||||
|
<PostMenuMobile selectedStyle={selectedStyle}
|
||||||
|
ref={el => {postMenuMobileRef.current = el}}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
style={{position: "absolute",
|
||||||
|
display: openMobileMenuCid === thread.cid ? "block" : "none",
|
||||||
|
top: mobileMenuPosition.top + 20,
|
||||||
|
left: mobileMenuPosition.left}}>
|
||||||
|
<ul className={`post-menu-mobile-thread-${thread.cid}`}>
|
||||||
|
<li onClick={() => {
|
||||||
|
handleMobileOptionClick(thread.cid);
|
||||||
|
handleShareClick(selectedAddress, thread.cid)
|
||||||
|
}}>Share thread</li>
|
||||||
|
<VerifiedAuthor commentCid={thread.cid}>{({ authorAddress }) => (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleAuthorEditClick(thread)}>Edit post</li>
|
||||||
|
<li onClick={() => handleAuthorDeleteClick(thread)}>Delete post</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{isModerator ? (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
null
|
||||||
|
) : (
|
||||||
|
<li onClick={() => {
|
||||||
|
setModeratingCommentCid(thread.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(thread.cid);
|
||||||
|
setDeletePost(true);
|
||||||
|
}}>
|
||||||
|
Delete post
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
<li
|
||||||
|
onClick={() => {
|
||||||
|
setModeratingCommentCid(thread.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(thread.cid);
|
||||||
|
}}>
|
||||||
|
Mod tools
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}</VerifiedAuthor>
|
||||||
|
{(commentMediaInfo && (
|
||||||
|
commentMediaInfo.type === 'image' ||
|
||||||
|
(commentMediaInfo.type === 'webpage' &&
|
||||||
|
commentMediaInfo.thumbnail))) ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleMobileOptionClick(thread.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://lens.google.com/uploadbyurl?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Google</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(thread.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://yandex.com/images/search?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Yandex</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(thread.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://saucenao.com/search.php?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on SauceNAO</a>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</PostMenuMobile>, document.body
|
||||||
|
)}
|
||||||
<span key={`mob-nbm-${index}`} className="name-block-mobile">
|
<span key={`mob-nbm-${index}`} className="name-block-mobile">
|
||||||
{thread.author.displayName
|
{thread.author.displayName
|
||||||
? thread.author.displayName.length > 20
|
? thread.author.displayName.length > 20
|
||||||
@@ -1985,7 +2102,96 @@ const All = () => {
|
|||||||
<div key={`mob-rc-${index}`} className="reply-container">
|
<div key={`mob-rc-${index}`} className="reply-container">
|
||||||
<div key={`mob-pr-${index}`} className="post-reply post-reply-mobile">
|
<div key={`mob-pr-${index}`} className="post-reply post-reply-mobile">
|
||||||
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
||||||
<button key={`pmbm-${index}`} className="post-menu-button-mobile" title="Post menu" style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
<button key={`mob-pb-${index}`} className="post-menu-button-mobile"
|
||||||
|
ref={el => {
|
||||||
|
replyMenuRefsMobile.current[reply.cid] = el;
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const rect = replyMenuRefsMobile.current[reply.cid].getBoundingClientRect();
|
||||||
|
setMobileMenuPosition({top: rect.top + window.scrollY, left: rect.left});
|
||||||
|
setOpenMobileMenuCid(prevCid => (prevCid === reply.cid ? null : reply.cid));
|
||||||
|
}}
|
||||||
|
style={{ all: 'unset', cursor: 'pointer' }}
|
||||||
|
>...</button>
|
||||||
|
{createPortal(
|
||||||
|
<PostMenuMobile selectedStyle={selectedStyle}
|
||||||
|
ref={el => {postMenuMobileRef.current = el}}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
style={{position: "absolute",
|
||||||
|
display: openMobileMenuCid === reply.cid ? "block" : "none",
|
||||||
|
top: mobileMenuPosition.top + 20,
|
||||||
|
left: mobileMenuPosition.left}}>
|
||||||
|
<ul className={`post-menu-mobile-thread-${reply.cid}`}>
|
||||||
|
<li onClick={() => {
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
handleShareClick(selectedAddress, reply.cid)
|
||||||
|
}}>Share thread</li>
|
||||||
|
<VerifiedAuthor commentCid={reply.cid}>{({ authorAddress }) => (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleAuthorEditClick(reply)}>Edit post</li>
|
||||||
|
<li onClick={() => handleAuthorDeleteClick(reply)}>Delete post</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{isModerator ? (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
null
|
||||||
|
) : (
|
||||||
|
<li onClick={() => {
|
||||||
|
setModeratingCommentCid(reply.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
setDeletePost(true);
|
||||||
|
}}>
|
||||||
|
Delete post
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
<li
|
||||||
|
onClick={() => {
|
||||||
|
setModeratingCommentCid(reply.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
}}>
|
||||||
|
Mod tools
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}</VerifiedAuthor>
|
||||||
|
{(replyMediaInfo && (
|
||||||
|
replyMediaInfo.type === 'image' ||
|
||||||
|
(replyMediaInfo.type === 'webpage' &&
|
||||||
|
replyMediaInfo.thumbnail))) ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://lens.google.com/uploadbyurl?url=${replyMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Google</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://yandex.com/images/search?img_url=${replyMediaInfo.url}&rpt=imageview`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Yandex</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://saucenao.com/search.php?url=${replyMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on SauceNAO</a>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</PostMenuMobile>, document.body
|
||||||
|
)}
|
||||||
<span key={`mob-nb-${index}`} className="name-block-mobile">
|
<span key={`mob-nb-${index}`} className="name-block-mobile">
|
||||||
{reply.author.displayName
|
{reply.author.displayName
|
||||||
? reply.author.displayName.length > 20
|
? reply.author.displayName.length > 20
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { Virtuoso } from 'react-virtuoso';
|
|||||||
import { useAccount, useAccountComments, useFeed, usePublishComment, usePublishCommentEdit, useSubplebbit, useSubscribe } from '@plebbit/plebbit-react-hooks';
|
import { useAccount, useAccountComments, useFeed, usePublishComment, usePublishCommentEdit, useSubplebbit, useSubscribe } from '@plebbit/plebbit-react-hooks';
|
||||||
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'
|
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'
|
||||||
import { debounce } from 'lodash';
|
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 { Footer } from '../styled/views/Thread.styled';
|
||||||
import { AlertModal } from '../styled/modals/AlertModal.styled';
|
import { AlertModal } from '../styled/modals/AlertModal.styled';
|
||||||
import { PostMenuCatalog } from '../styled/views/Catalog.styled';
|
import { PostMenuCatalog } from '../styled/views/Catalog.styled';
|
||||||
@@ -105,8 +105,11 @@ const Board = () => {
|
|||||||
const commentRef = useRef();
|
const commentRef = useRef();
|
||||||
const linkRef = useRef();
|
const linkRef = useRef();
|
||||||
const threadMenuRefs = useRef({});
|
const threadMenuRefs = useRef({});
|
||||||
|
const threadMenuRefsMobile = useRef({});
|
||||||
const replyMenuRefs = useRef({});
|
const replyMenuRefs = useRef({});
|
||||||
|
const replyMenuRefsMobile = useRef({});
|
||||||
const postMenuCatalogRef = useRef(null);
|
const postMenuCatalogRef = useRef(null);
|
||||||
|
const postMenuMobileRef = useRef(null);
|
||||||
const backlinkRefs = useRef({});
|
const backlinkRefs = useRef({});
|
||||||
const quoteRefs = useRef({});
|
const quoteRefs = useRef({});
|
||||||
const postRefs = useRef({});
|
const postRefs = useRef({});
|
||||||
@@ -136,7 +139,9 @@ const Board = () => {
|
|||||||
const [isModerator, setIsModerator] = useState(false);
|
const [isModerator, setIsModerator] = useState(false);
|
||||||
const [commentCid, setCommentCid] = useState(null);
|
const [commentCid, setCommentCid] = useState(null);
|
||||||
const [menuPosition, setMenuPosition] = useState({top: 0, left: 0});
|
const [menuPosition, setMenuPosition] = useState({top: 0, left: 0});
|
||||||
|
const [mobileMenuPosition, setMobileMenuPosition] = useState({top: 0, left: 0});
|
||||||
const [openMenuCid, setOpenMenuCid] = useState(null);
|
const [openMenuCid, setOpenMenuCid] = useState(null);
|
||||||
|
const [openMobileMenuCid, setOpenMobileMenuCid] = useState(null);
|
||||||
const [outOfViewCid, setOutOfViewCid] = useState(null);
|
const [outOfViewCid, setOutOfViewCid] = useState(null);
|
||||||
const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0});
|
const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0});
|
||||||
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
|
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
|
||||||
@@ -210,12 +215,22 @@ const Board = () => {
|
|||||||
setOpenMenuCid(null);
|
setOpenMenuCid(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleMobileOptionClick = () => {
|
||||||
|
setOpenMobileMenuCid(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleOutsideClick = useCallback((e) => {
|
const handleOutsideClick = useCallback((e) => {
|
||||||
if (openMenuCid !== null && !postMenuCatalogRef.current.contains(e.target)) {
|
if (openMenuCid !== null && !postMenuCatalogRef.current.contains(e.target)) {
|
||||||
setOpenMenuCid(null);
|
setOpenMenuCid(null);
|
||||||
}
|
}
|
||||||
}, [openMenuCid, postMenuCatalogRef]);
|
}, [openMenuCid, postMenuCatalogRef]);
|
||||||
|
|
||||||
|
const handleMobileOutsideClick = useCallback((e) => {
|
||||||
|
if (openMobileMenuCid !== null && !postMenuMobileRef.current.contains(e.target)) {
|
||||||
|
setOpenMobileMenuCid(null);
|
||||||
|
}
|
||||||
|
}, [openMobileMenuCid, postMenuMobileRef]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -231,6 +246,19 @@ const Board = () => {
|
|||||||
}, [openMenuCid, handleOutsideClick]);
|
}, [openMenuCid, handleOutsideClick]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (openMobileMenuCid !== null) {
|
||||||
|
document.addEventListener('click', handleMobileOutsideClick);
|
||||||
|
} else {
|
||||||
|
document.removeEventListener('click', handleMobileOutsideClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('click', handleMobileOutsideClick);
|
||||||
|
};
|
||||||
|
}, [openMobileMenuCid, handleMobileOutsideClick]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (postOnHoverRef.current) {
|
if (postOnHoverRef.current) {
|
||||||
const rect = postOnHoverRef.current.getBoundingClientRect();
|
const rect = postOnHoverRef.current.getBoundingClientRect();
|
||||||
@@ -1038,7 +1066,32 @@ const Board = () => {
|
|||||||
<div className="post op op-mobile">
|
<div className="post op op-mobile">
|
||||||
<div className="post-info-mobile">
|
<div className="post-info-mobile">
|
||||||
<button className="post-menu-button-mobile"
|
<button className="post-menu-button-mobile"
|
||||||
|
ref={el => {
|
||||||
|
threadMenuRefsMobile.current["rules"] = el;
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const rect = threadMenuRefsMobile.current["rules"].getBoundingClientRect();
|
||||||
|
setMobileMenuPosition({top: rect.top + window.scrollY, left: rect.left});
|
||||||
|
setOpenMobileMenuCid(prevCid => (prevCid === "rules" ? null : "rules"));
|
||||||
|
}}
|
||||||
style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
||||||
|
{createPortal(
|
||||||
|
<PostMenuMobile selectedStyle={selectedStyle}
|
||||||
|
ref={el => {postMenuMobileRef.current = el}}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
style={{position: "absolute",
|
||||||
|
display: openMobileMenuCid === "rules" ? "block" : "none",
|
||||||
|
top: mobileMenuPosition.top + 20,
|
||||||
|
left: mobileMenuPosition.left}}>
|
||||||
|
<ul className={`post-menu-mobile-thread-${"rules"}`}>
|
||||||
|
<li onClick={() => {
|
||||||
|
handleMobileOptionClick("rules");
|
||||||
|
handleShareClick(selectedAddress, "rules")
|
||||||
|
}}>Share board</li>
|
||||||
|
</ul>
|
||||||
|
</PostMenuMobile>, document.body
|
||||||
|
)}
|
||||||
<span className="name-block-mobile">
|
<span className="name-block-mobile">
|
||||||
<span className="name-mobile capcode"
|
<span className="name-mobile capcode"
|
||||||
style={{cursor: 'pointer'}}
|
style={{cursor: 'pointer'}}
|
||||||
@@ -1171,8 +1224,33 @@ const Board = () => {
|
|||||||
<div className="op-container">
|
<div className="op-container">
|
||||||
<div className="post op op-mobile">
|
<div className="post op op-mobile">
|
||||||
<div className="post-info-mobile">
|
<div className="post-info-mobile">
|
||||||
<button className="post-menu-button-mobile"
|
<button className="post-menu-button-mobile"
|
||||||
|
ref={el => {
|
||||||
|
threadMenuRefsMobile.current["rules"] = el;
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const rect = threadMenuRefsMobile.current["rules"].getBoundingClientRect();
|
||||||
|
setMobileMenuPosition({top: rect.top + window.scrollY, left: rect.left});
|
||||||
|
setOpenMobileMenuCid(prevCid => (prevCid === "rules" ? null : "rules"));
|
||||||
|
}}
|
||||||
style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
||||||
|
{createPortal(
|
||||||
|
<PostMenuMobile selectedStyle={selectedStyle}
|
||||||
|
ref={el => {postMenuMobileRef.current = el}}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
style={{position: "absolute",
|
||||||
|
display: openMobileMenuCid === "rules" ? "block" : "none",
|
||||||
|
top: mobileMenuPosition.top + 20,
|
||||||
|
left: mobileMenuPosition.left}}>
|
||||||
|
<ul className={`post-menu-mobile-thread-${"rules"}`}>
|
||||||
|
<li onClick={() => {
|
||||||
|
handleMobileOptionClick("rules");
|
||||||
|
handleShareClick(selectedAddress, "rules")
|
||||||
|
}}>Share board</li>
|
||||||
|
</ul>
|
||||||
|
</PostMenuMobile>, document.body
|
||||||
|
)}
|
||||||
<span className="name-block-mobile">
|
<span className="name-block-mobile">
|
||||||
<span className="name-mobile capcode"
|
<span className="name-mobile capcode"
|
||||||
style={{cursor: 'pointer'}}
|
style={{cursor: 'pointer'}}
|
||||||
@@ -1325,8 +1403,33 @@ const Board = () => {
|
|||||||
<div className="op-container">
|
<div className="op-container">
|
||||||
<div className="post op op-mobile">
|
<div className="post op op-mobile">
|
||||||
<div className="post-info-mobile">
|
<div className="post-info-mobile">
|
||||||
<button className="post-menu-button-mobile"
|
<button className="post-menu-button-mobile"
|
||||||
|
ref={el => {
|
||||||
|
threadMenuRefsMobile.current["description"] = el;
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const rect = threadMenuRefsMobile.current["description"].getBoundingClientRect();
|
||||||
|
setMobileMenuPosition({top: rect.top + window.scrollY, left: rect.left});
|
||||||
|
setOpenMobileMenuCid(prevCid => (prevCid === "description" ? null : "description"));
|
||||||
|
}}
|
||||||
style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
||||||
|
{createPortal(
|
||||||
|
<PostMenuMobile selectedStyle={selectedStyle}
|
||||||
|
ref={el => {postMenuMobileRef.current = el}}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
style={{position: "absolute",
|
||||||
|
display: openMobileMenuCid === "description" ? "block" : "none",
|
||||||
|
top: mobileMenuPosition.top + 20,
|
||||||
|
left: mobileMenuPosition.left}}>
|
||||||
|
<ul className={`post-menu-mobile-thread-${"description"}`}>
|
||||||
|
<li onClick={() => {
|
||||||
|
handleMobileOptionClick("description");
|
||||||
|
handleShareClick(selectedAddress, "description")
|
||||||
|
}}>Share board</li>
|
||||||
|
</ul>
|
||||||
|
</PostMenuMobile>, document.body
|
||||||
|
)}
|
||||||
<span className="name-block-mobile">
|
<span className="name-block-mobile">
|
||||||
<span className="name-mobile capcode"
|
<span className="name-mobile capcode"
|
||||||
style={{cursor: 'pointer'}}
|
style={{cursor: 'pointer'}}
|
||||||
@@ -2011,7 +2114,7 @@ const Board = () => {
|
|||||||
</li>
|
</li>
|
||||||
<li onClick={() => handleOptionClick(reply.cid)}>
|
<li onClick={() => handleOptionClick(reply.cid)}>
|
||||||
<a
|
<a
|
||||||
href={`https://yandex.com/images/search?url=${replyMediaInfo.url}`}
|
href={`https://yandex.com/images/search?img_url=${replyMediaInfo.url}&rpt=imageview`}
|
||||||
target="_blank" rel="noreferrer"
|
target="_blank" rel="noreferrer"
|
||||||
>Yandex</a>
|
>Yandex</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -2439,7 +2542,96 @@ const Board = () => {
|
|||||||
<div key={`mob-c-${index}`} className="op-container">
|
<div key={`mob-c-${index}`} className="op-container">
|
||||||
<div key={`mob-po-${index}`} className="post op op-mobile">
|
<div key={`mob-po-${index}`} className="post op op-mobile">
|
||||||
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
||||||
<button key={`mob-pb-${index}`} className="post-menu-button-mobile" style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
<button key={`mob-pb-${index}`} className="post-menu-button-mobile"
|
||||||
|
ref={el => {
|
||||||
|
threadMenuRefsMobile.current[thread.cid] = el;
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const rect = threadMenuRefsMobile.current[thread.cid].getBoundingClientRect();
|
||||||
|
setMobileMenuPosition({top: rect.top + window.scrollY, left: rect.left});
|
||||||
|
setOpenMobileMenuCid(prevCid => (prevCid === thread.cid ? null : thread.cid));
|
||||||
|
}}
|
||||||
|
style={{ all: 'unset', cursor: 'pointer' }}
|
||||||
|
>...</button>
|
||||||
|
{createPortal(
|
||||||
|
<PostMenuMobile selectedStyle={selectedStyle}
|
||||||
|
ref={el => {postMenuMobileRef.current = el}}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
style={{position: "absolute",
|
||||||
|
display: openMobileMenuCid === thread.cid ? "block" : "none",
|
||||||
|
top: mobileMenuPosition.top + 20,
|
||||||
|
left: mobileMenuPosition.left}}>
|
||||||
|
<ul className={`post-menu-mobile-thread-${thread.cid}`}>
|
||||||
|
<li onClick={() => {
|
||||||
|
handleMobileOptionClick(thread.cid);
|
||||||
|
handleShareClick(selectedAddress, thread.cid)
|
||||||
|
}}>Share thread</li>
|
||||||
|
<VerifiedAuthor commentCid={thread.cid}>{({ authorAddress }) => (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleAuthorEditClick(thread)}>Edit post</li>
|
||||||
|
<li onClick={() => handleAuthorDeleteClick(thread)}>Delete post</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{isModerator ? (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
null
|
||||||
|
) : (
|
||||||
|
<li onClick={() => {
|
||||||
|
setModeratingCommentCid(thread.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(thread.cid);
|
||||||
|
setDeletePost(true);
|
||||||
|
}}>
|
||||||
|
Delete post
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
<li
|
||||||
|
onClick={() => {
|
||||||
|
setModeratingCommentCid(thread.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(thread.cid);
|
||||||
|
}}>
|
||||||
|
Mod tools
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}</VerifiedAuthor>
|
||||||
|
{(commentMediaInfo && (
|
||||||
|
commentMediaInfo.type === 'image' ||
|
||||||
|
(commentMediaInfo.type === 'webpage' &&
|
||||||
|
commentMediaInfo.thumbnail))) ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleMobileOptionClick(thread.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://lens.google.com/uploadbyurl?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Google</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(thread.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://yandex.com/images/search?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Yandex</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(thread.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://saucenao.com/search.php?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on SauceNAO</a>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</PostMenuMobile>, document.body
|
||||||
|
)}
|
||||||
<span key={`mob-nbm-${index}`} className="name-block-mobile">
|
<span key={`mob-nbm-${index}`} className="name-block-mobile">
|
||||||
{thread.author.displayName
|
{thread.author.displayName
|
||||||
? thread.author.displayName.length > 20
|
? thread.author.displayName.length > 20
|
||||||
@@ -2677,7 +2869,96 @@ const Board = () => {
|
|||||||
<div key={`mob-rc-${index}`} className="reply-container">
|
<div key={`mob-rc-${index}`} className="reply-container">
|
||||||
<div key={`mob-pr-${index}`} className="post-reply post-reply-mobile">
|
<div key={`mob-pr-${index}`} className="post-reply post-reply-mobile">
|
||||||
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
||||||
<button key={`pmbm-${index}`} className="post-menu-button-mobile" title="Post menu" style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
<button key={`mob-pb-${index}`} className="post-menu-button-mobile"
|
||||||
|
ref={el => {
|
||||||
|
replyMenuRefsMobile.current[reply.cid] = el;
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const rect = replyMenuRefsMobile.current[reply.cid].getBoundingClientRect();
|
||||||
|
setMobileMenuPosition({top: rect.top + window.scrollY, left: rect.left});
|
||||||
|
setOpenMobileMenuCid(prevCid => (prevCid === reply.cid ? null : reply.cid));
|
||||||
|
}}
|
||||||
|
style={{ all: 'unset', cursor: 'pointer' }}
|
||||||
|
>...</button>
|
||||||
|
{createPortal(
|
||||||
|
<PostMenuMobile selectedStyle={selectedStyle}
|
||||||
|
ref={el => {postMenuMobileRef.current = el}}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
style={{position: "absolute",
|
||||||
|
display: openMobileMenuCid === reply.cid ? "block" : "none",
|
||||||
|
top: mobileMenuPosition.top + 20,
|
||||||
|
left: mobileMenuPosition.left}}>
|
||||||
|
<ul className={`post-menu-mobile-thread-${reply.cid}`}>
|
||||||
|
<li onClick={() => {
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
handleShareClick(selectedAddress, reply.cid)
|
||||||
|
}}>Share thread</li>
|
||||||
|
<VerifiedAuthor commentCid={reply.cid}>{({ authorAddress }) => (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleAuthorEditClick(reply)}>Edit post</li>
|
||||||
|
<li onClick={() => handleAuthorDeleteClick(reply)}>Delete post</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{isModerator ? (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
null
|
||||||
|
) : (
|
||||||
|
<li onClick={() => {
|
||||||
|
setModeratingCommentCid(reply.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
setDeletePost(true);
|
||||||
|
}}>
|
||||||
|
Delete post
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
<li
|
||||||
|
onClick={() => {
|
||||||
|
setModeratingCommentCid(reply.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
}}>
|
||||||
|
Mod tools
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}</VerifiedAuthor>
|
||||||
|
{(replyMediaInfo && (
|
||||||
|
replyMediaInfo.type === 'image' ||
|
||||||
|
(replyMediaInfo.type === 'webpage' &&
|
||||||
|
replyMediaInfo.thumbnail))) ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://lens.google.com/uploadbyurl?url=${replyMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Google</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://yandex.com/images/search?img_url=${replyMediaInfo.url}&rpt=imageview`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Yandex</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://saucenao.com/search.php?url=${replyMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on SauceNAO</a>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</PostMenuMobile>, document.body
|
||||||
|
)}
|
||||||
<span key={`mob-nb-${index}`} className="name-block-mobile">
|
<span key={`mob-nb-${index}`} className="name-block-mobile">
|
||||||
{reply.author.displayName
|
{reply.author.displayName
|
||||||
? reply.author.displayName.length > 20
|
? reply.author.displayName.length > 20
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { useAccount, useAccountComments, useFeed, usePublishCommentEdit, useSubp
|
|||||||
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'
|
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import useGeneralStore from '../../hooks/stores/useGeneralStore';
|
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 { Footer } from '../styled/views/Thread.styled';
|
||||||
import { AlertModal } from '../styled/modals/AlertModal.styled';
|
import { AlertModal } from '../styled/modals/AlertModal.styled';
|
||||||
import { PostMenuCatalog } from '../styled/views/Catalog.styled';
|
import { PostMenuCatalog } from '../styled/views/Catalog.styled';
|
||||||
@@ -85,8 +85,11 @@ const Subscriptions = () => {
|
|||||||
const [, setNewSuccessMessage] = useSuccess();
|
const [, setNewSuccessMessage] = useSuccess();
|
||||||
|
|
||||||
const threadMenuRefs = useRef({});
|
const threadMenuRefs = useRef({});
|
||||||
|
const threadMenuRefsMobile = useRef({});
|
||||||
const replyMenuRefs = useRef({});
|
const replyMenuRefs = useRef({});
|
||||||
|
const replyMenuRefsMobile = useRef({});
|
||||||
const postMenuCatalogRef = useRef(null);
|
const postMenuCatalogRef = useRef(null);
|
||||||
|
const postMenuMobileRef = useRef(null);
|
||||||
const backlinkRefs = useRef({});
|
const backlinkRefs = useRef({});
|
||||||
const quoteRefs = useRef({});
|
const quoteRefs = useRef({});
|
||||||
const postRefs = useRef({});
|
const postRefs = useRef({});
|
||||||
@@ -105,8 +108,10 @@ const Subscriptions = () => {
|
|||||||
const [isClientRedirectMenuOpen, setIsClientRedirectMenuOpen] = useState(false);
|
const [isClientRedirectMenuOpen, setIsClientRedirectMenuOpen] = useState(false);
|
||||||
const [commentCid, setCommentCid] = useState(null);
|
const [commentCid, setCommentCid] = useState(null);
|
||||||
const [menuPosition, setMenuPosition] = useState({top: 0, left: 0});
|
const [menuPosition, setMenuPosition] = useState({top: 0, left: 0});
|
||||||
|
const [mobileMenuPosition, setMobileMenuPosition] = useState({top: 0, left: 0});
|
||||||
const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false);
|
const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false);
|
||||||
const [openMenuCid, setOpenMenuCid] = useState(null);
|
const [openMenuCid, setOpenMenuCid] = useState(null);
|
||||||
|
const [openMobileMenuCid, setOpenMobileMenuCid] = useState(null);
|
||||||
const [outOfViewCid, setOutOfViewCid] = useState(null);
|
const [outOfViewCid, setOutOfViewCid] = useState(null);
|
||||||
const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0});
|
const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0});
|
||||||
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
|
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
|
||||||
@@ -196,12 +201,22 @@ const Subscriptions = () => {
|
|||||||
setOpenMenuCid(null);
|
setOpenMenuCid(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleMobileOptionClick = () => {
|
||||||
|
setOpenMobileMenuCid(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleOutsideClick = useCallback((e) => {
|
const handleOutsideClick = useCallback((e) => {
|
||||||
if (openMenuCid !== null && !postMenuCatalogRef.current.contains(e.target)) {
|
if (openMenuCid !== null && !postMenuCatalogRef.current.contains(e.target)) {
|
||||||
setOpenMenuCid(null);
|
setOpenMenuCid(null);
|
||||||
}
|
}
|
||||||
}, [openMenuCid, postMenuCatalogRef]);
|
}, [openMenuCid, postMenuCatalogRef]);
|
||||||
|
|
||||||
|
const handleMobileOutsideClick = useCallback((e) => {
|
||||||
|
if (openMobileMenuCid !== null && !postMenuMobileRef.current.contains(e.target)) {
|
||||||
|
setOpenMobileMenuCid(null);
|
||||||
|
}
|
||||||
|
}, [openMobileMenuCid, postMenuMobileRef]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -217,6 +232,19 @@ const Subscriptions = () => {
|
|||||||
}, [openMenuCid, handleOutsideClick]);
|
}, [openMenuCid, handleOutsideClick]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (openMobileMenuCid !== null) {
|
||||||
|
document.addEventListener('click', handleMobileOutsideClick);
|
||||||
|
} else {
|
||||||
|
document.removeEventListener('click', handleMobileOutsideClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('click', handleMobileOutsideClick);
|
||||||
|
};
|
||||||
|
}, [openMobileMenuCid, handleMobileOutsideClick]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (postOnHoverRef.current) {
|
if (postOnHoverRef.current) {
|
||||||
const rect = postOnHoverRef.current.getBoundingClientRect();
|
const rect = postOnHoverRef.current.getBoundingClientRect();
|
||||||
@@ -1329,7 +1357,7 @@ const Subscriptions = () => {
|
|||||||
</li>
|
</li>
|
||||||
<li onClick={() => handleOptionClick(reply.cid)}>
|
<li onClick={() => handleOptionClick(reply.cid)}>
|
||||||
<a
|
<a
|
||||||
href={`https://yandex.com/images/search?url=${replyMediaInfo.url}`}
|
href={`https://yandex.com/images/search?img_url=${replyMediaInfo.url}&rpt=imageview`}
|
||||||
target="_blank" rel="noreferrer"
|
target="_blank" rel="noreferrer"
|
||||||
>Yandex</a>
|
>Yandex</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -1756,7 +1784,96 @@ const Subscriptions = () => {
|
|||||||
<div key={`mob-c-${index}`} className="op-container">
|
<div key={`mob-c-${index}`} className="op-container">
|
||||||
<div key={`mob-po-${index}`} className="post op op-mobile">
|
<div key={`mob-po-${index}`} className="post op op-mobile">
|
||||||
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
||||||
<button key={`mob-pb-${index}`} className="post-menu-button-mobile" style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
<button key={`mob-pb-${index}`} className="post-menu-button-mobile"
|
||||||
|
ref={el => {
|
||||||
|
threadMenuRefsMobile.current[thread.cid] = el;
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const rect = threadMenuRefsMobile.current[thread.cid].getBoundingClientRect();
|
||||||
|
setMobileMenuPosition({top: rect.top + window.scrollY, left: rect.left});
|
||||||
|
setOpenMobileMenuCid(prevCid => (prevCid === thread.cid ? null : thread.cid));
|
||||||
|
}}
|
||||||
|
style={{ all: 'unset', cursor: 'pointer' }}
|
||||||
|
>...</button>
|
||||||
|
{createPortal(
|
||||||
|
<PostMenuMobile selectedStyle={selectedStyle}
|
||||||
|
ref={el => {postMenuMobileRef.current = el}}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
style={{position: "absolute",
|
||||||
|
display: openMobileMenuCid === thread.cid ? "block" : "none",
|
||||||
|
top: mobileMenuPosition.top + 20,
|
||||||
|
left: mobileMenuPosition.left}}>
|
||||||
|
<ul className={`post-menu-mobile-thread-${thread.cid}`}>
|
||||||
|
<li onClick={() => {
|
||||||
|
handleMobileOptionClick(thread.cid);
|
||||||
|
handleShareClick(selectedAddress, thread.cid)
|
||||||
|
}}>Share thread</li>
|
||||||
|
<VerifiedAuthor commentCid={thread.cid}>{({ authorAddress }) => (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleAuthorEditClick(thread)}>Edit post</li>
|
||||||
|
<li onClick={() => handleAuthorDeleteClick(thread)}>Delete post</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{isModerator ? (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
null
|
||||||
|
) : (
|
||||||
|
<li onClick={() => {
|
||||||
|
setModeratingCommentCid(thread.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(thread.cid);
|
||||||
|
setDeletePost(true);
|
||||||
|
}}>
|
||||||
|
Delete post
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
<li
|
||||||
|
onClick={() => {
|
||||||
|
setModeratingCommentCid(thread.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(thread.cid);
|
||||||
|
}}>
|
||||||
|
Mod tools
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}</VerifiedAuthor>
|
||||||
|
{(commentMediaInfo && (
|
||||||
|
commentMediaInfo.type === 'image' ||
|
||||||
|
(commentMediaInfo.type === 'webpage' &&
|
||||||
|
commentMediaInfo.thumbnail))) ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleMobileOptionClick(thread.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://lens.google.com/uploadbyurl?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Google</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(thread.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://yandex.com/images/search?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Yandex</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(thread.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://saucenao.com/search.php?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on SauceNAO</a>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</PostMenuMobile>, document.body
|
||||||
|
)}
|
||||||
<span key={`mob-nbm-${index}`} className="name-block-mobile">
|
<span key={`mob-nbm-${index}`} className="name-block-mobile">
|
||||||
{thread.author.displayName
|
{thread.author.displayName
|
||||||
? thread.author.displayName.length > 20
|
? thread.author.displayName.length > 20
|
||||||
@@ -1993,7 +2110,96 @@ const Subscriptions = () => {
|
|||||||
<div key={`mob-rc-${index}`} className="reply-container">
|
<div key={`mob-rc-${index}`} className="reply-container">
|
||||||
<div key={`mob-pr-${index}`} className="post-reply post-reply-mobile">
|
<div key={`mob-pr-${index}`} className="post-reply post-reply-mobile">
|
||||||
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
||||||
<button key={`pmbm-${index}`} className="post-menu-button-mobile" title="Post menu" style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
<button key={`mob-pb-${index}`} className="post-menu-button-mobile"
|
||||||
|
ref={el => {
|
||||||
|
replyMenuRefsMobile.current[reply.cid] = el;
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const rect = replyMenuRefsMobile.current[reply.cid].getBoundingClientRect();
|
||||||
|
setMobileMenuPosition({top: rect.top + window.scrollY, left: rect.left});
|
||||||
|
setOpenMobileMenuCid(prevCid => (prevCid === reply.cid ? null : reply.cid));
|
||||||
|
}}
|
||||||
|
style={{ all: 'unset', cursor: 'pointer' }}
|
||||||
|
>...</button>
|
||||||
|
{createPortal(
|
||||||
|
<PostMenuMobile selectedStyle={selectedStyle}
|
||||||
|
ref={el => {postMenuMobileRef.current = el}}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
style={{position: "absolute",
|
||||||
|
display: openMobileMenuCid === reply.cid ? "block" : "none",
|
||||||
|
top: mobileMenuPosition.top + 20,
|
||||||
|
left: mobileMenuPosition.left}}>
|
||||||
|
<ul className={`post-menu-mobile-thread-${reply.cid}`}>
|
||||||
|
<li onClick={() => {
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
handleShareClick(selectedAddress, reply.cid)
|
||||||
|
}}>Share thread</li>
|
||||||
|
<VerifiedAuthor commentCid={reply.cid}>{({ authorAddress }) => (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleAuthorEditClick(reply)}>Edit post</li>
|
||||||
|
<li onClick={() => handleAuthorDeleteClick(reply)}>Delete post</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{isModerator ? (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
null
|
||||||
|
) : (
|
||||||
|
<li onClick={() => {
|
||||||
|
setModeratingCommentCid(reply.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
setDeletePost(true);
|
||||||
|
}}>
|
||||||
|
Delete post
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
<li
|
||||||
|
onClick={() => {
|
||||||
|
setModeratingCommentCid(reply.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
}}>
|
||||||
|
Mod tools
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}</VerifiedAuthor>
|
||||||
|
{(replyMediaInfo && (
|
||||||
|
replyMediaInfo.type === 'image' ||
|
||||||
|
(replyMediaInfo.type === 'webpage' &&
|
||||||
|
replyMediaInfo.thumbnail))) ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://lens.google.com/uploadbyurl?url=${replyMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Google</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://yandex.com/images/search?img_url=${replyMediaInfo.url}&rpt=imageview`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Yandex</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://saucenao.com/search.php?url=${replyMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on SauceNAO</a>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</PostMenuMobile>, document.body
|
||||||
|
)}
|
||||||
<span key={`mob-nb-${index}`} className="name-block-mobile">
|
<span key={`mob-nb-${index}`} className="name-block-mobile">
|
||||||
{reply.author.displayName
|
{reply.author.displayName
|
||||||
? reply.author.displayName.length > 20
|
? reply.author.displayName.length > 20
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Tooltip } from 'react-tooltip';
|
|||||||
import { useAccount, useAccountComments, useComment, usePublishComment, usePublishCommentEdit, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
import { useAccount, useAccountComments, useComment, usePublishComment, usePublishCommentEdit, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||||
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'
|
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'
|
||||||
import { debounce } from 'lodash';
|
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 { ReplyFormLink, TopBar, BottomBar, BoardForm, Footer } from '../styled/views/Thread.styled';
|
||||||
import { AlertModal } from '../styled/modals/AlertModal.styled';
|
import { AlertModal } from '../styled/modals/AlertModal.styled';
|
||||||
import { PostMenuCatalog } from '../styled/views/Catalog.styled';
|
import { PostMenuCatalog } from '../styled/views/Catalog.styled';
|
||||||
@@ -91,8 +91,11 @@ const Thread = () => {
|
|||||||
const commentRef = useRef();
|
const commentRef = useRef();
|
||||||
const linkRef = useRef();
|
const linkRef = useRef();
|
||||||
const threadMenuRefs = useRef({});
|
const threadMenuRefs = useRef({});
|
||||||
|
const threadMenuRefsMobile = useRef({});
|
||||||
const replyMenuRefs = useRef({});
|
const replyMenuRefs = useRef({});
|
||||||
|
const replyMenuRefsMobile = useRef({});
|
||||||
const postMenuRef = useRef(null);
|
const postMenuRef = useRef(null);
|
||||||
|
const postMenuMobileRef = useRef(null);
|
||||||
const postMenuCatalogRef = useRef(null);
|
const postMenuCatalogRef = useRef(null);
|
||||||
const backlinkRefs = useRef({});
|
const backlinkRefs = useRef({});
|
||||||
const quoteRefs = useRef({});
|
const quoteRefs = useRef({});
|
||||||
@@ -114,7 +117,9 @@ const Thread = () => {
|
|||||||
const [isModerator, setIsModerator] = useState(false);
|
const [isModerator, setIsModerator] = useState(false);
|
||||||
const [commentCid, setCommentCid] = useState(null);
|
const [commentCid, setCommentCid] = useState(null);
|
||||||
const [menuPosition, setMenuPosition] = useState({top: 0, left: 0});
|
const [menuPosition, setMenuPosition] = useState({top: 0, left: 0});
|
||||||
|
const [mobileMenuPosition, setMobileMenuPosition] = useState({top: 0, left: 0});
|
||||||
const [openMenuCid, setOpenMenuCid] = useState(null);
|
const [openMenuCid, setOpenMenuCid] = useState(null);
|
||||||
|
const [openMobileMenuCid, setOpenMobileMenuCid] = useState(null);
|
||||||
const [outOfViewCid, setOutOfViewCid] = useState(null);
|
const [outOfViewCid, setOutOfViewCid] = useState(null);
|
||||||
const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0});
|
const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0});
|
||||||
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
|
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
|
||||||
@@ -189,12 +194,22 @@ const Thread = () => {
|
|||||||
setOpenMenuCid(null);
|
setOpenMenuCid(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleMobileOptionClick = () => {
|
||||||
|
setOpenMobileMenuCid(null);
|
||||||
|
};
|
||||||
|
|
||||||
const handleOutsideClick = useCallback((e) => {
|
const handleOutsideClick = useCallback((e) => {
|
||||||
if (openMenuCid !== null && !postMenuRef.current.contains(e.target) && !postMenuCatalogRef.current.contains(e.target)) {
|
if (openMenuCid !== null && !postMenuRef.current.contains(e.target) && !postMenuCatalogRef.current.contains(e.target)) {
|
||||||
setOpenMenuCid(null);
|
setOpenMenuCid(null);
|
||||||
}
|
}
|
||||||
}, [openMenuCid, postMenuRef, postMenuCatalogRef]);
|
}, [openMenuCid, postMenuRef, postMenuCatalogRef]);
|
||||||
|
|
||||||
|
const handleMobileOutsideClick = useCallback((e) => {
|
||||||
|
if (openMobileMenuCid !== null && !postMenuMobileRef.current.contains(e.target)) {
|
||||||
|
setOpenMobileMenuCid(null);
|
||||||
|
}
|
||||||
|
}, [openMobileMenuCid, postMenuMobileRef]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (openMenuCid !== null) {
|
if (openMenuCid !== null) {
|
||||||
document.addEventListener('click', handleOutsideClick);
|
document.addEventListener('click', handleOutsideClick);
|
||||||
@@ -208,6 +223,20 @@ const Thread = () => {
|
|||||||
}, [openMenuCid, handleOutsideClick]);
|
}, [openMenuCid, handleOutsideClick]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (openMobileMenuCid !== null) {
|
||||||
|
document.addEventListener('click', handleMobileOutsideClick);
|
||||||
|
} else {
|
||||||
|
document.removeEventListener('click', handleMobileOutsideClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('click', handleMobileOutsideClick);
|
||||||
|
};
|
||||||
|
}, [openMobileMenuCid, handleMobileOutsideClick]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (postOnHoverRef.current) {
|
if (postOnHoverRef.current) {
|
||||||
const rect = postOnHoverRef.current.getBoundingClientRect();
|
const rect = postOnHoverRef.current.getBoundingClientRect();
|
||||||
@@ -1423,7 +1452,7 @@ const Thread = () => {
|
|||||||
</li>
|
</li>
|
||||||
<li onClick={() => handleOptionClick(reply.cid)}>
|
<li onClick={() => handleOptionClick(reply.cid)}>
|
||||||
<a
|
<a
|
||||||
href={`https://yandex.com/images/search?url=${replyMediaInfo.url}`}
|
href={`https://yandex.com/images/search?img_url=${replyMediaInfo.url}&rpt=imageview`}
|
||||||
target="_blank" rel="noreferrer"
|
target="_blank" rel="noreferrer"
|
||||||
>Yandex</a>
|
>Yandex</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -1737,7 +1766,96 @@ const Thread = () => {
|
|||||||
<div className="op-container" key="op-container">
|
<div className="op-container" key="op-container">
|
||||||
<div key={`mob-po-${comment.cid}`} className="post op op-mobile">
|
<div key={`mob-po-${comment.cid}`} className="post op op-mobile">
|
||||||
<div key={`mob-pi-${comment.cid}`} className="post-info-mobile">
|
<div key={`mob-pi-${comment.cid}`} className="post-info-mobile">
|
||||||
<button style={{ all: 'unset', cursor: 'pointer' }} key={`mob-pb-${comment.cid}`} className="post-menu-button-mobile">...</button>
|
<button key={`mob-pb-${index}`} className="post-menu-button-mobile"
|
||||||
|
ref={el => {
|
||||||
|
threadMenuRefsMobile.current[comment.cid] = el;
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const rect = threadMenuRefsMobile.current[comment.cid].getBoundingClientRect();
|
||||||
|
setMobileMenuPosition({top: rect.top + window.scrollY, left: rect.left});
|
||||||
|
setOpenMobileMenuCid(prevCid => (prevCid === comment.cid ? null : comment.cid));
|
||||||
|
}}
|
||||||
|
style={{ all: 'unset', cursor: 'pointer' }}
|
||||||
|
>...</button>
|
||||||
|
{createPortal(
|
||||||
|
<PostMenuMobile selectedStyle={selectedStyle}
|
||||||
|
ref={el => {postMenuMobileRef.current = el}}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
style={{position: "absolute",
|
||||||
|
display: openMobileMenuCid === comment.cid ? "block" : "none",
|
||||||
|
top: mobileMenuPosition.top + 20,
|
||||||
|
left: mobileMenuPosition.left}}>
|
||||||
|
<ul className={`post-menu-mobile-thread-${comment.cid}`}>
|
||||||
|
<li onClick={() => {
|
||||||
|
handleMobileOptionClick(comment.cid);
|
||||||
|
handleShareClick(selectedAddress, comment.cid)
|
||||||
|
}}>Share thread</li>
|
||||||
|
<VerifiedAuthor commentCid={comment.cid}>{({ authorAddress }) => (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleAuthorEditClick(comment)}>Edit post</li>
|
||||||
|
<li onClick={() => handleAuthorDeleteClick(comment)}>Delete post</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{isModerator ? (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
null
|
||||||
|
) : (
|
||||||
|
<li onClick={() => {
|
||||||
|
setModeratingCommentCid(comment.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(comment.cid);
|
||||||
|
setDeletePost(true);
|
||||||
|
}}>
|
||||||
|
Delete post
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
<li
|
||||||
|
onClick={() => {
|
||||||
|
setModeratingCommentCid(comment.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(comment.cid);
|
||||||
|
}}>
|
||||||
|
Mod tools
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}</VerifiedAuthor>
|
||||||
|
{(commentMediaInfo && (
|
||||||
|
commentMediaInfo.type === 'image' ||
|
||||||
|
(commentMediaInfo.type === 'webpage' &&
|
||||||
|
commentMediaInfo.thumbnail))) ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleMobileOptionClick(comment.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://lens.google.com/uploadbyurl?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Google</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(comment.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://yandex.com/images/search?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Yandex</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(comment.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://saucenao.com/search.php?url=${commentMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on SauceNAO</a>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</PostMenuMobile>, document.body
|
||||||
|
)}
|
||||||
<span className="name-block-mobile">
|
<span className="name-block-mobile">
|
||||||
{comment?.author?.displayName
|
{comment?.author?.displayName
|
||||||
? comment?.author?.displayName.length > 15
|
? comment?.author?.displayName.length > 15
|
||||||
@@ -1940,7 +2058,96 @@ const Thread = () => {
|
|||||||
<div key={`mob-rc-${index}`} className="reply-container">
|
<div key={`mob-rc-${index}`} className="reply-container">
|
||||||
<div key={`mob-pr-${index}`} className="post-reply post-reply-mobile">
|
<div key={`mob-pr-${index}`} className="post-reply post-reply-mobile">
|
||||||
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
<div key={`mob-pi-${index}`} className="post-info-mobile">
|
||||||
<button className="post-menu-button-mobile" title="Post menu" style={{ all: 'unset', cursor: 'pointer' }}>...</button>
|
<button key={`mob-pb-${index}`} className="post-menu-button-mobile"
|
||||||
|
ref={el => {
|
||||||
|
replyMenuRefsMobile.current[reply.cid] = el;
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const rect = replyMenuRefsMobile.current[reply.cid].getBoundingClientRect();
|
||||||
|
setMobileMenuPosition({top: rect.top + window.scrollY, left: rect.left});
|
||||||
|
setOpenMobileMenuCid(prevCid => (prevCid === reply.cid ? null : reply.cid));
|
||||||
|
}}
|
||||||
|
style={{ all: 'unset', cursor: 'pointer' }}
|
||||||
|
>...</button>
|
||||||
|
{createPortal(
|
||||||
|
<PostMenuMobile selectedStyle={selectedStyle}
|
||||||
|
ref={el => {postMenuMobileRef.current = el}}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
style={{position: "absolute",
|
||||||
|
display: openMobileMenuCid === reply.cid ? "block" : "none",
|
||||||
|
top: mobileMenuPosition.top + 20,
|
||||||
|
left: mobileMenuPosition.left}}>
|
||||||
|
<ul className={`post-menu-mobile-thread-${reply.cid}`}>
|
||||||
|
<li onClick={() => {
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
handleShareClick(selectedAddress, reply.cid)
|
||||||
|
}}>Share thread</li>
|
||||||
|
<VerifiedAuthor commentCid={reply.cid}>{({ authorAddress }) => (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleAuthorEditClick(reply)}>Edit post</li>
|
||||||
|
<li onClick={() => handleAuthorDeleteClick(reply)}>Delete post</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{isModerator ? (
|
||||||
|
<>
|
||||||
|
{authorAddress === account?.author.address ||
|
||||||
|
authorAddress === account?.signer.address ? (
|
||||||
|
null
|
||||||
|
) : (
|
||||||
|
<li onClick={() => {
|
||||||
|
setModeratingCommentCid(reply.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
setDeletePost(true);
|
||||||
|
}}>
|
||||||
|
Delete post
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
<li
|
||||||
|
onClick={() => {
|
||||||
|
setModeratingCommentCid(reply.cid)
|
||||||
|
setIsModerationOpen(true);
|
||||||
|
handleMobileOptionClick(reply.cid);
|
||||||
|
}}>
|
||||||
|
Mod tools
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}</VerifiedAuthor>
|
||||||
|
{(replyMediaInfo && (
|
||||||
|
replyMediaInfo.type === 'image' ||
|
||||||
|
(replyMediaInfo.type === 'webpage' &&
|
||||||
|
replyMediaInfo.thumbnail))) ? (
|
||||||
|
<>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://lens.google.com/uploadbyurl?url=${replyMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Google</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://yandex.com/images/search?img_url=${replyMediaInfo.url}&rpt=imageview`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on Yandex</a>
|
||||||
|
</li>
|
||||||
|
<li onClick={() => handleMobileOptionClick(reply.cid)}>
|
||||||
|
<a style={{color: 'inherit', textDecoration: 'none'}}
|
||||||
|
href={`https://saucenao.com/search.php?url=${replyMediaInfo.url}`}
|
||||||
|
target="_blank" rel="noreferrer"
|
||||||
|
>Search image on SauceNAO</a>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</PostMenuMobile>, document.body
|
||||||
|
)}
|
||||||
<span key={`mob-nb-${index}`} className="name-block-mobile">
|
<span key={`mob-nb-${index}`} className="name-block-mobile">
|
||||||
{reply.author?.displayName
|
{reply.author?.displayName
|
||||||
? reply.author?.displayName.length > 12
|
? reply.author?.displayName.length > 12
|
||||||
|
|||||||
Reference in New Issue
Block a user