import React, { useState, useEffect, Fragment } from 'react'; import { Link, useNavigate, useParams } from 'react-router-dom'; import InfiniteScroll from 'react-infinite-scroller'; import { useFeed, usePublishComment } from '@plebbit/plebbit-react-hooks'; import VideoThumbnail from 'react-video-thumbnail'; import { debounce } from 'lodash'; import useAppStore from '../../useAppStore'; import { Container, NavBar, Header, Break, PostForm, PostFormLink, PostFormTable } from './styles/Board.styled'; import { Threads } from './styles/Catalog.styled'; import { TopBar } from './styles/Thread.styled'; import CaptchaModal from '../CaptchaModal'; import CatalogLoader from '../CatalogLoader'; import ImageBanner from '../ImageBanner'; import SettingsModal from '../SettingsModal'; import getCommentMediaInfo from '../../utils/getCommentMediaInfo'; import handleStyleChange from '../../utils/handleStyleChange'; import onError from '../../utils/onError'; import onSuccess from '../../utils/onSuccess'; import useClickForm from '../../hooks/useClickForm'; const Catalog = () => { const { captchaResponse, setCaptchaResponse, defaultSubplebbits, isSettingsOpen, setIsSettingsOpen, selectedAddress, setSelectedAddress, selectedStyle, setSelectedThread, selectedTitle, setSelectedTitle, showPostForm, showPostFormLink } = useAppStore(state => state); const [name, setName] = useState(''); const [subject, setSubject] = useState(''); const [comment, setComment] = useState(''); const { publishComment } = usePublishComment(); const [isCaptchaOpen, setIsCaptchaOpen] = useState(false); const [captchaImage, setCaptchaImage] = useState(''); const navigate = useNavigate(); const [prevScrollPos, setPrevScrollPos] = useState(0); const [visible, setVisible] = useState(true); const { feed, hasMore, loadMore } = useFeed({subplebbitAddresses: [`${selectedAddress}`], sortType: 'new'}); const { subplebbitAddress } = useParams(); const handleClickForm = useClickForm(); useEffect(() => { setSelectedAddress(subplebbitAddress); const selectedSubplebbit = defaultSubplebbits.find((subplebbit) => subplebbit.address === subplebbitAddress); if (selectedSubplebbit) { setSelectedTitle(selectedSubplebbit.title); } }, [subplebbitAddress, setSelectedAddress, setSelectedTitle, defaultSubplebbits]); // mobile navbar scroll effect useEffect(() => { const debouncedHandleScroll = debounce(() => { const currentScrollPos = window.pageYOffset; setVisible(prevScrollPos > currentScrollPos || currentScrollPos < 10); setPrevScrollPos(currentScrollPos); }, 50); window.addEventListener('scroll', debouncedHandleScroll); return () => window.removeEventListener('scroll', debouncedHandleScroll); }, [prevScrollPos, visible]); const tryLoadMore = async () => { try {loadMore()} catch (e) {await new Promise(resolve => setTimeout(resolve, 1000))} } const onChallengeVerification = (challengeVerification) => { if (challengeVerification.challengeSuccess === true) { onSuccess('challenge success', {publishedCid: challengeVerification.publication.cid}) } else if (challengeVerification.challengeSuccess === false) { onError('challenge failed', {reason: challengeVerification.reason, errors: challengeVerification.errors}); onError("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) { onError(error); } if (challengeAnswers) { await comment.publishChallengeAnswers(challengeAnswers) } } 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 = () => { setIsCaptchaOpen(true); setCaptchaImage(imageSource); const handleKeyDown = (event) => { if (event.key === 'Enter') { setCaptchaImage(''); resolve(captchaResponse); setIsCaptchaOpen(false); document.removeEventListener('keydown', handleKeyDown); } }; setCaptchaResponse(''); document.addEventListener('keydown', handleKeyDown); }; challengeImg.onerror = () => { reject(onError('Could not load challenge image')); }; }); }; const handleClickTitle = (title, address) => { setSelectedTitle(title); setSelectedAddress(address); }; const handleSelectChange = (event) => { const selected = event.target.value; const selectedTitle = defaultSubplebbits.find((subplebbit) => subplebbit.address === selected).title; setSelectedTitle(selectedTitle); setSelectedAddress(selected); navigate(`/${selected}`); } const handleClickHelp = () => { alert("- Embedding media is optional, posts can be text-only. \n- A CAPTCHA challenge will appear after posting. \n- The CAPTCHA is case-sensitive."); }; const handleClickThread = (thread) => { setSelectedThread(thread); } const handlePublishComment = async () => { // Event.preventDefault(); try { const pendingComment = await publishComment({ content: comment, title: subject, subplebbitAddress: selectedAddress, onChallengeVerification, onChallenge, onError: onError, }); console.log(`Comment pending with index: ${pendingComment.index}`); setName(''); setSubject(''); setComment(''); } catch (error) { onError(error); } }; const handleCaptchaClose = () => { setIsCaptchaOpen(false); }; const handleSettingsClose = () => { setIsSettingsOpen(false); } const handleSettingsOpen = () => { setIsSettingsOpen(true); } return ( <> {defaultSubplebbits.map(subplebbit => ( [ handleClickTitle(subplebbit.title, subplebbit.address)} >{subplebbit.title} ]  ))} [ Settings ] [ handleStyleChange({target: {value: "Yotsuba"}} )}>Home ]
Board  
Settings   handleStyleChange({target: {value: "Yotsuba"}} )}>Home
 
 
<>
<>
{selectedTitle}
p/{selectedAddress}
[ event.target.style.cursor='pointer'}>Start a New Thread ]
event.target.style.cursor='pointer'}>Start a New Thread
Name setName(event.target.value)} /> Subject setSubject(event.target.value)} /> Comment Embed File

Style:  
[ Return ]
Return
{feed.length > 0 ? (null) : (Fetching IPFS...)}

{feed.length < 1 ? ( ) : ( {feed.map(thread => { const commentMediaInfo = getCommentMediaInfo(thread); const fallbackImgUrl = "/assets/filedeleted-res.gif"; return (
handleClickThread(thread.cid)}> {commentMediaInfo?.url ? ( {commentMediaInfo?.type === "webpage" ? ( thread { e.target.src = fallbackImgUrl e.target.onerror = null;}} /> ) : null} {commentMediaInfo?.type === "image" ? ( thread { e.target.src = fallbackImgUrl e.target.onerror = null;}} /> ) : null} {commentMediaInfo?.type === "video" ? ( { const img = document.querySelector(`img[key="img-${thread.cid}"]`); if (img) { img.src = thumbnail; } }} /> ) : null} {commentMediaInfo?.type === "audio" ? ( ) : null} {/*
*/}
R: {thread.replyCount}
{thread.title ? `${thread.title}` : null} {thread.content ? `: ${thread.content}` : null}
)})}
)}
); } export default Catalog;