mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
initial posting functionality, captcha
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
"@babel/core": "^7.20.12",
|
"@babel/core": "^7.20.12",
|
||||||
"@babel/plugin-syntax-flow": "^7.18.6",
|
"@babel/plugin-syntax-flow": "^7.18.6",
|
||||||
"@babel/plugin-transform-react-jsx": "^7.20.13",
|
"@babel/plugin-transform-react-jsx": "^7.20.13",
|
||||||
|
"@plebbit/plebbit-logger": "https://github.com/plebbit/plebbit-logger.git",
|
||||||
"@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks",
|
"@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks",
|
||||||
"@testing-library/dom": "^8.20.0",
|
"@testing-library/dom": "^8.20.0",
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
"@testing-library/user-event": "^13.2.1",
|
"@testing-library/user-event": "^13.2.1",
|
||||||
"@types/node": "^18.13.0",
|
"@types/node": "^18.13.0",
|
||||||
"babel-plugin-styled-components": "^2.0.7",
|
"babel-plugin-styled-components": "^2.0.7",
|
||||||
|
"eslint": "^8.34.0",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
|||||||
+95
-13
@@ -3,7 +3,7 @@ import { Link, useNavigate } from 'react-router-dom';
|
|||||||
import { BoardContext } from '../App';
|
import { BoardContext } from '../App';
|
||||||
import { Container, NavBar, Header, Break, PostFormLink, PostFormTable, PostForm, TopBar, BoardForm } from './styles/Board.styled';
|
import { Container, NavBar, Header, Break, PostFormLink, PostFormTable, PostForm, TopBar, BoardForm } from './styles/Board.styled';
|
||||||
import ImageBanner from './ImageBanner';
|
import ImageBanner from './ImageBanner';
|
||||||
import { useFeed } from '@plebbit/plebbit-react-hooks';
|
import { useFeed, useAccountsActions } from '@plebbit/plebbit-react-hooks';
|
||||||
// import InfiniteScroll from 'react-infinite-scroller';
|
// import InfiniteScroll from 'react-infinite-scroller';
|
||||||
|
|
||||||
|
|
||||||
@@ -12,11 +12,77 @@ const Board = ({ setBodyStyle }) => {
|
|||||||
const { selectedTitle, setSelectedTitle, selectedAddress, setSelectedAddress, selectedStyle, setSelectedStyle } = useContext(BoardContext);
|
const { selectedTitle, setSelectedTitle, selectedAddress, setSelectedAddress, selectedStyle, setSelectedStyle } = useContext(BoardContext);
|
||||||
const [showPostFormLink, setShowPostFormLink] = useState(true);
|
const [showPostFormLink, setShowPostFormLink] = useState(true);
|
||||||
const [showPostForm, setShowPostForm] = useState(false);
|
const [showPostForm, setShowPostForm] = useState(false);
|
||||||
|
const [name, setName] = useState('');
|
||||||
|
const [subject, setSubject] = useState('');
|
||||||
|
const [comment, setComment] = useState('');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { feed, hasMore, loadMore } = useFeed([`${selectedAddress}`], 'new');
|
const { feed, hasMore, loadMore } = useFeed([`${selectedAddress}`], 'new');
|
||||||
|
const { publishComment } = useAccountsActions();
|
||||||
|
|
||||||
|
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'));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// console.log(feed);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let didCancel = false;
|
let didCancel = false;
|
||||||
@@ -52,6 +118,26 @@ const Board = ({ setBodyStyle }) => {
|
|||||||
navigate('/board/post-thread');
|
navigate('/board/post-thread');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePublishComment = async () => {
|
||||||
|
// Event.preventDefault();
|
||||||
|
try {
|
||||||
|
const pendingComment = await publishComment({
|
||||||
|
content: comment,
|
||||||
|
title: subject,
|
||||||
|
subplebbitAddress: selectedAddress,
|
||||||
|
onChallengeVerification,
|
||||||
|
onChallenge,
|
||||||
|
onError,
|
||||||
|
});
|
||||||
|
console.log(`Comment pending with index: ${pendingComment.index}`);
|
||||||
|
setName('');
|
||||||
|
setSubject('');
|
||||||
|
setComment('');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleStyleChange = (event) => {
|
const handleStyleChange = (event) => {
|
||||||
switch (event.target.value) {
|
switch (event.target.value) {
|
||||||
case "Yotsuba":
|
case "Yotsuba":
|
||||||
@@ -148,7 +234,7 @@ const Board = ({ setBodyStyle }) => {
|
|||||||
</>
|
</>
|
||||||
</Header>
|
</Header>
|
||||||
<Break selectedStyle={selectedStyle} />
|
<Break selectedStyle={selectedStyle} />
|
||||||
<PostForm selectedStyle={selectedStyle} name="post" action="" method="post" enctype="multipart/form-data">
|
<PostForm selectedStyle={selectedStyle}>
|
||||||
<PostFormLink id="post-form-link" showPostFormLink={showPostFormLink} >
|
<PostFormLink id="post-form-link" showPostFormLink={showPostFormLink} >
|
||||||
[
|
[
|
||||||
<a onClick={handleClickForm}>Start a New Thread</a>
|
<a onClick={handleClickForm}>Start a New Thread</a>
|
||||||
@@ -159,34 +245,30 @@ const Board = ({ setBodyStyle }) => {
|
|||||||
<tr data-type="Name">
|
<tr data-type="Name">
|
||||||
<td id="td-name">Name</td>
|
<td id="td-name">Name</td>
|
||||||
<td>
|
<td>
|
||||||
<input name="name" type="text" tabIndex={1} placeholder="Anonymous" />
|
<input name="name" type="text" tabIndex={1} placeholder="Anonymous" value={name} onChange={(event) => setName(event.target.value)} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr data-type="Subject">
|
<tr data-type="Subject">
|
||||||
<td>Subject</td>
|
<td>Subject</td>
|
||||||
<td>
|
<td>
|
||||||
<input name="sub" type="text" tabIndex={3} />
|
<input name="sub" type="text" tabIndex={3} value={subject} onChange={(event) => setSubject(event.target.value)} />
|
||||||
<input id="post-button" type="submit" value="Post" tabIndex={6} />
|
<input id="post-button" type="submit" value="Post" tabIndex={6} onClick={handlePublishComment} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr data-type="Comment">
|
<tr data-type="Comment">
|
||||||
<td>Comment</td>
|
<td>Comment</td>
|
||||||
<td>
|
<td>
|
||||||
<textarea name="com" cols="48" rows="4" tabIndex={4} wrap="soft"></textarea>
|
<textarea name="com" cols="48" rows="4" tabIndex={4} wrap="soft" value={comment} onChange={(event) => setComment(event.target.value)}></textarea>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="captchaFormPart">
|
<tr id="captchaFormPart">
|
||||||
<td>Verification</td>
|
<td>Verification</td>
|
||||||
<td colSpan={2}>
|
<td colSpan={2}>
|
||||||
<div id="t-root">
|
<div id="t-root">
|
||||||
<input id="t-resp" name="t-response" placeholder="Type the CAPTCHA here" autoComplete='off' type="text" />
|
<input id="t-resp" name="t-response" placeholder="Type the CAPTCHA here and hit return" autoComplete='off' type="text" />
|
||||||
<button id="t-help" type="button" onClick={handleClickHelp} data-tip="Help" tabIndex={-1}>?</button>
|
<button id="t-help" type="button" onClick={handleClickHelp} data-tip="Help" tabIndex={-1}>?</button>
|
||||||
<div id="t-cnt">
|
<div id="t-cnt">
|
||||||
<div id="t-bg"></div>
|
|
||||||
<div id="t-fg"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="t-msg"></div>
|
|
||||||
<input name="t-challenge" type="hidden"/>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -216,7 +298,7 @@ const Board = ({ setBodyStyle }) => {
|
|||||||
</span>
|
</span>
|
||||||
<hr />
|
<hr />
|
||||||
</TopBar>
|
</TopBar>
|
||||||
<BoardForm selectedStyle={selectedStyle} id="board-form" name="board-form" action="" method="post">
|
<BoardForm selectedStyle={selectedStyle}>
|
||||||
<div className="board">
|
<div className="board">
|
||||||
{/* <InfiniteScroll
|
{/* <InfiniteScroll
|
||||||
pageStart={0}
|
pageStart={0}
|
||||||
|
|||||||
@@ -415,6 +415,8 @@ export const PostFormTable = styled.table`
|
|||||||
outline: none;
|
outline: none;
|
||||||
border-radius: none;
|
border-radius: none;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
|
font-family: arial, helvetica, sans-serif;
|
||||||
|
font-size: 10pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#t-root {
|
#t-root {
|
||||||
@@ -455,6 +457,11 @@ export const PostFormTable = styled.table`
|
|||||||
height: 80px;
|
height: 80px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
case 'Yotsuba B':
|
case 'Yotsuba B':
|
||||||
@@ -492,6 +499,8 @@ export const PostFormTable = styled.table`
|
|||||||
outline: none;
|
outline: none;
|
||||||
border-radius: none;
|
border-radius: none;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
|
font-family: arial, helvetica, sans-serif;
|
||||||
|
font-size: 10pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#t-root {
|
#t-root {
|
||||||
@@ -533,6 +542,11 @@ export const PostFormTable = styled.table`
|
|||||||
height: 80px;
|
height: 80px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
case 'Futaba':
|
case 'Futaba':
|
||||||
@@ -568,6 +582,8 @@ export const PostFormTable = styled.table`
|
|||||||
outline: none;
|
outline: none;
|
||||||
border-radius: none;
|
border-radius: none;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
|
font-family: arial, helvetica, sans-serif;
|
||||||
|
font-size: 10pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#t-root {
|
#t-root {
|
||||||
@@ -610,6 +626,11 @@ export const PostFormTable = styled.table`
|
|||||||
height: 80px;
|
height: 80px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
case 'Burichan':
|
case 'Burichan':
|
||||||
@@ -645,6 +666,8 @@ export const PostFormTable = styled.table`
|
|||||||
outline: none;
|
outline: none;
|
||||||
border-radius: none;
|
border-radius: none;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
|
font-family: arial, helvetica, sans-serif;
|
||||||
|
font-size: 10pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#t-root {
|
#t-root {
|
||||||
@@ -687,6 +710,11 @@ export const PostFormTable = styled.table`
|
|||||||
height: 80px;
|
height: 80px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
case 'Tomorrow':
|
case 'Tomorrow':
|
||||||
@@ -729,6 +757,8 @@ export const PostFormTable = styled.table`
|
|||||||
background-color: #282a2e;
|
background-color: #282a2e;
|
||||||
color: #c5c8c6;
|
color: #c5c8c6;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
|
font-family: arial, helvetica, sans-serif;
|
||||||
|
font-size: 10pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#t-root {
|
#t-root {
|
||||||
@@ -770,6 +800,11 @@ export const PostFormTable = styled.table`
|
|||||||
height: 80px;
|
height: 80px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#post-button, button {
|
#post-button, button {
|
||||||
@@ -811,6 +846,8 @@ export const PostFormTable = styled.table`
|
|||||||
outline: none;
|
outline: none;
|
||||||
border-radius: none;
|
border-radius: none;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
|
font-family: arial, helvetica, sans-serif;
|
||||||
|
font-size: 10pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#t-root {
|
#t-root {
|
||||||
@@ -852,12 +889,17 @@ export const PostFormTable = styled.table`
|
|||||||
height: 80px;
|
height: 80px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const PostForm = styled.form`
|
export const PostForm = styled.div`
|
||||||
${({ selectedStyle }) => {
|
${({ selectedStyle }) => {
|
||||||
switch (selectedStyle) {
|
switch (selectedStyle) {
|
||||||
case 'Yotsuba':
|
case 'Yotsuba':
|
||||||
@@ -1035,7 +1077,7 @@ export const TopBar = styled.div`
|
|||||||
}}
|
}}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const BoardForm = styled.form`
|
export const BoardForm = styled.div`
|
||||||
${({ selectedStyle }) => {
|
${({ selectedStyle }) => {
|
||||||
switch (selectedStyle) {
|
switch (selectedStyle) {
|
||||||
case 'Yotsuba':
|
case 'Yotsuba':
|
||||||
|
|||||||
@@ -5592,6 +5592,51 @@ eslint@^8.3.0:
|
|||||||
strip-json-comments "^3.1.0"
|
strip-json-comments "^3.1.0"
|
||||||
text-table "^0.2.0"
|
text-table "^0.2.0"
|
||||||
|
|
||||||
|
eslint@^8.34.0:
|
||||||
|
version "8.34.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.34.0.tgz#fe0ab0ef478104c1f9ebc5537e303d25a8fb22d6"
|
||||||
|
integrity sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==
|
||||||
|
dependencies:
|
||||||
|
"@eslint/eslintrc" "^1.4.1"
|
||||||
|
"@humanwhocodes/config-array" "^0.11.8"
|
||||||
|
"@humanwhocodes/module-importer" "^1.0.1"
|
||||||
|
"@nodelib/fs.walk" "^1.2.8"
|
||||||
|
ajv "^6.10.0"
|
||||||
|
chalk "^4.0.0"
|
||||||
|
cross-spawn "^7.0.2"
|
||||||
|
debug "^4.3.2"
|
||||||
|
doctrine "^3.0.0"
|
||||||
|
escape-string-regexp "^4.0.0"
|
||||||
|
eslint-scope "^7.1.1"
|
||||||
|
eslint-utils "^3.0.0"
|
||||||
|
eslint-visitor-keys "^3.3.0"
|
||||||
|
espree "^9.4.0"
|
||||||
|
esquery "^1.4.0"
|
||||||
|
esutils "^2.0.2"
|
||||||
|
fast-deep-equal "^3.1.3"
|
||||||
|
file-entry-cache "^6.0.1"
|
||||||
|
find-up "^5.0.0"
|
||||||
|
glob-parent "^6.0.2"
|
||||||
|
globals "^13.19.0"
|
||||||
|
grapheme-splitter "^1.0.4"
|
||||||
|
ignore "^5.2.0"
|
||||||
|
import-fresh "^3.0.0"
|
||||||
|
imurmurhash "^0.1.4"
|
||||||
|
is-glob "^4.0.0"
|
||||||
|
is-path-inside "^3.0.3"
|
||||||
|
js-sdsl "^4.1.4"
|
||||||
|
js-yaml "^4.1.0"
|
||||||
|
json-stable-stringify-without-jsonify "^1.0.1"
|
||||||
|
levn "^0.4.1"
|
||||||
|
lodash.merge "^4.6.2"
|
||||||
|
minimatch "^3.1.2"
|
||||||
|
natural-compare "^1.4.0"
|
||||||
|
optionator "^0.9.1"
|
||||||
|
regexpp "^3.2.0"
|
||||||
|
strip-ansi "^6.0.1"
|
||||||
|
strip-json-comments "^3.1.0"
|
||||||
|
text-table "^0.2.0"
|
||||||
|
|
||||||
esm@^3.2.25:
|
esm@^3.2.25:
|
||||||
version "3.2.25"
|
version "3.2.25"
|
||||||
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
|
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
|
||||||
|
|||||||
Reference in New Issue
Block a user