diff --git a/src/App.js b/src/App.js index 2438af07..8f6a090a 100644 --- a/src/App.js +++ b/src/App.js @@ -13,6 +13,7 @@ import Home from './components/views/Home'; import NotFound from './components/views/NotFound'; import Pending from './components/views/Pending'; import Subscriptions from './components/views/Subscriptions'; +import SubscriptionsCatalog from './components/views/SubscriptionsCatalog'; import Thread from './components/views/Thread'; import CaptchaModal from './components/CaptchaModal'; import { importAll } from './components/ImageBanner'; @@ -165,9 +166,9 @@ export default function App() { }> } /> - {/* }> + }> } /> - */} + } /> diff --git a/src/components/views/Subscriptions.jsx b/src/components/views/Subscriptions.jsx index 1097ed6d..5587b56c 100644 --- a/src/components/views/Subscriptions.jsx +++ b/src/components/views/Subscriptions.jsx @@ -1,6 +1,6 @@ import React, { Fragment, useEffect, useMemo, useState } from 'react'; import { Helmet } from 'react-helmet-async'; -import { Link, useNavigate, useParams } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import { Tooltip } from 'react-tooltip'; import { Virtuoso } from 'react-virtuoso'; import { useAccount, useAccountComments, useFeed } from '@plebbit/plebbit-react-hooks'; @@ -52,8 +52,6 @@ const Subscriptions = () => { setSelectedFeed(feed.sort((a, b) => b.timestamp - a.timestamp)); }, [feed]); - const { subplebbitAddress } = useParams(); - const [errorMessage] = useState(null); useError(errorMessage, [errorMessage]); @@ -137,10 +135,6 @@ const Subscriptions = () => { setSelectedTitle(title); setSelectedAddress(address); setSelectedFeed(feed.filter(feed => feed.title === title)); - - if (subplebbitAddress === address) { - window.location.reload(); - } }; // mobile navbar board select functionality diff --git a/src/components/views/SubscriptionsCatalog.jsx b/src/components/views/SubscriptionsCatalog.jsx new file mode 100644 index 00000000..ac299979 --- /dev/null +++ b/src/components/views/SubscriptionsCatalog.jsx @@ -0,0 +1,363 @@ +import React, { Fragment, useEffect, useState } from 'react'; +import { Helmet } from 'react-helmet-async'; +import InfiniteScroll from 'react-infinite-scroller'; +import { Link, useNavigate } from 'react-router-dom'; +import { useAccount, useFeed } from '@plebbit/plebbit-react-hooks'; +import { debounce } from 'lodash'; +import useGeneralStore from '../../hooks/stores/useGeneralStore'; +import { Container, NavBar, Header, Break} from '../styled/Board.styled'; +import { Threads } from '../styled/Catalog.styled'; +import { TopBar, Footer } from '../styled/Thread.styled'; +import CatalogLoader from '../CatalogLoader'; +import ImageBanner from '../ImageBanner'; +import OfflineIndicator from '../OfflineIndicator'; +import SettingsModal from '../SettingsModal'; +import formatState from '../../utils/formatState'; +import getCommentMediaInfo from '../../utils/getCommentMediaInfo'; +import handleStyleChange from '../../utils/handleStyleChange'; +import useError from '../../hooks/useError'; +import packageJson from '../../../package.json' +const {version} = packageJson + + +const SubscriptionsCatalog = () => { + const { + defaultSubplebbits, + isSettingsOpen, setIsSettingsOpen, + selectedAddress, setSelectedAddress, + selectedStyle, + setSelectedThread, + setSelectedTitle, + } = useGeneralStore(state => state); + + const account = useAccount(); + + const navigate = useNavigate(); + const [prevScrollPos, setPrevScrollPos] = useState(0); + const [visible, setVisible] = useState(true); + const { feed, hasMore, loadMore } = useFeed({subplebbitAddresses: account?.subscriptions, sortType: 'new'}); + const [selectedFeed, setSelectedFeed] = useState(feed.sort((a, b) => b.timestamp - a.timestamp)); + + const [errorMessage, setErrorMessage] = useState(null); + useError(errorMessage, [errorMessage]); + + // 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))} + }; + + // desktop navbar board select functionality + const handleClickTitle = (title, address) => { + setSelectedTitle(title); + setSelectedAddress(address); + setSelectedFeed(feed.filter(feed => feed.title === title)); + }; + + // mobile navbar board select functionality + const handleSelectChange = (event) => { + const selected = event.target.value; + + if (selected === 'subscriptions') { + return; + } + + const selectedTitle = defaultSubplebbits.find((subplebbit) => subplebbit.address === selected).title; + setSelectedTitle(selectedTitle); + setSelectedAddress(selected); + navigate(`/p/${selected}`); + }; + + + return ( + <> + + Subscriptions - Catalog - plebchan + + + setIsSettingsOpen(false)} /> + + <> + + [ + Subscriptions + ] [ + {defaultSubplebbits.map((subplebbit, index) => ( + + {index === 0 ? null : "\u00a0"} + { + setSelectedTitle(subplebbit.title); + setSelectedAddress(subplebbit.address); + }} + >{subplebbit.title ? subplebbit.title : subplebbit.address} + + {index !== defaultSubplebbits.length - 1 ? " /" : null} + + ))} + ] + + + [ + alert( + 'To create a board, first you have to run a full node.\nYou can run a full node by simply browsing with the plebbit desktop app. After you download it, open it, wait for it loading, then click on "Home" in the top left, then "Create Community".\n\nAfter you create the community, you can go back to plebchan at any time to see it as a board by pasting its address (begins with p/12D3KooW...) in the search bar, which is in the Home.\n\nNote:\n\n- Your community will be online for as long as you leave the app open, because it functions like a server for the community.\n- The longer you leave the app open, the more data you are seeding to the protocol, which helps performance for everybody.\n - All the data in the plebbit protocol is just text, which is extremely lightweight. All media is generated by links, which is text, embedded by the clients.\n\nDownload the plebbit app here: https://github.com/plebbit/plebbit-react/releases\n\nYou can also use a CLI: https://github.com/plebbit/plebbit-cli\n\nRunning boards in the plebchan app is a planned feature.\n\n' + ) + }>Create Board + ] + [ + setIsSettingsOpen(true)}>Settings + ] + [ + handleStyleChange({target: {value: "Yotsuba"}} + )}>Home + ] + + + + Board + + + Subscriptions + {defaultSubplebbits.map(subplebbit => ( + {subplebbit.title ? subplebbit.title : subplebbit.address} + ))} + + alert( + 'To create a board, first you have to run a full node.\nYou can run a full node by simply browsing with the plebbit desktop app. After you download it, open it, wait for it loading, then click on "Home" in the top left, then "Create Community".\n\nAfter you create the community, you can go back to plebchan at any time to see it as a board by pasting its address (begins with p/12D3KooW...) in the search bar, which is in the Home.\n\nNote:\n\n- Your community will be online for as long as you leave the app open, because it functions like a server for the community.\n- The longer you leave the app open, the more data you are seeding to the protocol, which helps performance for everybody.\n - All the data in the plebbit protocol is just text, which is extremely lightweight. All media is generated by links, which is text, embedded by the clients.\n\nDownload the plebbit app here: https://github.com/plebbit/plebbit-react/releases\n\nYou can also use a CLI: https://github.com/plebbit/plebbit-cli\n\nRunning boards in the plebchan app is a planned feature.\n\n' + ) + }>Create Board + + + setIsSettingsOpen(true)}>Settings + + handleStyleChange({target: {value: "Yotsuba"}} + )}>Home + + + + + > + + + <> + + + + <> + Subscriptions + {feed.length < 1 ? ( + You haven't subscribed to any board yet. + ) : ( + + You have subscribed to {account.subscriptions.length} board{account.subscriptions.length > 1 ? "s" : null}. + + )} + > + > + + + + + + Style: + + + Yotsuba + Yotsuba B + Futaba + Burichan + Tomorrow + Photon + + + + [ + Return + ] + + + + Return + + + + + + {selectedFeed.length < 1 ? ( + + ) : ( + + {feed.map((thread, index) => { + const commentMediaInfo = getCommentMediaInfo(thread); + const fallbackImgUrl = "assets/filedeleted-res.gif"; + return ( + setSelectedThread(thread.cid)}> + + {commentMediaInfo?.url ? ( + + {commentMediaInfo?.type === "webpage" ? ( + thread.thumbnailUrl ? ( + { + e.target.src = fallbackImgUrl + e.target.onerror = null; + }} /> + ) : null + ) : null} + {commentMediaInfo?.type === "image" ? ( + { + e.target.src = fallbackImgUrl + e.target.onerror = null;}} /> + ) : null} + {commentMediaInfo?.type === "video" ? ( + e.target.src = fallbackImgUrl} /> + ) : null} + {commentMediaInfo?.type === "audio" ? ( + e.target.src = fallbackImgUrl} /> + ) : null} + + ) : null} + {/* + + */} + + R: + {thread.replyCount} + + + {thread.title ? `${thread.title}` : null} + {thread.content ? `: ${thread.content}` : null} + + + + )})} + + )} + + + + > + ); +} + +export default SubscriptionsCatalog; \ No newline at end of file