@@ -505,7 +516,7 @@ const Board = () => {
>
) : (
- Fetching IPFS...
+ {formatState(subplebbit.state)}
)}
-
setIsSettingsOpen(true)}>Settings
+
setIsSettingsOpen(true)}>Settings
handleStyleChange({target: {value: "Yotsuba"}}
)}>Home
@@ -267,6 +257,16 @@ const Subscriptions = () => {
+
+ [
+ Catalog
+ ]
+
+
+
+ Catalog
+
+
@@ -1037,7 +1037,7 @@ const Subscriptions = () => {
}>Create Board
]
[
- setIsSettingsOpen(true)}>Settings
+ setIsSettingsOpen(true)}>Settings
]
[
handleStyleChange({target: {value: "Yotsuba"}}
diff --git a/src/components/views/SubscriptionsCatalog.jsx b/src/components/views/SubscriptionsCatalog.jsx
new file mode 100644
index 00000000..d5b99b46
--- /dev/null
+++ b/src/components/views/SubscriptionsCatalog.jsx
@@ -0,0 +1,362 @@
+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 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] = 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}
+
+ ))}
+ ]
+
+
+ [
+
+ ]
+ [
+ setIsSettingsOpen(true)}>Settings
+ ]
+ [
+ handleStyleChange({target: {value: "Yotsuba"}}
+ )}>Home
+ ]
+
+
+
+ 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:
+
+
+
+
+ [
+ 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" ? (
+
+ ) : null}
+ {/*
+
+
*/}
+
+ R:
+ {thread.replyCount}
+
+
+ {thread.title ? `${thread.title}` : null}
+ {thread.content ? `: ${thread.content}` : null}
+
+
+
+ )})}
+
+ )}
+
+
+
+ >
+ );
+}
+
+export default SubscriptionsCatalog;
\ No newline at end of file
diff --git a/src/components/views/Thread.jsx b/src/components/views/Thread.jsx
index 74a0b97b..ac49693b 100644
--- a/src/components/views/Thread.jsx
+++ b/src/components/views/Thread.jsx
@@ -253,7 +253,7 @@ const Thread = () => {
const selected = event.target.value;
if (selected === 'subscriptions') {
- navigate(`/profile/p/subscriptions`);
+ navigate(`/p/subscriptions`);
return;
}
@@ -288,7 +288,7 @@ const Thread = () => {
<>
[
- Subscriptions
+ Subscriptions
] [
{defaultSubplebbits.map((subplebbit, index) => (
diff --git a/src/utils/formatState.js b/src/utils/formatState.js
index bfc4952b..4218972e 100644
--- a/src/utils/formatState.js
+++ b/src/utils/formatState.js
@@ -1,18 +1,27 @@
function formatState(str) {
- return (
- str
- .replace(/-/g, " ")
- .split(" ")
- .map((word) => {
- word = word.toLowerCase();
- if (word === "ipfs" || word === "ipns") {
- return word.toUpperCase();
- } else {
- return word.charAt(0).toUpperCase() + word.slice(1);
- }
- })
- .join(" ") + "..."
- );
+ if (!str) {
+ return null;
+ }
+
+ const formattedWords = str
+ .replace(/-/g, " ")
+ .split(" ")
+ .map((word, index) => {
+ word = word.toLowerCase();
+ if (word === "ipfs" || word === "ipns") {
+ return word.toUpperCase();
+ } else if (index === 0) {
+ return word.charAt(0).toUpperCase() + word.slice(1);
+ } else {
+ return word;
+ }
+ });
+
+ if (formattedWords.includes("Failed")) {
+ return formattedWords.join(" ") + ".";
+ }
+
+ return formattedWords.join(" ") + "...";
}
export default formatState;
\ No newline at end of file
diff --git a/src/utils/isValidUrl.js b/src/utils/isValidUrl.js
new file mode 100644
index 00000000..c8cab128
--- /dev/null
+++ b/src/utils/isValidUrl.js
@@ -0,0 +1,10 @@
+const isValidUrl = (url) => {
+ try {
+ new URL(url);
+ return true;
+ } catch (e) {
+ return false;
+ }
+};
+
+export default isValidUrl;
\ No newline at end of file