diff --git a/public/assets/sticky.gif b/public/assets/sticky.gif
new file mode 100644
index 00000000..f2f13b96
Binary files /dev/null and b/public/assets/sticky.gif differ
diff --git a/src/App.js b/src/App.js
index 74f6c2d0..daf4725d 100644
--- a/src/App.js
+++ b/src/App.js
@@ -4,6 +4,7 @@ import { Helmet } from 'react-helmet-async';
import Home from './components/Home';
import Board from './components/Board';
import Thread from './components/Thread';
+import Catalog from './components/Catalog';
import { createGlobalStyle } from 'styled-components';
export const BoardContext = React.createContext();
@@ -53,6 +54,9 @@ export default function App() {
- - {`>>${counterString}`}{counterString !== '' &&diff --git a/src/components/Catalog.jsx b/src/components/Catalog.jsx new file mode 100644 index 00000000..40b81f07 --- /dev/null +++ b/src/components/Catalog.jsx @@ -0,0 +1,345 @@ +import React, { useState, useEffect, useContext } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; +import { Container, NavBar, Header, Break, PostForm, PostFormLink, PostFormTable } from './styles/Board.styled'; +import { TopBar } from './styles/Thread.styled'; +import { Threads } from './styles/Catalog.styled'; +import { BoardContext } from '../App'; +import { useFeed, useAccountsActions } from '@plebbit/plebbit-react-hooks'; +import ImageBanner from './ImageBanner'; +import InfiniteScroll from 'react-infinite-scroller'; + +const Catalog = ({ setBodyStyle }) => { + const [defaultSubplebbits, setDefaultSubplebbits] = useState([]); + const { selectedTitle, setSelectedTitle, selectedAddress, setSelectedAddress, selectedStyle, setSelectedStyle } = useContext(BoardContext); + const [showPostFormLink, setShowPostFormLink] = useState(true); + const [showPostForm, setShowPostForm] = useState(false); + const [name, setName] = useState(''); + const [subject, setSubject] = useState(''); + const [comment, setComment] = useState(''); + const navigate = useNavigate(); + + const { feed, hasMore, loadMore } = useFeed([`${selectedAddress}`], 'new'); + + const tryLoadMore = async () => { + try {loadMore()} + catch (e) + {await new Promise(resolve => setTimeout(resolve, 1000))} + } + + 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')); + }; + }); + }; + + useEffect(() => { + let didCancel = false; + fetch( + "https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/subplebbits.json", + { cache: "no-cache" } + ) + .then((res) => res.json()) + .then(res => { + if (!didCancel) { + setDefaultSubplebbits(res); + } + }); + return () => { + didCancel = true; + }; + }, []); + + const handleVoidClick = () => {} + + const handleClick = (title, address) => { + setSelectedTitle(title); + setSelectedAddress(address); + }; + + 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."); + }; + + const handleClickForm = () => { + setShowPostFormLink(false); + setShowPostForm(true); + navigate('/board/catalog/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) => { + switch (event.target.value) { + case "Yotsuba": + setBodyStyle({ + background: "#ffe url(/assets/fade.png) top repeat-x", + color: "maroon", + fontFamily: "Arial, Helvetica, sans-serif" + }); + setSelectedStyle("Yotsuba"); + break; + + case "Yotsuba B": + setBodyStyle({ + background: "#eef2ff url(/assets/fade-blue.png) top center repeat-x", + color: "#000", + fontFamily: "Arial, Helvetica, sans-serif" + }); + setSelectedStyle("Yotsuba B"); + break; + + case "Futaba": + setBodyStyle({ + background: "#ffe", + color: "maroon", + fontFamily: "times new roman, serif" + }); + setSelectedStyle("Futaba"); + break; + + case "Burichan": + setBodyStyle({ + background: "#eef2ff", + color: "#000", + fontFamily: "times new roman, serif" + }); + setSelectedStyle("Burichan"); + break; + + case "Tomorrow": + setBodyStyle({ + background: "#1d1f21 none", + color: "#c5c8c6", + fontFamily: "Arial, Helvetica, sans-serif" + }); + setSelectedStyle("Tomorrow"); + break; + + case "Photon": + setBodyStyle({ + background: "#eee none", + color: "#333", + fontFamily: "Arial, Helvetica, sans-serif" + }); + setSelectedStyle("Photon"); + break; + + default: + setBodyStyle({ + background: "#ffe url(/assets/fade.png) top repeat-x", + color: "maroon", + fontFamily: "Arial, Helvetica, sans-serif" + }); + setSelectedStyle("Yotsuba"); + } + } + + return ( +
} + + {`>>${counterString}`}{
} {reply.content}
+
+