diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx index 6c9b6493..77860263 100644 --- a/src/components/post-desktop/post-desktop.tsx +++ b/src/components/post-desktop/post-desktop.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { Link, useLocation, useParams } from 'react-router-dom'; -import { Comment, useAccount, useAuthorAvatar, useComment, useEditedComment } from '@plebbit/plebbit-react-hooks'; +import { Comment, useAccount, useAccountComments, useAuthorAvatar, useComment, useEditedComment } from '@plebbit/plebbit-react-hooks'; import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js'; import styles from '../../views/post/post.module.css'; import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../lib/utils/media-utils'; @@ -383,6 +383,9 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies const visiblelinksCount = useCountLinksInReplies(post, 5); const totalLinksCount = useCountLinksInReplies(post); const replyCount = replies?.length; + const { accountComments } = useAccountComments(); + const isPostInAccountComments = accountComments?.find((comment) => comment.cid === cid); + const repliesCount = pinned ? replyCount : replyCount - 5; const linksCount = pinned ? totalLinksCount : totalLinksCount - visiblelinksCount; const { showOmittedReplies, setShowOmittedReplies } = useShowOmittedReplies(); @@ -439,7 +442,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies )} )} - {post?.replyCount === undefined && !isInPendingPostView && ( + {post?.replyCount === undefined && !isPostInAccountComments && !isInPendingPostView && ( diff --git a/src/hooks/use-catalog-feed-rows.ts b/src/hooks/use-catalog-feed-rows.ts new file mode 100644 index 00000000..e2119e80 --- /dev/null +++ b/src/hooks/use-catalog-feed-rows.ts @@ -0,0 +1,113 @@ +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useParams, useLocation } from 'react-router-dom'; +import { useAccountComments, Subplebbit } from '@plebbit/plebbit-react-hooks'; +import { isAllView } from '../lib/utils/view-utils'; +import { useMultisubMetadata } from './use-default-subplebbits'; +import useCatalogFiltersStore from '../stores/use-catalog-filters-store'; +import _ from 'lodash'; +import { getCommentMediaInfo, getHasThumbnail } from '../lib/utils/media-utils'; + +const useCatalogFeedRows = (columnCount: number, feed: any, isFeedLoaded: boolean, subplebbit: Subplebbit) => { + const { t } = useTranslation(); + const { address, createdAt, description, rules, shortAddress, suggested, title } = subplebbit || {}; + const { avatarUrl } = suggested || {}; + const { showTextOnlyThreads } = useCatalogFiltersStore(); + + const location = useLocation(); + const isInAllView = isAllView(location.pathname, useParams()); + const multisub = useMultisubMetadata(); + + const { accountComments } = useAccountComments(); + + const feedWithFakePostsOnTop = useMemo(() => { + if (!isFeedLoaded) { + return []; // prevent rules and description from appearing while feed is loading + } + + if (!description && !rules && !isInAllView) { + return feed; + } + + const _feed = [...feed]; + + // show account comments instantly in the feed instead of waiting for the next feed update, then hide them when they are in the feed + accountComments.forEach((comment) => { + const { cid, deleted, link, postCid, removed, subplebbitAddress } = comment || {}; + const commentMediaInfo = getCommentMediaInfo(comment); + const isMediaShowed = getHasThumbnail(commentMediaInfo, link); + + if ( + !deleted && + !removed && + (!showTextOnlyThreads || (showTextOnlyThreads && !isMediaShowed)) && + cid && + cid === postCid && + subplebbitAddress === address && + !_feed.some((feedItem) => feedItem.cid === cid) + ) { + _feed.unshift({ + ...comment, + isAccountComment: true, + }); + } + }); + + if ((description && description.length > 0 && (showTextOnlyThreads || (!showTextOnlyThreads && suggested?.avatarUrl))) || isInAllView) { + _feed.unshift({ + isDescription: true, + subplebbitAddress: address, + timestamp: createdAt, + author: { displayName: `## ${t('board_mods')}` }, + content: isInAllView ? multisub?.description : description, + link: avatarUrl, + title: t('welcome_to_board', { board: isInAllView ? multisub?.title : title || `p/${shortAddress}`, interpolation: { escapeValue: false } }), + pinned: true, + locked: true, + }); + } + + if (rules && rules.length > 0 && showTextOnlyThreads) { + _feed.unshift({ + isRules: true, + subplebbitAddress: address, + timestamp: createdAt, + author: { displayName: `## ${t('board_mods')}` }, + content: rules.map((rule: string, index: number) => `${index + 1}. ${rule}`).join('\n'), + title: _.capitalize(t('rules')), + pinned: true, + locked: true, + }); + } + + return _feed; + }, [ + accountComments, + feed, + description, + rules, + address, + isFeedLoaded, + createdAt, + title, + shortAddress, + avatarUrl, + t, + isInAllView, + multisub, + showTextOnlyThreads, + suggested?.avatarUrl, + ]); + + const rows = useMemo(() => { + const rows = []; + for (let i = 0; i < feedWithFakePostsOnTop.length; i += columnCount) { + rows.push(feedWithFakePostsOnTop.slice(i, i + columnCount)); + } + return rows; + }, [feedWithFakePostsOnTop, columnCount]); + + return rows; +}; + +export default useCatalogFeedRows; diff --git a/src/views/board/board.tsx b/src/views/board/board.tsx index eb1a87ad..90ef5d96 100644 --- a/src/views/board/board.tsx +++ b/src/views/board/board.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useRef } from 'react'; import { useLocation, useParams } from 'react-router-dom'; -import { useAccount, useBlock, useFeed, useSubplebbit } from '@plebbit/plebbit-react-hooks'; +import { useAccount, useAccountComments, useBlock, useFeed, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso'; import { useTranslation } from 'react-i18next'; import styles from './board.module.css'; @@ -20,6 +20,20 @@ import SubplebbitRules from '../../components/subplebbit-rules'; const lastVirtuosoStates: { [key: string]: StateSnapshot } = {}; +// show account comments instantly in the feed instead of waiting for the next feed update, then hide them when they are in the feed +const AccountCommentsNotYetInFeed = ({ subplebbitAddress, feed }: { subplebbitAddress: string; feed: any }) => { + const { accountComments } = useAccountComments(); + + const _feed = [...feed]; + + const filteredComments = accountComments.filter((comment) => { + const { cid, deleted, postCid, removed } = comment || {}; + return !deleted && !removed && cid && cid === postCid && comment?.subplebbitAddress === subplebbitAddress && !_feed.some((post) => post.cid === cid); + }); + + return filteredComments.map((comment) => ); +}; + const Board = () => { const { t } = useTranslation(); const location = useLocation(); @@ -161,6 +175,7 @@ const Board = () => { title={title} /> )} + {subplebbitAddress && } )} { - const { t } = useTranslation(); - const { address, createdAt, description, rules, shortAddress, suggested, title } = subplebbit || {}; - const { avatarUrl } = suggested || {}; - const { showTextOnlyThreads } = useCatalogFiltersStore(); - - const location = useLocation(); - const isInAllView = isAllView(location.pathname, useParams()); - const multisub = useMultisubMetadata(); - - const feedWithDescriptionAndRules = useMemo(() => { - if (!isFeedLoaded) { - return []; // prevent rules and description from appearing while feed is loading - } - - if (!description && !rules && !isInAllView) { - return feed; - } - - const _feed = [...feed]; - if ((description && description.length > 0 && (showTextOnlyThreads || (!showTextOnlyThreads && suggested?.avatarUrl))) || isInAllView) { - _feed.unshift({ - isDescription: true, - subplebbitAddress: address, - timestamp: createdAt, - author: { displayName: `## ${t('board_mods')}` }, - content: isInAllView ? multisub?.description : description, - link: avatarUrl, - title: t('welcome_to_board', { board: isInAllView ? multisub?.title : title || `p/${shortAddress}`, interpolation: { escapeValue: false } }), - pinned: true, - locked: true, - }); - } - if (rules && rules.length > 0 && showTextOnlyThreads) { - _feed.unshift({ - isRules: true, - subplebbitAddress: address, - timestamp: createdAt, - author: { displayName: `## ${t('board_mods')}` }, - content: rules.map((rule: string, index: number) => `${index + 1}. ${rule}`).join('\n'), - title: _.capitalize(t('rules')), - pinned: true, - locked: true, - }); - } - return _feed; - }, [feed, description, rules, address, isFeedLoaded, createdAt, title, shortAddress, avatarUrl, t, isInAllView, multisub, showTextOnlyThreads, suggested?.avatarUrl]); - - const rows = useMemo(() => { - const rows = []; - for (let i = 0; i < feedWithDescriptionAndRules.length; i += columnCount) { - rows.push(feedWithDescriptionAndRules.slice(i, i + columnCount)); - } - return rows; - }, [feedWithDescriptionAndRules, columnCount]); - - return rows; -}; - const catalogFilter = (comment: Comment) => { const commentMediaInfo = getCommentMediaInfo(comment); const hasThumbnail = getHasThumbnail(commentMediaInfo, comment?.link); @@ -212,7 +152,7 @@ const Catalog = () => { const isFeedLoaded = feed.length > 0 || state === 'failed'; - const rows = useFeedRows(columnCount, feed, isFeedLoaded, subplebbit); + const rows = useCatalogFeedRows(columnCount, feed, isFeedLoaded, subplebbit); // save the last Virtuoso state to restore it when navigating back const virtuosoRef = useRef(null);