- {link && !isHidden && isValidURL(link) &&
}
+ {link && !isHidden && !(deleted || removed) && isValidURL(link) &&
}
{!isHidden && !content &&
}
{!isHidden && content &&
}
diff --git a/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx b/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx
index c943afec..6e53427f 100644
--- a/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx
+++ b/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx
@@ -165,6 +165,12 @@ const PostMenuDesktop = ({ post }: { post: Comment }) => {
const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss, role]);
const headingId = useId();
+ const handleMenuClick = () => {
+ if (cid) {
+ setMenuBtnRotated((prev) => !prev);
+ }
+ };
+
const handleClose = () => setMenuBtnRotated(false);
return (
@@ -173,13 +179,14 @@ const PostMenuDesktop = ({ post }: { post: Comment }) => {
setMenuBtnRotated((prev) => !prev)}
- style={{ transform: menuBtnRotated ? 'rotate(90deg)' : 'rotate(0deg)' }}
+ onClick={handleMenuClick}
+ style={{ transform: menuBtnRotated && cid ? 'rotate(90deg)' : 'rotate(0deg)' }}
>
▶
{menuBtnRotated &&
+ cid &&
createPortal(
diff --git a/src/components/post-form/post-form.tsx b/src/components/post-form/post-form.tsx
index ad27de6f..ccc0e3e4 100644
--- a/src/components/post-form/post-form.tsx
+++ b/src/components/post-form/post-form.tsx
@@ -125,7 +125,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
const author = account?.author || {};
const { displayName } = author || {};
const [url, setUrl] = useState('');
- const { title, content, link, publishCommentOptions, setSubmitStore, resetSubmitStore } = useSubmitStore();
+ const { link, publishCommentOptions, setSubmitStore, resetSubmitStore } = useSubmitStore();
const { index, publishComment } = usePublishComment(publishCommentOptions);
const textRef = useRef
(null);
@@ -180,8 +180,12 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
}
}, [anonMode, getNewSigner, account, setSubmitStore, displayName]);
- const onPublishPost = async () => {
- if (!title && !content && !link) {
+ const onPublishPost = () => {
+ const currentTitle = subjectRef.current?.value.trim() || '';
+ const currentContent = textRef.current?.value.trim() || '';
+ const currentUrl = urlRef.current?.value.trim() || '';
+
+ if (!currentTitle && !currentContent && !currentUrl) {
alert(`Cannot post empty comment`);
return;
}
@@ -248,10 +252,10 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
};
const onPublishReply = () => {
- const currentContent = textRef.current?.value || '';
- const currentUrl = urlRef.current?.value || '';
+ const currentContent = textRef.current?.value.trim() || '';
+ const currentUrl = urlRef.current?.value.trim() || '';
- if (!currentContent.trim() && !currentUrl) {
+ if (!currentContent && !currentUrl) {
alert(`Cannot post empty comment`);
return;
}
diff --git a/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx b/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx
index 5756654f..9764cbb8 100644
--- a/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx
+++ b/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx
@@ -148,16 +148,23 @@ const PostMenuMobile = ({ post }: { post: Comment }) => {
const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss, role]);
const headingId = useId();
+ const handleMenuClick = () => {
+ if (cid) {
+ setIsMenuOpen((prev) => !prev);
+ }
+ };
+
const handleClose = () => setIsMenuOpen(false);
const isInBoardView = isBoardView(useLocation().pathname, useParams());
return (
<>
- setIsMenuOpen((prev) => !prev)} ref={refs.setReference} {...getReferenceProps()}>
+
...
{isMenuOpen &&
+ cid &&
createPortal(
@@ -171,7 +178,7 @@ const PostMenuMobile = ({ post }: { post: Comment }) => {
,
document.body,
)}
- {(isAccountMod || isAccountCommentAuthor) && (
+ {(isAccountMod || isAccountCommentAuthor) && cid && (
diff --git a/src/components/post-mobile/post-mobile.tsx b/src/components/post-mobile/post-mobile.tsx
index d4b4737b..3d6bfcf6 100644
--- a/src/components/post-mobile/post-mobile.tsx
+++ b/src/components/post-mobile/post-mobile.tsx
@@ -141,28 +141,27 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
)}
{getFormattedDate(timestamp)}} content={getFormattedTimeAgo(timestamp)} />{' '}
- {!(isDescription || isRules) && (
-
- {cid ? (
- <>
- !cid && e.preventDefault()}>
- c/
-
- openReplyModal && openReplyModal(cid, postCid, subplebbitAddress)}>
- {shortCid.slice(0, -4)}
-
- >
- ) : (
- <>
- c/
- {state === 'failed' || stateString === 'Failed' ? 'Failed' : 'Pending'}
- >
- )}
-
- )}
+ {!(isDescription || isRules) &&
+ (cid ? (
+
+ !cid && e.preventDefault()}>
+ c/
+
+ openReplyModal && openReplyModal(cid, postCid, subplebbitAddress)}>
+ {shortCid.slice(0, -4)}
+
+
+ ) : (
+ <>
+ c/
+ {state === 'failed' || stateString === 'Failed' ? 'Failed' : 'Pending'}
+ >
+ ))}
- {(hasThumbnail || link) && }
+ {(hasThumbnail || link) && !(deleted || removed) && (
+
+ )}
>
);
};
diff --git a/src/components/reply-modal/reply-modal.tsx b/src/components/reply-modal/reply-modal.tsx
index bac1bc9e..1bae61b3 100644
--- a/src/components/reply-modal/reply-modal.tsx
+++ b/src/components/reply-modal/reply-modal.tsx
@@ -66,10 +66,10 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
}, [address, getExistingSigner, getNewSigner, setPublishReplyOptions, anonMode, displayName]);
const onPublishReply = () => {
- const currentContent = textRef.current?.value || '';
- const currentUrl = urlRef.current?.value || '';
+ const currentContent = textRef.current?.value.slice(contentPrefix.length).trim() || '';
+ const currentUrl = urlRef.current?.value.trim() || '';
- if (!currentContent.trim() && !currentUrl) {
+ if (!currentContent && !currentUrl) {
alert(`Cannot post empty comment`);
return;
}
diff --git a/src/components/subplebbit-stats/subplebbit-stats.tsx b/src/components/subplebbit-stats/subplebbit-stats.tsx
index 4b527b46..cba3f1f3 100644
--- a/src/components/subplebbit-stats/subplebbit-stats.tsx
+++ b/src/components/subplebbit-stats/subplebbit-stats.tsx
@@ -1,9 +1,9 @@
-import { useState } from 'react';
import { useLocation, useParams } from 'react-router-dom';
-import { useAccountComment, useComment, useSubplebbit, useSubplebbitStats } from '@plebbit/plebbit-react-hooks';
-import styles from './subplebbit-stats.module.css';
import { Trans, useTranslation } from 'react-i18next';
+import { useAccountComment, useComment, useSubplebbit, useSubplebbitStats } from '@plebbit/plebbit-react-hooks';
+import useSubplebbitStatsVisibilityStore from '../../stores/use-subplebbit-stats-visibility-store';
import { isDescriptionView, isRulesView } from '../../lib/utils/view-utils';
+import styles from './subplebbit-stats.module.css';
const SubplebbitStats = () => {
const { t } = useTranslation();
@@ -16,7 +16,8 @@ const SubplebbitStats = () => {
const { address, createdAt } = subplebbit || {};
const stats = useSubplebbitStats({ subplebbitAddress: address });
- const [showStats, setShowStats] = useState(true);
+ const { hiddenStats, toggleVisibility } = useSubplebbitStatsVisibilityStore();
+ const isHidden = hiddenStats[address];
const location = useLocation();
const isInDescriptionView = isDescriptionView(location.pathname, params);
@@ -44,7 +45,7 @@ const SubplebbitStats = () => {
- {showStats && (
+ {!isHidden && (
|
@@ -93,8 +94,8 @@ const SubplebbitStats = () => {
|
|
[
- setShowStats(!showStats)}>
- {showStats ? t('hide') : t('show_stats')}
+ toggleVisibility(address)}>
+ {isHidden ? t('show_stats') : t('hide')}
]
|
diff --git a/src/hooks/use-initial-theme.ts b/src/hooks/use-initial-theme.ts
index 2d494102..2bf46d28 100644
--- a/src/hooks/use-initial-theme.ts
+++ b/src/hooks/use-initial-theme.ts
@@ -4,10 +4,13 @@ import useThemeStore from '../stores/use-theme-store';
import useDefaultSubplebbits from './use-default-subplebbits';
import { isAllView, isHomeView, isNotFoundView, isPendingPostView, isSubscriptionsView } from '../lib/utils/view-utils';
import { nsfwTags } from '../views/home/home';
+import { useAccountComment } from '@plebbit/plebbit-react-hooks';
-const useInitialTheme = () => {
+const useInitialTheme = (pendingPostSubplebbitAddress?: string) => {
const location = useLocation();
- const { subplebbitAddress } = useParams<{ subplebbitAddress: string }>();
+ const { subplebbitAddress: paramsSubplebbitAddress, accountCommentIndex } = useParams<{ subplebbitAddress: string; accountCommentIndex?: string }>();
+ const commentIndex = accountCommentIndex ? parseInt(accountCommentIndex) : undefined;
+ const pendingPost = useAccountComment({ commentIndex });
const getTheme = useThemeStore((state) => state.getTheme);
const currentTheme = useThemeStore((state) => state.currentTheme);
const subplebbits = useDefaultSubplebbits();
@@ -22,13 +25,23 @@ const useInitialTheme = () => {
let theme = 'yotsuba';
if (isInPendingPostView) {
- theme = currentTheme || 'yotsuba';
+ const subplebbitAddress = pendingPostSubplebbitAddress || pendingPost?.subplebbitAddress;
+ if (subplebbitAddress) {
+ const subplebbit = subplebbits.find((s) => s.address === subplebbitAddress);
+ if (subplebbit && subplebbit.tags && subplebbit.tags.some((tag) => nsfwTags.includes(tag))) {
+ theme = getTheme('nsfw', false) || 'yotsuba';
+ } else {
+ theme = getTheme('sfw', false) || 'yotsuba-b';
+ }
+ } else {
+ theme = currentTheme || 'yotsuba';
+ }
} else if (isInAllView || isInSubscriptionsView) {
theme = getTheme('sfw', false) || 'yotsuba-b'; // Add 'false' parameter
} else if (isInHomeView || isInNotFoundView) {
theme = 'yotsuba';
- } else if (subplebbitAddress) {
- const subplebbit = subplebbits.find((s) => s.address === subplebbitAddress);
+ } else if (paramsSubplebbitAddress) {
+ const subplebbit = subplebbits.find((s) => s.address === paramsSubplebbitAddress);
if (subplebbit && subplebbit.tags && subplebbit.tags.some((tag) => nsfwTags.includes(tag))) {
theme = getTheme('nsfw', false) || 'yotsuba'; // Add 'false' parameter
} else {
@@ -37,7 +50,19 @@ const useInitialTheme = () => {
}
return theme;
- }, [isInPendingPostView, isInAllView, isInSubscriptionsView, isInHomeView, isInNotFoundView, subplebbitAddress, getTheme, currentTheme, subplebbits]);
+ }, [
+ isInPendingPostView,
+ isInAllView,
+ isInSubscriptionsView,
+ isInHomeView,
+ isInNotFoundView,
+ paramsSubplebbitAddress,
+ getTheme,
+ currentTheme,
+ subplebbits,
+ pendingPostSubplebbitAddress,
+ pendingPost,
+ ]);
return initialTheme;
};
diff --git a/src/hooks/use-publish-reply.ts b/src/hooks/use-publish-reply.ts
index 192d1603..5d1329ec 100644
--- a/src/hooks/use-publish-reply.ts
+++ b/src/hooks/use-publish-reply.ts
@@ -87,7 +87,7 @@ const useReplyStore = create((set) => ({
const useReply = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress: string }) => {
const parentCid = cid;
- const { author, displayName, signer, content, link, spoiler, publishCommentOptions } = useReplyStore((state) => ({
+ const { author, signer, content, link, spoiler, publishCommentOptions } = useReplyStore((state) => ({
author: state.author[parentCid],
displayName: state.displayName[parentCid],
signer: state.signer[parentCid],
@@ -107,17 +107,29 @@ const useReply = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress:
const newOptions: Partial = {
subplebbitAddress,
parentCid,
- content: options.content || content,
- displayName: options.displayName || displayName,
- link: options.link || link,
+ content: options.content ?? content,
+ link: options.link ?? link,
spoiler: options.spoiler ?? spoiler,
- author: anonMode ? signer?.author || options.author || author || { displayName } : undefined,
signer: anonMode ? signer || options.signer : undefined,
};
+ if ('displayName' in options) {
+ newOptions.displayName = options.displayName;
+ }
+
+ if (anonMode) {
+ const currentAuthor = signer?.author || author || {};
+ newOptions.author = {
+ ...currentAuthor,
+ ...('displayName' in options && { displayName: options.displayName }),
+ };
+ } else {
+ newOptions.author = undefined;
+ }
+
setReplyStore(newOptions as SetReplyStoreData);
},
- [subplebbitAddress, parentCid, author, displayName, signer, content, link, spoiler, setReplyStore, anonMode],
+ [subplebbitAddress, parentCid, author, signer, content, link, spoiler, setReplyStore, anonMode],
);
const resetPublishReplyOptions = useCallback(() => resetReplyStore(parentCid), [parentCid, resetReplyStore]);
diff --git a/src/hooks/use-replies.ts b/src/hooks/use-replies.ts
index c4f1ac43..0426f009 100644
--- a/src/hooks/use-replies.ts
+++ b/src/hooks/use-replies.ts
@@ -1,4 +1,4 @@
-import { useMemo, useCallback } from 'react';
+import { useMemo, useCallback, useState, useEffect } from 'react';
import { Comment, useAccountComments } from '@plebbit/plebbit-react-hooks';
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils';
@@ -9,24 +9,45 @@ const useReplies = (comment: Comment) => {
// generate a Set of CIDs from flattened replies for quick lookup
const replyCids = useMemo(() => new Set(flattenedReplies.map((reply) => reply?.cid)), [flattenedReplies]);
- // filter against the original comment's CID and all CIDs in flattened replies
- const filter = useCallback(
- (accountComment: Comment) => {
- const parentCid = accountComment.parentCid;
- return parentCid === (comment?.cid || 'n/a') || replyCids.has(parentCid);
+ const [filteredAccountComments, setFilteredAccountComments] = useState([]);
+
+ const getPostCid = useCallback(
+ (accountComment: Comment, allComments: Comment[]): string | null => {
+ if (accountComment.parentCid === comment?.cid) {
+ return comment?.cid;
+ }
+ const parent = allComments.find((c) => c.cid === accountComment.parentCid);
+ if (!parent) {
+ return null;
+ }
+ return getPostCid(parent, allComments);
},
- [comment?.cid, replyCids],
+ [comment?.cid],
);
- const { accountComments } = useAccountComments({ filter });
+ const { accountComments } = useAccountComments();
+
+ useEffect(() => {
+ const filterComments = (comments: Comment[]) => {
+ return comments.filter((accountComment) => {
+ const parentCid = accountComment.parentCid;
+ if (parentCid === (comment?.cid || 'n/a') || replyCids.has(parentCid)) {
+ return true;
+ }
+ return getPostCid(accountComment, comments) === comment?.cid;
+ });
+ };
+
+ setFilteredAccountComments(filterComments(accountComments));
+ }, [accountComments, comment?.cid, replyCids, getPostCid]);
// the account's replies have a delay before getting published, so get them locally from accountComments instead
const accountRepliesNotYetPublished = useMemo(() => {
const replies = flattenedReplies || [];
const replyCids = new Set(replies.map((reply: Comment) => reply?.cid));
// filter out the account comments already in comment.replies, so they don't appear twice
- return accountComments.filter((accountReply) => !replyCids.has(accountReply?.cid));
- }, [flattenedReplies, accountComments]);
+ return filteredAccountComments.filter((accountReply) => !replyCids.has(accountReply?.cid));
+ }, [flattenedReplies, filteredAccountComments]);
const repliesAndNotYetPublishedReplies = useMemo(() => {
const repliesSortedByPinnedAndTimestamp = [...accountRepliesNotYetPublished.reverse(), ...(flattenedReplies || [])];
diff --git a/src/hooks/use-theme.ts b/src/hooks/use-theme.ts
index a19c65da..e5fea198 100644
--- a/src/hooks/use-theme.ts
+++ b/src/hooks/use-theme.ts
@@ -15,7 +15,7 @@ const updateThemeClass = (newTheme: string) => {
}
};
-const useTheme = (): [string, (theme: string) => void] => {
+const useTheme = (pendingPostSubplebbitAddress?: string): [string, (theme: string) => void] => {
const location = useLocation();
const params = useParams<{ subplebbitAddress: string }>();
const setThemeStore = useThemeStore((state) => state.setTheme);
@@ -23,7 +23,7 @@ const useTheme = (): [string, (theme: string) => void] => {
const loadThemes = useThemeStore((state) => state.loadThemes);
const subplebbits = useDefaultSubplebbits();
- const initialTheme = useInitialTheme();
+ const initialTheme = useInitialTheme(pendingPostSubplebbitAddress);
const [currentTheme, setCurrentTheme] = useState(initialTheme);
const getCurrentTheme = useCallback(() => {
diff --git a/src/lib/utils/post-utils.ts b/src/lib/utils/post-utils.ts
index e0d008ad..01dcee51 100644
--- a/src/lib/utils/post-utils.ts
+++ b/src/lib/utils/post-utils.ts
@@ -24,10 +24,16 @@ export function getTextColorForBackground(rgb: string): string {
export const formatMarkdown = (content: string): string => {
let md = content;
if (md) {
- // Replace single newline with "/n /n" if followed by a newline
- md = md.replace(/\n(?=\n)/g, '\n \n');
- // Replace single newline with double newline if between two characters
- md = md.replace(/\n(?=\S)/g, '\n\n');
+ // Check if the content already contains valid Markdown patterns
+ const alreadyFormattedPattern = /\n \n/;
+
+ // help the user by formatting the content if it's not already formatted
+ if (!alreadyFormattedPattern.test(md)) {
+ // Replace single newline with "/n /n" if followed by a newline
+ md = md.replace(/\n(?=\n)/g, '\n \n');
+ // Replace single newline with double newline if between two characters
+ md = md.replace(/\n(?=\S)/g, '\n\n');
+ }
}
return md;
};
diff --git a/src/stores/use-subplebbit-stats-visibility-store.ts b/src/stores/use-subplebbit-stats-visibility-store.ts
new file mode 100644
index 00000000..c8f334cd
--- /dev/null
+++ b/src/stores/use-subplebbit-stats-visibility-store.ts
@@ -0,0 +1,27 @@
+import { create } from 'zustand';
+import { persist } from 'zustand/middleware';
+
+interface SubplebbitStatsVisibilityState {
+ hiddenStats: { [subplebbitAddress: string]: boolean };
+ toggleVisibility: (subplebbitAddress: string) => void;
+}
+
+const useSubplebbitStatsVisibilityStore = create()(
+ persist(
+ (set) => ({
+ hiddenStats: {},
+ toggleVisibility: (subplebbitAddress) =>
+ set((state) => ({
+ hiddenStats: {
+ ...state.hiddenStats,
+ [subplebbitAddress]: !state.hiddenStats[subplebbitAddress],
+ },
+ })),
+ }),
+ {
+ name: 'subplebbit-stats-visibility',
+ },
+ ),
+);
+
+export default useSubplebbitStatsVisibilityStore;
diff --git a/src/views/home/boards-box/boards-box.tsx b/src/views/home/boards-box/boards-box.tsx
index 81902c00..a02d18f7 100644
--- a/src/views/home/boards-box/boards-box.tsx
+++ b/src/views/home/boards-box/boards-box.tsx
@@ -9,11 +9,10 @@ import { nsfwTags } from '../home';
import BoxModal from '../box-modal';
import useIsSubplebbitOffline from '../../../hooks/use-is-subplebbit-offline';
-const Board = ({ subplebbit, subplebbits }: { subplebbit: Subplebbit; subplebbits: any }) => {
+const Board = ({ subplebbit, subplebbits, useCatalog }: { subplebbit: Subplebbit; subplebbits: any; useCatalog?: boolean }) => {
const { t } = useTranslation();
const { address, title, tags } = subplebbit || {};
const nsfwTag = tags.find((tag: string) => nsfwTags.includes(tag));
- const { useCatalog } = useHomeFiltersStore();
const boardLink = useCatalog ? `/p/${address}/catalog` : `/p/${address}`;
@@ -37,6 +36,8 @@ const BoardsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebb
const accountSubplebbitAddresses = Object.keys(accountSubplebbits);
const { showNsfwBoardsOnly, showWorksafeBoardsOnly } = useHomeFiltersStore();
+ const { useCatalog } = useHomeFiltersStore();
+
const filterSubs = (subs: Subplebbit[], includeTags: string[], excludeTags: string[] = []) =>
subs.filter((sub) => includeTags.every((tag) => sub.tags?.includes(tag)) && excludeTags.every((tag) => !sub.tags?.includes(tag)));
@@ -72,10 +73,10 @@ const BoardsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebb
Multiboards
- {multisubMetadata?.title || 'All'}
+ {multisubMetadata?.title || 'All'}
- Subscriptions
+ Subscriptions
{plebbitSubs.length > 0 && (
@@ -139,7 +140,7 @@ const BoardsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebb
{subscriptions.length > 0
? subscriptions.map((address: string, index: number) => (
- p/{address && Plebbit.getShortAddress(address)}
+ p/{address && Plebbit.getShortAddress(address)}
))
: t('not_subscribed')}
@@ -149,7 +150,7 @@ const BoardsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[]; subplebb
{accountSubplebbitAddresses.length > 0
? accountSubplebbitAddresses.map((address: string, index: number) => (
- p/{address && Plebbit.getShortAddress(address)}
+ p/{address && Plebbit.getShortAddress(address)}
))
: t('not_moderating')}
diff --git a/src/views/pending-post/pending-post.tsx b/src/views/pending-post/pending-post.tsx
index 4348938a..72980b28 100644
--- a/src/views/pending-post/pending-post.tsx
+++ b/src/views/pending-post/pending-post.tsx
@@ -4,6 +4,7 @@ import { useAccountComment } from '@plebbit/plebbit-react-hooks';
import { isSettingsView } from '../../lib/utils/view-utils';
import { Post } from '../post';
import SettingsModal from '../../components/settings-modal';
+import useTheme from '../../hooks/use-theme';
const PendingPost = () => {
const { accountCommentIndex } = useParams<{ accountCommentIndex?: string }>();
@@ -14,6 +15,8 @@ const PendingPost = () => {
const params = useParams();
const isInSettingsView = isSettingsView(location.pathname, params);
+ useTheme(post?.subplebbitAddress);
+
useEffect(() => window.scrollTo(0, 0), []);
useEffect(() => {
@@ -25,7 +28,7 @@ const PendingPost = () => {
return (
<>
{isInSettingsView && }
-
+
>
);
};