diff --git a/src/components/Board.jsx b/src/components/Board.jsx index 03a3541b..cbdfa0cd 100644 --- a/src/components/Board.jsx +++ b/src/components/Board.jsx @@ -237,7 +237,7 @@ const Board = ({ setBodyStyle }) => { const handleClickHelp = () => { - alert("- The media will appear in the post after sharing its link. \n- A CAPTCHA challenge will appear after posting. \n- The CAPTCHA is case-sensitive."); + alert("- Embedding media is optional, posts can be text-only. \n- A CAPTCHA challenge will appear after posting. \n- The CAPTCHA is case-sensitive."); }; diff --git a/src/components/Catalog.jsx b/src/components/Catalog.jsx index 362e2ffb..5dfb0db0 100644 --- a/src/components/Catalog.jsx +++ b/src/components/Catalog.jsx @@ -11,7 +11,7 @@ import InfiniteScroll from 'react-infinite-scroller'; const Catalog = ({ setBodyStyle }) => { const [defaultSubplebbits, setDefaultSubplebbits] = useState([]); - const { selectedTitle, setSelectedTitle, selectedAddress, setSelectedAddress, setSelectedThread, selectedStyle, setSelectedStyle } = useContext(BoardContext); + const { selectedTitle, setSelectedTitle, selectedAddress, setSelectedAddress, setSelectedThread, selectedStyle, setSelectedStyle, setIsCaptchaOpen } = useContext(BoardContext); const [showPostFormLink, setShowPostFormLink] = useState(true); const [showPostForm, setShowPostForm] = useState(false); const [name, setName] = useState(''); @@ -208,7 +208,7 @@ const Catalog = ({ setBodyStyle }) => { const handleClickHelp = () => { - alert("- The CAPTCHA loads after you click \"Post\" \n- The CAPTCHA is case-sensitive. \n- Make sure to not block any cookies set by plebchan."); + alert("- Embedding media is optional, posts can be text-only. \n- A CAPTCHA challenge will appear after posting. \n- The CAPTCHA is case-sensitive."); }; @@ -403,7 +403,7 @@ const Catalog = ({ setBodyStyle }) => { - + Name @@ -423,24 +423,18 @@ const Catalog = ({ setBodyStyle }) => { setComment(event.target.value)}> - - Verification - - - - ? - - - - - Embed File + ? + + + + + setIsCaptchaOpen(true)}>Show Captcha - diff --git a/src/components/Thread.jsx b/src/components/Thread.jsx index b060aa9f..81817b5c 100644 --- a/src/components/Thread.jsx +++ b/src/components/Thread.jsx @@ -1,9 +1,9 @@ import React, { useState, useEffect, useContext } from 'react'; import { Link, useNavigate, useParams, useLocation } from 'react-router-dom'; -import { Container, NavBar, Header, Break, PostForm, BoardForm } from './styles/Board.styled'; -import { ReplyFormTable, ReplyFormLink, TopBar, BottomBar } from './styles/Thread.styled'; +import { Container, NavBar, Header, Break, PostForm, PostFormTable, BoardForm } from './styles/Board.styled'; +import { ReplyFormLink, TopBar, BottomBar } from './styles/Thread.styled'; import { BoardContext } from '../App'; -import { useComment } from '@plebbit/plebbit-react-hooks'; +import { useComment, useAccountsActions } from '@plebbit/plebbit-react-hooks'; import ImageBanner from './ImageBanner'; import { Tooltip } from 'react-tooltip'; import getDate from '../utils/getDate'; @@ -12,9 +12,13 @@ import renderThreadComments from '../utils/renderThreadComments'; const Thread = ({ setBodyStyle }) => { const [defaultSubplebbits, setDefaultSubplebbits] = useState([]); - const { selectedTitle, setSelectedTitle, selectedAddress, setSelectedAddress, selectedThread, setSelectedThread, selectedStyle, setSelectedStyle } = useContext(BoardContext); + const { selectedTitle, setSelectedTitle, selectedAddress, setSelectedAddress, selectedThread, setSelectedThread, selectedStyle, setSelectedStyle, setIsCaptchaOpen } = useContext(BoardContext); const [showPostFormLink, setShowPostFormLink] = useState(true); const [showPostForm, setShowPostForm] = useState(false); + const [name, setName] = useState(''); + const [subject, setSubject] = useState(''); + const [commentContent, setCommentContent] = useState(''); + const { publishComment } = useAccountsActions(); const navigate = useNavigate(); const location = useLocation(); const [prevScrollPos, setPrevScrollPos] = useState(0); @@ -113,6 +117,73 @@ const Thread = ({ setBodyStyle }) => { + const onChallengeVerification = (challengeVerification) => { + if (challengeVerification.challengeSuccess === true) { + console.log('challenge success', {publishedCid: challengeVerification.publication.cid}) + } + else if (challengeVerification.challengeSuccess === false) { + console.error('challenge failed', {reason: challengeVerification.reason, errors: challengeVerification.errors}); + alert("Error: You seem to have mistyped the CAPTCHA. Please try again."); + } + } + + + const onChallenge = async (challenges, comment) => { + let challengeAnswers = []; + try { + challengeAnswers = await getChallengeAnswersFromUser(challenges) + } + catch (error) { + console.log(error); + } + if (challengeAnswers) { + await comment.publishChallengeAnswers(challengeAnswers) + } + } + + + const onError = (error) => console.error(error) + + + const getChallengeAnswersFromUser = async (challenges) => { + return new Promise((resolve, reject) => { + const imageString = challenges?.challenges[0].challenge; + const imageSource = `data:image/png;base64,${imageString}`; + const challengeImg = new Image(); + challengeImg.src = imageSource; + + challengeImg.onload = () => { + const inputEl = document.getElementById('t-resp'); + const cntEl = document.getElementById('t-cnt'); + cntEl.appendChild(challengeImg); + inputEl.focus(); + + const handleKeyDown = (event) => { + if (event.key === 'Enter') { + const challengeResponse = inputEl.value; + inputEl.value = ''; + + if (cntEl.contains(challengeImg)) { + cntEl.removeChild(challengeImg); + } + + document.removeEventListener('keydown', handleKeyDown); + + resolve(challengeResponse); + } + }; + + document.addEventListener('keydown', handleKeyDown); + }; + + challengeImg.onerror = () => { + reject(new Error('Could not load challenge image')); + }; + }); + }; + + + const handleVoidClick = () => {} // desktop navbar board select functionality @@ -132,7 +203,7 @@ const Thread = ({ setBodyStyle }) => { const handleClickHelp = () => { - alert("- The CAPTCHA loads after you click \"Post\" \n- The CAPTCHA is case-sensitive. \n- Make sure to not block any cookies set by plebchan."); + alert("- Embedding media is optional, posts can be text-only. \n- A CAPTCHA challenge will appear after posting. \n- The CAPTCHA is case-sensitive."); }; @@ -152,6 +223,26 @@ const Thread = ({ setBodyStyle }) => { window.scrollTo(0, document.body.scrollHeight); } + + const handlePublishComment = async () => { + try { + const pendingComment = await publishComment({ + content: commentContent, + title: subject, + subplebbitAddress: selectedAddress, + onChallengeVerification, + onChallenge, + onError, + }); + console.log(`Comment pending with index: ${pendingComment.index}`); + setName(''); + setSubject(''); + setCommentContent(''); + } catch (error) { + console.error(error); + } + }; + // scroll to post when quote is clicked function handleQuoteClick(reply, event) { event.preventDefault(); @@ -338,45 +429,41 @@ const Thread = ({ setBodyStyle }) => { - + - - Name + + Name - - + setName(event.target.value)} /> + + + + Subject + + setSubject(event.target.value)} /> + Comment - - - - - Verification - - - - ? - - - - - - - + setCommentContent(event.target.value)}> Embed File + ? + + + + + setIsCaptchaOpen(true)}>Show Captcha - - + diff --git a/src/components/styles/Board.styled.jsx b/src/components/styles/Board.styled.jsx index f01d269a..ec89e3a9 100644 --- a/src/components/styles/Board.styled.jsx +++ b/src/components/styles/Board.styled.jsx @@ -1117,6 +1117,11 @@ export const PostFormTable = styled.table` }`; } }} + + #t-help { + margin: 0; + margin-top: -5px + } `; export const PostForm = styled.div` diff --git a/src/components/styles/Thread.styled.jsx b/src/components/styles/Thread.styled.jsx index d7275efc..e69d84fe 100644 --- a/src/components/styles/Thread.styled.jsx +++ b/src/components/styles/Thread.styled.jsx @@ -169,472 +169,6 @@ export const ReplyFormLink = styled.div` }} `; -export const ReplyFormTable = styled.table` - display: ${props => (props.showReplyForm ? 'table' : 'none')}; - width: 418px; - border-spacing: 1px; - margin-left: auto; - margin-right: auto; - - ${({ selectedStyle }) => { - switch (selectedStyle) { - case 'Yotsuba': - return `tbody > tr > td:first-child { - background-color: #ea8; - color: #800; - font-weight: 700; - border: 1px solid #800; - padding: 0 5px; - font-size: 10pt; - } - - td { - padding: 0; - font-size: 10pt; - } - - tbody > tr > td > input[type="text"] { - width: 244px; - } - - input[type="text"], input[type="password"] > tbody textarea { - margin: 0; - margin-right: 2px; - padding: 2px 4px 3px; - border: 1px solid #aaa; - outline: none; - font-family: arial, helvetica, sans-serif; - font-size: 10pt; - } - - textarea { - width: 292px; - margin-bottom: -3px; - outline: none; - border-radius: none; - } - - #t-root { - position: relative; - overflow: hidden; - box-sizing: border-box; - background: #eee; - border: 1px solid #777; - margin: 2px 0; - width: 300px; - } - - #t-resp { - width: 254px; - box-sizing: border-box; - text-transform: uppercase; - font-size: 11px; - height: 18px; - margin: 0px; - padding: 0px 2px; - font-family: monospace; - vertical-align: middle; - -webkit-appearance: none; - } - - #t-help { - font-size: 11px; - padding: 0; - width: 20px; - box-sizing: border-box; - margin: 0px 0px 0px 6px; - vertical-align: middle; - height: 18px; - } - - #t-cnt { - height: 80px; - margin-top: 2px; - position: relative; - }`; - - case 'Yotsuba-B': - return `tbody > tr > td:first-child { - background-color: #98e; - color: #000; - font-weight: 700; - border: 1px solid #000; - padding: 0 5px; - font-size: 10pt; - } - - td { - padding: 0; - font-size: 10pt; - } - - tbody > tr > td > input[type="text"] { - width: 244px; - } - - input[type="text"], input[type="password"] > tbody textarea { - margin: 0; - margin-right: 2px; - padding: 2px 4px 3px; - border: 1px solid #aaa; - outline: none; - font-family: arial, helvetica, sans-serif; - font-size: 10pt; - } - - textarea { - width: 292px; - margin-bottom: -3px; - outline: none; - border-radius: none; - } - - #t-root { - position: relative; - overflow: hidden; - box-sizing: border-box; - background: #eee; - border: 1px solid #777; - margin: 2px 0; - width: 300px; - } - - #t-resp { - width: 254px; - box-sizing: border-box; - text-transform: uppercase; - font-size: 11px; - height: 18px; - margin: 0px; - padding: 0px 2px; - font-family: monospace; - vertical-align: middle; - -webkit-appearance: none; - } - - #t-help { - font-size: 11px; - padding: 0; - width: 20px; - box-sizing: border-box; - margin: 0px 0px 0px 6px; - vertical-align: middle; - height: 18px; - } - - #t-cnt { - height: 80px; - margin-top: 2px; - position: relative; - }`; - - case 'Futaba': - return `tbody > tr > td:first-child { - background-color: #ea8; - color: #800; - font-weight: 700; - padding: 0; - } - - td { - padding: 0; - font-size: 12pt; - } - - tbody > tr > td > input[type="text"] { - width: 244px; - } - - input[type="text"], input[type="password"] > tbody textarea { - margin: 0; - margin-right: 2px; - padding: 2px 4px 3px; - border: 1px solid #aaa; - outline: none; - font-family: arial, helvetica, sans-serif; - font-size: 10pt; - } - - textarea { - width: 292px; - margin-bottom: -3px; - outline: none; - border-radius: none; - } - - #t-root { - position: relative; - overflow: hidden; - box-sizing: border-box; - background: #eee; - border: 1px solid #777; - margin: 2px 0; - width: 300px; - } - - #t-resp { - width: 254px; - box-sizing: border-box; - text-transform: uppercase; - font-size: 11px; - height: 18px; - margin: 0px; - padding: 0px 2px; - font-family: monospace; - vertical-align: middle; - -webkit-appearance: none; - } - - #t-help { - font-size: 11px; - padding: 0; - width: 20px; - box-sizing: border-box; - margin: 0px 0px 0px 6px; - vertical-align: middle; - height: 18px; - } - - #t-cnt { - height: 80px; - margin-top: 2px; - position: relative; - }`; - - case 'Burichan': - return `tbody > tr > td:first-child { - background-color: #98e; - color: #000; - font-weight: 700; - padding: 0; - font-size: 12pt; - } - - td { - padding: 0; - } - - tbody > tr > td > input[type="text"] { - width: 244px; - } - - input[type="text"], input[type="password"] > tbody textarea { - margin: 0; - margin-right: 2px; - padding: 2px 4px 3px; - border: 1px solid #aaa; - outline: none; - font-family: arial, helvetica, sans-serif; - font-size: 10pt; - } - - textarea { - width: 292px; - margin-bottom: -3px; - outline: none; - border-radius: none; - } - - #t-root { - position: relative; - overflow: hidden; - box-sizing: border-box; - background: #eee; - border: 1px solid #777; - margin: 2px 0; - width: 300px; - } - - #t-resp { - width: 254px; - box-sizing: border-box; - text-transform: uppercase; - font-size: 11px; - height: 18px; - margin: 0px; - padding: 0px 2px; - font-family: monospace; - vertical-align: middle; - -webkit-appearance: none; - } - - #t-help { - font-size: 11px; - padding: 0; - width: 20px; - box-sizing: border-box; - margin: 0px 0px 0px 6px; - vertical-align: middle; - height: 18px; - } - - #t-cnt { - height: 80px; - margin-top: 2px; - position: relative; - }`; - - case 'Tomorrow': - return `tbody > tr > td:first-child { - background-color: #282a2e; - color: #c5c8c6; - font-weight: 700; - border: 1px solid #111; - padding: 0 5px; - font-size: 10pt; - } - - td { - padding: 0; - font-size: 10pt; - } - - tbody > tr > td > input[type="text"] { - width: 244px; - } - - input[type="text"], input[type="password"] > tbody textarea { - margin: 0; - margin-right: 2px; - border: 1px solid #000; - background-color: #282a2e; - color: #c5c8c6; - outline: none; - font-family: arial, helvetica, sans-serif; - font-size: 10pt; - } - - textarea { - width: 292px; - margin-bottom: -3px; - outline: none; - border-radius: none; - border: 1px solid #000; - background-color: #282a2e; - color: #c5c8c6; - } - - #t-root { - position: relative; - overflow: hidden; - box-sizing: border-box; - background: #2d2d2d; - border: 1px solid #777; - margin: 2px 0; - width: 300px; - } - - #t-resp { - width: 254px; - box-sizing: border-box; - text-transform: uppercase; - font-size: 11px; - height: 18px; - margin: 0px; - padding: 0px 2px; - font-family: monospace; - vertical-align: middle; - -webkit-appearance: none; - } - - #t-help { - font-size: 11px; - padding: 0; - width: 20px; - box-sizing: border-box; - margin: 0px 0px 0px 6px; - vertical-align: middle; - height: 18px; - } - - #t-cnt { - height: 80px; - margin-top: 2px; - position: relative; - } - - #post-button, button { - filter: brightness(80%); - }`; - - case 'Photon': - return `tbody > tr > td:first-child { - background-color: #ddd; - color: #333; - font-weight: 700; - border: 1px solid #ccc; - padding: 0 5px; - font-size: 10pt; - } - - td { - padding: 0; - font-size: 10pt; - } - - tbody > tr > td > input[type="text"] { - width: 244px; - } - - input[type="text"], input[type="password"] > tbody textarea { - margin: 0; - margin-right: 2px; - padding: 2px 4px 3px; - border: 1px solid #aaa; - outline: none; - font-family: arial, helvetica, sans-serif; - font-size: 10pt; - } - - textarea { - width: 292px; - margin-bottom: -3px; - outline: none; - border-radius: none; - } - - #t-root { - position: relative; - overflow: hidden; - box-sizing: border-box; - background: #eee; - border: 1px solid #777; - margin: 2px 0; - width: 300px; - } - - #t-resp { - width: 254px; - box-sizing: border-box; - text-transform: uppercase; - font-size: 11px; - height: 18px; - margin: 0px; - padding: 0px 2px; - font-family: monospace; - vertical-align: middle; - -webkit-appearance: none; - } - - #t-help { - font-size: 11px; - padding: 0; - width: 20px; - box-sizing: border-box; - margin: 0px 0px 0px 6px; - vertical-align: middle; - height: 18px; - } - - #t-cnt { - height: 80px; - margin-top: 2px; - position: relative; - }`; - } - }} -`; - export const TopBar = styled.div` @media (min-width: 480px) { #return-button-mobile {