Merge branch 'master' of github.com:plebbit/plebchan

This commit is contained in:
Esteban Abaroa
2023-06-20 12:05:10 +00:00
19 changed files with 2104 additions and 1341 deletions
+13 -9
View File
@@ -1,17 +1,19 @@
import React, { useState, useEffect } from "react";
import React
// , { useState, useEffect }
from "react";
import { useAccountComment } from "@plebbit/plebbit-react-hooks";
import useStateString from "../hooks/useStateString";
// import useStateString from "../hooks/useStateString";
const PendingLabel = ({ commentIndex }) => {
const comment = useAccountComment({commentIndex: commentIndex});
const stateString = useStateString(comment);
// const stateString = useStateString(comment);
const [isLoading, setIsLoading] = useState(true);
// const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const timer = setTimeout(() => setIsLoading(false), 2000);
return () => clearTimeout(timer);
}, []);
// useEffect(() => {
// const timer = setTimeout(() => setIsLoading(false), 2000);
// return () => clearTimeout(timer);
// }, []);
if (commentIndex === undefined) return null;
@@ -19,7 +21,9 @@ const PendingLabel = ({ commentIndex }) => {
comment.cid ? (
<span>{comment.cid.slice(2, 14)}</span>
) : (
(comment.state === "failed" || (stateString === undefined && !isLoading)) ? (
(comment.state === "failed"
// || (stateString === undefined && !isLoading)
) ? (
<span style={{color: 'red', fontWeight: '700'}}>
Failed
</span>
+54 -25
View File
@@ -1,6 +1,6 @@
import React from 'react';
import ReactMarkdown from 'react-markdown';
// import { Link } from 'react-router-dom';
import { Link } from 'react-router-dom';
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
import breaks from 'remark-breaks';
@@ -39,31 +39,60 @@ const blockquoteToGreentext = () => (tree) => {
};
// const createQuotelink = (handlequoteclick, comment, children) => {
// const text = children.map((child) => (typeof child === 'string' ? child : child.props.children)).join('');
// const regex = /(\s|^)(c\/[A-Za-z0-9]{12}|c\/[A-Za-z0-9]{45})(?=\s|$)/g;
// const parts = text.split(regex);
const createQuotelink = (children) => {
const regexC = /(c\/[A-Za-z0-9]{46}|c\/[A-Za-z0-9]{12})/g;
const regexP = /(p\/([A-Za-z0-9]{52}|[A-Za-z0-9-.]*\.eth))/g;
const regexU = /(u\/([A-Za-z0-9]{52}|[A-Za-z0-9-.]*\.eth))/g;
const regexPC = /(p\/([A-Za-z0-9]{52}|[A-Za-z0-9-.]*\.eth)\/c\/[A-Za-z0-9]{46})/g;
// return parts.flatMap((part, i) => {
// if (regex.test(part)) {
// return [
// <Link
// key={`link-${i}`}
// className="quotelink"
// to=""
// onClick={handlequoteclick}
// >
// {part.trim()}
// </Link>,
// ];
// } else {
// return [part];
// }
// });
// };
const regexArray = [regexU, regexPC, regexP, regexC];
let parts = [children];
regexArray.forEach(regex => {
parts = parts.flatMap((child, i) => {
if (typeof child === 'string') {
const newParts = [];
let match;
let lastIndex = 0;
while ((match = regex.exec(child)) !== null) {
const matchedText = match[0];
const index = match.index;
if (index > lastIndex) {
newParts.push(child.substring(lastIndex, index));
}
newParts.push(
<Link
key={`link-${i}-${matchedText}`}
className="quotelink"
to={() => {}}
>
{matchedText}
</Link>
);
lastIndex = index + matchedText.length;
}
if (lastIndex < child.length) {
newParts.push(child.substring(lastIndex));
}
return newParts;
} else {
return child;
}
});
});
return parts;
};
const Post = ({ content, handlequoteclick, comment }) => {
const Post = ({ content }) => {
const doubleNewlineContent = content?.replaceAll(/\n(?!\n)/g, '\n\n');
return (
@@ -76,10 +105,10 @@ const Post = ({ content, handlequoteclick, comment }) => {
video: ({ src }) => <span>{src}</span>,
source: ({ src }) => <span>{src}</span>,
gif: ({ src }) => <span>{src}</span>,
p: ({ children }) => <div className="custom-paragraph">{children}</div>,
p: ({ children }) => <div className='custom-paragraph'>{createQuotelink(children)}</div>,
}}
/>
);
};
export default Post;
export default React.memo(Post);
+4 -4
View File
@@ -190,15 +190,15 @@ const PostOnHover = ({ cid, feed }) => {
<div className="post-reply post-reply-mobile">
<div className="post-info-mobile">
<span className="name-block-mobile">
{reply.author.displayName
? reply.author.displayName.length > 20
{reply.author?.displayName
? reply.author?.displayName.length > 20
? <Fragment>
<span className="name-mobile">
{reply.author.displayName.slice(0, 20) + " (...)"}
{reply.author?.displayName.slice(0, 20) + " (...)"}
</span>
</Fragment>
: <span className="name-mobile">
{reply.author.displayName}</span>
{reply.author?.displayName}</span>
: <span className="name-mobile">
Anonymous</span>}
&nbsp;
+12 -10
View File
@@ -17,16 +17,18 @@ const StateLabel = ({ commentIndex, className }) => {
return (
commentIndex && stateString !== "Succeeded" ? (
(comment.state === "failed" || (stateString === undefined && !isLoading && comment.cid === undefined)) ? (
<span className="ttl">
<br />
(
<span className="ttl">
Failed
</span>
)
</span>
(comment.state === "failed") ? (
null
) : (
stateString === undefined && !isLoading && comment.cid === undefined ? (
<span className="ttl">
<br />
(
<span className={className}>
</span>
)
</span>
) : (
<span className="ttl">
<br />
(
@@ -35,7 +37,7 @@ const StateLabel = ({ commentIndex, className }) => {
</span>
)
</span>
)
))
) : null
);
};
+20 -65
View File
@@ -1,22 +1,22 @@
import React, { useState, useEffect, useRef } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import { confirmAlert } from "react-confirm-alert";
import Modal from "react-modal";
import { deleteCaches, exportAccount, importAccount, setAccount, setActiveAccount, useAccount, useAccounts } from "@plebbit/plebbit-react-hooks";
import { StyledModal } from "../styled/modals/SettingsModal.styled";
import { AuthorDeleteAlert } from '../styled/views/Thread.styled';
import useGeneralStore from "../../hooks/stores/useGeneralStore";
import useError from "../../hooks/useError";
import useSuccess from "../../hooks/useSuccess";
import useAnonModeStore from '../../hooks/stores/useAnonModeStore';
import useGeneralStore from '../../hooks/stores/useGeneralStore';
import packageJson from '../../../package.json'
const {version} = packageJson
const SettingsModal = ({ isOpen, closeModal }) => {
const {
anonymousMode, setAnonymousMode,
selectedStyle,
} = useGeneralStore(state => state);
const { anonymousMode, setAnonymousMode } = useAnonModeStore();
const navigate = useNavigate();
const location = useLocation();
@@ -194,38 +194,6 @@ const SettingsModal = ({ isOpen, closeModal }) => {
}
};
const handleAnonymousMode = () => {
confirmAlert({
customUI: ({ onClose }) => {
return (
<AuthorDeleteAlert selectedStyle={selectedStyle}>
<div className='author-delete-alert'>
<p>
{ !anonymousMode ?
"Enabling anonymous mode will turn off all account-based features, such as subscribing to boards." :
"Disabling anonymous mode will turn on all account-based features, such as subscribing to boards."
}
</p>
<div className="author-delete-buttons">
<button onClick={onClose}>Cancel</button>
<button
onClick={() => {
setAnonymousMode(!anonymousMode);
onClose();
}}
>
{ !anonymousMode ? "Enable" : "Disable" }
</button>
</div>
</div>
</AuthorDeleteAlert>
)
}
})
};
return (
<StyledModal
@@ -254,20 +222,6 @@ const SettingsModal = ({ isOpen, closeModal }) => {
</button>
]
</div>
{/* <ul>
<li className="settings-cat-lbl">
<span className={`${expanded.includes(0) ? 'minus' : 'plus'}`}
onClick={() => toggleExpanded(0)}
/>
<span className="settings-pointer" style={{cursor: "pointer"}}
onClick={() => toggleExpanded(0)}
>Account</span>
</li>
<ul className="settings-cat" style={{ display: expanded.includes(0) ? 'block' : 'none' }}>
<li>
</li>
</ul>
</ul>*/}
<ul>
<li className="settings-cat-lbl">
<span className={`${expanded.includes(1) ? 'minus' : 'plus'}`}
@@ -282,6 +236,22 @@ const SettingsModal = ({ isOpen, closeModal }) => {
</div>
</li>
<ul className="settings-cat" style={{ display: expanded.includes(1) ? 'block' : 'none' }}>
<li className="anon-off">
<label title={ !anonymousMode ?
"Enable anon mode" :
"Disable anon mode"
}>
<input
type="checkbox"
checked={anonymousMode}
onChange={() => {setAnonymousMode(!anonymousMode)}}
/>
&nbsp;Anon mode
</label>
</li>
<li className="settings-tip anon-tip">
Automatically uses a different account (u/address) per thread.
</li>
<li className="settings-option disc">
Account Data
</li>
@@ -414,21 +384,6 @@ const SettingsModal = ({ isOpen, closeModal }) => {
</ul>
</ul> */}
</ul>
<ul>
<li className="anon-off">
<label title={ !anonymousMode ?
"Enable anonymous mode (turns off all account-based features)" :
"Disable anonymous mode (turns on all account-based features)"
}>
<input
type="checkbox"
checked={!anonymousMode}
onChange={handleAnonymousMode}
/>
Disable anonymous mode
</label>
</li>
</ul>
<div>
<button
className="cache-button"
@@ -191,8 +191,33 @@ export const StyledModal = styled(Modal)`
}
.anon-off {
padding-left: 3px;
padding-top: 15px;
margin: 10px 0 5px -2px
}
.anon-tip {
margin-bottom: 10px;
}
.anon-tip::before {
content: '';
display: block;
width: 10px;
height: 1px;
background-color: currentColor;
position: absolute;
left: 18px;
top: 120px;
}
.anon-tip::after {
content: '';
display: block;
width: 1px;
height: 12px;
background-color: currentColor;
position: absolute;
left: 18px;
top: 108px;
}
@@ -1740,6 +1740,10 @@ export const BoardForm = styled.div`
border-left: none !important;
border-top: none !important;
}
.capcode {
color: purple !important;
}
#post-menu {
border-right: 1px solid #d9bfb7;
@@ -1763,6 +1767,10 @@ export const BoardForm = styled.div`
border-left: none !important;
border-top: none !important;
}
.capcode {
color: purple !important;
}
#post-menu {
border-right: 1px solid #b7c5d9;
@@ -1783,6 +1791,10 @@ export const BoardForm = styled.div`
return `.highlighted {
background-color: #f0c0b0 !important;
}
.capcode {
color: purple !important;
}
#post-menu {
font-size: 13px !important;
@@ -1803,6 +1815,10 @@ export const BoardForm = styled.div`
return `.highlighted {
background-color: #d6bad0 !important;
}
.capcode {
color: purple !important;
}
#post-menu {
font-size: 13px !important;
@@ -1824,6 +1840,10 @@ export const BoardForm = styled.div`
background-color: #1d1d21 !important;
outline: 1px solid #111 !important;
}
.capcode {
color: #81a2be !important;
}
#post-menu {
border: 1px solid #000;
@@ -1844,6 +1864,10 @@ export const BoardForm = styled.div`
background-color: #ccc !important;
outline: 1px solid #ccc !important;
}
.capcode {
color: #f60 !important;
}
#post-menu {
border: 1px solid #ccc;
@@ -3242,6 +3266,9 @@ export const BoardForm = styled.div`
switch (selectedStyle) {
case 'Yotsuba':
return `
.capcode {
color: purple !important;
}
.op-container {
background-color: #f5e9e1;
border: 1px solid #d9bfb7 !important;
@@ -3359,6 +3386,9 @@ export const BoardForm = styled.div`
case 'Yotsuba-B':
return `
.capcode {
color: purple !important;
}
.op-container {
background-color: #d6daf0;
border: 1px solid #b7c5d9 !important;
@@ -3472,6 +3502,9 @@ export const BoardForm = styled.div`
case 'Futaba':
return `
.capcode {
color: purple !important;
}
.op-container {
background-color: #f5e9e1;
border: 1px solid #d9bfb7 !important;
@@ -3588,6 +3621,9 @@ export const BoardForm = styled.div`
case 'Burichan':
return `
.capcode {
color: purple !important;
}
.op-container {
background-color: #d6daf0;
border: 1px solid #b7c5d9 !important;
@@ -3700,6 +3736,10 @@ export const BoardForm = styled.div`
case 'Tomorrow':
return `
.capcode {
color: #81a2be !important;
}
.op-container {
background-color: #282A2E;
border: 1px solid #2D2F33 !important;
@@ -3822,6 +3862,9 @@ export const BoardForm = styled.div`
case 'Photon':
return `
.capcode {
color: #f60 !important;
}
.op-container {
background-color: #eee;
border: 1px solid #ccc !important;
+38 -14
View File
@@ -8,7 +8,6 @@ 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 useGeneralStore from '../../hooks/stores/useGeneralStore';
import { Container, NavBar, Header, Break, TopBar, BoardForm, PostMenu } from '../styled/views/Board.styled';
import { AuthorDeleteAlert, Footer } from '../styled/views/Thread.styled';
import { PostMenuCatalog } from '../styled/views/Catalog.styled';
@@ -33,9 +32,12 @@ import handleQuoteClick from '../../utils/handleQuoteClick';
import handleQuoteHover from '../../utils/handleQuoteHover';
import handleStyleChange from '../../utils/handleStyleChange';
import removeHighlight from '../../utils/removeHighlight';
import useAnonModeRef from '../../hooks/useAnonModeRef';
import useError from '../../hooks/useError';
import useFeedStateString from '../../hooks/useFeedStateString';
import useSuccess from '../../hooks/useSuccess';
import useAnonModeStore from '../../hooks/stores/useAnonModeStore';
import useGeneralStore from '../../hooks/stores/useGeneralStore';
import packageJson from '../../../package.json'
const {version} = packageJson
@@ -61,6 +63,8 @@ const All = () => {
setSelectedThread,
setSelectedTitle,
} = useGeneralStore(state => state);
const { anonymousMode } = useAnonModeStore();
const account = useAccount();
const navigate = useNavigate();
@@ -75,6 +79,7 @@ const All = () => {
const backlinkRefsMobile = useRef({});
const quoteRefsMobile = useRef({});
const postOnHoverRef = useRef(null);
const selectedThreadCidRef = useRef(null);
const [isReplyOpen, setIsReplyOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
@@ -91,6 +96,13 @@ const All = () => {
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
const [deletePost, setDeletePost] = useState(false);
const [moderatorPermissions, setModeratorPermissions] = useState({});
const [executeAnonMode, setExecuteAnonMode] = useState(false);
const setSelectedThreadCid = (cid) => {
selectedThreadCidRef.current = cid;
}
useAnonModeRef(selectedThreadCidRef, anonymousMode && executeAnonMode);
const addresses = defaultSubplebbits.map(subplebbit => subplebbit.address);
const { feed, hasMore, loadMore } = useFeed({subplebbitAddresses: addresses, sortType: 'active'});
@@ -302,6 +314,14 @@ const All = () => {
});
};
useEffect(() => {
if (anonymousMode) {
setExecuteAnonMode(true);
}
}, [anonymousMode, selectedThreadCidRef]);
const [publishCommentEditOptions, setPublishCommentEditOptions] = useState({
commentCid: commentCid,
content: editedComment || undefined,
@@ -317,8 +337,8 @@ const All = () => {
const { publishCommentEdit } = usePublishCommentEdit(publishCommentEditOptions);
const handleAuthorDeleteClick = (comment) => {
handleOptionClick(comment.cid);
const handleAuthorDeleteClick = (cid) => {
handleOptionClick(cid);
confirmAlert({
customUI: ({ onClose }) => {
@@ -332,7 +352,7 @@ const All = () => {
onClick={() => {
setIsAuthorDelete(true);
setIsAuthorEdit(false);
setCommentCid(comment.cid);
setCommentCid(cid);
setPublishCommentEditOptions(prevOptions => ({
...prevOptions,
deleted: true,
@@ -667,7 +687,8 @@ const All = () => {
onClick={(e) => {
if (e.button === 2) return;
e.preventDefault();
setIsReplyOpen(true);
setSelectedThreadCid(thread.cid);
setIsReplyOpen(true);
setSelectedShortCid(thread.shortCid);
setSelectedParentCid(thread.cid);
setSelectedAddress(thread.subplebbitAddress);
@@ -725,7 +746,7 @@ const All = () => {
{thread.author.shortAddress === account?.author.shortAddress ? (
<>
<li onClick={() => {handleAuthorEditClick(thread); setSelectedAddress(thread.subplebbitAddress);}}>Edit post</li>
<li onClick={() => {handleAuthorDeleteClick(thread); setSelectedAddress(thread.subplebbitAddress);}}>Delete post</li>
<li onClick={() => {handleAuthorDeleteClick(thread.cid); setSelectedAddress(thread.subplebbitAddress);}}>Delete post</li>
</>
) : null}
{isModerator ? (
@@ -946,7 +967,8 @@ const All = () => {
onClick={(e) => {
if (e.button === 2) return;
e.preventDefault();
setIsReplyOpen(true);
setSelectedThreadCid(thread.cid);
setIsReplyOpen(true);
setSelectedShortCid(reply.shortCid);
setSelectedParentCid(reply.cid);
setSelectedAddress(thread.subplebbitAddress);
@@ -1004,7 +1026,7 @@ const All = () => {
{reply.author.shortAddress === account?.author.shortAddress ? (
<>
<li onClick={() => {handleAuthorEditClick(reply); setSelectedAddress(thread.subplebbitAddress);}}>Edit post</li>
<li onClick={() => {handleAuthorDeleteClick(reply); setSelectedAddress(thread.subplebbitAddress);}}>Delete post</li>
<li onClick={() => {handleAuthorDeleteClick(reply.cid); setSelectedAddress(thread.subplebbitAddress);}}>Delete post</li>
</>
) : null}
{isModerator ? (
@@ -1379,7 +1401,8 @@ const All = () => {
onClick={(e) => {
if (e.button === 2) return;
e.preventDefault();
setIsReplyOpen(true);
setSelectedThreadCid(thread.cid);
setIsReplyOpen(true);
setSelectedShortCid(thread.shortCid);
setSelectedParentCid(thread.cid);
setSelectedAddress(thread.subplebbitAddress);
@@ -1551,7 +1574,8 @@ const All = () => {
onClick={(e) => {
if (e.button === 2) return;
e.preventDefault();
setIsReplyOpen(true);
setSelectedThreadCid(thread.cid);
setIsReplyOpen(true);
setSelectedShortCid(reply.shortCid);
setSelectedParentCid(reply.cid);
setSelectedAddress(thread.subplebbitAddress);
@@ -1842,10 +1866,10 @@ const All = () => {
}}>
<>
<span className="boardList">
[
<Link to={`/p/all`}>All</Link>
 / 
<Link to={`/p/subscriptions`}>Subscriptions</Link>
[
<Link to={`/p/all`}>All</Link>
 / 
<Link to={`/p/subscriptions`}>Subscriptions</Link>
]&nbsp;[
</span>
{defaultSubplebbits.map((subplebbit, index) => (
+3 -2
View File
@@ -7,7 +7,6 @@ import { confirmAlert } from 'react-confirm-alert';
import { Tooltip } from 'react-tooltip';
import { useAccount, useFeed, usePublishCommentEdit, useSubplebbits } from '@plebbit/plebbit-react-hooks';
import { debounce } from 'lodash';
import useGeneralStore from '../../hooks/stores/useGeneralStore';
import { Container, NavBar, Header, Break, PostMenu, BoardForm } from '../styled/views/Board.styled';
import { Threads, PostMenuCatalog } from '../styled/views/Catalog.styled';
import { TopBar, Footer, AuthorDeleteAlert } from '../styled/views/Thread.styled';
@@ -22,6 +21,7 @@ import handleStyleChange from '../../utils/handleStyleChange';
import useError from '../../hooks/useError';
import useFeedStateString from '../../hooks/useFeedStateString';
import useSuccess from '../../hooks/useSuccess';
import useGeneralStore from '../../hooks/stores/useGeneralStore';
import packageJson from '../../../package.json'
const {version} = packageJson
@@ -44,6 +44,7 @@ const AllCatalog = () => {
setSelectedThread,
setSelectedTitle,
} = useGeneralStore(state => state);
const threadMenuRefs = useRef({});
const postMenuRef = useRef(null);
@@ -337,7 +338,7 @@ const AllCatalog = () => {
<span className="boardList">
[
<Link to={`/p/all`}>All</Link>
 / 
 / 
<Link to={`/p/subscriptions`}>Subscriptions</Link>
]&nbsp;[
{defaultSubplebbits.map((subplebbit, index) => (
File diff suppressed because it is too large Load Diff
+353 -160
View File
@@ -7,7 +7,6 @@ import { confirmAlert } from 'react-confirm-alert';
import { Tooltip } from 'react-tooltip';
import { useAccount, useFeed, usePublishComment, usePublishCommentEdit, useSubplebbit, useSubscribe } from '@plebbit/plebbit-react-hooks';
import { debounce } from 'lodash';
import useGeneralStore from '../../hooks/stores/useGeneralStore';
import { Container, NavBar, Header, Break, PostForm, PostFormLink, PostFormTable, PostMenu, BoardForm } from '../styled/views/Board.styled';
import { Threads, PostMenuCatalog } from '../styled/views/Catalog.styled';
import { TopBar, Footer, AuthorDeleteAlert } from '../styled/views/Thread.styled';
@@ -23,6 +22,7 @@ import useClickForm from '../../hooks/useClickForm';
import useError from '../../hooks/useError';
import useStateString from '../../hooks/useStateString';
import useSuccess from '../../hooks/useSuccess';
import useGeneralStore from '../../hooks/stores/useGeneralStore';
import packageJson from '../../../package.json'
const {version} = packageJson
@@ -49,6 +49,7 @@ const Catalog = () => {
showPostForm,
showPostFormLink,
} = useGeneralStore(state => state);
const nameRef = useRef();
const subjectRef = useRef();
@@ -77,7 +78,6 @@ const Catalog = () => {
const [menuPosition, setMenuPosition] = useState({top: 0, left: 0});
const [openMenuCid, setOpenMenuCid] = useState(null);
const { feed, hasMore, loadMore } = useFeed({subplebbitAddresses: [`${selectedAddress}`], sortType: 'active'});
const { subplebbitAddress } = useParams();
const subplebbit = useSubplebbit({subplebbitAddress: selectedAddress});
@@ -273,8 +273,8 @@ const Catalog = () => {
setTriggerPublishComment(true);
};
useEffect(() => {
if (publishCommentOptions && triggerPublishComment) {
(async () => {
@@ -462,7 +462,7 @@ const Catalog = () => {
<span className="boardList">
[
<Link to={`/p/all`}>All</Link>
 / 
 / 
<Link to={`/p/subscriptions`}>Subscriptions</Link>
]&nbsp;[
{defaultSubplebbits.map((subplebbit, index) => (
@@ -612,7 +612,7 @@ const Catalog = () => {
</div>
{feed.length > 0 ? (
<>
<span className="subscribe-button-desktop">
<span className="subscribe-button-desktop">
[
<span id="subscribe" style={{cursor: 'pointer'}}>
<span onClick={() => handleSubscribe()}>
@@ -645,88 +645,44 @@ const Catalog = () => {
null
) : (
feed.length > 0 ? (
<InfiniteScroll
pageStart={0}
loadMore={tryLoadMore}
hasMore={hasMore}
>
{feed.map((thread, index) => {
const commentMediaInfo = getCommentMediaInfo(thread);
const fallbackImgUrl = "assets/filedeleted-res.gif";
return (
<div key={`thread-${index}`} className="thread"
onMouseOver={() => {setIsHoveringOnThread(thread.cid)}}
onMouseLeave={() => {setIsHoveringOnThread('')}}>
{commentMediaInfo?.url ? (
<Link style={{all: "unset", cursor: "pointer"}} key={`link-${index}`} to={`/p/${selectedAddress}/c/${thread.cid}`}
onClick={() => setSelectedThread(thread.cid)}>
{commentMediaInfo?.type === "webpage" ? (
thread.thumbnailUrl ? (
<img className="card" key={`img-${index}`}
src={commentMediaInfo.thumbnail} alt={commentMediaInfo.type}
onError={(e) => {
e.target.src = fallbackImgUrl
e.target.onerror = null;
}} />
) : null
) : null}
{commentMediaInfo?.type === "image" ? (
<img className="card" key={`img-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type}
onError={(e) => {
e.target.src = fallbackImgUrl
e.target.onerror = null;}} />
) : null}
{commentMediaInfo?.type === "video" ? (
<video className="card" key={`fti-${index}`}
src={commentMediaInfo.url}
alt={commentMediaInfo.type}
onError={(e) => e.target.src = fallbackImgUrl} />
) : null}
{commentMediaInfo?.type === "audio" ? (
<audio className="card" controls
key={`fti-${index}`}
src={commentMediaInfo.url}
alt={commentMediaInfo.type}
onError={(e) => e.target.src = fallbackImgUrl} />
) : null}
</Link>
) : null}
<div key={`ti-${index}`} className="thread-icons" >
{thread.pinned ? (
<span key={`si-${index}`} className="thread-icon sticky-icon" title="Sticky" />
) : null}
{thread.locked ? (
<span key={`li-${index}`} className="thread-icon closed-icon" title="Closed" />
) : null}
</div>
<BoardForm selectedStyle={selectedStyle}
style={{ all: "unset"}}>
<div key={`meta-${index}`} className="meta" title="(R)eplies / (I)mage Replies" >
R:
<b key={`b-${index}`}>{thread.replyCount}</b>
<PostMenu
style={{ display: isHoveringOnThread === thread.cid ? 'inline-block' : 'none',
position: 'absolute', lineHeight: '1em', marginTop: '-1px', outline: 'none',
zIndex: '999'}}
key={`pmb-${index}`}
title="Post menu"
ref={el => {
threadMenuRefs.current[thread.cid] = el;
postMenuRef.current = el;
}}
className='post-menu-button'
id='post-menu-button-catalog'
rotated={openMenuCid === thread.cid}
onClick={(event) => {
event.stopPropagation();
const rect = threadMenuRefs.current[thread.cid].getBoundingClientRect();
setMenuPosition({top: rect.top + window.scrollY, left: rect.left});
setOpenMenuCid(prevCid => (prevCid === thread.cid ? null : thread.cid));
}}
>
</PostMenu>
<InfiniteScroll
pageStart={0}
loadMore={tryLoadMore}
hasMore={hasMore}
>
{subplebbit.rules ? (
<div className='thread'
onMouseOver={() => setIsHoveringOnThread('rules')}
onMouseLeave={() => setIsHoveringOnThread('')}>
<BoardForm selectedStyle={selectedStyle} style={{all: 'unset'}}>
<div className='meta' title="(R)eplies / (I)mage Replies">
R:<b>0</b>
<div className='thread-icons'
style={{position: 'absolute', top: '-2px', right: '15px'}}>
<span className="thread-icon sticky-icon" title="Sticky" />
<span className="thread-icon closed-icon" title="Closed" />
</div>
<PostMenu
style={{ display: isHoveringOnThread === "rules" ? 'inline-block' : 'none',
position: 'absolute', lineHeight: '1em', marginTop: '-1px', outline: 'none',
zIndex: '999'}}
title="Post menu"
ref={el => {
threadMenuRefs.current["rules"] = el;
postMenuRef.current = el;
}}
className='post-menu-button'
id='post-menu-button-catalog'
rotated={openMenuCid === "rules"}
onClick={(event) => {
event.stopPropagation();
const rect = threadMenuRefs.current["rules"].getBoundingClientRect();
setMenuPosition({top: rect.top + window.scrollY, left: rect.left});
setOpenMenuCid(prevCid => (prevCid === "rules" ? null : "rules"));
}}
>
</PostMenu>
</div>
{createPortal(
<PostMenuCatalog selectedStyle={selectedStyle}
@@ -735,88 +691,325 @@ const Catalog = () => {
style={{position: "absolute",
top: menuPosition.top + 7,
left: menuPosition.left}}>
<div className={`post-menu-thread post-menu-thread-${thread.cid}`}
style={{ display: openMenuCid === thread.cid ? 'block' : 'none' }}
<div className={`post-menu-thread post-menu-thread-${"rules"}`}
style={{ display: openMenuCid === "rules" ? 'block' : 'none' }}
>
<ul className="post-menu-catalog">
<li onClick={() => handleOptionClick(thread.cid)}>Hide thread</li>
{thread.author.shortAddress === account?.author.shortAddress ? (
<li onClick={() => handleOptionClick("rules")}>Hide thread</li>
{/* {isModerator ? (
<>
<li onClick={() => handleAuthorEditClick(thread)}>Edit post</li>
<li onClick={() => handleAuthorDeleteClick(thread)}>Delete post</li>
change rules
</>
) : null}
{isModerator ? (
<>
{thread.author.shortAddress === account?.author.shortAddress ? (
null
) : (
<li onClick={() => {
setModeratingCommentCid(thread.cid)
setIsModerationOpen(true);
handleOptionClick(thread.cid);
setDeletePost(true);
}}>
Delete post
</li>
)}
<li
onClick={() => {
setModeratingCommentCid(thread.cid)
setIsModerationOpen(true);
handleOptionClick(thread.cid);
}}>
Mod tools
</li>
</>
) : null}
{(commentMediaInfo && (
commentMediaInfo.type === 'image' ||
(commentMediaInfo.type === 'webpage' &&
commentMediaInfo.thumbnail))) ? (
<li
onMouseOver={() => {setIsImageSearchOpen(true)}}
onMouseLeave={() => {setIsImageSearchOpen(false)}}>
Image search »
<ul className="dropdown-menu post-menu-catalog"
style={{display: isImageSearchOpen ? 'block': 'none'}}>
<li onClick={() => handleOptionClick(thread.cid)}>
<a
href={`https://lens.google.com/uploadbyurl?url=${commentMediaInfo.url}`}
target="_blank" rel="noreferrer"
>Google</a>
</li>
<li onClick={() => handleOptionClick(thread.cid)}>
<a
href={`https://yandex.com/images/search?url=${commentMediaInfo.url}`}
target="_blank" rel="noreferrer"
>Yandex</a>
</li>
<li onClick={() => handleOptionClick(thread.cid)}>
<a
href={`https://saucenao.com/search.php?url=${commentMediaInfo.url}`}
target="_blank" rel="noreferrer"
>SauceNAO</a>
</li>
</ul>
</li>
) : null
}
) : null} */}
</ul>
</div>
</PostMenuCatalog>, document.body
)}
</BoardForm>
<Link style={{all: "unset", cursor: "pointer"}} key={`link2-${index}`} to={`/p/${selectedAddress}/c/${thread.cid}`}
onClick={() => setSelectedThread(thread.cid)}>
<div key={`t-${index}`} className="teaser">
<b key={`b2-${index}`}>{thread.title ? `${thread.title}` : null}</b>
{thread.content ? `: ${thread.content}` : null}
</div>
</Link>
<Link style={{all: "unset", cursor: "pointer"}} to={() => {}}
onClick={() => setSelectedThread("rules")}>
<div className="teaser">
<b>Rules</b>
{": " + subplebbit.rules.map((rule, index) => `${index + 1}. ${rule}`).join(' ')}
</div>
</Link>
</div>
)})}
</InfiniteScroll>
) : null}
{subplebbit.description ? (
<div className='thread'
onMouseOver={() => setIsHoveringOnThread('description')}
onMouseLeave={() => setIsHoveringOnThread('')}>
<Link style={{all: 'unset', cursor: 'pointer'}} to={() => {}}>
{subplebbit.suggested?.avatarUrl ? (
<img className='card'
src={subplebbit.suggested.avatarUrl} alt="board avatar" />
) : null}
</Link>
{subplebbit.suggested?.avatarUrl ? (
<div className='thread-icons'>
<span className="thread-icon sticky-icon" title="Sticky" />
<span className="thread-icon closed-icon" title="Closed" />
</div>
) : null}
<BoardForm selectedStyle={selectedStyle} style={{all: 'unset'}}>
<div className='meta' title="(R)eplies / (I)mage Replies">
R:<b>0</b>
{subplebbit.suggested?.avatarUrl ? null : (
<div className='thread-icons'
style={{position: 'absolute', top: '-2px', right: '15px'}}>
<span className="thread-icon sticky-icon" title="Sticky" />
<span className="thread-icon closed-icon" title="Closed" />
</div>
)}
<PostMenu
style={{ display: isHoveringOnThread === "description" ? 'inline-block' : 'none',
position: 'absolute', lineHeight: '1em', marginTop: '-1px', outline: 'none',
zIndex: '999'}}
title="Post menu"
ref={el => {
threadMenuRefs.current["description"] = el;
postMenuRef.current = el;
}}
className='post-menu-button'
id='post-menu-button-catalog'
rotated={openMenuCid === "description"}
onClick={(event) => {
event.stopPropagation();
const rect = threadMenuRefs.current["description"].getBoundingClientRect();
setMenuPosition({top: rect.top + window.scrollY, left: rect.left});
setOpenMenuCid(prevCid => (prevCid === "description" ? null : "description"));
}}
>
</PostMenu>
</div>
{createPortal(
<PostMenuCatalog selectedStyle={selectedStyle}
ref={el => {postMenuCatalogRef.current = el}}
onClick={(event) => event.stopPropagation()}
style={{position: "absolute",
top: menuPosition.top + 7,
left: menuPosition.left}}>
<div className={`post-menu-thread post-menu-thread-${"description"}`}
style={{ display: openMenuCid === "description" ? 'block' : 'none' }}
>
<ul className="post-menu-catalog">
<li onClick={() => handleOptionClick("description")}>Hide thread</li>
{/* {isModerator ? (
<>
change description
</>
) : null} */}
{subplebbit.suggested?.avatarUrl ? (
<li
onMouseOver={() => {setIsImageSearchOpen(true)}}
onMouseLeave={() => {setIsImageSearchOpen(false)}}>
Image search »
<ul className="dropdown-menu post-menu-catalog"
style={{display: isImageSearchOpen ? 'block': 'none'}}>
<li onClick={() => handleOptionClick("description")}>
<a
href={`https://lens.google.com/uploadbyurl?url=${subplebbit.suggested.avatarUrl}`}
target="_blank" rel="noreferrer"
>Google</a>
</li>
<li onClick={() => handleOptionClick("description")}>
<a
href={`https://yandex.com/images/search?url=${subplebbit.suggested.avatarUrl}`}
target="_blank" rel="noreferrer"
>Yandex</a>
</li>
<li onClick={() => handleOptionClick("description")}>
<a
href={`https://saucenao.com/search.php?url=${subplebbit.suggested.avatarUrl}`}
target="_blank" rel="noreferrer"
>SauceNAO</a>
</li>
</ul>
</li>
) : null}
</ul>
</div>
</PostMenuCatalog>, document.body
)}
</BoardForm>
<Link style={{all: "unset", cursor: "pointer"}} to={() => {}}
onClick={() => setSelectedThread("description")}>
<div className="teaser">
<b>Welcome to {subplebbit.title || subplebbit.address}</b>
{": " + subplebbit.description}
</div>
</Link>
</div>
) : null}
{feed.map((thread, index) => {
const commentMediaInfo = getCommentMediaInfo(thread);
const fallbackImgUrl = "assets/filedeleted-res.gif";
return (
<div key={`thread-${index}`} className="thread"
onMouseOver={() => {setIsHoveringOnThread(thread.cid)}}
onMouseLeave={() => {setIsHoveringOnThread('')}}>
{commentMediaInfo?.url ? (
<Link style={{all: "unset", cursor: "pointer"}} key={`link-${index}`} to={`/p/${selectedAddress}/c/${thread.cid}`}
onClick={() => setSelectedThread(thread.cid)}>
{commentMediaInfo?.type === "webpage" ? (
thread.thumbnailUrl ? (
<img className="card" key={`img-${index}`}
src={commentMediaInfo.thumbnail} alt={commentMediaInfo.type}
onError={(e) => {
e.target.src = fallbackImgUrl
e.target.onerror = null;
}} />
) : null
) : null}
{commentMediaInfo?.type === "image" ? (
<img className="card" key={`img-${index}`}
src={commentMediaInfo.url} alt={commentMediaInfo.type}
onError={(e) => {
e.target.src = fallbackImgUrl
e.target.onerror = null;}} />
) : null}
{commentMediaInfo?.type === "video" ? (
<video className="card" key={`fti-${index}`}
src={commentMediaInfo.url}
alt={commentMediaInfo.type}
onError={(e) => e.target.src = fallbackImgUrl} />
) : null}
{commentMediaInfo?.type === "audio" ? (
<audio className="card" controls
key={`fti-${index}`}
src={commentMediaInfo.url}
alt={commentMediaInfo.type}
onError={(e) => e.target.src = fallbackImgUrl} />
) : null}
</Link>
) : null}
{(commentMediaInfo && (
commentMediaInfo.type === 'image' ||
commentMediaInfo.type === 'video' ||
(commentMediaInfo.type === 'webpage' &&
commentMediaInfo.thumbnail))) ? (
<div key={`ti-${index}`} className="thread-icons" >
{thread.pinned ? (
<span key={`si-${index}`} className="thread-icon sticky-icon" title="Sticky" />
) : null}
{thread.locked ? (
<span key={`li-${index}`} className="thread-icon closed-icon" title="Closed" />
) : null}
</div>
) : null}
<BoardForm selectedStyle={selectedStyle}
style={{ all: "unset"}}>
<div key={`meta-${index}`} className="meta" title="(R)eplies / (I)mage Replies" >
R:
<b key={`b-${index}`}>{thread.replyCount}</b>
{(commentMediaInfo && (
commentMediaInfo.type === 'image' ||
commentMediaInfo.type === 'video' ||
(commentMediaInfo.type === 'webpage' &&
commentMediaInfo.thumbnail))) ? null : (
<div className='thread-icons'
style={{position: 'absolute', top: '-2px', right: '15px'}}>
{thread.pinned ? (
<span key={`si-${index}`} className="thread-icon sticky-icon" title="Sticky" />
) : null}
{thread.locked ? (
<span key={`li-${index}`} className="thread-icon closed-icon" title="Closed" />
) : null}
</div>
)}
<PostMenu
style={{ display: isHoveringOnThread === thread.cid ? 'inline-block' : 'none',
position: 'absolute', lineHeight: '1em', marginTop: '-1px', outline: 'none',
zIndex: '999'}}
key={`pmb-${index}`}
title="Post menu"
ref={el => {
threadMenuRefs.current[thread.cid] = el;
postMenuRef.current = el;
}}
className='post-menu-button'
id='post-menu-button-catalog'
rotated={openMenuCid === thread.cid}
onClick={(event) => {
event.stopPropagation();
const rect = threadMenuRefs.current[thread.cid].getBoundingClientRect();
setMenuPosition({top: rect.top + window.scrollY, left: rect.left});
setOpenMenuCid(prevCid => (prevCid === thread.cid ? null : thread.cid));
}}
>
</PostMenu>
</div>
{createPortal(
<PostMenuCatalog selectedStyle={selectedStyle}
ref={el => {postMenuCatalogRef.current = el}}
onClick={(event) => event.stopPropagation()}
style={{position: "absolute",
top: menuPosition.top + 7,
left: menuPosition.left}}>
<div className={`post-menu-thread post-menu-thread-${thread.cid}`}
style={{ display: openMenuCid === thread.cid ? 'block' : 'none' }}
>
<ul className="post-menu-catalog">
<li onClick={() => handleOptionClick(thread.cid)}>Hide thread</li>
{thread.author.shortAddress === account?.author.shortAddress ? (
<>
<li onClick={() => handleAuthorEditClick(thread)}>Edit post</li>
<li onClick={() => handleAuthorDeleteClick(thread)}>Delete post</li>
</>
) : null}
{isModerator ? (
<>
{thread.author.shortAddress === account?.author.shortAddress ? (
null
) : (
<li onClick={() => {
setModeratingCommentCid(thread.cid)
setIsModerationOpen(true);
handleOptionClick(thread.cid);
setDeletePost(true);
}}>
Delete post
</li>
)}
<li
onClick={() => {
setModeratingCommentCid(thread.cid)
setIsModerationOpen(true);
handleOptionClick(thread.cid);
}}>
Mod tools
</li>
</>
) : null}
{(commentMediaInfo && (
commentMediaInfo.type === 'image' ||
(commentMediaInfo.type === 'webpage' &&
commentMediaInfo.thumbnail))) ? (
<li
onMouseOver={() => {setIsImageSearchOpen(true)}}
onMouseLeave={() => {setIsImageSearchOpen(false)}}>
Image search »
<ul className="dropdown-menu post-menu-catalog"
style={{display: isImageSearchOpen ? 'block': 'none'}}>
<li onClick={() => handleOptionClick(thread.cid)}>
<a
href={`https://lens.google.com/uploadbyurl?url=${commentMediaInfo.url}`}
target="_blank" rel="noreferrer"
>Google</a>
</li>
<li onClick={() => handleOptionClick(thread.cid)}>
<a
href={`https://yandex.com/images/search?url=${commentMediaInfo.url}`}
target="_blank" rel="noreferrer"
>Yandex</a>
</li>
<li onClick={() => handleOptionClick(thread.cid)}>
<a
href={`https://saucenao.com/search.php?url=${commentMediaInfo.url}`}
target="_blank" rel="noreferrer"
>SauceNAO</a>
</li>
</ul>
</li>
) : null
}
</ul>
</div>
</PostMenuCatalog>, document.body
)}
</BoardForm>
<Link style={{all: "unset", cursor: "pointer"}} key={`link2-${index}`} to={`/p/${selectedAddress}/c/${thread.cid}`}
onClick={() => setSelectedThread(thread.cid)}>
<div key={`t-${index}`} className="teaser">
<b key={`b2-${index}`}>{thread.title ? `${thread.title}` : null}</b>
{thread.content ? `: ${thread.content}` : null}
</div>
</Link>
</div>
)})}
</InfiniteScroll>
) : (<CatalogLoader />)
)}
</Threads>
+2 -2
View File
@@ -1,11 +1,11 @@
import React, { Fragment, useEffect, useRef } from 'react';
import { Helmet } from 'react-helmet-async';
import { useAccount } from '@plebbit/plebbit-react-hooks';
import useGeneralStore from '../../hooks/stores/useGeneralStore';
import { Link, useNavigate } from 'react-router-dom';
import { Container, Header, Logo, Page, Search, About, AboutTitle, AboutContent, Boards, BoardsTitle, BoardsContent, Footer } from '../styled/views/Home.styled';
import BoardAvatar from '../BoardAvatar';
import OfflineIndicator from '../OfflineIndicator';
import useGeneralStore from '../../hooks/stores/useGeneralStore';
import packageJson from '../../../package.json'
import { Tooltip } from 'react-tooltip';
const {version} = packageJson
@@ -14,7 +14,7 @@ const commitRef = process?.env?.REACT_APP_COMMIT_REF ? ` ${process.env.REACT_APP
const Home = () => {
const {
const {
bodyStyle, setBodyStyle,
defaultSubplebbits,
setSelectedAddress,
+32 -9
View File
@@ -33,9 +33,11 @@ import handleQuoteClick from '../../utils/handleQuoteClick';
import handleQuoteHover from '../../utils/handleQuoteHover';
import handleStyleChange from '../../utils/handleStyleChange';
import removeHighlight from '../../utils/removeHighlight';
import useAnonModeRef from '../../hooks/useAnonModeRef';
import useError from '../../hooks/useError';
import useFeedStateString from '../../hooks/useFeedStateString';
import useSuccess from '../../hooks/useSuccess';
import useAnonModeStore from '../../hooks/stores/useAnonModeStore';
import packageJson from '../../../package.json'
const {version} = packageJson
@@ -62,6 +64,8 @@ const Subscriptions = () => {
setSelectedTitle,
} = useGeneralStore(state => state);
const { anonymousMode } = useAnonModeStore();
const account = useAccount();
const navigate = useNavigate();
const [, setNewErrorMessage] = useError();
@@ -75,6 +79,7 @@ const Subscriptions = () => {
const backlinkRefsMobile = useRef({});
const quoteRefsMobile = useRef({});
const postOnHoverRef = useRef(null);
const selectedThreadCidRef = useRef(null);
const [isReplyOpen, setIsReplyOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
@@ -91,6 +96,13 @@ const Subscriptions = () => {
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
const [deletePost, setDeletePost] = useState(false);
const [moderatorPermissions, setModeratorPermissions] = useState({});
const [executeAnonMode, setExecuteAnonMode] = useState(false);
const setSelectedThreadCid = (cid) => {
selectedThreadCidRef.current = cid;
}
useAnonModeRef(selectedThreadCidRef, anonymousMode && executeAnonMode);
const { feed, hasMore, loadMore } = useFeed({subplebbitAddresses: account?.subscriptions, sortType: 'active'});
const [selectedFeed, setSelectedFeed] = useState(feed.sort((a, b) => b.timestamp - a.timestamp));
@@ -316,11 +328,18 @@ const Subscriptions = () => {
});
useEffect(() => {
if (anonymousMode) {
setExecuteAnonMode(true);
}
}, [anonymousMode, selectedThreadCidRef]);
const { publishCommentEdit } = usePublishCommentEdit(publishCommentEditOptions);
const handleAuthorDeleteClick = (comment) => {
handleOptionClick(comment.cid);
const handleAuthorDeleteClick = (cid) => {
handleOptionClick(cid);
confirmAlert({
customUI: ({ onClose }) => {
@@ -334,7 +353,7 @@ const Subscriptions = () => {
onClick={() => {
setIsAuthorDelete(true);
setIsAuthorEdit(false);
setCommentCid(comment.cid);
setCommentCid(cid);
setPublishCommentEditOptions(prevOptions => ({
...prevOptions,
deleted: true,
@@ -678,7 +697,8 @@ const Subscriptions = () => {
onClick={(e) => {
if (e.button === 2) return;
e.preventDefault();
setIsReplyOpen(true);
setSelectedThreadCid(thread.cid);
setIsReplyOpen(true);
setSelectedShortCid(thread.shortCid);
setSelectedParentCid(thread.cid);
setSelectedAddress(thread.subplebbitAddress);
@@ -736,7 +756,7 @@ const Subscriptions = () => {
{thread.author.shortAddress === account?.author.shortAddress ? (
<>
<li onClick={() => {handleAuthorEditClick(thread); setSelectedAddress(thread.subplebbitAddress);}}>Edit post</li>
<li onClick={() => {handleAuthorDeleteClick(thread); setSelectedAddress(thread.subplebbitAddress);}}>Delete post</li>
<li onClick={() => {handleAuthorDeleteClick(thread.cid); setSelectedAddress(thread.subplebbitAddress);}}>Delete post</li>
</>
) : null}
{isModerator ? (
@@ -957,7 +977,8 @@ const Subscriptions = () => {
onClick={(e) => {
if (e.button === 2) return;
e.preventDefault();
setIsReplyOpen(true);
setSelectedThreadCid(thread.cid);
setIsReplyOpen(true);
setSelectedShortCid(reply.shortCid);
setSelectedParentCid(reply.cid);
setSelectedAddress(thread.subplebbitAddress);
@@ -1015,7 +1036,7 @@ const Subscriptions = () => {
{reply.author.shortAddress === account?.author.shortAddress ? (
<>
<li onClick={() => {handleAuthorEditClick(reply); setSelectedAddress(thread.subplebbitAddress);}}>Edit post</li>
<li onClick={() => {handleAuthorDeleteClick(reply); setSelectedAddress(thread.subplebbitAddress);}}>Delete post</li>
<li onClick={() => {handleAuthorDeleteClick(reply.cid); setSelectedAddress(thread.subplebbitAddress);}}>Delete post</li>
</>
) : null}
{isModerator ? (
@@ -1390,7 +1411,8 @@ const Subscriptions = () => {
onClick={(e) => {
if (e.button === 2) return;
e.preventDefault();
setIsReplyOpen(true);
setSelectedThreadCid(thread.cid);
setIsReplyOpen(true);
setSelectedShortCid(thread.shortCid);
setSelectedParentCid(thread.cid);
setSelectedAddress(thread.subplebbitAddress);
@@ -1562,7 +1584,8 @@ const Subscriptions = () => {
onClick={(e) => {
if (e.button === 2) return;
e.preventDefault();
setIsReplyOpen(true);
setSelectedThreadCid(thread.cid);
setIsReplyOpen(true);
setSelectedShortCid(reply.shortCid);
setSelectedParentCid(reply.cid);
setSelectedAddress(thread.subplebbitAddress);
+25 -11
View File
@@ -7,7 +7,6 @@ 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 useGeneralStore from '../../hooks/stores/useGeneralStore';
import { Container, NavBar, Header, Break, PostForm, PostFormTable, PostMenu } from '../styled/views/Board.styled';
import { ReplyFormLink, TopBar, BottomBar, BoardForm, Footer, AuthorDeleteAlert } from '../styled/views/Thread.styled';
import { PostMenuCatalog } from '../styled/views/Catalog.styled';
@@ -32,11 +31,14 @@ import handleQuoteClick from '../../utils/handleQuoteClick';
import handleQuoteHover from '../../utils/handleQuoteHover';
import handleStyleChange from '../../utils/handleStyleChange';
import removeHighlight from '../../utils/removeHighlight';
import useAnonMode from '../../hooks/useAnonMode';
import useClickForm from '../../hooks/useClickForm';
import useError from '../../hooks/useError';
import useStateString from '../../hooks/useStateString';
import packageJson from '../../../package.json'
import useSuccess from '../../hooks/useSuccess';
import useAnonModeStore from '../../hooks/stores/useAnonModeStore';
import useGeneralStore from '../../hooks/stores/useGeneralStore';
import packageJson from '../../../package.json'
const {version} = packageJson
@@ -65,6 +67,8 @@ const Thread = () => {
showPostFormLink,
} = useGeneralStore(state => state);
const { anonymousMode } = useAnonModeStore();
const account = useAccount();
const navigate = useNavigate();
const handleClickForm = useClickForm();
@@ -82,6 +86,8 @@ const Thread = () => {
const backlinkRefs = useRef({});
const quoteRefs = useRef({});
const postOnHoverRef = useRef(null);
const backlinkRefsMobile = useRef({});
const quoteRefsMobile = useRef({});
const [triggerPublishComment, setTriggerPublishComment] = useState(false);
const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false);
@@ -99,9 +105,9 @@ const Thread = () => {
const [outOfViewCid, setOutOfViewCid] = useState(null);
const [outOfViewPosition, setOutOfViewPosition] = useState({top: 0, left: 0});
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
const backlinkRefsMobile = useRef({});
const quoteRefsMobile = useRef({});
const [executeAnonMode, setExecuteAnonMode] = useState(false);
useAnonMode(selectedThread, anonymousMode && executeAnonMode);
const comment = useComment({commentCid: selectedThread});
const { subplebbitAddress, threadCid } = useParams();
@@ -323,7 +329,15 @@ const Thread = () => {
}));
setTriggerPublishComment(true);
setExecuteAnonMode(false);
};
useEffect(() => {
if (anonymousMode) {
setExecuteAnonMode(true);
}
}, [anonymousMode, selectedThread]);
useEffect(() => {
@@ -387,8 +401,8 @@ const Thread = () => {
const { publishCommentEdit } = usePublishCommentEdit(publishCommentEditOptions);
const handleAuthorDeleteClick = (comment) => {
handleOptionClick(comment.cid);
const handleAuthorDeleteClick = (cid) => {
handleOptionClick(cid);
confirmAlert({
customUI: ({ onClose }) => {
@@ -402,7 +416,7 @@ const Thread = () => {
onClick={() => {
setIsAuthorDelete(true);
setIsAuthorEdit(false);
setCommentCid(comment.cid);
setCommentCid(cid);
setPublishCommentEditOptions(prevOptions => ({
...prevOptions,
deleted: true,
@@ -518,7 +532,7 @@ const Thread = () => {
<span className="boardList">
[
<Link to={`/p/all`}>All</Link>
 / 
 / 
<Link to={`/p/subscriptions`}>Subscriptions</Link>
]&nbsp;[
{defaultSubplebbits.map((subplebbit, index) => (
@@ -865,7 +879,7 @@ const Thread = () => {
{comment.author?.shortAddress === account?.author.shortAddress ? (
<>
<li onClick={() => handleAuthorEditClick(comment)}>Edit post</li>
<li onClick={() => handleAuthorDeleteClick(comment)}>Delete post</li>
<li onClick={() => handleAuthorDeleteClick(comment.cid)}>Delete post</li>
</>
) : null}
{isModerator ? (
@@ -1088,7 +1102,7 @@ const Thread = () => {
{reply.author.shortAddress === account?.author.shortAddress ? (
<>
<li onClick={() => handleAuthorEditClick(reply)}>Edit post</li>
<li onClick={() => handleAuthorDeleteClick(reply)}>Delete post</li>
<li onClick={() => handleAuthorDeleteClick(reply.cid)}>Delete post</li>
</>
) : null}
{isModerator ? (
+15
View File
@@ -0,0 +1,15 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useAnonModeStore = create(persist(
(set) => ({
anonymousMode: true,
setAnonymousMode: (mode) => set({ anonymousMode: mode }),
}),
{
name: "anonmode_store",
getStorage: () => localStorage,
}
));
export default useAnonModeStore;
-3
View File
@@ -1,9 +1,6 @@
import { create } from 'zustand';
const useGeneralStore = create((set) => ({
anonymousMode: true,
setAnonymousMode: (mode) => set({ anonymousMode: mode }),
bodyStyle: JSON.parse(localStorage.getItem('bodyStyle')) || {
background: '#ffe url(assets/fade.png) top repeat-x',
color: 'maroon',
+32
View File
@@ -0,0 +1,32 @@
import { useEffect } from "react";
import { createAccount, setActiveAccount, useAccounts } from "@plebbit/plebbit-react-hooks";
import useAnonModeStore from "./stores/useAnonModeStore";
const useAnonMode = (threadCid, execute) => {
const {accounts} = useAccounts();
const { anonymousMode } = useAnonModeStore();
useEffect(() => {
const handleAnonMode = async () => {
let storedAccounts = JSON.parse(localStorage.getItem('storedAccounts')) || {};
if (!anonymousMode) return;
if (!storedAccounts[threadCid] && execute) {
await createAccount();
const lastAccount = accounts[accounts.length - 1];
await setActiveAccount(lastAccount.name);
storedAccounts[threadCid] = lastAccount.name;
localStorage.setItem('storedAccounts', JSON.stringify(storedAccounts));
} else {
await setActiveAccount(storedAccounts[threadCid]);
}
}
handleAnonMode();
}, [threadCid, execute, accounts, anonymousMode]);
return;
}
export default useAnonMode;
+32
View File
@@ -0,0 +1,32 @@
import { useEffect } from "react";
import { createAccount, setActiveAccount, useAccounts } from "@plebbit/plebbit-react-hooks";
import useAnonModeStore from "./stores/useAnonModeStore";
const useAnonModeRef = (threadCidRef, execute) => {
const {accounts} = useAccounts();
const { anonymousMode } = useAnonModeStore();
useEffect(() => {
const handleAnonMode = async () => {
let storedAccounts = JSON.parse(localStorage.getItem('storedAccounts')) || {};
if (!anonymousMode) return;
if (!storedAccounts[threadCidRef.current] && execute) {
await createAccount();
const lastAccount = accounts[accounts.length - 1];
await setActiveAccount(lastAccount.name);
storedAccounts[threadCidRef.current] = lastAccount.name;
localStorage.setItem('storedAccounts', JSON.stringify(storedAccounts));
} else {
await setActiveAccount(storedAccounts[threadCidRef.current]);
}
}
handleAnonMode();
}, [threadCidRef, execute, accounts, anonymousMode]);
return;
}
export default useAnonModeRef;
+89 -1
View File
@@ -7,6 +7,7 @@
resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.1.1.tgz#9274ec7460652f9c632c59addf24efb1684ef876"
integrity sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==
"@adraffy/ens-normalize@1.8.3", "@adraffy/ens-normalize@1.9.0", "@adraffy/ens-normalize@1.9.2":
version "1.8.3"
resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.8.3.tgz#6084dd00eac37a99bcf91a3a3249cc737cbcb005"
@@ -2591,11 +2592,12 @@
resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123"
integrity sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==
"@noble/hashes@1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183"
integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==
"@noble/hashes@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1"
@@ -2606,6 +2608,7 @@
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9"
integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==
"@noble/secp256k1@1.7.1", "@noble/secp256k1@^1.3.0":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c"
@@ -2648,7 +2651,53 @@
mkdirp "^1.0.4"
rimraf "^3.0.2"
"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#574d1730da725a6266da6d69f2fa01f027dadb96":
version "0.0.3"
resolved "https://github.com/plebbit/plebbit-js.git#220c709566d8b680e7dc13722f4e81c8d0f13b3b"
dependencies:
"@adraffy/ens-normalize" "1.9.2"
"@keyv/sqlite" "3.6.2"
"@plebbit/plebbit-logger" "github:plebbit/plebbit-logger"
"@types/node-fetch" "2.6.2"
"@types/proper-lockfile" "4.1.2"
"@types/uuid" "8.3.4"
assert "2.0.0"
async-wait-until "2.0.12"
buffer "6.0.3"
captcha-canvas "3.2.1"
err-code "3.0.1"
file-type "16.5.4"
form-data "4.0.0"
hpagent "1.2.0"
ipfs-http-client "56.0.3"
ipfs-only-hash "4.0.0"
is-ipfs "6.0.2"
jose "4.11.0"
js-sha256 "0.9.0"
keyv "4.5.2"
knex "2.4.2"
libp2p-crypto "0.21.2"
limiter "2.1.0"
localforage "1.10.0"
lodash-es "4.17.21"
lru-cache "7.18.3"
open-graph-scraper "5.2.3"
p-limit "3.1.0"
peer-id "0.16.0"
proper-lockfile "github:plebbit/node-proper-lockfile"
retry "0.13.1"
safe-stable-stringify "2.4.1"
sha1-uint8array "0.10.3"
skia-canvas "1.0.0"
sqlite3 "5.1.6"
tiny-typed-emitter "2.1.0"
tinycache "1.1.2"
ts-custom-error "3.3.1"
uuid "9.0.0"
viem "1.0.7"
"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#27df54b8c591c9245deb91e131d15de2bc8bf4d3":
version "0.0.3"
resolved "https://github.com/plebbit/plebbit-js.git#574d1730da725a6266da6d69f2fa01f027dadb96"
dependencies:
@@ -2722,6 +2771,22 @@
uuid "8.3.2"
zustand "4.0.0"
"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#338324289dffa20ecde429fa5f428a2894851512":
version "0.0.1"
resolved "https://github.com/plebbit/plebbit-react-hooks.git#338324289dffa20ecde429fa5f428a2894851512"
dependencies:
"@adraffy/ens-normalize" "1.9.2"
"@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#220c709566d8b680e7dc13722f4e81c8d0f13b3b"
"@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git"
assert "2.0.0"
ethers "5.6.9"
localforage "1.10.0"
lodash.isequal "4.5.0"
memoizee "0.4.15"
quick-lru "5.1.1"
uuid "8.3.2"
zustand "4.0.0"
"@pmmmwh/react-refresh-webpack-plugin@^0.5.3":
version "0.5.10"
resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz#2eba163b8e7dbabb4ce3609ab5e32ab63dda3ef8"
@@ -2859,6 +2924,28 @@
"@noble/hashes" "~1.3.0"
"@scure/base" "~1.1.0"
"@scure/base@~1.1.0":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938"
integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==
"@scure/bip32@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.0.tgz#6c8d980ef3f290987736acd0ee2e0f0d50068d87"
integrity sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==
dependencies:
"@noble/curves" "~1.0.0"
"@noble/hashes" "~1.3.0"
"@scure/base" "~1.1.0"
"@scure/bip39@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.0.tgz#a207e2ef96de354de7d0002292ba1503538fc77b"
integrity sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==
dependencies:
"@noble/hashes" "~1.3.0"
"@scure/base" "~1.1.0"
"@sideway/address@^4.1.3":
version "4.1.4"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0"
@@ -3716,6 +3803,7 @@
dependencies:
"@webassemblyjs/floating-point-hex-parser" "1.11.6"
"@webassemblyjs/helper-api-error" "1.11.6"
"@xtuc/long" "4.2.2"
"@webassemblyjs/helper-wasm-bytecode@1.11.6":